` in web development. The `bg` prop resolves through the theme β same lookup rules as `
`.
## Metadata
- Canonical name: `Block`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Block } from '@platform-blocks/react-ui-library';`
- Status: stable
- Since: 1.0.0
- Category: layout
- Tags: layout, building-block, polymorphic, foundational
- Docs: https://react-ui-library.com/components/Block
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Block
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | React.ReactNode | No | | Child elements to render inside the block |
| `component` | React.ElementType | No | | The component to render as |
| `style` | StyleProp | No | | Custom style object |
| `testID` | string | No | | Test ID for testing purposes |
| `accessibilityLabel` | string | No | | Accessibility label |
| `accessible` | boolean | No | | Whether the element is accessible |
| `accessibilityRole` | string | No | | Accessibility role |
| `className` | string | No | | Custom className (for web) |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `bg` | string | No | | Background color for the block |
| `radius` | number \| 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full' | No | | Border radius for rounded corners |
| `borderWidth` | number | No | | Border width |
| `borderColor` | string | No | | Border color |
| `shadow` | number \| 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' | No | | Shadow depth (0-5) |
| `opacity` | number | No | | Opacity (0-1) |
| `w` | number \| string \| 'auto' \| 'full' | No | | Width of the block |
| `h` | number \| string \| 'auto' \| 'full' | No | | Height of the block |
| `fullWidth` | boolean | No | | Whether to take full width (100%) - shorthand for w="full" |
| `fluid` | boolean | No | | Makes block take full available height (flex: 1) - useful for scrollable containers |
| `minW` | number \| string | No | | Minimum width |
| `minH` | number \| string | No | | Minimum height |
| `maxW` | number \| string | No | | Maximum width |
| `maxH` | number \| string | No | | Maximum height |
| `grow` | boolean \| number | No | | Flex grow |
| `shrink` | boolean \| number | No | | Flex shrink |
| `basis` | number \| string | No | | Flex basis |
| `direction` | 'row' \| 'column' \| 'row-reverse' \| 'column-reverse' | No | | Flex direction |
| `align` | 'stretch' \| 'flex-start' \| 'flex-end' \| 'center' \| 'baseline' | No | | Align items |
| `justify` | 'flex-start' \| 'flex-end' \| 'center' \| 'space-between' \| 'space-around' \| 'space-evenly' | No | | Justify content |
| `wrap` | boolean \| 'nowrap' \| 'wrap' \| 'wrap-reverse' | No | | Flex wrap |
| `gap` | number \| 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' | No | | Gap between children. Defaults to `'sm'`; pass `0` to remove it. |
| `position` | 'relative' \| 'absolute' | No | | Position type |
| `top` | number \| string | No | | Top position |
| `right` | number \| string | No | | Right position |
| `bottom` | number \| string | No | | Bottom position |
| `left` | number \| string | No | | Left position |
| `start` | number \| string | No | | Start position (logical property - becomes left in LTR, right in RTL) |
| `end` | number \| string | No | | End position (logical property - becomes right in LTR, left in RTL) |
| `zIndex` | number | No | | Z-index |
| `flex` | boolean | No | | Whether to render as a flex container |
## Examples
### Basic usage
ID: `Block.basic` β’ Tags: layout, polymorphic β’ Category: basics β’ Status: stable β’ Since: 0.3.0
Combine spacing, layout, and polymorphic props on `Block` to build cards, responsive rows, and button-style actions without custom wrappers.
```tsx
return (
Release summary
Apply `bg`, `p`, and `radius` props on `Block` to build a card without custom stylesheets.
Velocity
Use `grow` so sibling Blocks share remaining space.
Backlog
Combine fixed widths with flexible layouts via the `w` prop.
Create project
View roadmap
);
}
```
### bg shorthand
ID: `Block.bg-shorthand` β’ Tags: bg, theme, shorthand, customization β’ Category: general β’ Status: stable β’ Since: 1.0.0
`bg` resolves through the theme. Pass a palette name (`'primary'`, `'success'`) for a subtle tint (shade-1), a `'palette.shade'` like `'primary.6'` for a specific shade, a theme-background key (`'surface'`, `'subtle'`, `'elevated'`), or any CSS color string. The same resolver powers ``.
```tsx
return (
Palette names β subtle tint (shade-1)
primary
success
warning
error
Specific shade with `palette.shade` syntax
primary.6
gray.2
Theme background keys
surface
subtle
Plain CSS color string still works
Custom hex
);
}
```
--------------------------------------------------------------------------------
# Blockquote
The Blockquote component is used to highlight and stylize quotations or important text within your content. It supports various styles and can be customized to fit the design of your application.
## Metadata
- Canonical name: `Blockquote`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Blockquote } from '@platform-blocks/react-ui-library';`
- Category: typography
- Tags: blockquote, text, typography
- Docs: https://react-ui-library.com/components/Blockquote
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Blockquote
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | React.ReactNode | Yes | | Core content |
| `variant` | 'default' \| 'testimonial' \| 'featured' \| 'minimal' | No | | Styling |
| `size` | SizeValue | No | | |
| `color` | string | No | | |
| `quoteIcon` | string \| React.ReactNode | No | | Quote icon |
| `quoteIconPosition` | 'top-left' \| 'top-center' \| 'bottom-right' \| 'none' | No | | |
| `quoteIconSize` | SizeValue | No | | |
| `author` | BlockquoteAuthor | No | | Author attribution |
| `links` | BlockquoteLinks | No | | Social/profile links |
| `date` | Date \| string | No | | Metadata |
| `rating` | BlockquoteRating | No | | |
| `source` | BlockquoteSource | No | | Brand/source |
| `verified` | boolean | No | | Verification |
| `verifiedTooltip` | string | No | | |
| `alignment` | 'left' \| 'center' \| 'right' | No | | Layout |
| `attributionAlignment` | 'left' \| 'center' \| 'right' | No | | Which side the attribution block (avatar, name, source, meta) sits on. Defaults to `'right'`, or `'center'` when `alignment` is `'center'`. |
| `border` | boolean | No | | |
| `shadow` | boolean | No | | |
| `style` | StyleProp | No | | Standard props |
| `onPress` | () => void | No | | |
## Examples
### Pull quote
ID: `Blockquote.basic` β’ Tags: blockquote, testimonial β’ Category: content β’ Status: stable β’ Since: 1.0.0
Frames a simple pull quote with author details.
```tsx
const AUTHOR = {
name: 'Jamie Ortega',
title: 'Principal Product Designer',
};
return (
The Blockquote component keeps editorial typography consistent so our brand voice always feels elevated.
);
}
```
### Testimonial card
ID: `Blockquote.testimonial` β’ Tags: blockquote, testimonial β’ Category: content β’ Status: stable β’ Since: 1.0.0
Full-fidelity testimonial with avatar, organization, rating, verified badge, and shadow.
```tsx
return (
React UI Library helped us ship an entirely new settings experience in a single sprint. The components feel native on every platform.
);
}
```
### Social proof
ID: `Blockquote.social` β’ Tags: blockquote, social β’ Category: content β’ Status: stable β’ Since: 1.0.0
Maps social-style quotes into `Blockquote` with avatars, verification, and network metadata.
```tsx
return (
The future is going to be wild π
Just finished testing the new React UI Library UI library. The component quality and developer experience is outstanding!
This library has saved us countless hours of development time. Clean API, great documentation, and excellent TypeScript support.
);
}
```
### Attribution side
ID: `Blockquote.attribution` β’ Tags: blockquote, attribution, layout β’ Category: content β’ Status: stable β’ Since: 1.0.0
Attribution sits on the right by default. Use `attributionAlignment` to move the avatar, name, and metadata to the left or center it under the quote.
```tsx
return (
Right (default)
{QUOTE}
Left
{QUOTE}
);
}
```
### Variants overview
ID: `Blockquote.variants` β’ Tags: blockquote, variants β’ Category: content β’ Status: stable β’ Since: 1.0.0
Renders each preset to compare layout, alignment, and metadata options.
```tsx
return (
Default
The best way to predict the future is to create it.
Testimonial
Great experience with this service. The team was professional and delivered quality results.
Featured
Imagination is more important than knowledge.
Minimal
Just discovered this amazing new feature! π
);
}
```
--------------------------------------------------------------------------------
# BrandButton
The BrandButton component renders a branded pressable for any platform in the brand icon registry. By default it's a single-line button supporting variants, sizes, icons, loading state, and full-width layout. Pass `primaryText` and `secondaryText` instead of `title` and it renders the two-line store-badge layout ("Download on the / App Store").
## Metadata
- Canonical name: `BrandButton`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { BrandButton } from '@platform-blocks/react-ui-library';`
- Category: input
- Tags: action, pressable, interactive, badge, app-store
- Docs: https://react-ui-library.com/components/BrandButton
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/BrandButton
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `brand` | BrandPlatform | Yes | | The brand/platform to style the button for |
| `iconPosition` | 'left' \| 'right' | No | | Position of the brand icon |
| `iconVariant` | 'full' \| 'mono' | No | | Icon variant: 'full' for multi-color, 'mono' for single-color outline |
| `icon` | React.ReactNode | No | | Override the default brand icon |
| `title` | string | No | | Button text. Omit when rendering a store badge. |
| `color` | string | No | | Override icon color (overrides brand default colors) |
| `primaryText` | string | No | | Badge lead-in line, e.g. "Download on the" / "Listen on". Supplying this or `secondaryText` switches the component to the two-line store-badge layout, where `variant`, `loading`, `fullWidth` and the spacing props do not apply. |
| `secondaryText` | string | No | | Badge headline, e.g. "App Store" / "Spotify" |
| `backgroundColor` | string | No | | Badge shell background (badge layout only) |
| `borderColor` | string | No | | Badge shell border color (badge layout only) |
| `darkMode` | boolean | No | | Force the badge's dark-mode styling instead of following the theme |
| `key` | React.Key | No | | allow React key without complaint in TS where JSX key is forwarded in type checking |
| `children` | React.ReactNode | No | | Button text content - alternative to title prop |
| `onPress` | () => void | No | | Called when the button is pressed |
| `onPressIn` | () => void | No | | Called when the button press starts (for immediate feedback) |
| `onPressOut` | () => void | No | | Called when the button press ends |
| `onHoverIn` | () => void | No | | Called when the button is hovered (web/desktop only) |
| `onHoverOut` | () => void | No | | Called when the button is no longer hovered (web/desktop only) |
| `onLongPress` | () => void | No | | Called when the button is long-pressed |
| `onLayout` | (event: any) => void | No | | Called when the button layout is calculated |
| `variant` | 'default' \| 'filled' \| 'light' \| 'subtle' \| 'secondary' \| 'outline' \| 'ghost' \| 'gradient' \| 'link' \| 'none' | No | 'default' | Button visual variant. `default` is a neutral button β the card surface with a hairline border and body text β so an unstyled `` never claims the accent color. A solid primary fill is opt-in via `filled`. |
| `size` | SizeValue | No | | Button size |
| `disabled` | boolean | No | | Whether the button is disabled |
| `loading` | boolean | No | | Whether button is in loading state (shows loader) |
| `loadingTitle` | string | No | | Text to show when loading (if not provided, shows empty text but maintains original width) |
| `fullWidth` | boolean | No | | Whether button should fill the full width of its parent container. Buttons size to their content by default; `fullWidth`, an explicit `w`, or a flex value in `style` makes them fill instead. |
| `textColor` | string | No | | Explicit text color override (else derived automatically from variant & color) |
| `tooltip` | TooltipPropValue | No | | Tooltip shown on hover/focus β wraps the button in a `Tooltip`. Pass a string for the common case, or a config object to tune the tooltip: `tooltip={{ label: 'Long explanationβ¦', maxWidth: 320, withArrow: true }}`. |
| `tooltipPosition` | TooltipProps['position'] | No | | Tooltip position when the string form of `tooltip` is used |
| `transitionDuration` | number | No | 110 | Length of the press / pulse / hover transitions in ms. `0` applies each state instantly (no scale animation). Always 0 under reduced motion. |
| `style` | any | No | | Style overrides for the button container |
| `testID` | string | No | | Test ID for testing library queries |
| `accessibilityLabel` | string | No | | Accessibility label for screen readers |
| `accessibilityHint` | string | No | | Accessibility hint for screen readers |
| `labelProps` | Omit | No | | Override props applied to the inner label `` (style, weight, ff, size, color). |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
| `shadow` | ShadowValue | No | | Shadow value - supports size tokens and 'none' |
## Examples
### BrandButton
ID: `BrandButton.basic` β’ Tags: basic, getting-started β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Basic Button usage with a title prop.
```tsx
const toast = useToast()
return toast.warn({
title: 'What the Zuck!',
message: 'This is a Facebook brand button',
position: 'top-center'
})}
/>
}
```
### BrandButton
ID: `BrandButton.colorOverride` β’ Tags: basic, getting-started β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Basic Button usage with a title prop.
```tsx
return (
Brand Icon Color Override
Use the color prop to override default brand colors with a single color
Default Colors:
Custom Color (#666666):
Red Override (#E53E3E):
Outline Variant with Color Override:
);
}
```
### Universal Props Demo
ID: `BrandButton.universalProps` β’ Tags: basic, getting-started β’ Category: basics β’ Status: stable β’ Since: 1.0.0
These components demonstrate universal props like `lightHidden` and `darkHidden` that work across the entire library. Toggle dark/light mode or resize the window.
```tsx
return (
Light Mode Only (darkHidden):
Dark Mode Only (lightHidden):
Responsive Visibility:
Combined Props:
);
}
```
### Store badges
ID: `BrandButton.badge` β’ Tags: badge, app-store, store β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Supplying `primaryText` and `secondaryText` switches BrandButton from the single-line button to the two-line store-badge layout. Any brand in the icon registry works, and the shell defaults to black until `backgroundColor` says otherwise.
```tsx
const toast = useToast();
const announce = (store: string) =>
toast.info({
title: `${store} pressed`,
message: `Wire onPress up to Linking.openURL with your ${store} listing.`,
autoHide: 3000,
});
return (
{/* Passing primaryText/secondaryText switches BrandButton to the badge layout. */}
announce('App Store')}
/>
announce('Google Play')}
/>
{/* Badges default to a black shell whatever the brand β `backgroundColor` opts one out. */}
announce('Spotify')}
/>
);
}
```
### Badge sizes
ID: `BrandButton.badgeSizes` β’ Tags: badge, size β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Every badge metric β padding, icon, radius, height β is derived from the size token's headline type, so the proportions hold from `xs` through `3xl`.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const;
return (
{SIZES.map((size) => (
{size}
))}
);
}
```
### Badge colors
ID: `BrandButton.badgeColors` β’ Tags: badge, color, theming β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Override the badge shell with `backgroundColor`, `textColor`, and `borderColor` to match your brand or design requirements.
```tsx
return (
console.log('GitHub light pressed')}
/>
console.log('Spotify custom pressed')}
/>
);
}
```
--------------------------------------------------------------------------------
# BrandIcon
Common brand logos rendered as SVG
## Metadata
- Canonical name: `BrandIcon`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { BrandIcon } from '@platform-blocks/react-ui-library';`
- Category: typography
- Tags: action, pressable, interactive
- Docs: https://react-ui-library.com/components/BrandIcon
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/BrandIcon
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `brand` | BrandName | Yes | | Brand name from the registry. camelCase names are deprecated aliases. |
| `size` | SizeValue | No | | Size of the icon |
| `color` | string | No | | Override all colors with a single color |
| `variant` | 'full' \| 'mono' | No | | Icon variant - 'full' for multi-color, 'mono' for single-color with clipping |
| `style` | StyleProp | No | | Additional styles |
| `label` | string | No | | Accessibility label |
| `decorative` | boolean | No | | Whether the icon is purely decorative (skip a11y) |
| `invertInDarkMode` | boolean | No | | Whether to automatically invert black colors in dark mode |
| `colorScheme` | 'light' \| 'dark' | No | | Force color scheme for testing (overrides automatic detection) |
## Examples
### Overview
ID: `BrandIcon.basic` β’ Tags: overview, gallery β’ Category: basics β’ Status: stable β’ Since: 0.3.0
High-quality brand icons with multi-color support, mono variants, and automatic dark mode theming.
```tsx
return (
{SAMPLE_BRANDS.map((brand) => (
))}
);
}
```
### Colors & Mono
ID: `BrandIcon.colors` β’ Tags: colors, branding, mono, custom-colors β’ Category: theming β’ Status: stable β’ Since: 0.3.0
Authentic brand palettes plus custom single-color overrides β passing `color` implies `variant="mono"`, so the two never need to be set together.
```tsx
return (
Authentic brand palettes
Custom blue
Custom red
);
}
```
### Sizes
ID: `BrandIcon.sizes` β’ Tags: sizes, layout β’ Category: layout β’ Status: stable β’ Since: 0.3.0
Size presets from small through extra large for consistent placement.
```tsx
const SIZES: BrandIconProps['size'][] = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'];
return (
{SIZES.map((size) => (
{String(size)}
))}
);
}
```
### Dark Mode Support
ID: `BrandIcon.dark-mode` β’ Tags: dark-mode, accessibility β’ Category: theming β’ Status: stable β’ Since: 0.3.0
Supported logos automatically invert for dark themes.
```tsx
return (
{DARK_MODE_BRANDS.map((brand) => (
))}
);
}
```
### All Available Brands
ID: `BrandIcon.all-brands` β’ Tags: catalog, brands β’ Category: reference β’ Status: stable β’ Since: 0.3.0
Complete collection of every supported brand icon, laid out with `Grid` so the column count adapts from 3 on narrow screens up to 8 on wide ones.
```tsx
return (
{ALL_BRANDS.map((brand) => (
{brand}
))}
);
}
```
--------------------------------------------------------------------------------
# Breadcrumbs
The Breadcrumbs component displays hierarchical navigation links to help users understand their current location within the application. Item labels and string separators each accept the full `` API via `labelProps` / `separatorProps`.
## Metadata
- Canonical name: `Breadcrumbs`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Breadcrumbs } from '@platform-blocks/react-ui-library';`
- Category: navigation
- Tags: navigation, breadcrumb, path, hierarchy
- Docs: https://react-ui-library.com/components/Breadcrumbs
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Breadcrumbs
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `items` | BreadcrumbItem[] | Yes | | Array of breadcrumb items |
| `separator` | ReactNode | No | '/' | Custom separator between breadcrumbs (string, icon, or any React component) |
| `maxItems` | number | No | | Maximum number of items to show (will collapse middle items) |
| `size` | ComponentSizeValue | No | 'md' | Size of the breadcrumbs |
| `showIcons` | boolean | No | true | Whether to show icons |
| `style` | StyleProp | No | | Custom styles |
| `textStyle` | StyleProp | No | | Custom text styles |
| `separatorStyle` | StyleProp | No | | Custom separator styles |
| `accessibilityLabel` | string | No | 'Breadcrumb navigation' | Accessibility label |
| `labelProps` | Omit | No | | Override props applied to each item's label `` (style, weight, ff, size, color). |
| `separatorProps` | Omit | No | | Override props applied to the separator `` when it's a string. |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Hierarchy
ID: `Breadcrumbs.basic` β’ Tags: breadcrumbs β’ Category: navigation β’ Status: stable β’ Since: 1.0.0
Simple breadcrumb trail showing the current page within a product hierarchy.
```tsx
const ITEMS = [
{ label: 'Home', href: '/' },
{ label: 'Products', href: '/products' },
{ label: 'Electronics', href: '/products/electronics' },
{ label: 'Smartphones' },
];
return ;
}
```
### Separators
ID: `Breadcrumbs.separators` β’ Tags: breadcrumbs β’ Category: navigation β’ Status: stable β’ Since: 1.0.0
Shows how to replace the default slash with characters or React nodes via the `separator` prop.
```tsx
const ITEMS = [
{ label: 'Home', href: '/' },
{ label: 'Category', href: '/category' },
{ label: 'Subcategory', href: '/category/subcategory' },
{ label: 'Product' },
];
return (
} />
);
}
```
--------------------------------------------------------------------------------
# Button
The Button component provides a flexible interactive element supporting variants, sizes, icons, loading state, and full-width layout. The inner label `` accepts the full Text-prop API via `labelProps` (`ff`, `weight`, `tracking`, `uppercase`, `color`, `style`).
Buttons default to the `default` variant β a neutral button (card surface, hairline border, body text) that sizes to its content. Reach for `variant="filled"` on the primary action of a view, and `fullWidth` (or `w`) when the button should span its container.
## Metadata
- Canonical name: `Button`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Button } from '@platform-blocks/react-ui-library';`
- Category: input
- Tags: action, pressable, interactive
- Docs: https://react-ui-library.com/components/Button
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Button
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `key` | React.Key | No | | allow React key without complaint in TS where JSX key is forwarded in type checking |
| `title` | string | No | | Button text content - can be provided via title prop or children |
| `children` | React.ReactNode | No | | Button text content - alternative to title prop |
| `onPress` | () => void | No | | Called when the button is pressed |
| `onPressIn` | () => void | No | | Called when the button press starts (for immediate feedback) |
| `onPressOut` | () => void | No | | Called when the button press ends |
| `onHoverIn` | () => void | No | | Called when the button is hovered (web/desktop only) |
| `onHoverOut` | () => void | No | | Called when the button is no longer hovered (web/desktop only) |
| `onLongPress` | () => void | No | | Called when the button is long-pressed |
| `onLayout` | (event: any) => void | No | | Called when the button layout is calculated |
| `variant` | 'default' \| 'filled' \| 'light' \| 'subtle' \| 'secondary' \| 'outline' \| 'ghost' \| 'gradient' \| 'link' \| 'none' | No | 'default' | Button visual variant. `default` is a neutral button β the card surface with a hairline border and body text β so an unstyled `` never claims the accent color. A solid primary fill is opt-in via `filled`. |
| `color` | string | No | | Theme color the button is tinted with. A palette token (`primary`, `success`, `error`, β¦) or any raw CSS/hex color. Applies to the color-bearing variants (`filled`, `light`, `subtle`, `outline`, `gradient`) and to the text of `ghost`/`link`. Defaults to `primary`. `secondary` stays neutral by design. |
| `size` | SizeValue | No | | Button size |
| `disabled` | boolean | No | | Whether the button is disabled |
| `loading` | boolean | No | | Whether button is in loading state (shows loader) |
| `loadingTitle` | string | No | | Text to show when loading (if not provided, shows empty text but maintains original width) |
| `fullWidth` | boolean | No | | Whether button should fill the full width of its parent container. Buttons size to their content by default; `fullWidth`, an explicit `w`, or a flex value in `style` makes them fill instead. |
| `textColor` | string | No | | Explicit text color override (else derived automatically from variant & color) |
| `icon` | React.ReactNode | No | | Icon to show in the center (for icon-only buttons) |
| `startIcon` | React.ReactNode | No | | Icon to show on the left side of the button |
| `endIcon` | React.ReactNode | No | | Icon to show on the right side of the button |
| `tooltip` | TooltipPropValue | No | | Tooltip shown on hover/focus β wraps the button in a `Tooltip`. Pass a string for the common case, or a config object to tune the tooltip: `tooltip={{ label: 'Long explanationβ¦', maxWidth: 320, withArrow: true }}`. |
| `tooltipPosition` | TooltipProps['position'] | No | | Tooltip position when the string form of `tooltip` is used |
| `transitionDuration` | number | No | 110 | Length of the press / pulse / hover transitions in ms. `0` applies each state instantly (no scale animation). Always 0 under reduced motion. |
| `style` | any | No | | Style overrides for the button container |
| `testID` | string | No | | Test ID for testing library queries |
| `accessibilityLabel` | string | No | | Accessibility label for screen readers |
| `accessibilityHint` | string | No | | Accessibility hint for screen readers |
| `labelProps` | Omit | No | | Override props applied to the inner label `` (style, weight, ff, size, color). |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
| `shadow` | ShadowValue | No | | Shadow value - supports size tokens and 'none' |
## Examples
### Basics
ID: `Button.basic` β’ Tags: buttons β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Invokes a primary action and surfaces feedback through a toast helper.
```tsx
const toast = useToast();
return (
toast.success('Launch command sent')}
/>
toast.error('Sequence aborted')}
/>
);
}
```
### Colors
ID: `Button.colors` β’ Tags: buttons, colors β’ Category: styling β’ Status: stable β’ Since: 1.0.0
Pair `color` with a color-bearing variant (`filled`, `light`, `subtle`, `outline`, `gradient`) to align actions with brand intent. The default variant is neutral chrome and ignores `color`.
```tsx
return (
Primary
Secondary
Success
Warning
Error
);
}
```
### Loading state
ID: `Button.loading` β’ Tags: buttons, loading β’ Category: feedback β’ Status: stable β’ Since: 1.0.0
Demonstrates consistent width preservation, custom `loadingTitle`, and disabling actions while background work completes.
```tsx
const LOADING_DURATION_MS = 2000;
const timeoutRef = useRef | null>(null);
const [activeKey, setActiveKey] = useState(null);
useEffect(() => () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
}, []);
const triggerLoading = (key: string) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
setActiveKey(key);
timeoutRef.current = setTimeout(() => {
setActiveKey(null);
timeoutRef.current = null;
}, LOADING_DURATION_MS);
};
return (
triggerLoading('default')}>
Submit application
triggerLoading('custom')}
>
Submit application
triggerLoading('disabled')}
>
Submit application
);
}
```
### Variants
ID: `Button.variants` β’ Tags: buttons, variants β’ Category: styling β’ Status: stable β’ Since: 1.0.0
Preview the available button variants to match the desired emphasis level.
```tsx
return (
Default
Filled
Light
Subtle
Secondary
Outline
Gradient
Ghost
Text only
);
}
```
### Sizes
ID: `Button.sizes` β’ Tags: buttons, sizes β’ Category: styling β’ Status: stable β’ Since: 1.0.0
Preview the available button size tokens for different density requirements.
```tsx
return (
Small
Medium
Large
Extra large
);
}
```
### Localized labels
ID: `Button.localized-basic` β’ Tags: buttons, i18n β’ Category: accessibility β’ Status: stable β’ Since: 1.0.0
Switch locales at runtime and render translated button copy with `useI18n` helpers.
```tsx
const LOCALES = [
{ label: 'English', value: 'en' },
{ label: 'EspaΓ±ol', value: 'es' },
{ label: 'FranΓ§ais', value: 'fr' },
];
const { t, locale, setLocale } = useI18n();
return (
{ if (value) setLocale(value); }}
/>
);
}
```
### Tooltips
ID: `Button.tooltip` β’ Tags: buttons, tooltip β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Add contextual hints to buttons with `tooltip` and optional placement overrides.
```tsx
return (
Save
Delete
Help
Download
} tooltip="Open the settings panel." accessibilityLabel="Open settings" />
Upload
);
}
```
### Width
ID: `Button.width` β’ Tags: buttons, layout β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Demonstrates fixed, percentage, and full-width buttons alongside loading states that preserve dimensions.
```tsx
const LOADING_DURATION_MS = 2000;
const timeoutRef = useRef | null>(null);
const [loadingKey, setLoadingKey] = useState(null);
useEffect(() => () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
}, []);
const triggerLoading = (key: string) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
setLoadingKey(key);
timeoutRef.current = setTimeout(() => {
setLoadingKey(null);
timeoutRef.current = null;
}, LOADING_DURATION_MS);
};
return (
Default width
Buttons size themselves to the label length by default.
Fixed width (200)
Provide an exact `w` value for pixel-perfect toolbars.
triggerLoading('long')}
>
Preserve width while loading
triggerLoading('short')}>
Short text
When `loading` is true, the button keeps its original width so layouts stay stable.
);
}
```
--------------------------------------------------------------------------------
# Calendar
A versatile calendar component for selecting dates, months, and years with customizable styles and behaviors.
## Metadata
- Canonical name: `Calendar`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Calendar } from '@platform-blocks/react-ui-library';`
- Category: dates
- Docs: https://react-ui-library.com/components/Calendar
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Calendar
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `level` | CalendarLevel | No | | View control |
| `defaultLevel` | CalendarLevel | No | 'month' | |
| `onLevelChange` | (level: CalendarLevel) => void | No | | |
| `date` | Date | No | | Date management |
| `defaultDate` | Date | No | | |
| `onDateChange` | (date: Date) => void | No | | |
| `value` | CalendarValue | No | | Value handling (for selection) |
| `onChange` | (value: CalendarValue) => void | No | | |
| `type` | CalendarType | No | 'single' | |
| `minDate` | Date | No | | Constraints |
| `maxDate` | Date | No | | |
| `excludeDate` | (date: Date) => boolean | No | | |
| `locale` | string | No | | Localization |
| `firstDayOfWeek` | 0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 | No | 0 | |
| `weekendDays` | number[] | No | [0, 6] | |
| `withCellSpacing` | boolean | No | | Display options |
| `hideOutsideDates` | boolean | No | false | |
| `hideWeekdays` | boolean | No | false | |
| `highlightToday` | boolean | No | true | |
| `numberOfMonths` | number | No | 1 | |
| `getDayProps` | (date: Date) => Partial | No | | Customization |
| `renderDay` | (date: Date) => React.ReactNode | No | | |
| `size` | SizeValue | No | 'md' | |
| `fullWidth` | boolean | No | false | Stretch to fill the container instead of sizing to the day grid. Default `false`. |
| `static` | boolean | No | | Static mode (non-interactive) |
## Examples
### Basic selection
ID: `Calendar.basic` β’ Tags: single, selection β’ Category: basics β’ Status: stable β’ Since: 0.3.0
Bind `value` and `onChange` to local state to capture the selected day while `highlightToday` keeps the current date visually distinct.
```tsx
const formatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' });
const [selectedDate, setSelectedDate] = useState(new Date());
return (
setSelectedDate(date as Date | null)}
highlightToday
/>
Selected date: {selectedDate ? formatter.format(selectedDate) : 'none'}
);
}
```
### Date constraints
ID: `Calendar.constrained` β’ Tags: min-date, max-date β’ Category: rules β’ Status: stable β’ Since: 0.3.0
Set `minDate` and `maxDate` to keep navigation inside the current month while still allowing the calendar to show surrounding weeks.
```tsx
const monthFormatter = new Intl.DateTimeFormat('en-US', { month: 'long', year: 'numeric' });
const dateFormatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' });
const [selectedDate, setSelectedDate] = useState(new Date());
const { minDate, maxDate, monthLabel } = useMemo(() => {
const today = new Date();
const start = new Date(today.getFullYear(), today.getMonth(), 1);
const end = new Date(today.getFullYear(), today.getMonth() + 1, 0);
return { minDate: start, maxDate: end, monthLabel: monthFormatter.format(start) };
}, []);
return (
setSelectedDate(date as Date | null)}
minDate={minDate}
maxDate={maxDate}
highlightToday
/>
Selected date: {selectedDate ? dateFormatter.format(selectedDate) : 'none'}
Only dates in {monthLabel} are enabled.
);
}
```
### Multiple selection
ID: `Calendar.multiple` β’ Tags: multiple, events β’ Category: selection β’ Status: stable β’ Since: 0.3.0
Switch `type="multiple"` to let teammates flag several event days at once; the component returns an array you can format for summaries or badges.
```tsx
const formatter = new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric' });
const [selectedDates, setSelectedDates] = useState([]);
const summary = useMemo(() => {
if (selectedDates.length === 0) return 'No dates picked yet.';
if (selectedDates.length === 1) {
return `1 date picked: ${formatter.format(selectedDates[0])}`;
}
return `${selectedDates.length} dates picked: ${selectedDates.map((date) => formatter.format(date)).join(', ')}`;
}, [selectedDates]);
return (
setSelectedDates(dates as Date[])}
highlightToday
/>
{summary}
);
}
```
### Range selection
ID: `Calendar.range` β’ Tags: range, bookings β’ Category: selection β’ Status: stable β’ Since: 0.3.0
Use `type="range"` to capture a start and end date for bookings or sprints; the component returns a tuple you can translate into summaries or validation.
```tsx
const formatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' });
const [selectedRange, setSelectedRange] = useState<[Date | null, Date | null]>([null, null]);
const summary = useMemo(() => {
const [start, end] = selectedRange;
if (!start) {
return 'No dates selected yet.';
}
if (!end) {
return `Start date chosen: ${formatter.format(start)} β pick an end date.`;
}
return `${formatter.format(start)} β ${formatter.format(end)}`;
}, [selectedRange]);
return (
setSelectedRange(range as [Date | null, Date | null])}
highlightToday
/>
{summary}
);
}
```
--------------------------------------------------------------------------------
# Card
The Card component provides a flexible container for displaying content. Six variants (`filled`, `outline`, `elevated`, `subtle`, `ghost`, `gradient`) each set their own background + default shadow. `withBorder`, `borderColor`, `borderWidth`, and `bg` compose on top of any variant, so you can mix and match (` `) without forking a new variant for every combination.
## Metadata
- Canonical name: `Card`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Card } from '@platform-blocks/react-ui-library';`
- Category: display
- Tags: card, container, content, layout
- Docs: https://react-ui-library.com/components/Card
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Card
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | React.ReactNode | No | | children optional to reduce noisy TS errors during composition |
| `variant` | 'outline' \| 'filled' \| 'elevated' \| 'subtle' \| 'ghost' \| 'gradient' | No | | Visual variant. Each variant sets its own background + default shadow. - `filled` (default) β surface background - `outline` β transparent + border - `elevated` β surface with a stronger shadow - `subtle` β subtle background + soft border - `ghost` β transparent until pressed - `gradient` β primary-palette gradient overlay |
| `withBorder` | boolean | No | | Add a 1px border on top of *any* variant. Composes with `variant="elevated"` etc. without forcing you into the `outline` variant. |
| `borderColor` | string | No | | Custom border color. When set, implies `withBorder` if `borderWidth` isn't 0. |
| `borderWidth` | number | No | | Custom border width in px. Defaults to 1 when `withBorder` or `borderColor` is set. |
| `clip` | boolean | No | | Clip children to the card's radius. Turn this on when a `Card.Section` carries full-bleed content (image, code surface) that would otherwise square off the card's rounded corners. Off by default so overlays that escape the card β menus, popovers, tooltips β keep working. |
| `bg` | string | No | | Background color override. Accepts any CSS color string or a theme color palette name (`'primary' \| 'secondary' \| 'gray' \| 'success' \| 'warning' \| 'error'`), which resolves to that palette's shade-1 (subtle tint). |
| `padding` | SizeValue | No | | Internal padding. Accepts a size token (`'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl' \| '3xl'`) or a pixel number. |
| `style` | any | No | | |
| `onPress` | () => void | No | | Interactive props |
| `disabled` | boolean | No | | |
| `onContextMenu` | (e: any) => void | No | | Web-only events passthrough |
| `testID` | string | No | | |
| `accessibilityRole` | AccessibilityRole | No | | Accessibility role forwarded to the underlying element. Pressable cards default to a button; set `'link'` when the card navigates. |
| `accessibilityLabel` | string | No | | Accessibility label forwarded to the underlying element. |
| `accessibilityState` | AccessibilityState | No | | Accessibility state forwarded to the underlying element. Required for the selectable roles β a card with `accessibilityRole="radio"` or `"checkbox"` has to announce its `checked` state or screen readers read every option as unselected. |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
| `shadow` | ShadowValue | No | | Shadow value - supports size tokens and 'none' |
## Examples
### Basics
ID: `Card.basic` β’ Tags: content β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Compose card content with spacing primitives and a primary action for quick scenarios.
```tsx
return (
Upcoming match
Falcons at Bears
Kickoff is set for 7:30 PM with rain in the forecast. Review the lineup and
travel logistics before departure.
{}}>
View itinerary
);
}
```
### Variants
ID: `Card.variants` β’ Tags: surface β’ Category: theming β’ Status: stable β’ Since: 1.0.0
Tour the available card `variant` treatments to pick the right surface style for your layout.
```tsx
const VARIANTS = ['filled', 'outline', 'elevated', 'subtle', 'ghost', 'gradient'] as const;
return (
{VARIANTS.map((variant) => (
{String(variant).toUpperCase()} variant
Apply the {variant} treatment to match surface contrast needs.
))}
);
}
```
--------------------------------------------------------------------------------
# Carousel
The Carousel component displays a series of content in a horizontal scrollable view with optional navigation dots and controls.
## Metadata
- Canonical name: `Carousel`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Carousel } from '@platform-blocks/react-ui-library';`
- Category: display
- Tags: carousel, slider, gallery, swipe
- Docs: https://react-ui-library.com/components/Carousel
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Carousel
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | React.ReactNode[] | Yes | | Array of carousel slide elements |
| `orientation` | 'horizontal' \| 'vertical' | No | | Orientation of the carousel |
| `showArrows` | boolean | No | | Show navigation arrow buttons |
| `showDots` | boolean | No | | Show navigation dots |
| `autoPlay` | boolean | No | | Enable autoplay |
| `autoPlayInterval` | number | No | | Autoplay interval in ms |
| `autoPlayPauseOnTouch` | boolean | No | | Pause autoplay on user interaction |
| `loop` | boolean | No | | Enable looping |
| `itemsPerPage` | number | No | | Number of visible items per page |
| `slidesToScroll` | number | No | | Number of slides to advance per snap (defaults to itemsPerPage for backwards compatibility) |
| `align` | 'start' \| 'center' \| 'end' | No | | Align the visible slides within the viewport when there is extra space |
| `containScroll` | false \| 'trimSnaps' \| 'keepSnaps' | No | | Contain leading/trailing space by trimming or keeping snap points |
| `startIndex` | number | No | | Initial slide index to show on mount |
| `dragFree` | boolean | No | | Allow momentum scrolling without forced snaps |
| `skipSnaps` | boolean | No | | Permit gestures to skip over multiple snap points (default true) |
| `dragThreshold` | number | No | | Drag distance (in px) required before a swipe is committed |
| `duration` | number | No | | Duration (ms) for programmatic scroll animations |
| `transitionDuration` | number | No | | Slide transition length in ms. Cross-component spelling that takes precedence over `duration`; `0` jumps between slides with no animation (and also stills the pagination dots). |
| `breakpoints` | Record> | No | | Embla-style breakpoint overrides applied via media queries |
| `slideSize` | number \| string \| { base?: number \| string; xs?: number \| string; sm?: number \| string; md?: number \| string; lg?: number \| string; xl?: number \| string; } | No | | Explicit slide size. Accepts: - percentage string: e.g. "70%" - fraction (0..1) number: 0.7 -> 70% of container - absolute pixel number (>1) When provided it overrides width derived from itemsPerPage. itemsPerPage still controls cloning + pagination grouping. |
| `slideGap` | ResponsiveSize | No | | Responsive gap between slides (overrides itemGap). Accepts spacing token string or number or responsive map. |
| `itemGap` | number | No | | Gap between slides in pixels |
| `height` | number | No | | Fixed height of the carousel container |
| `onSlideChange` | (index: number) => void | No | | Callback fired when the active slide changes |
| `style` | StyleProp | No | | Style override for the carousel container |
| `itemStyle` | StyleProp | No | | Style override applied to each slide item |
| `snapToItem` | boolean | No | | Enable snapping to individual items |
| `arrowPosition` | 'inside' \| 'outside' | No | | Position of navigation arrows relative to the carousel |
| `arrowSize` | ComponentSizeValue | No | | Size of the navigation arrow buttons |
| `dotSize` | ComponentSizeValue | No | | Size of the navigation dots |
| `scrollEnabled` | boolean | No | | Enable or disable scroll gestures |
| `reducedMotion` | boolean | No | | Disable animated width/color transitions for dots and snapping |
| `windowSize` | number | No | | Number of logical pages to render for virtualization |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic usage
ID: `Carousel.basic` β’ Tags: auto-play, loop β’ Category: basics β’ Status: stable β’ Since: 0.3.0
Enable `autoPlay` and `loop` on `Carousel` to rotate a small set of slides without custom pagination controls.
```tsx
const slides = ['#4C1D95', '#155E75', '#166534'];
return (
{slides.map((bg, index) => (
Slide {index + 1}
))}
);
}
```
### Vertical orientation
ID: `Carousel.vertical` β’ Tags: vertical, navigation β’ Category: layout β’ Status: stable β’ Since: 0.3.0
Set `orientation="vertical"` to rotate content along the Y-axis while keeping arrow and dot controls aligned for keyboard and touch users. Vertical carousels size to their container, so give the carousel an explicit height.
```tsx
const slides = ['#DC2626', '#2563EB', '#0F766E'];
return (
{slides.map((bg, index) => (
Slide {index + 1}
))}
);
}
```
### Image overlay
ID: `Carousel.imageOverlay` β’ Tags: hero, overlay β’ Category: media β’ Status: stable β’ Since: 0.3.0
Layer an absolutely positioned `Block` with a semi-transparent `bg` on top of each slide to keep text and buttons readable on photography.
```tsx
const scenes = [
{ title: 'Mountain escape', src: require('../../../../assets/images/scene-mountains.png') },
{ title: 'Forest retreat', src: require('../../../../assets/images/scene-forest.png') },
{ title: 'Desert journey', src: require('../../../../assets/images/scene-desert.png') },
];
return (
{scenes.map(({ title, src }) => (
{title}
))}
);
}
```
### Multiple slides
ID: `Carousel.multi` β’ Tags: responsive, items-per-page β’ Category: layout β’ Status: stable β’ Since: 0.3.0
Combine `itemsPerPage` with the Embla-style `breakpoints` prop to show more slides as the viewport grows. Keep `slidesToScroll={1}` so only one card advances at a time, even when desktop layouts show multiple slides side-by-side.
```tsx
const slides = ['#1D4ED8', '#0F766E', '#C026D3', '#B45309', '#7C3AED'];
return (
{slides.map((bg, index) => (
Slide {index + 1}
))}
);
}
```
### Performance tuning
ID: `Carousel.performance` β’ Tags: virtualization, reduced-motion β’ Category: performance β’ Status: stable β’ Since: 0.3.0
Pair `windowSize` with `reducedMotion` to keep large or data-heavy carousels responsive while still exposing arrow navigation.
```tsx
const slides = ['#1E3A8A', '#047857', '#9333EA', '#B91C1C', '#B45309', '#0F766E'];
return (
{slides.map((bg, index) => (
Slide {index + 1}
))}
);
}
```
### Drag & motion
ID: `Carousel.motionControls` β’ Tags: drag-free, motion β’ Category: behavior β’ Status: stable β’ Since: 0.4.0
Tune the interaction model with `dragFree`, `skipSnaps`, `dragThreshold`, and `duration` to match Embla-style motion control.
```tsx
function slides(colors: string[]) {
return colors.map((bg, index) => (
Slide {index + 1}
));
}
return (
Free momentum (dragFree)
{slides(['#0EA5E9', '#6366F1', '#8B5CF6', '#A855F7'])}
Locked snaps (skipSnaps off)
{slides(['#F97316', '#EA580C', '#C2410C', '#9A3412'])}
);
}
```
--------------------------------------------------------------------------------
# Checkbox
The Checkbox component allows users to select one or more options from a set. Supports different states, colors, and group functionality.
## Metadata
- Canonical name: `Checkbox`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Checkbox } from '@platform-blocks/react-ui-library';`
- Category: input
- Tags: checkbox, input, form, selection, toggle
- Docs: https://react-ui-library.com/components/Checkbox
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Checkbox
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `checked` | boolean | No | | Whether checkbox is checked |
| `defaultChecked` | boolean | No | false | Initial checked value for uncontrolled usage |
| `onChange` | (checked: boolean) => void | No | | Change handler |
| `indeterminate` | boolean | No | false | Indeterminate state for partial selections |
| `color` | string | No | | Indicator color. A palette token (`'success'`), `'primary.6'` shade syntax, or any CSS color. |
| `size` | SizeValue | No | 'md' | Checkbox size |
| `label` | React.ReactNode | No | | Checkbox label |
| `disabled` | boolean | No | false | Whether checkbox is disabled |
| `required` | boolean | No | false | Whether checkbox is required |
| `error` | string | No | | Error message |
| `description` | string | No | | Helper text |
| `icon` | React.ReactNode | No | | Icon to show when checked |
| `indeterminateIcon` | React.ReactNode | No | | Icon to show when indeterminate |
| `labelPosition` | 'left' \| 'right' \| 'top' \| 'bottom' | No | 'right' | Label position relative to checkbox |
| `labelProps` | Omit | No | | Override styles/props applied to the label `` |
| `descriptionProps` | Omit | No | | Override styles/props applied to the description `` |
| `transitionDuration` | number | No | 160 | Length of the check/uncheck animation in ms; the fill and mark phases scale against it. `0` applies the state instantly. Always 0 under reduced motion. |
| `children` | React.ReactNode | No | | Checkbox content/children (alternative to label) |
| `accessibilityLabel` | string | No | | Accessibility label, used when there is no visible text label |
| `testID` | string | No | | Component test ID for testing |
| `style` | any | No | | Additional CSS styles |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basics
ID: `Checkbox.basic` β’ Tags: checkboxes β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Controlled checkbox example with a helper message that reacts to user selection.
```tsx
const [checked, setChecked] = useState(false);
return (
);
}
```
### Sizes
ID: `Checkbox.sizes` β’ Tags: checkboxes, sizes β’ Category: styling β’ Status: stable β’ Since: 1.0.0
Explore available checkbox sizes with guidance on where each fits best.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const;
return (
{SIZES.map((size) => (
{size}
))}
);
}
```
### Colors
ID: `Checkbox.colors` β’ Tags: checkboxes, colors β’ Category: styling β’ Status: stable β’ Since: 1.0.0
Toggle checkboxes styled with semantic `color` options and a default-checked example.
```tsx
const COLORS = ['primary', 'secondary', 'success', 'warning', 'error'] as const;
const [values, setValues] = useState>({});
const toggle = (color: string) => {
setValues((current) => ({
...current,
[color]: !current[color]
}));
};
return (
Semantic colors
{COLORS.map((color) => (
toggle(color)}
/>
))}
Use `color` to match checkbox accents with message intent while keeping labels readable.
);
}
```
### States
ID: `Checkbox.states` β’ Tags: checkboxes, states β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Highlight enabled, disabled, required, and error states to cover validation scenarios.
```tsx
const [enabled, setEnabled] = useState(true);
const [required, setRequired] = useState(true);
const [withError, setWithError] = useState(false);
return (
);
}
```
### Indeterminate
ID: `Checkbox.indeterminate` β’ Tags: checkboxes, indeterminate β’ Category: interaction β’ Status: stable β’ Since: 1.0.0
Demonstrates a parent checkbox that toggles a group and reflects partial selection with `indeterminate`.
```tsx
const [selected, setSelected] = useState([]);
const allIds = ITEMS.map((item) => item.id);
const allChecked = selected.length === ITEMS.length;
const someChecked = selected.length > 0 && !allChecked;
const toggleAll = () => {
setSelected((current) => (current.length === ITEMS.length ? [] : allIds));
};
const toggleItem = (id: number) => {
setSelected((current) =>
current.includes(id) ? current.filter((itemId) => itemId !== id) : [...current, id]
);
};
return (
{ITEMS.map(({ id, label }) => (
toggleItem(id)}
/>
))}
);
}
```
### Descriptions
ID: `Checkbox.withDescription` β’ Tags: checkboxes, helper-text β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Descriptions and helper text
```tsx
const [newsletter, setNewsletter] = useState(false);
const [termsAccepted, setTermsAccepted] = useState(false);
return (
Use `description` for supporting copy and pair with `error` to surface validation details.
);
}
```
--------------------------------------------------------------------------------
# Chip
The Chip component displays compact elements that represent an input, attribute, or action. Supports different colors, sizes, and interactive features like removal. Inner label accepts the full Text-prop API via `labelProps`.
## Metadata
- Canonical name: `Chip`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Chip } from '@platform-blocks/react-ui-library';`
- Category: data
- Tags: chip, tag, badge, label, removable
- Docs: https://react-ui-library.com/components/Chip
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Chip
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | React.ReactNode | Yes | | |
| `size` | SizeValue | No | | |
| `variant` | 'filled' \| 'outline' \| 'light' \| 'subtle' \| 'surface' \| 'gradient' | No | | Visual style. `surface` is the neutral option β it fills from the theme's background tokens instead of the `color` palette, sitting one step darker than the surface behind it (input tokens, filter pills). Ignores `color`. |
| `color` | ThemeColor | No | | Theme palette name or CSS color. Not used by the `surface` variant. |
| `onPress` | () => void | No | | |
| `dot` | boolean | No | | Show a small leading status dot. Defaults to the chip's resolved text color. |
| `dotColor` | string | No | | Override the dot color (any CSS/theme color string). Only used when `dot` is set. |
| `startIcon` | React.ReactNode | No | | |
| `endIcon` | React.ReactNode | No | | |
| `onRemove` | () => void | No | | |
| `removePosition` | 'left' \| 'right' | No | | |
| `disabled` | boolean | No | | |
| `style` | StyleProp | No | | |
| `textStyle` | StyleProp | No | | |
| `labelProps` | Omit | No | | Override props applied to the inner label `` (style, weight, ff, size, color). |
| `radius` | any | No | | |
| `shadow` | any | No | | |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basics
ID: `Chip.basic` β’ Tags: chip, getting-started β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Wrap any label in a Chip to render a compact tag β the default `filled` variant and `primary` color apply automatically.
```tsx
return (
Design
Engineering
Research
)
}
```
### Semantic colors
ID: `Chip.colors` β’ Tags: colors, theming β’ Category: theming β’ Status: stable β’ Since: 1.0.0
Map the `color` prop to semantic tokens like `primary`, `success`, `warning`, `error`, or `gray` so Chips inherit your design system palette without inline styles.
```tsx
return (
Primary
Success
Warning
Error
Gray
)
}
```
### Size scale
ID: `Chip.sizes` β’ Tags: sizes, density β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Select a `size` from `xs` through `3xl` to match the Chip density with the surrounding controls.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const;
return (
{SIZES.map((size) => (
Chip
{size}
))}
);
}
```
### Variant styles
ID: `Chip.variants` β’ Tags: variants, styling β’ Category: appearance β’ Status: stable β’ Since: 1.0.0
Choose a `variant` such as `filled`, `outline`, `light`, `subtle`, or `gradient` to adjust visual weight without changing the Chip label or color. `surface` is the neutral option: it fills from the theme's background tokens instead of the `color` palette, landing one step darker than whatever it sits on in both light and dark. That recessed read makes it the right pick for input tokens, filter pills, and other chrome that shouldn't look like a status color.
```tsx
return (
Filled
Outline
Light
Subtle
Surface
Gradient
)
}
```
### Status dot
ID: `Chip.dot` β’ Tags: dot, status, indicator β’ Category: appearance β’ Status: stable β’ Since: 1.0.0
Add a leading status dot with the `dot` prop. It defaults to the chip's resolved text color; override it with `dotColor`.
```tsx
return (
Active
Pending
Failed
Draft
Online
Away
Busy
Use dot to toggle the indicator and{' '}
dotColor to set a custom color.
)
}
```
### Shadow depth
ID: `Chip.shadow` β’ Tags: shadow, emphasis β’ Category: appearance β’ Status: stable β’ Since: 1.0.0
Use the `shadow` prop from `none` to `xl` when a Chip needs extra elevation to stand out from nearby content.
```tsx
return (
No Shadow
XS Shadow
SM Shadow
MD Shadow
LG Shadow
XL Shadow
)
}
```
### Removable tags
ID: `Chip.interactive` β’ Tags: interactive, removable β’ Category: interactions β’ Status: stable β’ Since: 1.0.0
Provide an `onRemove` handler to turn Chips into editable tags; the component renders a dismiss icon and calls your callback with no extra wiring.
```tsx
const initialSports = [
{ label: 'Soccer', emoji: 'β½' },
{ label: 'Basketball', emoji: 'π' },
{ label: 'Tennis', emoji: 'πΎ' },
]
const [chips, setChips] = useState(initialSports)
const handleRemove = (chipToRemove: string) => {
setChips((current) => current.filter((chip) => chip.label !== chipToRemove))
}
return (
{chips.map((chip) => (
handleRemove(chip.label)}
>
{chip.label}
))}
)
}
```
### Theme Matrix
ID: `Chip.theme-matrix` β’ Category: general
```tsx
const VARIANTS: NonNullable[] = [
'filled',
'outline',
'light',
'subtle',
'gradient',
]
// Core palette colors plus one raw custom color, to prove the resolver works for both.
const ROWS: { color: string; label: string }[] = [
{ color: 'primary', label: 'Primary' },
{ color: 'secondary', label: 'Secondary' },
{ color: 'success', label: 'Success' },
{ color: 'warning', label: 'Warning' },
{ color: 'error', label: 'Error' },
{ color: 'gray', label: 'Gray' },
{ color: '#7C3AED', label: 'Custom' },
]
const LABEL_W = 78
const CELL_W = 104
// A single chip with a leading status dot. The dot defaults to the chip's own
// resolved text color, so the indicator stays legible across every variant +
// scheme without any per-cell color plumbing.
function ChipCell({ variant, color, label }: { variant: NonNullable; color: string; label: string }) {
return (
{label}
)
}
function Matrix() {
return (
{/* Column headers */}
{VARIANTS.map((v) => (
{v}
))}
{ROWS.map(({ color, label }) => (
{label}
{VARIANTS.map((v) => (
))}
))}
)
}
function Panel({
theme,
title,
surface,
}: {
theme: typeof DEFAULT_THEME
title: string
surface: string
}) {
return (
{title}
{surface}
)
}
// Two panels, each locked to a scheme, so every variant can be read side by side
// on the real light and dark surfaces. Text is resolved by measured contrast, so
// every variant stays legible on both.
return (
)
}
```
--------------------------------------------------------------------------------
# CodeBlock
The CodeBlock component renders source code with optional syntax highlighting, copy-to-clipboard, GitHub integration, line wrapping, and width controls (content-fit by default, with an opt-in full width mode). Set `wrap={false}` to disable soft wrapping and enable horizontal scrolling for long lines. `radius` and `withBorder` control the code surface itself β pair `radius="none"` with `withBorder={false}` to sit flush inside a bordered container such as `Card.Section`.
## Metadata
- Canonical name: `CodeBlock`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { CodeBlock } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: typography
- Tags: code, syntax, formatting, developer, github
- Docs: https://react-ui-library.com/components/CodeBlock
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/CodeBlock
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `language` | string | No | 'tsx' | Optional language for syntax highlighting |
| `children` | string | No | | Source to display. Optional when `files` is provided. |
| `files` | CodeBlockFile[] | No | | The block's source files. One entry renders its name as a header label; several render as switchable tabs. `children` is ignored while `files` is set, and a lone entry may omit `code` to keep using `children`. |
| `defaultFile` | string | No | | File name that starts active (uncontrolled). Defaults to the first file. |
| `activeFile` | string | No | | Active file name (controlled). Pair with `onFileChange`. |
| `onFileChange` | (fileName: string) => void | No | | Fired when the reader switches tabs |
| `title` | string | No | | Optional title displayed above the code block |
| `showLineNumbers` | boolean | No | false | Show line numbers in the code block |
| `highlight` | boolean | No | true | Enable syntax highlighting |
| `fullWidth` | boolean | No | true | Make the code block take the full width of its container |
| `radius` | RadiusValue | No | DEFAULT_CODE_RADIUS | Corner radius of the code surface (size token or px). Set `'none'` to sit flush inside a bordered container such as `Card.Section`. |
| `withBorder` | boolean | No | true | Draw the code surface's 1px border. Defaults to `true`. |
| `showCopyButton` | boolean | No | true | Show a copy button to copy the code to clipboard |
| `onCopy` | (code: string) => void | No | | Callback when code is copied |
| `style` | StyleProp | No | | Custom styles for the code block container and text |
| `textStyle` | StyleProp | No | | Custom styles for the code text |
| `titleStyle` | StyleProp | No | | Custom styles for the title text |
| `highlightLines` | Array | No | | Lines to highlight, e.g. ["1", "3-5"] or [1, 3] |
| `spoiler` | boolean | No | false | Show a spoiler for the code block |
| `spoilerMaxHeight` | number | No | 160 | Maximum height for the spoiler, if exceeded a "Show More" button appears |
| `variant` | CodeBlockVariant | No | 'code' | Visual variant: default code styling, terminal emulation, or hacker theme |
| `promptSymbol` | string | No | '$' | Optional prompt prefix for terminal variant (ignored if lines already prefixed) |
| `githubUrl` | string | No | | GitHub URL for the source shown here. Adds an edit button beside the copy button that opens it. Per-file URLs (`files[].githubUrl`) win over this one, so a multi-file block points each tab at its own source. |
| `fileHeader` | boolean | No | false | Render the file name in a detached bar above the panel instead of inline inside it. Single-file blocks only β tabs always sit inside the panel. |
| `colors` | CodeBlockColorOverrides | No | | Override base colors (background, text, highlights) |
| `wrap` | boolean | No | true | Control whether long lines wrap (defaults to true). Set to false to enable horizontal scrolling instead. |
| `fontFamily` | string | No | | Custom font family for the code text (overrides the default monospace stack) |
| `ff` | string | No | | Shorthand alias for `fontFamily` |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic
ID: `CodeBlock.basic` β’ Tags: basic, code, snippet β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Default CodeBlock showing a single snippet with automatic language detection and copy controls.
```tsx
const sample = `import { View, Text } from 'react-native';
return (
Hello, World!
);
}`;
return (
Basic code block
The default CodeBlock renders formatted code with copy support and automatic language detection.
{sample}
);
}
```
### Interactive
ID: `CodeBlock.interactive` β’ Tags: interactive, copy, events β’ Category: features β’ Status: stable β’ Since: 1.0.0
Custom onCopy handling with inline feedback and a manual trigger button.
```tsx
const sampleCode = `const greeting = "Hello, World!";
console.log(greeting);
// A simple function
function add(a, b) {
return a + b;
}
const result = add(5, 3);
console.log(\`5 + 3 = \${result}\`);`;
const [copiedLength, setCopiedLength] = useState(null);
const timeoutRef = useRef | null>(null);
useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);
const handleCopy = (code: string) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
setCopiedLength(code.length);
timeoutRef.current = setTimeout(() => {
setCopiedLength(null);
timeoutRef.current = null;
}, 2000);
};
return (
Interactive copy
Attach an onCopy handler to trigger custom feedback and reuse it outside the CodeBlock controls.
{copiedLength !== null && (
Copied {copiedLength} characters to the clipboard.
)}
{sampleCode}
handleCopy(sampleCode)} />
);
}
```
### File tabs
ID: `CodeBlock.files` β’ Tags: files, tabs, multi-file β’ Category: content β’ Status: stable β’ Since: 1.0.0
Pass `files` to show one source per tab. Each tab carries its language's logo where one exists (TypeScript, CSS) and a glyph otherwise, and highlighting follows the active file's language β `data.ts` highlights as TypeScript even though the block's `language` is `tsx`.
```tsx
const FILES = [
{
name: 'index.tsx',
code: `import { Blockquote } from '@platform-blocks/react-ui-library';
return {QUOTE} ;
}`,
},
{
name: 'data.ts',
code: `export const AUTHOR = {
name: 'Priya Shah',
title: 'CTO',
organization: 'Northwind Labs',
};
},
{
name: 'quote.css',
code: `.quote {
border-left: 4px solid var(--primary-5);
padding: 16px 20px;
}`,
},
{
name: 'theme.json',
code: `{
"primaryColor": "blue",
"defaultRadius": "md"
}`,
},
];
return (
Switch files from the tab strip in the top-left corner.
);
}
```
### GitHub
ID: `CodeBlock.github` β’ Tags: github, toolbar, links β’ Category: integrations β’ Status: stable β’ Since: 1.0.0
Surface GitHub shortcuts alongside copy controls across variants using the `githubUrl` prop.
```tsx
const componentExample = `import { View, Text } from 'react-native';
return (
Hello, World!
);
}`;
const inlineExample = `// This code has both copy and GitHub buttons
return Hello with GitHub button!
;
}`;
const terminalExample = `$ npm install platform-blocks
$ npm start
Server running on http://localhost:3000`;
const floatingExample = `// Floating buttons example (no title)
return Hover to see buttons ;
}`;
return (
GitHub actions
Provide a GitHub URL to render quick links beside copy controls across any CodeBlock variant.
Basic component
{componentExample}
File name and language
{inlineExample}
Terminal variant
{terminalExample}
Floating controls
{floatingExample}
);
}
```
### Languages
ID: `CodeBlock.languages` β’ Tags: syntax, languages, highlighting β’ Category: features β’ Status: stable β’ Since: 1.0.0
Examples highlighting TypeScript, JSON, and Markdown syntax rendering in CodeBlock.
```tsx
const tsxExample = `interface Props {
title: string;
onPress?: () => void;
}
return (
{title}
);
}`;
const jsonExample = `{
"name": "my-app",
"version": "1.0.0",
"dependencies": {
"react": "^18.2.0",
"react-native": "^0.72.0"
},
"scripts": {
"start": "expo start",
"build": "expo build"
}
}`;
const markdownExample = `# Getting Started
This is a **markdown** example with \`inline code\`.
## Features
- Syntax highlighting
- Multiple languages
- Copy functionality
> Blockquote with *emphasis* and **bold** text.`;
return (
Language presets
CodeBlock detects syntax styles across languages like TypeScript, JSON, and Markdown.
{tsxExample}
{jsonExample}
{markdownExample}
);
}
```
### Features
ID: `CodeBlock.features` β’ Tags: line-numbers, full-width, copy β’ Category: features β’ Status: stable β’ Since: 1.0.0
Demonstrates line numbers, full-width layouts, and copy button customization in a single CodeBlock showcase.
```tsx
const fibonacciExample = `function fibonacci(n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
// Calculate the 10th Fibonacci number
const result = fibonacci(10);
console.log(\`Fibonacci(10) = \${result}\`);`;
const fullWidthExample = `// This code block spans the full width of its container
const data = [
{ id: 1, name: 'Alice', age: 30, city: 'New York' },
{ id: 2, name: 'Bob', age: 25, city: 'San Francisco' },
{ id: 3, name: 'Charlie', age: 35, city: 'Los Angeles' }
];
const processData = (rows) => {
return rows
.filter((person) => person.age >= 30)
.map((person) => ({
...person,
isAdult: true,
displayName: \`\${person.name} (\${person.age})\`
}))
.sort((left, right) => left.name.localeCompare(right.name));
};
console.log(processData(data));`;
const disabledCopyExample = `// This example has the copy button disabled
const message = "Hello, World!";
console.log(message);`;
return (
Feature highlights
Combine titles, line numbers, full-width layouts, and copy controls to match different documentation needs.
Title with line numbers
{fibonacciExample}
Full-width layout
{fullWidthExample}
Copy button disabled
{disabledCopyExample}
);
}
```
### Variants
ID: `CodeBlock.variants` β’ Tags: variant, terminal, hacker β’ Category: styling β’ Status: stable β’ Since: 1.0.0
Compare the default, terminal, and hacker themes available through the variant prop.
```tsx
const sampleCode = `function hackTheMatrix() {
const matrix = generateMatrix();
console.log('Entering the matrix...');
for (let i = 0; i < matrix.length; i += 1) {
matrix[i].decrypt();
}
return 'Welcome to the real world.';
}`;
const terminalCode = `$ npm install platform-blocks
$ cd my-app
$ npm start
Server running on port 3000`;
return (
Visual variants
Switch between default, terminal, and hacker themes using the variant prop.
Default code block
{sampleCode}
Terminal variant
{terminalCode}
Hacker variant
{sampleCode}
);
}
```
### Highlighting
ID: `CodeBlock.highlighting` β’ Tags: highlight, lines, emphasis β’ Category: features β’ Status: stable β’ Since: 1.0.0
Use highlightLines for single lines or ranges to draw attention to important snippets.
```tsx
const sample = `import { View, Text } from 'react-native';
interface User {
id: number;
name: string; // highlighted
active: boolean;
}
if (!user.active) {
return null; // early return highlighted
}
return (
{user.name}
);
}
// Utility function (range highlighted)
return users.filter((u) => u.active);
}`;
return (
Highlighted lines
Combine individual lines and ranges in the highlightLines prop to emphasize key logic.
{sample}
);
}
```
--------------------------------------------------------------------------------
# Collapse
Displays content that can be revealed or hidden with an animation.
## Metadata
- Canonical name: `Collapse`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Collapse } from '@platform-blocks/react-ui-library';`
- Status: beta
- Category: display
- Docs: https://react-ui-library.com/components/Collapse
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Collapse
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `isCollapsed` | boolean | Yes | | Whether the content is collapsed (hidden). `false` reveals/expands it. |
| `children` | ReactNode | Yes | | Content to show/hide |
| `duration` | number | No | 300 | Animation duration in milliseconds |
| `transitionDuration` | number | No | 300 | Duration (ms) of the height transition. Cross-component spelling that takes precedence over `duration`; `0` snaps open/closed with no animation. |
| `timing` | 'linear' \| 'ease' \| 'ease-in' \| 'ease-out' \| 'ease-in-out' | No | 'ease-out' | Animation timing function |
| `style` | StyleProp | No | | Style for the container |
| `contentStyle` | StyleProp | No | | Style for the content wrapper |
| `onAnimationStart` | () => void | No | | Callback fired when animation starts |
| `onAnimationEnd` | () => void | No | | Callback fired when animation completes |
| `easing` | (value: number) => number | No | | Custom easing function overriding the timing preset |
| `animateOnMount` | boolean | No | false | Whether to animate on initial mount |
| `collapsedHeight` | number | No | 0 | Custom height when collapsed (useful for partial reveals) |
| `fadeContent` | boolean | No | true | Whether to fade content in/out along with height animation |
## Examples
### Basic
ID: `Collapse.basic` β’ Tags: basic, collapse β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Basic usage of the Collapse component to show and hide content with animation.
```tsx
const [isCollapsed, setIsCollapsed] = useState(false);
return (
setIsCollapsed(!isCollapsed)}>
{isCollapsed ? 'Show' : 'Hide'}
This is some content inside the Collapse component. It will be shown or hidden based on the isCollapsed prop.
);
}
```
--------------------------------------------------------------------------------
# ColorInput
The ColorInput component provides an intuitive interface for selecting colors through various input methods including color wheel, hex input, and preset swatches.
## Metadata
- Canonical name: `ColorInput`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { ColorInput } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: input
- Docs: https://react-ui-library.com/components/ColorInput
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/ColorInput
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | string | No | | Current color value in hex format (e.g., "#ff0000") |
| `defaultValue` | string | No | '' | Default color value for uncontrolled usage |
| `onChange` | (color: string) => void | No | | Callback when color changes |
| `label` | string | No | | Label for the color picker |
| `placeholder` | string | No | 'Select color' | Placeholder text when no color is selected |
| `disabled` | boolean | No | false | Whether the picker is disabled |
| `required` | boolean | No | false | Whether the picker is required |
| `error` | string | No | | Error message to display |
| `description` | string | No | | Help text to display below the picker |
| `size` | ComponentSizeValue | No | 'md' | Size of the picker |
| `variant` | 'default' \| 'filled' \| 'unstyled' | No | 'default' | Variant of the picker |
| `radius` | 'none' \| 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' | No | | Radius of the picker |
| `showPreview` | boolean | No | true | Whether to show the color preview |
| `showInput` | boolean | No | true | Whether to show the hex input |
| `swatches` | string[] | No | DEFAULT_SWATCHES | Predefined color swatches to show |
| `withSwatches` | boolean | No | true | Whether to show common color swatches |
| `format` | 'hex' \| 'rgb' \| 'hsl' | No | 'hex' | Format for the color value |
| `withAlpha` | boolean | No | false | Whether to include alpha channel |
| `placement` | PlacementType | No | | Dropdown placement |
| `flip` | boolean | No | true | Whether to flip placement when no space |
| `shift` | boolean | No | true | Whether to shift position to stay in viewport |
| `boundary` | any | No | | Boundary element for positioning constraints |
| `offset` | number | No | 8 | Offset from anchor element in pixels |
| `autoReposition` | boolean | No | true | Whether to automatically reposition on resize/scroll |
| `fallbackPlacements` | PlacementType[] | No | DEFAULT_FALLBACK_PLACEMENTS | Fallback placements to try |
| `keyboardAvoidance` | boolean | No | true | Whether dropdown should avoid the on-screen keyboard when visible |
| `style` | StyleProp | No | | Custom style for the container |
| `previewStyle` | StyleProp | No | | Custom style for the preview |
| `inputStyle` | StyleProp | No | | Custom style for the input |
| `clearable` | boolean | No | false | Whether the picker should display a clear button |
| `clearButtonLabel` | string | No | 'Clear color' | Accessible label for the clear button |
| `testID` | string | No | | Test ID for testing |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
## Examples
### Basic Usage
ID: `ColorInput.basic` β’ Category: general
Simple color picker with default settings.
```tsx
const [color, setColor] = useState('#FF6B6B');
return (
Selected: {color || 'none'}
);
}
```
### Custom Swatches
ID: `ColorInput.swatches` β’ Category: general
Color picker with custom swatch palettes.
```tsx
const [color1, setColor1] = useState('#2196F3');
const [color2, setColor2] = useState('#4CAF50');
const [color3, setColor3] = useState('#FF9800');
const blueSwatches = [
'#E3F2FD', '#BBDEFB', '#90CAF9', '#64B5F6', '#42A5F5',
'#2196F3', '#1E88E5', '#1976D2', '#1565C0', '#0D47A1',
];
const greenSwatches = [
'#E8F5E8', '#C8E6C9', '#A5D6A7', '#81C784', '#66BB6A',
'#4CAF50', '#43A047', '#388E3C', '#2E7D32', '#1B5E20',
];
return (
Custom blue palette
Custom green palette
Without swatches
);
}
```
--------------------------------------------------------------------------------
# ColorPicker
ColorPicker is a lightweight alternative to ColorInput for cases where a full hex input and dropdown chrome are unnecessary. It renders a single color preview that toggles a compact popover of preset swatches β no text input, no positioning engine.
## Metadata
- Canonical name: `ColorPicker`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { ColorPicker } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: input
- Docs: https://react-ui-library.com/components/ColorPicker
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/ColorPicker
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | string | No | | Current color value in hex format (controlled) |
| `defaultValue` | string | No | '' | Initial color value for uncontrolled usage |
| `onChange` | (color: string) => void | No | | Callback fired when a swatch is selected |
| `swatches` | string[] | No | DEFAULT_SWATCHES | Preset colors to choose from |
| `size` | number | No | 28 | Size of the trigger + swatches in pixels |
| `columns` | number | No | 5 | Number of swatches per row in the popover |
| `disabled` | boolean | No | false | Whether the picker is disabled |
| `accessibilityLabel` | string | No | | Accessibility label for the trigger |
| `style` | StyleProp | No | | Custom style for the outer wrapper |
| `testID` | string | No | | Test ID |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic
ID: `ColorPicker.basic` β’ Category: general
```tsx
const [color, setColor] = useState('#4ECDC4');
return (
Selected: {color}
Larger, custom swatches
);
}
```
--------------------------------------------------------------------------------
# ColorSwatch
A simple square component for displaying colors, designed as a building block for color pickers and palette interfaces.
## Metadata
- Canonical name: `ColorSwatch`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { ColorSwatch } from '@platform-blocks/react-ui-library';`
- Category: display
- Tags: color, swatch, palette, picker
- Docs: https://react-ui-library.com/components/ColorSwatch
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/ColorSwatch
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `color` | string | Yes | | The color value to display (hex, rgb, hsl, etc.) |
| `size` | number | No | 32 | Size of the swatch in pixels |
| `selected` | boolean | No | false | Whether the swatch is selected/active |
| `disabled` | boolean | No | false | Whether the swatch is disabled |
| `onPress` | () => void | No | | Callback when swatch is pressed |
| `showBorder` | boolean | No | true | Show a border around the swatch |
| `borderColor` | string | No | | Custom border color (defaults to theme color) |
| `borderWidth` | number | No | 1 | Border width in pixels |
| `borderRadius` | number | No | 4 | Border radius in pixels |
| `showCheckmark` | boolean | No | true | Show a checkmark when selected |
| `checkmarkColor` | string | No | | Custom checkmark color |
| `accessibilityLabel` | string | No | | Accessibility label |
| `testID` | string | No | | Test ID |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Advanced
ID: `ColorSwatch.advanced` β’ Category: general
```tsx
const [selectedColor, setSelectedColor] = useState('#E74C3C');
return (
Grayscale palette:
{[
'#000000', '#1a1a1a', '#333333', '#4d4d4d', '#666666',
'#808080', '#999999', '#b3b3b3', '#cccccc', '#e6e6e6', '#ffffff'
].map(color => (
setSelectedColor(color)}
/>
))}
Large display swatch:
{selectedColor}
Click any swatch above to change
);
}
```
### Basic
ID: `ColorSwatch.basic` β’ Category: general
```tsx
const colors = [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7',
'#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9'
];
const [selectedColor, setSelectedColor] = useState('#FF6B6B');
return (
Basic Color Swatches
Click to select a color:
{colors.map(color => (
setSelectedColor(color)}
/>
))}
Selected: {selectedColor}
Different sizes:
Without borders:
{colors.slice(0, 5).map(color => (
))}
Disabled state:
{colors.slice(0, 3).map(color => (
))}
);
}
```
### Palette
ID: `ColorSwatch.palette` β’ Category: general
```tsx
const [selectedColor, setSelectedColor] = useState('#E74C3C');
const colorPalettes = {
'Material Colors': [
'#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5',
'#2196F3', '#03A9F4', '#00BCD4', '#009688', '#4CAF50',
'#8BC34A', '#CDDC39', '#FFEB3B', '#FFC107', '#FF9800',
'#FF5722', '#795548', '#9E9E9E', '#607D8B', '#000000'
],
'Pastel Colors': [
'#FFB3BA', '#FFDFBA', '#FFFFBA', '#BAFFC9', '#BAE1FF',
'#C5B3FF', '#FFB3E6', '#FFD1DC', '#E6E6FA', '#F0E68C'
],
'Dark Colors': [
'#2C3E50', '#34495E', '#7F8C8D', '#95A5A6', '#BDC3C7',
'#1ABC9C', '#16A085', '#27AE60', '#229954', '#E74C3C'
]
};
return (
Color Palette Builder
Selected Color: {selectedColor}
{Object.entries(colorPalettes).map(([paletteName, colors]) => (
{paletteName}
{colors.map(color => (
setSelectedColor(color)}
borderRadius={paletteName === 'Pastel Colors' ? 16 : 4}
/>
))}
))}
Custom Styles Examples
Round Gold
No Border
Square
);
}
```
--------------------------------------------------------------------------------
# ContextMenu
The ContextMenu component provides a context-sensitive menu that appears on right-click (web) or long-press (mobile) with customizable actions.
## Metadata
- Canonical name: `ContextMenu`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { ContextMenu } from '@platform-blocks/react-ui-library';`
- Category: overlay
- Tags: menu, context, rightclick, longpress, actions
- Docs: https://react-ui-library.com/components/ContextMenu
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/ContextMenu
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | (props: { onContextMenu: (e: any) => void; onPressIn: (e: any) => void }) => React.ReactNode | Yes | | Render prop for the trigger |
| `items` | ContextMenuItem[] | Yes | | |
| `closeOnSelect` | boolean | No | | Close after selection (default true) |
| `longPressDelay` | number | No | | Long press duration (ms) for native |
| `maxHeight` | number | No | | Optional maximum height (scrolls) |
| `onOpen` | () => void | No | | Called when menu opens |
| `onClose` | () => void | No | | Called when menu closes |
| `open` | boolean | No | | Controlled open |
| `position` | { x: number; y: number } | No | | Controlled position |
| `portalId` | string | No | | Portal target id (web) - simple placeholder for future portal integration |
| `style` | any | No | | |
## Examples
### Basic Usage
ID: `ContextMenu.basic` β’ Tags: rightclick, longpress β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Wrap any trigger element with `ContextMenu` and pass an `items` array. The menu opens on right-click (web) or long-press (mobile); the `children` render prop receives the handlers to spread onto your trigger.
```tsx
const ITEMS = [
{ id: 'copy', label: 'Copy' },
{ id: 'rename', label: 'Rename' },
{ id: 'delete', label: 'Delete', danger: true },
];
return (
{(triggerProps) => (
Right-click or long-press me
)}
);
}
```
--------------------------------------------------------------------------------
# ControlField
ControlField combines a label, description, and a control (Switch, Checkbox, or Radio) into a single pressable row.
## Metadata
- Canonical name: `ControlField`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { ControlField } from '@platform-blocks/react-ui-library';`
- Category: input
- Tags: control, field, checkbox, switch, radio, toggle, form, selection, row
- Docs: https://react-ui-library.com/components/ControlField
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/ControlField
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `isSelected` | boolean | No | | Controlled selected state |
| `checked` | boolean | No | | Alias for `isSelected` to match other controls in the library |
| `defaultSelected` | boolean | No | | Initial selected state for uncontrolled usage |
| `onSelectedChange` | (selected: boolean) => void | No | | Change handler β fires with the next selected value |
| `onChange` | (selected: boolean) => void | No | | Alias for `onSelectedChange` to match other controls in the library |
| `isDisabled` | boolean | No | | Whether the field is disabled |
| `disabled` | boolean | No | | Alias for `isDisabled` |
| `isInvalid` | boolean | No | | Whether the field is in an invalid state (defaults to true when `error` is set) |
| `isRequired` | boolean | No | | Whether the field is required (renders an asterisk on the label) |
| `required` | boolean | No | | Alias for `isRequired` |
| `variant` | ControlFieldVariant | No | | Which built-in control renders in the indicator slot |
| `label` | React.ReactNode | No | | Primary label text |
| `description` | React.ReactNode | No | | Helper text shown beneath the label |
| `error` | string | No | | Error message shown below the row when invalid |
| `color` | ColorValue | No | | Indicator color. A palette token, `'primary.6'` shade syntax, or any CSS color. |
| `size` | SizeValue | No | | Indicator + label size |
| `indicatorPosition` | 'left' \| 'right' | No | | Which side the indicator sits on. Defaults to `right`. |
| `control` | React.ReactElement | No | | Custom control element used instead of the built-in `variant` indicator. `checked` / `disabled` are injected automatically when not already set. |
| `labelProps` | Omit | No | | Override props applied to the label `` |
| `descriptionProps` | Omit | No | | Override props applied to the description `` |
| `children` | React.ReactNode | No | | Compound composition. When provided, children replace the built-in label/description/indicator layout. Use `ControlField.Label`, `ControlField.Description`, `ControlField.Indicator` and `ControlField.Error`. |
| `accessibilityLabel` | string | No | | Accessibility label, used when there is no visible text label |
| `testID` | string | No | | Component test ID for testing |
| `style` | any | No | | Additional CSS styles |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basics
ID: `ControlField.basic` β’ Tags: control-field, switch β’ Category: basics β’ Status: stable β’ Since: 1.0.0
A controlled switch row β the whole row is a single tap target.
```tsx
const [enabled, setEnabled] = useState(true);
return (
);
}
```
### Checkbox with validation
ID: `ControlField.checkbox-validation` β’ Tags: control-field, checkbox, validation β’ Category: basics β’ Status: stable β’ Since: 1.0.0
A consent row using the `checkbox` variant. When left unchecked the field shows an error message below the row.
```tsx
const [agreed, setAgreed] = useState(false);
return (
);
}
```
### Custom control
ID: `ControlField.custom-control` β’ Tags: control-field, composition, checkbox β’ Category: composition β’ Status: stable β’ Since: 1.0.0
Compose the row explicitly with `ControlField.Indicator` to drop in a custom control β here a warning-colored checkbox.
```tsx
const [subscribed, setSubscribed] = useState(false);
return (
Subscribe to newsletter
One email a week, unsubscribe anytime
);
}
```
### Grouped surface
ID: `ControlField.grouped-surface` β’ Tags: control-field, group, surface, settings, dividers β’ Category: composition β’ Status: stable β’ Since: 1.0.0
Wrap rows in `ControlField.Group` to get an iOS-style settings surface β a rounded, filled background with hairline dividers between rows. The group can carry an optional title and footer, and sets a shared size for its children.
```tsx
const [wifi, setWifi] = useState(true);
const [bluetooth, setBluetooth] = useState(false);
const [airplane, setAirplane] = useState(false);
return (
);
}
```
--------------------------------------------------------------------------------
# CopyButton
Small utility component for copying text values to the clipboard with optional toast feedback. Used inside components like `CodeBlock` and `QRCode` to standardize UX.
## Metadata
- Canonical name: `CopyButton`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { CopyButton } from '@platform-blocks/react-ui-library';`
- Status: beta
- Since: 0.1.0
- Category: input
- Docs: https://react-ui-library.com/components/CopyButton
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/CopyButton
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | string | Yes | | The text to copy to clipboard |
| `onCopy` | (value: string) => void | No | | Callback fired after copy action |
| `iconOnly` | boolean | No | | If true, only the icon is shown (no button chrome or label) |
| `label` | string | No | | Accessible label for the button |
| `toastTitle` | string | No | | Title for the toast |
| `toastMessage` | string | No | | Detailed message for the toast |
| `size` | ComponentSizeValue | No | | Visual size token |
| `style` | StyleProp | No | | Style overrides for the button container |
| `disableToast` | boolean | No | | Disable the "copied to clipboard" toast |
| `tooltip` | TooltipPropValue | No | | Tooltip text, or a full Tooltip config (`{ label, maxWidth, β¦ }`) |
| `tooltipPosition` | 'top' \| 'bottom' \| 'left' \| 'right' | No | | Tooltip position when the string form of `tooltip` is used |
| `mode` | 'button' \| 'icon' | No | | Presentation mode: default button (legacy) or bare icon |
| `buttonVariant` | 'none' \| 'secondary' \| 'ghost' \| 'filled' \| 'outline' \| 'gradient' \| undefined | No | | Button variant override when in button mode |
| `iconName` | string | No | | Icon name to display (defaults to copy) when in icon mode |
| `copiedIconName` | string | No | | Icon name to display after copy (default check) in icon mode |
| `iconColor` | string | No | | Base icon color (icon mode) |
| `copiedIconColor` | string | No | | Copied state icon color (icon mode) |
## Examples
### Basic
ID: `CopyButton.basic` β’ Category: general
Icon-only copy button with default toast feedback.
```tsx
return (
Invite Code:
ABCD-1234
);
}
```
### Labeled
ID: `CopyButton.labeled` β’ Category: general
Copy control showing label text instead of icon-only presentation.
```tsx
return (
API Key
sk_live_1a2b3c4d5e6f7g8h9i10
);
}
```
### Long Value
ID: `CopyButton.long-value` β’ Category: general
Long value truncation in toast (shows ellipsis for >60 chars).
```tsx
const longToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.long.payload.value.with.many.sections.and.characters.for.demo.purposes.only';
return (
JWT Token
{longToken}
);
}
```
### No Toast
ID: `CopyButton.no-toast` β’ Category: general
Copy control with toast notifications disabled.
```tsx
const [copiedValue, setCopiedValue] = useState(null);
return (
Secret:
hunter2
setCopiedValue(v)} iconOnly={false} label="Copy" />
{copiedValue && Copied locally: {copiedValue} }
);
}
```
### Sizes
ID: `CopyButton.sizes` β’ Category: general
Scale the copy affordance with the `size` prop, from `xs` through `3xl`.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const;
return (
{SIZES.map((size) => (
{size}
))}
);
}
```
--------------------------------------------------------------------------------
# DataList
DataList displays a set of label/value pairs, such as user details or metadata, in a clean, aligned layout. Compose items with `DataList.Item`, `DataList.ItemLabel`, and `DataList.ItemValue`, or pass a `data` array for a quick setup.
## Metadata
- Canonical name: `DataList`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { DataList } from '@platform-blocks/react-ui-library';`
- Status: stable
- Since: 0.10.1
- Category: data
- Tags: datalist, description, definition, key-value, label, value, details
- Docs: https://react-ui-library.com/components/DataList
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/DataList
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | ReactNode | No | | `DataList.Item` children. Ignored when `data` is provided. |
| `data` | DataListDataItem[] | No | | Shorthand for rendering items without composing `DataList.Item` manually |
| `orientation` | DataListOrientation | No | | Layout direction of each label/value pair |
| `withDivider` | boolean | No | | Render a divider between items |
| `size` | ComponentSizeValue | No | | Controls font size and spacing |
| `spacing` | ComponentSizeValue \| number | No | | Override the vertical gap between items |
| `labelWidth` | number \| string | No | | Width of the label column in horizontal orientation |
| `labelColor` | string | No | | Override the label text color for all items |
| `valueColor` | string | No | | Override the value text color for all items |
| `dividerColor` | string | No | | Override the divider color when `withDivider` is set |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic Usage
ID: `DataList.basic` β’ Tags: datalist β’ Category: basics β’ Status: stable β’ Since: 0.10.1
Compose `DataList.Item` with `DataList.ItemLabel` and `DataList.ItemValue` to render aligned label/value pairs.
```tsx
return (
Name
John Doe
Email
john@example.com
Role
Software Engineer
);
}
```
### Vertical Orientation
ID: `DataList.vertical` β’ Tags: datalist, orientation β’ Category: basics β’ Status: stable β’ Since: 0.10.1
Set `orientation="vertical"` to stack each label above its value β useful for longer values or narrow layouts.
```tsx
return (
Shipping address
2825 Winding Way, Providence, RI 02908
Tracking number
1Z 999 AA1 01 2345 6784
Estimated delivery
July 12, 2026
);
}
```
### Dividers & Aligned Labels
ID: `DataList.dividers` β’ Tags: datalist, divider β’ Category: basics β’ Status: stable β’ Since: 0.10.1
Enable `withDivider` to separate items with a border, and set `labelWidth` to keep the value column aligned.
```tsx
return (
Plan
Pro (annual)
Seats
12 of 20 used
Renews
January 1, 2027
);
}
```
### Sizes
ID: `DataList.sizes` β’ Tags: datalist, size β’ Category: basics β’ Status: stable β’ Since: 0.10.1
Use the `size` prop (`xs`β`3xl`, or a number) to scale font size and spacing together.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const;
return (
{SIZES.map((size) => (
{size}
))}
);
}
```
### Data Prop Shorthand
ID: `DataList.data` β’ Tags: datalist, data β’ Category: basics β’ Status: stable β’ Since: 0.10.1
Skip the composition and pass a `data` array of `{ label, value }` objects to render items automatically.
```tsx
const details = [
{ label: 'Order', value: '#SS-10428' },
{ label: 'Placed', value: 'July 3, 2026' },
{ label: 'Total', value: '$248.00' },
{ label: 'Payment', value: 'Visa β’β’β’β’ 4242' },
];
return ;
}
```
--------------------------------------------------------------------------------
# DataTable
The DataTable component provides a feature-rich interface for displaying tabular data with sorting, pagination, row selection, and customizable columns.
## Metadata
- Canonical name: `DataTable`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { DataTable } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: data
- Docs: https://react-ui-library.com/components/DataTable
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/DataTable
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `id` | string | No | | Stable id for user preference persistence |
| `data` | T[] | Yes | | Data rows |
| `columns` | DataTableColumn[] | Yes | | Column definitions |
| `loading` | boolean | No | | Loading state |
| `error` | string \| null | No | | Error message (when defined overrides table body) |
| `emptyMessage` | string | No | | Message to display when there is no data |
| `searchable` | boolean | No | | Enable global search input |
| `searchPlaceholder` | string | No | | Placeholder text for global search |
| `searchValue` | string | No | | Controlled global search value |
| `onSearchChange` | (value: string) => void | No | | Global search change handler |
| `sortBy` | DataTableSort[] | No | | Current sorting state |
| `onSortChange` | (sort: DataTableSort[]) => void | No | | Sorting change callback |
| `filters` | DataTableFilter[] | No | | Active column filters |
| `onFilterChange` | (filters: DataTableFilter[]) => void | No | | Filter change callback |
| `showColumnFilters` | boolean | No | | Render an always-visible filter row directly beneath the column headers. Each `filterable` column gets an inline control β a text input for text/number/date columns and a dropdown for `select`/`boolean` columns (options auto-derived from the data when `filterOptions` is omitted). This is separate from the per-header filter popover and can be used alongside it. |
| `pagination` | DataTablePagination | No | | Pagination state |
| `onPaginationChange` | (pagination: DataTablePagination) => void | No | | Pagination change handler |
| `manualPagination` | boolean | No | | Server-side (manual) pagination for API-backed tables. When true the `data` prop is treated as the already-fetched current page: the table performs no client-side slicing, filtering, sorting, or search, and uses `pagination.total` as the authoritative row count for the page count and "X-Y of N" summary. The sort / filter / search controls still fire their respective callbacks so you can refetch β use them in controlled mode (`sortBy`+`onSortChange`, `filters`+`onFilterChange`, `searchValue`+ `onSearchChange`). Requires `pagination.total` to be set. |
| `paginationProps` | Omit | No | | Props forwarded to the underlying `Pagination` component in the footer (e.g. `siblings`, `boundaries`, `variant`, `size`, `color`, `showFirst`, `showPrevNext`, `labels`). Values here override the DataTable defaults, so you can also disable the built-in total (`showTotal={false}`) or size changer (`showSizeChanger={false}`). |
| `selectable` | boolean | No | | Enable row selection |
| `selectedRows` | (string \| number)[] | No | | Selected row identifiers |
| `onSelectionChange` | (selected: (string \| number)[]) => void | No | | Selection change handler |
| `getRowId` | (row: T, index: number) => string \| number | No | | Function to extract a stable id for each row |
| `onRowClick` | (row: T, index: number) => void | No | | Row click handler |
| `editMode` | boolean | No | | Whether table is in edit mode |
| `onEditModeChange` | (editMode: boolean) => void | No | | Edit mode toggle callback |
| `onCellEdit` | (rowIndex: number, columnKey: string, newValue: any) => void | No | | Commit cell edit |
| `bulkActions` | Array<{ key: string; label: string; icon?: React.ReactNode; action: (selectedRows: (string \| number)[], data: T[]) => void; }> | No | | Bulk action definitions |
| `variant` | 'default' \| 'striped' \| 'bordered' | No | | Visual table variant |
| `density` | 'compact' \| 'normal' \| 'comfortable' | No | | Row density |
| `height` | number | No | | Fixed table height (enables internal scroll) |
| `virtual` | boolean | No | | Enable FlashList-powered virtualization for large datasets |
| `style` | any | No | | Container style override |
| `enableColumnResizing` | boolean | No | | Enable interactive column resizing |
| `rowFeatureToggle` | (row: T, index: number) => ({ selectable?: boolean; editable?: boolean; sortable?: boolean; filterable?: boolean; searchable?: boolean; } \| null \| undefined) | No | | Per-row feature overrides |
| `initialHiddenColumns` | string[] | No | | Initially hidden column keys |
| `onColumnVisibilityChange` | (hidden: string[]) => void | No | | Hidden column change callback |
| `showColumnVisibilityManager` | boolean | No | | Show built-in column visibility manager button |
| `rowsPerPageOptions` | number[] | No | | Pagination size choices |
| `showRowsPerPageControl` | boolean | No | | Show rows-per-page selector |
| `rowActions` | (row: T, index: number) => Array<{ key: string; icon?: React.ReactNode; label?: string; onPress?: (row: T, index: number) => void; disabled?: boolean; hidden?: boolean; tooltip?: TooltipPropValue; }> | No | | Per-row action icon buttons (renders trailing actions column when provided) |
| `actionsColumnWidth` | number | No | | Width of the actions column |
| `striped` | boolean | No | | Force striped row backgrounds regardless of variant |
| `headerBackgroundColor` | string | No | | Custom header background color |
| `enhancedLoading` | boolean | No | | Show enhanced loading skeletons instead of basic loading text |
| `enhancedEmptyState` | boolean | No | | Show enhanced empty state with icon and description |
| `enhancedHover` | boolean | No | | Enable enhanced hover effects |
| `hoverColor` | string | No | | Custom row hover color |
| `enhancedSelection` | boolean | No | | Enable enhanced selection styling |
| `showRowDividers` | boolean | No | | Horizontal hairlines between rows. Defaults to on for `variant="bordered"` and off otherwise; set explicitly to override either way. `rowBorderWidth` takes precedence when provided. |
| `borderColor` | string | No | | Custom border color for enhanced styling |
| `hoverHighlight` | boolean | No | | Enable simple row background hover highlight |
| `fullWidth` | boolean | No | | Make table take full width of container |
| `rowBorderWidth` | number | No | | Row border width. Overrides `showRowDividers` / the variant default, including at 0. |
| `rowBorderColor` | string | No | | Custom row border color |
| `rowBorderStyle` | 'solid' \| 'dashed' \| 'dotted' | No | | Row border style |
| `columnBorderWidth` | number | No | | Vertical rules between columns, off unless set β `variant="bordered"` only draws row dividers and the outer border. The rule spans the header, filter row, body, and group/footer rows. |
| `columnBorderColor` | string | No | | Custom column border color |
| `columnBorderStyle` | 'solid' \| 'dashed' \| 'dotted' | No | | Column border style |
| `showOuterBorder` | boolean | No | | Whether to show outer border around entire table. Defaults to `true`. |
| `outerBorderWidth` | number | No | | Outer border width |
| `outerBorderColor` | string | No | | Outer border color |
| `expandableRowRender` | (row: T, index: number) => React.ReactNode | No | | Function to render expanded row content |
| `initialExpandedRows` | (string \| number)[] | No | | Initially expanded row identifiers |
| `expandedRows` | (string \| number)[] | No | | Controlled expanded rows |
| `onExpandedRowsChange` | (expanded: (string \| number)[]) => void | No | | Expanded rows change handler |
| `allowMultipleExpanded` | boolean | No | | Allow multiple rows to be expanded at once |
| `expandIcon` | React.ReactNode | No | | Custom expand/collapse icons |
| `collapseIcon` | React.ReactNode | No | | |
| `headerTextProps` | Omit | No | | Override props applied to every column header `` (style, weight, ff, size, color). |
| `cellTextProps` | Omit | No | | Override props applied to default-rendered cell text (cells without a custom `cell` renderer). |
| `ariaLabel` | string | No | | Accessible name for the grid, exposed as `aria-label` on web (screen readers announce it when entering the table). Defaults to "Data table". |
| `exportable` | boolean | No | | Show a CSV export button in the toolbar. Exports the current view (filtered + sorted, all pages) using the visible columns. |
| `exportFileName` | string | No | | File name for the downloaded CSV (default: "data.csv"). |
| `onExport` | (csv: string, rows: T[]) => void | No | | Called with the generated CSV string and the exported rows. When provided it replaces the built-in web download (use it to handle export on native or to post the data elsewhere). |
| `enableColumnReordering` | boolean | No | | Enable drag-to-reorder of column headers (web). |
| `columnOrder` | string[] | No | | Controlled column order (array of column keys). |
| `onColumnOrderChange` | (order: string[]) => void | No | | Called with the new key order after a drag-reorder. |
| `groupBy` | string | No | | Group rows by this column key. Renders a collapsible group-header row before each group (showing the value, count, and per-column aggregates). Grouping spans all filtered rows, so client pagination is bypassed while active, and it is not applied in `virtual` mode. |
| `groupsDefaultExpanded` | boolean | No | | Whether groups start expanded (default: true). |
| `renderGroupHeader` | (info: { value: any; rows: T[]; count: number; expanded: boolean; toggle: () => void; }) => React.ReactNode | No | | Custom renderer for the group-header label cell. |
| `showFooterTotals` | boolean | No | | Render a footer row with grand-total aggregates for aggregate columns. |
| `footerLabel` | string | No | | Label shown in the first cell of the footer totals row (default: "Total"). |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Getting Started
ID: `DataTable.basic` β’ Tags: datatable β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Define columns, feed the dataset, and let `DataTable` handle search, sorting, and pagination out of the box.
```tsx
const columns: DataTableColumn[] = [
{ key: 'name', header: 'Name', accessor: 'name', sortable: true },
{ key: 'email', header: 'Email', accessor: 'email', sortable: true, minWidth: 200 },
{ key: 'title', header: 'Role', accessor: 'title', sortable: true },
{ key: 'department', header: 'Department', accessor: 'department', sortable: true },
];
const [sortBy, setSortBy] = useState([]);
const [pagination, setPagination] = useState({
page: 1,
pageSize: 5,
total: people.length,
});
return (
);
}
```
### Column Filters
ID: `DataTable.advanced-filtering` β’ Tags: datatable, filters β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Mark columns `filterable` and set `filterType` to pick the control: an input for `text`/`number`/`date`, a dropdown for `select`/`boolean` (options auto-derived from the data when `filterOptions` is omitted). `showColumnFilters` renders those controls as a persistent row under the headers; omit it to keep them in each header's filter menu.
```tsx
const columns: DataTableColumn[] = [
// text β inline text input
{ key: 'name', header: 'Name', accessor: 'name', sortable: true, filterable: true, filterType: 'text' },
// select with explicit options β dropdown
{
key: 'department',
header: 'Department',
accessor: 'department',
sortable: true,
filterable: true,
filterType: 'select',
filterOptions: departmentFilterOptions,
},
{
key: 'status',
header: 'Status',
accessor: 'status',
sortable: true,
filterable: true,
filterType: 'select',
filterOptions: statusFilterOptions,
},
// boolean β Yes / No / All dropdown
{ key: 'remote', header: 'Remote', accessor: 'remote', filterable: true, filterType: 'boolean', cell: (v) => (v ? 'Yes' : 'No') },
// number β inline numeric input
{
key: 'salary',
header: 'Salary',
accessor: 'salary',
sortable: true,
filterable: true,
filterType: 'number',
dataType: 'currency',
align: 'right',
},
];
const [filters, setFilters] = useState([]);
return (
);
}
```
### Row Selection
ID: `DataTable.row-selection` β’ Tags: datatable, selection β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Set `selectable` and wire `selectedRows` / `onSelectionChange` to track checked rows. Give `getRowId` so selection survives sorting and paging.
```tsx
const columns: DataTableColumn[] = [
{ key: 'name', header: 'Name', accessor: 'name', sortable: true },
{ key: 'email', header: 'Email', accessor: 'email', sortable: true, minWidth: 200 },
{ key: 'role', header: 'Role', accessor: 'role', sortable: true },
];
const [pagination, setPagination] = useState({
page: 1,
pageSize: 5,
total: people.length,
});
const [selectedRows, setSelectedRows] = useState<(string | number)[]>([]);
return (
{selectedRows.length ? `${selectedRows.length} selected` : 'No rows selected'}
row.id}
searchable={false}
/>
);
}
```
### Rich Cells
ID: `DataTable.enhanced-styling` β’ Tags: datatable, styling β’ Category: appearance β’ Status: stable β’ Since: 1.0.0
Combine avatars, chips, and status cues inside custom `cell` renderers to create a readable, on-brand table.
```tsx
const rows = people.slice(0, 5);
const columns: DataTableColumn[] = [
{
key: 'name',
header: 'Teammate',
accessor: 'name',
sortable: true,
cell: (_value, row) => (
part[0])
.join('')}
label={{row.name} }
description={{row.title} }
gap={8}
/>
),
},
{
key: 'team',
header: 'Team',
accessor: 'team',
sortable: true,
cell: (value) => (
{value}
),
},
{
key: 'status',
header: 'Status',
accessor: 'status',
sortable: true,
cell: (value: Person['status']) => (
{value.charAt(0).toUpperCase() + value.slice(1)}
),
},
{
key: 'performance',
header: 'Score',
accessor: 'performance',
sortable: true,
align: 'right',
cell: (value) => {value.toFixed(1)} ,
},
];
return (
);
}
```
### Expandable Rows
ID: `DataTable.expandable-rows` β’ Tags: datatable, expand β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Provide `expandedRows`, update them via `onExpandedRowsChange`, and use `expandableRowRender` to reveal supporting context.
```tsx
const columns: DataTableColumn[] = [
{ key: 'name', header: 'Project', accessor: 'name', sortable: true },
{ key: 'owner', header: 'Owner', accessor: 'owner', sortable: true },
{
key: 'budget',
header: 'Budget',
accessor: 'budget',
align: 'right',
sortable: true,
dataType: 'currency',
},
];
const [expandedRows, setExpandedRows] = useState<(string | number)[]>([projects[0].id]);
return (
row.id}
expandedRows={expandedRows}
onExpandedRowsChange={setExpandedRows}
expandableRowRender={(project) => (
{project.summary}
)}
searchable={false}
/>
);
}
```
### Grouping & Totals
ID: `DataTable.grouping` β’ Tags: datatable, grouping, aggregation, totals, footer β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Set `groupBy` to a column key to group rows under collapsible group-header rows. Add `aggregate` (`sum`, `avg`, `min`, `max`, `count`, or a function) to any column to show its per-group total in the group header, and set `showFooterTotals` for a grand-total footer row aligned to the same columns. Grouping spans all filtered rows, so client pagination is bypassed while it is active.
```tsx
const columns: DataTableColumn[] = [
{ key: 'region', header: 'Region', accessor: 'region' },
{ key: 'rep', header: 'Rep', accessor: 'rep', aggregate: 'count' },
{ key: 'product', header: 'Product', accessor: 'product' },
{ key: 'units', header: 'Units', accessor: 'units', dataType: 'number', align: 'right', aggregate: 'sum' },
{ key: 'revenue', header: 'Revenue', accessor: 'revenue', dataType: 'currency', align: 'right', aggregate: 'sum' },
];
return (
);
}
```
### Fixed height & sticky columns
ID: `DataTable.fixed-height` β’ Tags: datatable, height, scroll, sticky-header, sticky, pinned, columns β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Pass a fixed `height` to cap the table's size β the header row stays pinned while the body scrolls, so a long list fits a constrained panel without paginating. Pin columns to the edges with `sticky: 'left'` or `sticky: 'right'` so they stay put while the rest scroll horizontally; give each pinned column an explicit numeric `width` so its frozen offset lines up. Sticky positioning is web-only (a no-op on native).
```tsx
type Server = {
id: number;
host: string;
region: string;
cpu: string;
memory: string;
uptime: string;
status: 'healthy' | 'degraded' | 'offline';
};
const REGIONS = ['us-east-1', 'us-west-2', 'eu-west-1', 'ap-south-1'];
const STATUSES: Server['status'][] = ['healthy', 'degraded', 'offline'];
const rows: Server[] = Array.from({ length: 40 }, (_, i) => ({
id: i + 1,
host: `node-${String(i + 1).padStart(2, '0')}.cluster.internal`,
region: REGIONS[i % REGIONS.length],
cpu: `${((i * 7) % 90) + 5}%`,
memory: `${((i * 13) % 80) + 10}%`,
uptime: `${(i % 30) + 1}d`,
status: STATUSES[i % STATUSES.length],
}));
const columns: DataTableColumn[] = [
// Pinned left, so the host stays visible while the rest scroll horizontally.
{ key: 'host', header: 'Host', accessor: 'host', sticky: 'left', width: 240, sortable: true },
{ key: 'region', header: 'Region', accessor: 'region', width: 160, sortable: true },
{ key: 'cpu', header: 'CPU', accessor: 'cpu', width: 120, align: 'right', sortable: true },
{ key: 'memory', header: 'Memory', accessor: 'memory', width: 120, align: 'right', sortable: true },
{ key: 'uptime', header: 'Uptime', accessor: 'uptime', width: 120, align: 'right' },
{ key: 'status', header: 'Status', accessor: 'status', sticky: 'right', width: 140, sortable: true },
];
return (
row.id}
height={320}
fullWidth={false}
searchable={false}
/>
);
}
```
### Server-side pagination
ID: `DataTable.server-side` β’ Tags: datatable, pagination, server, api, manual β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Set `manualPagination` when the data comes from a paginated API. The `data` prop is treated as the already-fetched current page β the table does no client-side slicing, filtering, or sorting β and `pagination.total` drives the page count and "X-Y of N" summary. The sort, filter, search, and page controls still fire their callbacks (`onSortChange`, `onFilterChange`, `onSearchChange`, `onPaginationChange`) so you can refetch. Pair it with `loading` to show the skeleton during each fetch.
```tsx
type Order = {
id: number;
customer: string;
product: string;
amount: number;
};
// Pretend this table lives on a server; the component only ever sees one page.
const DB: Order[] = Array.from({ length: 137 }, (_, i) => ({
id: i + 1,
customer: `Customer ${String(i + 1).padStart(3, '0')}`,
product: ['Starter', 'Pro', 'Team', 'Enterprise'][i % 4],
amount: Math.round(((i * 37) % 900) + 100),
}));
// Simulate an API endpoint: GET /orders?page&pageSize&sort
function fetchOrders(
page: number,
pageSize: number,
sort?: DataTableSort
): Promise<{ rows: Order[]; total: number }> {
return new Promise((resolve) => {
setTimeout(() => {
const sorted = [...DB];
if (sort?.direction) {
sorted.sort((a, b) => {
const av = a[sort.column as keyof Order];
const bv = b[sort.column as keyof Order];
const cmp = typeof av === 'number' && typeof bv === 'number' ? av - bv : String(av).localeCompare(String(bv));
return sort.direction === 'desc' ? -cmp : cmp;
});
}
const start = (page - 1) * pageSize;
resolve({ rows: sorted.slice(start, start + pageSize), total: DB.length });
}, 500);
});
}
const columns: DataTableColumn[] = [
{ key: 'id', header: 'Order', accessor: 'id', sortable: true, dataType: 'number' },
{ key: 'customer', header: 'Customer', accessor: 'customer', sortable: true },
{ key: 'product', header: 'Plan', accessor: 'product', sortable: true },
{ key: 'amount', header: 'Amount', accessor: 'amount', sortable: true, dataType: 'currency' },
];
const [rows, setRows] = useState([]);
const [loading, setLoading] = useState(true);
const [sortBy, setSortBy] = useState([]);
const [pagination, setPagination] = useState({ page: 1, pageSize: 10, total: 0 });
// Refetch whenever the page, page size, or sort changes.
useEffect(() => {
let cancelled = false;
setLoading(true);
fetchOrders(pagination.page, pagination.pageSize, sortBy[0]).then((res) => {
if (cancelled) return;
setRows(res.rows);
setPagination((p) => (p.total === res.total ? p : { ...p, total: res.total }));
setLoading(false);
});
return () => {
cancelled = true;
};
}, [pagination.page, pagination.pageSize, sortBy]);
return (
row.id}
searchable={false}
/>
);
}
```
--------------------------------------------------------------------------------
# DatePicker
DatePicker renders an inline calendar focused on keyboard-friendly, accessible selection flows. Pair it with `DatePickerInput` when you need an input trigger and modal or popover calendar.
## Metadata
- Canonical name: `DatePicker`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { DatePicker } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: dates
- Docs: https://react-ui-library.com/components/DatePicker
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/DatePicker
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | CalendarValue | No | | Selected value; type depends on `type` prop |
| `defaultValue` | CalendarValue | No | | Initial value for uncontrolled usage |
| `onChange` | (value: CalendarValue) => void | No | | Called when value changes |
| `type` | CalendarType | No | | Selection behavior |
| `calendarProps` | Partial | No | | Pass-through customization for underlying Calendar |
| `style` | StyleProp | No | | Optional container style for the inline calendar |
| `testID` | string | No | | Test identifier |
| `accessibilityLabel` | string | No | | Accessibility label for the inline calendar region |
| `accessibilityHint` | string | No | | Accessibility hint for the inline calendar region |
## Examples
### Basic Date Picker
ID: `DatePicker.basic` β’ Tags: basic, single-date, label, placeholder β’ Category: general
Standard single date selection with label and placeholder text.
```tsx
const [value, setValue] = useState(null);
return (
setValue(next as Date | null)}
calendarProps={{ numberOfMonths: 1, highlightToday: true }}
/>
{value ? `Selected: ${value.toLocaleDateString()}` : 'No date selected'}
);
}
```
### Date Range Picker
ID: `DatePicker.range` β’ Tags: range, start-date, end-date, date-range β’ Category: general
Select a range of dates with start and end date selection.
```tsx
const [value, setValue] = useState<[Date | null, Date | null] | null>(null);
const start = value?.[0];
const end = value?.[1];
return (
setValue(next as [Date | null, Date | null] | null)}
calendarProps={{ numberOfMonths: 2, withCellSpacing: true }}
/>
{start && end
? `${start.toLocaleDateString()} β ${end.toLocaleDateString()}`
: 'Select a start and end date'}
);
}
```
### Date Validation
ID: `DatePicker.validation` β’ Tags: validation, error, rules, min-date, future-date β’ Category: general
Date picker with validation rules and error handling for invalid selections.
```tsx
const [value, setValue] = useState(null);
const [inlineError, setInlineError] = useState('');
const today = useMemo(() => {
const date = new Date();
date.setHours(0, 0, 0, 0);
return date;
}, []);
const handleChange = (next: Date | [Date | null, Date | null] | Date[] | null) => {
const dateValue = next as Date | null;
setValue(dateValue);
setInlineError(dateValue && dateValue < today ? 'Date cannot be in the past' : '');
};
return (
{inlineError || 'Only dates today or later are enabled'}
);
}
```
### Multiple Dates Selection
ID: `DatePicker.multiple` β’ Tags: multiple, independent-dates, array, events β’ Category: general
Select multiple independent dates for events or availability.
```tsx
const [value, setValue] = useState([]);
return (
setValue((next as Date[]) ?? [])}
calendarProps={{ numberOfMonths: 2, withCellSpacing: true }}
/>
{value.length > 0
? value.map((date) => date.toLocaleDateString()).join(', ')
: 'Select one or more dates'}
);
}
```
--------------------------------------------------------------------------------
# DatePickerInput
DatePickerInput wraps the inline `DatePicker` in an accessible input experience. It handles focus management, modal presentation, and formatting so people can choose dates without leaving the form flow.
## Metadata
- Canonical name: `DatePickerInput`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { DatePickerInput } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: dates
- Docs: https://react-ui-library.com/components/DatePickerInput
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/DatePickerInput
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | CalendarValue | No | | Selected value; type depends on `type` prop |
| `defaultValue` | CalendarValue | No | | Initial value for uncontrolled usage |
| `onChange` | (value: CalendarValue) => void | No | | Called when value changes |
| `type` | CalendarType | No | | Selection behavior |
| `calendarProps` | Partial | No | | Pass-through customization for underlying Calendar |
| `placeholder` | string | No | | Input placeholder text |
| `displayFormat` | string | No | | Format string for displaying value in the input |
| `valueFormat` | string | No | | Serialization/parsing format (reserved for future) |
| `clearable` | boolean | No | | Show a clear button |
| `size` | SizeValue | No | | Visual size |
| `disabled` | boolean | No | | Disable interaction |
| `withAsterisk` | boolean | No | | Show required indicator |
| `dropdownType` | 'modal' \| 'popover' | No | | Presentation modality |
| `closeOnSelect` | boolean | No | | Close picker after single selection (for single mode) |
| `onOpen` | () => void | No | | Lifecycle events |
| `onClose` | () => void | No | | |
| `onFocus` | () => void | No | | |
| `onBlur` | () => void | No | | |
| `variant` | InputVariant | No | | Visual variant of the input. `default` (light surface + border), `filled` (gray fill, no border), `outline` (transparent fill, border only), `unstyled` (no border, no fill). |
| `label` | React.ReactNode | No | | Input label (string or component) |
| `required` | boolean | No | | Whether input is required |
| `error` | string | No | | Error message |
| `helperText` | string | No | | Helper text |
| `description` | string | No | | Optional short description displayed directly under the label (above the field) |
| `name` | string | No | | Input name for form integration |
| `startSection` | React.ReactNode | No | | Left section content |
| `endSection` | React.ReactNode | No | | Right section content |
| `style` | any | No | | Additional styling |
| `accessibilityLabel` | string | No | | Accessibility label |
| `accessibilityHint` | string | No | | Accessibility hint |
| `testID` | string | No | | Test ID for testing |
| `debounceMs` | number | No | | Debounce delay for validation in milliseconds |
| `onEnter` | () => void | No | | Enter key press handler |
| `clearButtonLabel` | string | No | | Accessible label for the clear button |
| `onClear` | () => void | No | | Callback when the clear button is pressed |
| `keyboardFocusId` | string | No | | Identifier used with KeyboardManagerProvider to request refocus |
| `labelProps` | Omit | No | | Override props applied to the field label `` (style, weight, ff, etc.) |
| `descriptionProps` | Omit | No | | Override props applied to the field description `` |
| `placeholderTextColor` | string | No | | Color of the placeholder text. Falls back to `theme.text.muted`. |
| `startSectionProps` | Omit | No | | Props applied to the wrapping `` around `startSection` (style, accessibility, etc.). |
| `endSectionProps` | Omit | No | | Props applied to the wrapping `` around `endSection`. |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
## Examples
### Basic
ID: `DatePickerInput.basic` β’ Category: general
```tsx
const [value, setValue] = useState(null);
return (
setValue(next as Date | null)}
placeholder="Select a date"
label="Date"
clearable
fullWidth
/>
{value ? `Selected: ${value.toLocaleDateString()}` : 'No date selected'}
);
}
```
### Multiple
ID: `DatePickerInput.multiple` β’ Category: general
```tsx
const [value, setValue] = useState([]);
return (
setValue((next as Date[]) || [])}
label="Multiple dates"
placeholder="Select dates"
fullWidth
/>
{value.length > 0
? `Selected: ${value.map((date) => date.toLocaleDateString()).join(', ')}`
: 'Select one or more dates'}
);
}
```
### Range
ID: `DatePickerInput.range` β’ Category: general
```tsx
const [value, setValue] = useState<[Date | null, Date | null] | null>(null);
return (
setValue((next as [Date | null, Date | null]) || null)}
label="Date range"
placeholder="Select range"
closeOnSelect
fullWidth
/>
{value && value[0] && value[1]
? `${value[0].toLocaleDateString()} β ${value[1].toLocaleDateString()}`
: 'Select a start and end date'}
);
}
```
### Validation
ID: `DatePickerInput.validation` β’ Category: general
```tsx
const [value, setValue] = useState(null);
const [error, setError] = useState();
const today = useMemo(() => {
const date = new Date();
date.setHours(0, 0, 0, 0);
return date;
}, []);
const handleChange = (next: Date | [Date | null, Date | null] | Date[] | null) => {
const dateValue = next as Date | null;
setValue(dateValue);
if (dateValue && dateValue < today) {
setError('Date cannot be in the past');
} else {
setError(undefined);
}
};
return (
Past dates show the validation state
);
}
```
--------------------------------------------------------------------------------
# Dialog
The Dialog component presents content above the app, supporting focus trapping, scroll locking, and multiple presentation styles (modal, confirmation, bottom sheet).
## Metadata
- Canonical name: `Dialog`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Dialog } from '@platform-blocks/react-ui-library';`
- Status: experimental
- Category: overlay
- Tags: modal, dialog, overlay, sheet
- Docs: https://react-ui-library.com/components/Dialog
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Dialog
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `visible` | boolean | Yes | | Controls whether the dialog is visible. |
| `variant` | 'modal' \| 'bottomsheet' \| 'fullscreen' | No | | Presentation style of the dialog. |
| `title` | string \| null | No | | Optional title text shown in the header area. |
| `children` | ReactNode | Yes | | Dialog body content. |
| `closable` | boolean | No | | Allows the user to close the dialog via UI controls or escape/back. |
| `backdrop` | boolean | No | | Whether to render the dimming backdrop behind the dialog. |
| `backdropClosable` | boolean | No | | Whether tapping the backdrop should close the dialog. |
| `shouldClose` | boolean | No | | Triggers close animation when set to true. |
| `onClose` | () => void | No | | Called when the dialog requests to close. |
| `w` | number | No | | Optional explicit width for the dialog content (modal/bottomsheet). |
| `h` | number | No | | Optional explicit height for the dialog content. |
| `radius` | number | No | | Corner radius for the dialog container (bottom sheet rounds top corners only). |
| `style` | object | No | | Optional style overrides for the dialog container. |
| `showHeader` | boolean | No | | Whether to show the styled header area with background and border (default true). |
| `bottomSheetSwipeZone` | 'container' \| 'handle' \| 'none' | No | | Controls which part of the bottom sheet responds to swipe-to-dismiss gestures |
| `transitionDuration` | number | No | 300 | Length of the open/close transition in ms; the built-in timings scale against a 300ms baseline. `0` shows and dismisses the dialog instantly. Always 0 under reduced motion. |
| `titleProps` | Omit | No | | Override props applied to the title `` (style, weight, ff, size, color). |
| `autoFocus` | DialogAutoFocus | No | false | Moves focus into the dialog once it has finished opening. See {@link DialogAutoFocus}. |
| `trapFocus` | boolean | No | true | Keeps Tab focus cycling inside the dialog while it is open and restores focus to the previously focused element when it closes. Web only. |
## Examples
### Basic Modal
ID: `Dialog.basic` β’ Tags: dialog, modal, actions β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Call `openDialog` with `variant: 'modal'` to show a titled dialog and wire action buttons to `closeDialog` when the user makes a choice.
```tsx
const { openDialog, closeDialog } = useDialog();
const showBasicDialog = () => {
const dialogId = openDialog({
variant: 'modal',
title: 'Basic Dialog',
content: (
This is a basic modal dialog with theme-aware styling.
Works in both light and dark mode.
closeDialog(dialogId)}>
Cancel
{
console.log('OK button pressed!');
closeDialog(dialogId);
}}
variant="filled"
>
OK
)
});
};
return (
Open Basic Dialog
);
}
```
### Bottom Sheet
ID: `Dialog.bottomsheet` β’ Tags: dialog, bottomsheet, gestures β’ Category: variants β’ Status: stable β’ Since: 1.0.0
Switch the dialog `variant` to `'bottomsheet'` to get swipe-to-dismiss behavior and retain full control with `closeDialog` handlers.
```tsx
const { openDialog, closeDialog } = useDialog();
const showBottomSheetDialog = () => {
const dialogId = openDialog({
variant: 'bottomsheet',
content: (
This dialog slides up from the bottom with theme-aware styling.
Drag the handle or surface to move it.
Swipe down to dismiss with velocity thresholds.
Rubber-band resistance keeps the sheet anchored.
closeDialog(dialogId)}>
Close programmatically
)
});
};
return (
Open Bottom Sheet
);
}
```
### Confirmation
ID: `Dialog.confirmation` β’ Tags: dialog, confirmation, destructive β’ Category: patterns β’ Status: stable β’ Since: 1.0.0
Pair `variant: 'modal'` with a destructive button (`color="error"`) to confirm irreversible actions before calling your business logic.
```tsx
const { openDialog, closeDialog } = useDialog();
const showConfirmationDialog = () => {
const dialogId = openDialog({
variant: 'modal',
title: 'Confirm Action',
content: (
Are you sure you want to delete this item?
This action cannot be undone.
closeDialog(dialogId)}>
Cancel
{
console.log('Item has been deleted');
closeDialog(dialogId);
}}
>
Delete
)
});
};
return (
Show Confirmation
);
}
```
### Form Dialog
ID: `Dialog.form` β’ Tags: dialog, forms, validation β’ Category: patterns β’ Status: stable β’ Since: 1.0.0
Embed inputs in the dialog `content`, collect values via controlled callbacks, and validate before resolving the promise or calling `closeDialog`.
```tsx
const { openDialog, closeDialog } = useDialog();
const nameRef = useRef(null);
const showFormDialog = () => {
let formData = { name: '', email: '' };
const dialogId = openDialog({
variant: 'modal',
title: 'Create Account',
// Focus the name field once the open transition settles. `autoFocus: true`
// picks the first focusable field automatically, but only on web β a ref
// works on every platform.
autoFocus: nameRef,
content: (
Fill in your details to create an account.
{
formData.name = text;
}}
/>
{
formData.email = text;
}}
/>
closeDialog(dialogId)}>
Cancel
{
if (!formData.name || !formData.email) {
Alert.alert('Error', 'Please fill in all fields');
return;
}
Alert.alert('Success', `Account created for ${formData.name}`);
closeDialog(dialogId);
}}
variant="filled"
>
Create account
)
});
};
return (
Open Form Dialog
);
}
```
### Title customization
ID: `Dialog.title-customization` β’ Tags: titleProps, customization, slot-props β’ Category: general β’ Status: stable β’ Since: 1.0.0
`titleProps` accepts any `` props (`ff`, `weight`, `tracking`, `uppercase`, `size`, `color`, `style`) and applies them to the dialog header without changing the rest of the chrome. The same prop is also accepted by `openDialog({ titleProps })` for imperative dialogs.
```tsx
const { openDialog, closeDialog } = useDialog();
const open = (titleProps: any) => {
const id = openDialog({
variant: 'modal',
title: 'Welcome aboard',
titleProps,
content: (
Dialog title styled via `titleProps`.
closeDialog(id)}>Close
),
});
};
return (
open(undefined)}>Default
open({
uppercase: true,
tracking: 1.5,
weight: '700',
size: 'sm',
})
}
>
Uppercase tracked
open({
ff: 'Georgia, serif',
size: 'xl',
weight: '600',
})
}
>
Serif headline
open({
color: 'primary',
weight: '700',
ff: 'monospace',
})
}
>
Brand-coloured monospace
);
}
```
--------------------------------------------------------------------------------
# Divider
The Divider component provides a visual separator between content sections. Supports horizontal and vertical orientations, four visual variants (`solid`, `dashed`, `dotted`, `gradient`), an aligned `color` vocabulary with a soft default tuned for separators, an `opacity` shorthand, and optional labels.
## Metadata
- Canonical name: `Divider`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Divider } from '@platform-blocks/react-ui-library';`
- Category: layout
- Tags: divider, separator, line, section
- Docs: https://react-ui-library.com/components/Divider
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Divider
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `orientation` | DividerOrientation | No | | Layout direction of the line. `'horizontal'` spans width; `'vertical'` spans height. Defaults to `'horizontal'`. |
| `variant` | DividerVariant | No | | Visual style of the line. `'gradient'` fades transparent β color β transparent. Defaults to `'solid'`. |
| `color` | ThemeColor | No | | Line color. Accepts the named tokens `'border'` / `'subtle'` / `'muted'`, a palette name (`'success'` β a shade well below the accent, so a tinted rule still reads as chrome), `'primary.6'` shade syntax, or any CSS color. Defaults to `'border'`. |
| `size` | SizeValue \| number | No | | Thickness of the divider (default 1). Accepts a size token or pixel value. |
| `opacity` | number | No | | Multiplied with the divider's overall opacity. Convenience prop equivalent to `style={{ opacity }}`. |
| `label` | React.ReactNode | No | | Optional content rendered in the middle of the line. |
| `labelPosition` | 'left' \| 'center' \| 'right' | No | | Where the `label` sits along the line. Defaults to `'center'`. |
| `labelProps` | Omit | No | | Override props applied to the label `` (only when `label` is a string). |
| `style` | StyleProp | No | | Style override applied to the outer wrapping `View`. |
| `testID` | string | No | | Test identifier forwarded to the wrapping `View`. |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic Usage
ID: `Divider.basic` β’ Tags: solid, dashed β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Insert horizontal dividers between sections to separate content; switch the `variant` prop to toggle between solid and dashed lines.
```tsx
return (
Q1 Highlights
Revenue grew 12% year over year.
Customer retention improved across every region.
Product roadmap updates will ship next quarter.
);
}
```
### Color Variants
ID: `Divider.colors` β’ Tags: color, label, variant β’ Category: theming β’ Status: stable β’ Since: 1.0.0
Select a `color` to match semantic palettes and combine it with `label` plus `variant` to fit your divider into content sections.
```tsx
const COLORS: Array<{ label: string; tone?: DividerProps['color'] }> = [
{ label: 'Border (default)' },
{ label: 'Subtle', tone: 'subtle' },
{ label: 'Muted', tone: 'muted' },
{ label: 'Gray', tone: 'gray' },
{ label: 'Primary', tone: 'primary' },
{ label: 'Secondary', tone: 'secondary' },
{ label: 'Success', tone: 'success' },
{ label: 'Warning', tone: 'warning' },
{ label: 'Error', tone: 'error' },
];
return (
Semantic color variants
{COLORS.map(({ label, tone }) => (
{label}
))}
Labeled dividers
Variant styles
);
}
```
### Gradient & opacity
ID: `Divider.gradient-opacity` β’ Tags: gradient, opacity, customization, variants β’ Category: general β’ Status: stable β’ Since: 1.0.0
The `gradient` variant fades transparent β color β transparent, perfect for breaking up sections without a hard edge. The `opacity` prop is a shorthand for `style={{ opacity }}` β combine it with `color` to dial in subtle separators.
```tsx
return (
Gradient variant
Opacity prop β same color, different emphasis
Subtle separator (border default + low opacity)
Custom color + opacity
);
}
```
### Labeled Dividers
ID: `Divider.labeled` β’ Tags: label, labelPosition β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Provide a `label` node and adjust `labelPosition` plus `color` to separate form sections with contextual dividers.
```tsx
return (
Sign in with email
Continue with social accounts
Settings}
labelPosition="left"
color="secondary"
/>
Manage notification preferences
Invite admins or export account data
);
}
```
### Vertical Dividers
ID: `Divider.vertical` β’ Tags: vertical, navigation, label β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Switch `orientation="vertical"` to separate horizontal layouts like navigation and add `label` or `color` when you need emphasis.
```tsx
return (
Profile
View details
Settings
Preferences
Support
Help center
Home
Fixtures
Standings
Highlights
);
}
```
### Sizes
ID: `Divider.sizes` β’ Category: general
Demonstrates how the `size` prop accepts both numeric values and spacing tokens so you can dial in subtle, comfortable, or bold divider weights in horizontal and vertical layouts.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const;
return (
{SIZES.map((size) => (
{size}
))}
1 (numeric)
);
}
```
### Variants
ID: `Divider.variants` β’ Category: general
Showcase solid, dashed, and dotted dividers in both horizontal and vertical layouts to highlight how each variant can communicate different section breaks.
```tsx
return (
Release Notes
Default solid divider keeps sections crisp.
The winter update introduces a revamped queue and faster syncing.
Sprint Checklist
Dashed lines work nicely for in-progress flows.
QA sign-off, regression pass, and rollout comms are scheduled for Friday.
Creator Status
Dotted borders add a softer visual break.
Enable payouts once verification documents finish processing.
Section break
Gradient variant fades the line in and out β softer than a hard rule.
The fade keeps long-form content breathable without dropping a horizontal stripe.
Published
Drafts
Scheduled
Archived
);
}
```
--------------------------------------------------------------------------------
# FileInput
The FileInput component provides a user-friendly interface for file uploads with drag-and-drop functionality, file validation, and preview capabilities.
## Metadata
- Canonical name: `FileInput`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { FileInput } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: input
- Docs: https://react-ui-library.com/components/FileInput
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/FileInput
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `variant` | 'standard' \| 'dropzone' \| 'compact' | No | | File input variant |
| `accept` | string[] | No | | Accepted file types (MIME types or extensions) |
| `multiple` | boolean | No | | Multiple file selection |
| `maxSize` | number | No | | Maximum file size in bytes |
| `maxFiles` | number | No | | Maximum number of files |
| `onUpload` | (files: FileInputFile[]) => Promise | No | | Upload handler |
| `onProgress` | (fileId: string, progress: number) => void | No | | Upload progress callback |
| `onFilesChange` | (files: FileInputFile[]) => void | No | | File change handler |
| `onFileRemove` | (fileId: string) => void | No | | File remove handler |
| `PreviewComponent` | React.ComponentType<{ file: FileInputFile; onRemove: () => void }> | No | | File preview component |
| `children` | React.ReactNode | No | | Custom drop zone content |
| `showFileList` | boolean | No | | Whether to show file list |
| `enableDragDrop` | boolean | No | | Whether to enable drag and drop |
| `validateFile` | (file: File \| DocumentPickerAssetLike) => string \| null | No | | Custom validation function |
| `imagePreview` | { enabled?: boolean; maxWidth?: number; maxHeight?: number; quality?: number; } | No | | Image preview settings |
| `uploadSettings` | { url?: string; method?: 'POST' \| 'PUT'; headers?: Record; fieldName?: string; formData?: Record; } | No | | Upload settings |
| `value` | string | No | | Input value |
| `onChangeText` | (text: string) => void | No | | Change handler |
| `label` | React.ReactNode | No | | Input label (string or component) |
| `disabled` | boolean | No | | Whether input is disabled |
| `required` | boolean | No | | Whether input is required |
| `placeholder` | string | No | | Input placeholder |
| `error` | string | No | | Error message |
| `helperText` | string | No | | Helper text |
| `description` | string | No | | Optional short description displayed directly under the label (above the field) |
| `size` | SizeValue | No | | Input size |
| `withAsterisk` | boolean | No | | Whether to show required indicator |
| `name` | string | No | | Input name for form integration |
| `startSection` | React.ReactNode | No | | Left section content |
| `endSection` | React.ReactNode | No | | Right section content |
| `style` | any | No | | Additional styling |
| `accessibilityLabel` | string | No | | Accessibility label |
| `accessibilityHint` | string | No | | Accessibility hint |
| `testID` | string | No | | Test ID for testing |
| `debounceMs` | number | No | | Debounce delay for validation in milliseconds |
| `onFocus` | () => void | No | | Focus handler |
| `onBlur` | () => void | No | | Blur handler |
| `onEnter` | () => void | No | | Enter key press handler |
| `clearable` | boolean | No | | Show built-in clear button when input has value |
| `clearButtonLabel` | string | No | | Accessible label for the clear button |
| `onClear` | () => void | No | | Callback when the clear button is pressed |
| `keyboardFocusId` | string | No | | Identifier used with KeyboardManagerProvider to request refocus |
| `labelProps` | Omit | No | | Override props applied to the field label `` (style, weight, ff, etc.) |
| `descriptionProps` | Omit | No | | Override props applied to the field description `` |
| `placeholderTextColor` | string | No | | Color of the placeholder text. Falls back to `theme.text.muted`. |
| `startSectionProps` | Omit | No | | Props applied to the wrapping `` around `startSection` (style, accessibility, etc.). |
| `endSectionProps` | Omit | No | | Props applied to the wrapping `` around `endSection`. |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
## Examples
### Basic
ID: `FileInput.basic` β’ Tags: basic, upload, files β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Simple file input with helper text and multiple file selection.
```tsx
const [files, setFiles] = useState([]);
return (
{files.length > 0 && (
Selected: {files.map((file) => file.name).join(', ')}
)}
);
}
```
### Dropzone
ID: `FileInput.dropzone` β’ Tags: dropzone, drag-and-drop, upload β’ Category: variants β’ Status: stable β’ Since: 1.0.0
Drag-and-drop dropzone variant with native fallback instructions and selected file list.
```tsx
const [files, setFiles] = useState([]);
const instructions =
Platform.OS === 'web'
? 'Drag files into the dropzone or click Browse files to pick from your desktop.'
: 'Tap the dropzone to open the native file picker on touch devices.';
return (
{instructions}
{files.length > 0 && (
Selected files ({files.length})
{files.map((file) => (
{file.name}
))}
)}
);
}
```
### File Type Restrictions
ID: `FileInput.fileTypes` β’ Tags: accept, validation, upload β’ Category: features β’ Status: stable β’ Since: 1.0.0
Configure different file inputs with MIME filters, extension lists, and size limits.
```tsx
const [imageFiles, setImageFiles] = useState([]);
const [documentFiles, setDocumentFiles] = useState([]);
const [videoFiles, setVideoFiles] = useState([]);
return (
File type restrictions
Limit accepted file types per uploader using MIME types, extensions, and size caps.
Images only
{imageFiles.length > 0 && (
Selected: {imageFiles.map((file) => file.name).join(', ')}
)}
Documents only
{documentFiles.length > 0 && (
Selected: {documentFiles.map((file) => file.name).join(', ')}
)}
Videos (max 50MB)
{videoFiles.length > 0 && (
Selected: {videoFiles.map((file) => file.name).join(', ')}
)}
);
}
```
### Image Preview
ID: `FileInput.imagePreview` β’ Category: general
Image upload with preview thumbnails and remove functionality.
```tsx
const [images, setImages] = useState([]);
const handleRemoveFile = (index: number) => {
setImages((prev) => prev.filter((_, itemIndex) => itemIndex !== index));
};
return (
{images.length > 0 && (
Selected images ({images.length})
{images.map((file, index) => (
{file.previewUrl && (
)}
{file.name}
handleRemoveFile(index)}>
Remove
))}
)}
);
}
```
### Size Variants
ID: `FileInput.variants` β’ Category: general
Different size variants and customization options.
```tsx
const sizes = [
{ label: 'Small', size: 'sm' as const },
{ label: 'Medium (default)', size: 'md' as const },
{ label: 'Large', size: 'lg' as const },
];
const [files, setFiles] = useState>({});
const handleChange = (key: string) => (next: FileInputFile[]) => {
setFiles((prev) => ({ ...prev, [key]: next }));
};
return (
{sizes.map(({ label, size }) => (
{label}
{files[label]?.length ? (
Selected: {files[label].length}
) : null}
))}
Custom placeholder
);
}
```
### Upload Progress
ID: `FileInput.upload` β’ Category: general
File upload with progress simulation and bulk actions.
```tsx
const [files, setFiles] = useState([]);
const [isUploading, setIsUploading] = useState(false);
const [uploadProgress, setUploadProgress] = useState<{[key: string]: number}>({});
const handleUpload = async () => {
if (files.length === 0) return;
setIsUploading(true);
// Simulate upload process for each file
for (const file of files) {
// Simulate progress updates
for (let progress = 0; progress <= 100; progress += 25) {
setUploadProgress(prev => ({ ...prev, [file.name]: progress }));
await new Promise(resolve => setTimeout(resolve, 300));
}
}
setIsUploading(false);
alert('Files uploaded successfully!');
setFiles([]);
setUploadProgress({});
};
const handleRemove = (index: number) => {
setFiles(prev => prev.filter((_, i) => i !== index));
};
return (
{files.length > 0 && (
Selected files
{files.map((file, index) => (
{file.name}
{(file.size / 1024).toFixed(1)} KB
{uploadProgress[file.name] !== undefined && (
Progress: {uploadProgress[file.name]}%
)}
handleRemove(index)} disabled={isUploading}>
Remove
))}
{isUploading ? 'Uploadingβ¦' : 'Upload files'}
{
setFiles([]);
setUploadProgress({});
}}
disabled={isUploading}
>
Clear all
)}
);
}
```
### Validation & States
ID: `FileInput.validation` β’ Category: general
File input with various validation rules and states.
```tsx
const [validatedFiles, setValidatedFiles] = useState([]);
const [singleFile, setSingleFile] = useState([]);
const [limitedFiles, setLimitedFiles] = useState([]);
return (
Size validation (max 2MB)
{validatedFiles.length > 0 && (
Selected: {validatedFiles.length}
)}
Single file only
{singleFile[0] && (
Selected: {singleFile[0].name}
)}
Limited file count
{limitedFiles.length > 0 && (
Selected: {limitedFiles.length}
)}
With error state
{}}
error="Please select at least one file"
required
fullWidth
/>
Disabled state
{}}
disabled
fullWidth
/>
);
}
```
--------------------------------------------------------------------------------
# Flex
Flex provides a powerful and intuitive way to create flexible layouts using CSS Flexbox principles. It handles spacing, alignment, and direction with a clean API that works consistently across platforms.
## Metadata
- Canonical name: `Flex`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Flex } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: layout
- Docs: https://react-ui-library.com/components/Flex
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Flex
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `direction` | 'row' \| 'column' \| 'row-reverse' \| 'column-reverse' | No | | Flex direction |
| `align` | 'flex-start' \| 'flex-end' \| 'center' \| 'stretch' \| 'baseline' | No | | Align items on the cross axis |
| `justify` | 'flex-start' \| 'flex-end' \| 'center' \| 'space-between' \| 'space-around' \| 'space-evenly' | No | | Justify content on the main axis |
| `wrap` | 'nowrap' \| 'wrap' \| 'wrap-reverse' | No | | Flex wrap |
| `gap` | SizeValue | No | | Gap between children (applies to both row and column gap) |
| `rowGap` | SizeValue | No | | Row gap between children |
| `columnGap` | SizeValue | No | | Column gap between children |
| `grow` | number | No | | Flex grow |
| `shrink` | number | No | | Flex shrink |
| `basis` | DimensionValue | No | | Flex basis |
| `children` | React.ReactNode | No | | Children elements |
| `style` | StyleProp | No | | Custom styles |
| `testID` | string | No | | Test ID for testing |
| `disableRTLMirroring` | boolean | No | | Disable automatic RTL mirroring for row direction |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
## Examples
### Align Items
ID: `Flex.align` β’ Tags: align, items, cross-axis, alignment, stretch β’ Category: general
Item alignment options along the cross axis (flex-start, center, stretch, etc.).
```tsx
const ALIGNMENTS = ['flex-start', 'center', 'flex-end', 'stretch', 'baseline'] as const;
const theme = useTheme();
// Baseline is only legible if each Text has a visible box; pull the fill from
// the theme so it reads in both light and dark.
const chip = { backgroundColor: theme.backgrounds.elevated, paddingHorizontal: 8 };
return (
// wrap="wrap" β five fixed-width examples in a row would overflow on narrow
// viewports, since flex children don't shrink by default here.
{ALIGNMENTS.map((value) => (
align="{value}"
{value === 'baseline' ? (
{/* Text of varying sizes β their baselines line up, not their boxes */}
Aa
Bb
Cc
) : value === 'stretch' ? (
{/* No fixed heights so children stretch to the container's cross-size */}
1
2
3
) : (
{/* Different heights to showcase flex-start/center/flex-end */}
A
B
C
)}
))}
);
}
```
### Basic Flex Layout
ID: `Flex.basic` β’ Tags: basic, gap, layout, container β’ Category: general
Simple flex container with three items and gap spacing.
```tsx
return (
Item 1
Item 2
Item 3
);
}
```
### Flex Direction
ID: `Flex.direction` β’ Tags: direction, row, column, arrangement, axis β’ Category: general
Row and column direction layouts for different item arrangements.
```tsx
return (
Row Direction
Item 1
Item 2
Item 3
Column Direction
Item 1
Item 2
Item 3
);
}
```
### Justify Content
ID: `Flex.justify` β’ Tags: justify, content, spacing, distribution, main-axis β’ Category: general
Content justification options along the main axis (flex-start, center, space-between, etc.).
```tsx
const theme = useTheme();
return (
{[
{ label: 'Start', value: 'flex-start' },
{ label: 'Center', value: 'center' },
{ label: 'End', value: 'flex-end' },
{ label: 'Between', value: 'space-between' },
{ label: 'Around', value: 'space-around' },
{ label: 'Evenly', value: 'space-evenly' }
].map(({ value }) => (
justify="{value}"
{/* Small fixed squares with no shrink so free space is obvious */}
A
B
C
))}
);
}
```
--------------------------------------------------------------------------------
# Form
Form manages values, validation, and submission state for a group of inputs. Wrap each control in a `Form.Field` (which injects value and change handlers via context) and submit with `Form.Submit`.
## Metadata
- Canonical name: `Form`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Form } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: input
- Tags: form, fields, validation, submit
- Docs: https://react-ui-library.com/components/Form
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Form
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `initialValues` | Record | No | | Initial form values |
| `validationSchema` | ValidationSchema | No | | Form validation schema |
| `onSubmit` | (values: Record) => void \| Promise | No | | Submit handler |
| `validate` | (values: Record) => Record \| Promise> | No | | Validation handler |
| `disabled` | boolean | No | | Whether form is disabled |
| `validateOnChange` | boolean | No | | Whether to validate on change |
| `validateOnBlur` | boolean | No | | Whether to validate on blur |
| `children` | React.ReactNode | Yes | | Children components |
## Examples
### Basic Usage
ID: `Form.basic` β’ Tags: form, fields, validation β’ Category: basics β’ Status: stable β’ Since: 1.0.0
`Form` manages values, validation, and submission state. Wrap each input in a `Form.Field` (which injects value/change handlers via context) and trigger submission with `Form.Submit`.
```tsx
return (
);
}
```
--------------------------------------------------------------------------------
# Gallery
The Gallery component displays a collection of images or media with thumbnail navigation, keyboard controls, and optional fullscreen/modal viewing.
## Metadata
- Canonical name: `Gallery`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Gallery } from '@platform-blocks/react-ui-library';`
- Status: experimental
- Category: media
- Tags: gallery, images, thumbnails, media
- Docs: https://react-ui-library.com/components/Gallery
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Gallery
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `images` | GalleryItem[] | Yes | | Array of images to display in the gallery. |
| `initialIndex` | number | No | 0 | Index of the image shown when the gallery first opens. |
| `onClose` | () => void | No | | Called when the gallery is closed. |
| `onImageChange` | (index: number, image: GalleryItem) => void | No | | Called when the active image changes, receiving the new index and image. |
| `onDownload` | (image: GalleryItem) => void | No | | Called when the download action is triggered for the current image. |
| `showMetadata` | boolean | No | false | Whether to display the metadata panel for the current image. |
| `showThumbnails` | boolean | No | true | Whether to display the thumbnail strip for navigating between images. |
| `showDownloadButton` | boolean | No | true | Whether to display the download button in the gallery controls. |
| `allowKeyboardNavigation` | boolean | No | true | Whether arrow keys and Escape can be used to navigate and close the gallery. |
| `allowSwipeNavigation` | boolean | No | true | Whether swipe gestures can be used to move between images. |
| `overlayOpacity` | number | No | 0.9 | Opacity of the backdrop overlay behind the gallery, from 0 to 1. |
| `animationDuration` | number | No | 250 | Duration of open/close and transition animations, in milliseconds. |
## Examples
### Basic
ID: `Gallery.basic` β’ Tags: gallery, images, navigation, metadata β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Basic image gallery with navigation and metadata.
```tsx
// `null` closes the gallery; any index opens it on that image.
const [openIndex, setOpenIndex] = useState(null);
return (
{SAMPLE_IMAGES.map((image, index) => (
setOpenIndex(index)}>
))}
setOpenIndex(null)}
showMetadata
/>
);
}
```
### Advanced
ID: `Gallery.advanced` β’ Tags: advanced, customization, handlers, minimal β’ Category: features β’ Status: stable β’ Since: 1.0.0
Advanced gallery configurations with custom handlers.
```tsx
const [active, setActive] = useState<'minimal' | 'custom' | null>(null);
const [downloaded, setDownloaded] = useState(null);
return (
setActive('minimal')}>
Minimal
setActive('custom')}>
Custom download
{/* Chrome stripped back to the image itself β swipe and arrow keys still navigate. */}
setActive(null)}
showThumbnails={false}
showDownloadButton={false}
/>
{/* `onDownload` replaces the built-in behaviour, so the host app decides what saving means. */}
setActive(null)}
onDownload={(image: GalleryItem) => setDownloaded(image.title ?? image.id)}
showMetadata
/>
{downloaded ? (
Downloaded {downloaded}
) : null}
);
}
```
--------------------------------------------------------------------------------
# GradientText
A text component that displays text with gradient colors. Supports customizable gradients with multiple colors, different angles, and animated transitions (web only).
## Metadata
- Canonical name: `GradientText`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { GradientText } from '@platform-blocks/react-ui-library';`
- Category: typography
- Tags: text, gradient, animation, color
- Docs: https://react-ui-library.com/components/GradientText
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/GradientText
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `colors` | string[] | Yes | | Array of colors for the gradient (at least 2 required) |
| `locations` | number[] | No | | Color stops (0-1) for each color. If not provided, colors are evenly distributed |
| `angle` | number | No | | Gradient direction angle in degrees (0 = left to right, 90 = top to bottom, etc.) |
| `start` | [number, number] | No | | Start point [x, y] (0-1). Overrides angle if provided |
| `end` | [number, number] | No | | End point [x, y] (0-1). Overrides angle if provided |
| `position` | number | No | | Gradient position offset (0-1). Moves the gradient along the line |
| `animation` | GradientTextAnimation | No | | Sweep the gradient position continuously (web only). Runs as a CSS animation, so no JavaScript executes per frame. Overrides `position` while it is running; on native the gradient stays static. |
| `testID` | string | No | | Custom testID for testing |
## Examples
### Basic
ID: `GradientText.basic` β’ Category: typography β’ Status: stable β’ Since: 1.0.0
Pass two or more `colors` to fill text with a gradient. All other `Text` props still apply.
```tsx
return (
Hello World
);
}
```
### Angles
ID: `GradientText.angles` β’ Category: typography β’ Status: stable β’ Since: 1.0.0
Different gradient directions using the `angle` prop.
```tsx
const angles = [0, 45, 90, 135];
return (
{angles.map((angle) => (
{angle}Β° gradient
))}
);
}
```
### Controlled
ID: `GradientText.controlled` β’ Category: typography β’ Status: stable β’ Since: 1.0.0
Control the gradient position manually using the `position` prop (0.0 to 1.0).
```tsx
const [position, setPosition] = useState(0);
return (
);
}
```
--------------------------------------------------------------------------------
# Grid
Responsive 12βcolumn layout primitive with span-based children. Each `GridItem` declares how many columns it consumes; container controls total columns and gaps. Supports responsive values for `columns` and `span` using breakpoint-aware props.
## Metadata
- Canonical name: `Grid`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Grid } from '@platform-blocks/react-ui-library';`
- Status: beta
- Since: 0.1.0
- Category: layout
- Docs: https://react-ui-library.com/components/Grid
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Grid
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `columns` | ResponsiveProp | No | | Number of columns (can be responsive) |
| `gap` | SizeValue | No | 0 | Gap between items |
| `rowGap` | SizeValue | No | | Row gap between items |
| `columnGap` | SizeValue | No | | Column gap between items |
| `fullWidth` | boolean | No | false | Make the grid take full width (100%) |
| `children` | React.ReactNode | No | | Children elements |
| `style` | StyleProp | No | | Custom styles |
| `testID` | string | No | | Test ID for testing |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic
ID: `Grid.basic` β’ Category: general
Basic 12-column grid with equal width items.
```tsx
return (
{Array.from({ length: 12 }).map((_, index) => (
{index + 1}
))}
Twelve even columns, each spanning a single track
);
}
```
### Gaps
ID: `Grid.gaps` β’ Category: general
Row and column gutters come from the container: `gap` sets both, `rowGap` and `columnGap` override one each. Items never carry their own padding or margin.
```tsx
const sections = [
{
label: 'Compact gap (xs)',
props: { gap: 'xs' as const },
},
{
label: 'Roomy gap (2xl)',
props: { gap: '2xl' as const },
},
{
label: 'Wide rows, tight columns',
props: { rowGap: '2xl' as const, columnGap: 'xs' as const },
},
];
return (
{sections.map(({ label, props }) => (
{label}
{Array.from({ length: 12 }).map((_, index) => (
Item {index + 1}
))}
))}
);
}
```
### Nesting
ID: `Grid.nesting` β’ Category: general
Nested grids demonstrating composition inside a grid item.
```tsx
return (
{/* Block's own gap separates the label from the nested grid β no
margin on either one. */}
Parent span=8
{Array.from({ length: 6 }).map((_, index) => (
Nested {index + 1}
))}
Sidebar span=4
GridItem components can render another Grid to illustrate nested layouts
);
}
```
### Responsive
ID: `Grid.responsive` β’ Category: general
Responsive columns and item spans using breakpoint-aware props.
```tsx
// Responsive props match the breakpoint configuration used in Grid
return (
Hero (4/8/6)
Hero (4/8/6)
Side (2/4/3)
Side (2/4/3)
Footer (4/8/12)
Column and span props adapt at base, md, and lg breakpoints
);
}
```
### Spans
ID: `Grid.spans` β’ Category: general
Demonstrates varying column spans within a 12-column grid.
```tsx
const spans = [6, 6, 4, 4, 4, 3, 3, 3, 3];
return (
{spans.map((span, index) => (
{`span=${span}`}
))}
Mix spans within a 12-column grid to create varied layouts
);
}
```
--------------------------------------------------------------------------------
# Highlight
Highlight emphasizes matching fragments inside longer strings, reusing the Text component so typography settings stay consistent across platforms.
## Metadata
- Canonical name: `Highlight`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Highlight } from '@platform-blocks/react-ui-library';`
- Category: typography
- Tags: text, emphasis, highlight, mark
- Docs: https://react-ui-library.com/components/Highlight
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Highlight
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `highlight` | HighlightValue \| HighlightValue[] | No | | Substring or substrings to emphasize within the provided children |
| `highlightStyles` | any \| ((theme: PlatformBlocksTheme) => any) | No | | Optional override for the highlighted segment styles. Accepts either a style object/array or a callback that receives the current theme and returns styles. |
| `highlightColor` | string | No | | When provided, overrides the default highlight background/text palette. If the value matches a key from the theme color palettes it will use the related swatch, otherwise the value is treated as a raw color string. |
| `caseSensitive` | boolean | No | | Toggle case-sensitive matching (defaults to case-insensitive). |
| `trim` | boolean | No | | Trim highlight values before matching to ignore accidental whitespace. Defaults to true. |
| `highlightProps` | Partial | No | | Additional props applied to the highlighted Text nodes. |
## Examples
### Basic
ID: `Highlight.basic` β’ Category: general
Default highlight behavior with a single search term. Matching fragments are wrapped with the theme-aware highlight styles.
```tsx
const PARAGRAPH = 'Highlight This, definitely THIS and also this!';
return (
Case-insensitive match
{PARAGRAPH}
);
}
```
### Multiple
ID: `Highlight.multiple` β’ Category: general
Pass an array to highlight several distinct substrings. Every match shares the same styles by default.
```tsx
const SENTENCE = 'React UI Library brings patterns, blocks, and building tools together.';
return (
Multiple values
{SENTENCE}
);
}
```
### Styles
ID: `Highlight.styles` β’ Category: general
Swap the marker color with the `highlightColor` prop, passing any theme palette name. The default marker style (yellow background, unchanged text) is preserved.
```tsx
const copy = 'You can switch the highlight color while keeping the default marker style.';
return (
Highlight color
{copy}
{copy}
{copy}
);
}
```
--------------------------------------------------------------------------------
# Icon
The `Icon` component displays icons with optional captions and overlays, providing a flexible way to present visual content in your application.
## Metadata
- Canonical name: `Icon`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Icon } from '@platform-blocks/react-ui-library';`
- Status: beta
- Category: typography
- Docs: https://react-ui-library.com/components/Icon
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Icon
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `name` | string | No | | Icon name from the registry |
| `icon` | ExternalIconComponent \| React.ReactElement | No | | An external icon library component or element, rendered instead of `name`. Enables using any icon library (e.g. Tabler) without registry registration. |
| `size` | IconSize | No | 'md' | Size of the icon |
| `color` | string | No | | Color of the icon |
| `stroke` | number | No | 1.5 | Stroke thickness for outlined icons. Defaults to 1.5. |
| `variant` | IconVariant | No | 'outlined' | Icon variant - overrides the default variant from icon definition |
| `style` | StyleProp | No | | Additional styles |
| `label` | string | No | | Accessibility label |
| `decorative` | boolean | No | false | Whether the icon is purely decorative (skip a11y) |
| `mirrorInRTL` | boolean | No | | Whether to mirror this icon in RTL mode. If not specified, uses auto-detection based on icon name |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic
ID: `Icon.basic` β’ Category: general
```tsx
return (
{/* Header */}
Icon
Scalable vector icons with consistent sizing and theming integration.
{/* Different Sizes */}
Sizes
Available in multiple sizes using the UI theme system.
sm
md
lg
xl
{/* Navigation Icons */}
Navigation Icons
Common navigation and directional icons.
{['home', 'arrow-left', 'arrow-right', 'arrow-up', 'arrow-down', 'chevron-left', 'chevron-right', 'chevron-up', 'chevron-down', 'menu'].map(iconName => (
{iconName}
))}
{/* Action Icons */}
Action Icons
Icons for common user actions and operations.
{['plus', 'minus', 'x', 'check', 'search', 'edit', 'delete', 'save', 'copy', 'funnel', 'phone', 'toggle', 'qrcode', 'pin', 'spotlight'].map(iconName => (
{iconName}
))}
{/* UI Icons */}
UI Icons
Interface and user experience icons.
{['eye', 'eyeOff', 'settings', 'user', 'heart', 'star'].map(iconName => (
{iconName}
))}
{/* Variants */}
Icon Variants
Icons can be displayed in outlined or filled variants.
Outlined (Default):
Filled:
{/* Custom Colors */}
Custom Colors
Override icon colors to match your design.
Pink
Amber
Green
Blue
{/* New Icons */}
New Icons
Recently added icons for common use cases.
link
exclamation
funnel
camera
mic
bell
calendar
phone
email
folder
file
timeline
loader
switch
carousel
avatar
toggle
bone
toast
radio
qrcode
progress
map
list
gallery
pin
tree
keycap
breadcrumbs
pagination
table of contents
stepper
context menu
grid
dialog
card
tooltip
slider
input
emoji
button
select
textarea
autocomplete
rating
datatable
chip
markdown
accordion
text
title
waveform
);
}
```
### Stroke
ID: `Icon.stroke` β’ Category: general
```tsx
const strokeVariants = [
{ label: 'Thin (0.75)', value: 0.75 },
{ label: 'Default (1.5)', value: 1.5 },
{ label: 'Bold (3)', value: 3 },
];
return (
Stroke thickness
Adjust the stroke thickness to match different visual weights. Filled icons that opt in to preserving stroke (like{' '}
contrast) keep their outline while the fill still applies.
{strokeVariants.map(({ label, value }) => (
{label}
))}
);
}
```
--------------------------------------------------------------------------------
# IconButton
An IconButton is a clickable button that contains an icon and is used to perform actions or trigger events. It is typically used in toolbars, action bars, or as standalone buttons in user interfaces.
## Metadata
- Canonical name: `IconButton`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { IconButton } from '@platform-blocks/react-ui-library';`
- Status: stable
- Since: 1.0.0
- Category: input
- Tags: button, icon, clickable, action
- Docs: https://react-ui-library.com/components/IconButton
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/IconButton
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `icon` | string \| ExternalIconComponent \| React.ReactElement | Yes | | Icon to render. Accepts a registry name, or an external icon library component/element (e.g. a Tabler icon) for use without registration. |
| `onPress` | () => void | No | | Called when the button is pressed |
| `onLayout` | (event: any) => void | No | | Called when the button layout is calculated |
| `variant` | 'default' \| 'filled' \| 'secondary' \| 'outline' \| 'ghost' \| 'gradient' \| 'none' | No | 'default' | Button visual variant. `default` is the neutral surface-plus-hairline button, matching `Button`; a solid primary fill is opt-in via `filled`. |
| `size` | SizeValue | No | | Button size |
| `disabled` | boolean | No | | Whether the button is disabled |
| `loading` | boolean | No | | Whether button is in loading state (shows loader) |
| `color` | string | No | | Tint for the button. Accepts raw CSS color OR theme token syntax: - 'primary' (palette key -> uses middle shade 5) - 'primary.6' (palette key + shade index) - '#ff0000' / 'rgb(...)' direct colors `filled`, `secondary` and `outline` tint the container; `ghost` and the neutral `default`/`none` keep their chrome and tint only the icon. `gradient` draws its own overlay and ignores this. |
| `iconColor` | string | No | | Explicit icon color override (else derived automatically from variant & color) |
| `iconVariant` | IconProps['variant'] | No | | Icon variant override |
| `iconSize` | IconProps['size'] | No | | Icon size override (defaults to appropriate size for button size) |
| `tooltip` | TooltipPropValue | No | | Tooltip shown on hover/focus β wraps the button in a `Tooltip`. Pass a string, or a config object (`{ label, maxWidth, withArrow, β¦ }`) for long labels that need a wider bubble. |
| `tooltipPosition` | TooltipProps['position'] | No | | Tooltip position when the string form of `tooltip` is used |
| `accessibilityLabel` | string | No | | Accessibility label - highly recommended for icon-only buttons |
| `transitionDuration` | number | No | 100 | Length of the press scale transition in ms. `0` applies the pressed state instantly. Always 0 under reduced motion. |
| `style` | any | No | | Style overrides for the button container |
| `testID` | string | No | | Test ID for testing |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
| `shadow` | ShadowValue | No | | Shadow value - supports size tokens and 'none' |
## Examples
### Basic
ID: `IconButton.basic` β’ Category: general
```tsx
const [loading, setLoading] = useState(false);
const [disabled, setDisabled] = useState(false);
const handlePress = (action: string) => {
console.log(`IconButton pressed: ${action}`);
};
return (
IconButton Component Demo
IconButton is designed specifically for displaying icons in square or circular shapes.
Use radius="xl" for circular buttons.
{/* Controls */}
Controls
{/* Variants */}
Variants
handlePress('default')}
loading={loading}
disabled={disabled}
tooltip="Home (Default)"
/>
handlePress('filled')}
loading={loading}
disabled={disabled}
tooltip="Home (Filled)"
/>
handlePress('secondary')}
loading={loading}
disabled={disabled}
tooltip="Favorite (Secondary)"
/>
handlePress('outline')}
loading={loading}
disabled={disabled}
tooltip="Settings (Outline)"
/>
handlePress('ghost')}
loading={loading}
disabled={disabled}
tooltip="Search (Ghost)"
/>
handlePress('gradient')}
loading={loading}
disabled={disabled}
tooltip="Star (Gradient)"
/>
{/* Sizes */}
Sizes
handlePress('xs')}
tooltip="Extra Small"
/>
handlePress('sm')}
tooltip="Small"
/>
handlePress('md')}
tooltip="Medium"
/>
handlePress('lg')}
tooltip="Large"
/>
handlePress('xl')}
tooltip="Extra Large"
/>
{/* Shape: Square vs Circular */}
Shape: Square vs Circular
handlePress('square-sm')}
tooltip="Small Radius (Square-ish)"
/>
radius="sm"
handlePress('square-md')}
tooltip="Medium Radius"
/>
radius="md"
handlePress('square-lg')}
tooltip="Large Radius"
/>
radius="lg"
handlePress('circular')}
tooltip="Circular (XL Radius)"
/>
radius="xl" (circular)
{/* Custom Colors */}
Custom Colors
handlePress('red')}
tooltip="Red Heart"
/>
handlePress('green')}
tooltip="Green Check"
/>
handlePress('blue')}
tooltip="Blue Info"
/>
handlePress('orange')}
tooltip="Orange Warning"
/>
handlePress('purple')}
tooltip="Purple Star (Circular)"
/>
{/* Common Use Cases */}
Common Use Cases
{/* Toolbar */}
Toolbar Actions
{/* Social Actions */}
Social Actions (Circular)
{/* Navigation */}
Navigation
);
}
```
--------------------------------------------------------------------------------
# Image
The `Image` component displays images with optional captions and overlays, providing a flexible way to present visual content in your application.
## Metadata
- Canonical name: `Image`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Image } from '@platform-blocks/react-ui-library';`
- Status: beta
- Category: media
- Docs: https://react-ui-library.com/components/Image
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Image
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `src` | string \| ImageSourcePropType | No | | Remote image URI, or a bundled asset from `require('./photo.png')` |
| `source` | ImageSourcePropType | No | | Image source object (alternative to src) |
| `alt` | string | No | | Alternative text for accessibility |
| `accessibilityLabel` | string | No | | Accessibility label |
| `resizeMode` | 'cover' \| 'contain' \| 'stretch' \| 'repeat' \| 'center' | No | | Image resize mode |
| `size` | SizeValue \| number | No | | Image size preset |
| `w` | number \| string | No | | Custom width |
| `h` | number \| string | No | | Custom height |
| `aspectRatio` | number | No | | Aspect ratio |
| `borderWidth` | number | No | | Border width |
| `borderColor` | ColorValue | No | | Border color |
| `rounded` | boolean | No | | Whether image should be rounded |
| `circle` | boolean | No | | Whether image should be circular |
| `fallback` | React.ReactNode | No | | Fallback element to show on error |
| `loading` | React.ReactNode | No | | Loading state element |
| `onLoad` | () => void | No | | Called when image loads successfully |
| `onError` | (error: any) => void | No | | Called when image fails to load |
| `onLoadStart` | () => void | No | | Called when image starts loading |
| `onLoadEnd` | () => void | No | | Called when image finishes loading (success or error) |
| `containerStyle` | StyleProp | No | | Container style |
| `imageStyle` | StyleProp | No | | Image style overrides |
| `testID` | string | No | | Component test ID for testing |
| `style` | any | No | | Additional CSS styles |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
## Examples
### Sizes
ID: `Image.sizes` β’ Tags: size, layout β’ Category: layout β’ Status: stable β’ Since: 0.3.0
Set the `size` prop to any token (`xs`β`3xl`) to scale the image box, or pass `w`/`h` when you need exact dimensions.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const;
/** Inline 8x8 PNG β keeps the demo offline and identical on web and native. */
const SAMPLE_SRC =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAALklEQVR42mNITvsIR409P+CIAasokMuAVRQqgSkKksAqiiKB5goGrKJQCawuBgC2Wnfh+zNA9wAAAABJRU5ErkJggg==';
return (
{SIZES.map((size) => (
{size}
))}
);
}
```
### Basic
ID: `Image.basic` β’ Category: general
```tsx
return (
Basic Image Usage
A simple image with specified dimensions
);
}
```
### Fallback
ID: `Image.fallback` β’ Category: general
```tsx
return (
Image Fallback & Error Handling
}
alt="Failed to load"
/>
With Icon Fallback
Image not found
}
alt="Failed to load"
/>
With Text Fallback
When images fail to load, fallback content is displayed
);
}
```
### Shapes
ID: `Image.shapes` β’ Category: general
```tsx
return (
Image Shapes
Default
Rounded
Circle
Shape variations: default, rounded corners, and circular
);
}
```
### Spacing
ID: `Image.spacing` β’ Category: general
```tsx
return (
Universal Spacing Props
{/* Auto margin example */}
Auto Margin Example
Image with m="auto" should be centered
{/* Theme spacing example */}
Theme Spacing Values
Image with m="lg" using theme spacing
{/* Numeric spacing example */}
Numeric Spacing Values
Image with m={`{20}`} using numeric spacing
{/* Zero margin example */}
Zero Margin Example
Image with m="0" should have no margin
{/* Mixed spacing props example */}
Mixed Spacing Props
Image with mx="auto", my="md", p="sm"
);
}
```
--------------------------------------------------------------------------------
# Indicator
The Indicator component renders a small dot or pill-shaped badge in the corner of a parent container β perfect for online status, unread counts, or "new" markers. Pass `label` for text content (the dot auto-expands to fit multi-digit counts); use `children` for arbitrary custom content like icons.
## Metadata
- Canonical name: `Indicator`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Indicator } from '@platform-blocks/react-ui-library';`
- Category: data
- Tags: indicator, badge, status, count, dot
- Docs: https://react-ui-library.com/components/Indicator
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Indicator
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `size` | SizeValue \| number | No | | |
| `color` | string | No | | |
| `borderColor` | string | No | | |
| `borderWidth` | number | No | | |
| `placement` | 'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right' | No | | |
| `offset` | number | No | | |
| `style` | StyleProp | No | | |
| `children` | React.ReactNode | No | | Free-form content rendered inside the indicator dot. Useful when a custom icon is needed; for plain text counts prefer `label`, which auto-resizes the dot and applies a contrast-aware text color. |
| `label` | React.ReactNode | No | | Convenience text content (typically a count). When set, the dot expands to fit the label and the text uses a contrast-aware color. |
| `labelProps` | Omit | No | | Override props applied to the label `` (style, weight, ff, size, color). |
| `invisible` | boolean | No | | |
## Examples
### Basic usage
ID: `Indicator.basic` β’ Tags: status, notification β’ Category: basics β’ Status: stable β’ Since: 0.4.0
Use `Indicator` to layer small notices on any container: position it at a corner, pair it with avatars for presence, or wrap children to show counters without building custom badges.
```tsx
return (
Corner indicator
Panel
Avatar status
Numeric counter
Inbox
5
);
}
```
### Placements
ID: `Indicator.placements` β’ Tags: placement, offset β’ Category: basics β’ Status: stable β’ Since: 0.4.0
Control positioning by combining the `placement` prop (top/bottom + left/right) with `offset` to nudge the badge; add children inside `Indicator` when you need numeric or icon content.
```tsx
const cornerPlacements = [
{ label: 'Top left', placement: 'top-left', color: '#F59E0B' },
{ label: 'Top right', placement: 'top-right', color: '#10B981' },
{ label: 'Bottom left', placement: 'bottom-left', color: '#6366F1' },
{ label: 'Bottom right', placement: 'bottom-right', color: '#EF4444' },
] as const;
const offsetPlacements = [
{ label: '9 unread', placement: 'top-right', color: '#6366F1', value: '9', offset: 6 },
{ label: '2 new', placement: 'bottom-right', color: '#10B981', value: '2', offset: 4 },
] as const;
const Tile = ({ children }: { children: ReactNode }) => (
{children}
);
return (
Corner placements
{cornerPlacements.map((placement) => (
{placement.label}
))}
Offset and content
{offsetPlacements.map((placement) => (
{placement.label}
{placement.value}
))}
);
}
```
### Sizes
ID: `Indicator.sizes` β’ Tags: sizes, tokens β’ Category: basics β’ Status: stable β’ Since: 0.4.0
Set the `size` prop to any token (`xs`β`3xl`) for theme-aligned dots, or provide a raw number when you need a bespoke diameter for your badge.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', 24] as const;
return (
{SIZES.map((size) => (
{typeof size === 'number' ? `${size} (numeric)` : size}
))}
);
}
```
### Statuses
ID: `Indicator.statuses` β’ Tags: presence, status β’ Category: basics β’ Status: stable β’ Since: 0.4.0
Map semantic states (online, idle, busy) to palette colors and cap large notification counts by rendering text inside `Indicator`βperfect for presence chips or inbox badges.
```tsx
const presenceStatuses = [
{ label: 'Online', palette: 'success', avatar: require('../../../../assets/avatars/avatar-1.png') },
{ label: 'Idle', palette: 'warning', avatar: require('../../../../assets/avatars/avatar-2.png') },
{ label: 'Busy', palette: 'error', avatar: require('../../../../assets/avatars/avatar-3.png') },
{ label: 'Offline', palette: 'gray', avatar: require('../../../../assets/avatars/avatar-4.png') },
] as const;
const notificationCounts = [3, 47, 99, 134, 1005];
const theme = useTheme();
const resolveColor = (palette: (typeof presenceStatuses)[number]['palette']) => {
const swatch = (theme.colors as any)[palette];
return Array.isArray(swatch) ? swatch[5] : swatch;
};
return (
Presence indicators
{presenceStatuses.map((status) => (
{status.label}
))}
Max count handling
{notificationCounts.map((count) => {
const display = count > 99 ? '99+' : `${count}`;
return (
{display}
{count}
);
})}
);
}
```
### Labels & counts
ID: `Indicator.labels` β’ Tags: label, count, labelProps, customization β’ Category: general β’ Status: stable β’ Since: 1.0.0
Pass `label` to render a count or short text inside the indicator β the dot expands to a pill so multi-digit values fit. `labelProps` accepts any `` props for fonts, weights, etc. For arbitrary custom content (icons, status markers), use `children` instead.
```tsx
const Anchor = ({ children }: { children?: React.ReactNode }) => (
{children}
);
return (
Numeric counts
Monospace badge with custom label styling
Custom child content (children, not label)
{/* anything you want β icon, custom shape, etc. */}
);
}
```
--------------------------------------------------------------------------------
# Input
A versatile text input component that provides a consistent interface for text entry across different platforms. The Input component supports various types, validation states, and accessibility features.
## Metadata
- Canonical name: `Input`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Input } from '@platform-blocks/react-ui-library';`
- Status: stable
- Since: 1.0.0
- Category: input
- Tags: input, form, text, validation
- Docs: https://react-ui-library.com/components/Input
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Input
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `type` | 'text' \| 'password' \| 'email' \| 'tel' \| 'number' \| 'search' | No | 'text' | Input type - determines styling and behavior |
| `validation` | ValidationRule[] | No | | Input validation rules |
| `autoComplete` | 'off' \| 'password' \| 'email' \| 'tel' \| 'url' \| 'name' \| 'additional-name' \| 'address-line1' \| 'address-line2' \| 'birthdate-day' \| 'birthdate-full' \| 'birthdate-month' \| 'birthdate-year' \| 'cc-csc' \| 'cc-exp' \| 'cc-exp-month' \| 'cc-exp-year' \| 'cc-number' \| 'country' \| 'current-password' \| 'family-name' \| 'given-name' \| 'honorific-prefix' \| 'honorific-suffix' \| 'new-password' \| 'one-time-code' \| 'organization' \| 'organization-title' \| 'postal-code' \| 'street-address' \| 'username' | No | | Auto-complete type |
| `keyboardType` | KeyboardTypeOptions | No | | Keyboard type for mobile |
| `multiline` | boolean | No | | Whether input is multiline |
| `numberOfLines` | number | No | | Number of lines for multiline input |
| `minLines` | number | No | 1 | Minimum number of lines for multiline input (default: 1) |
| `maxLines` | number | No | | Maximum number of lines for multiline input |
| `maxLength` | number | No | | Maximum length |
| `secureTextEntry` | boolean | No | | Whether to secure text entry |
| `textInputProps` | ExtendedTextInputProps | No | | Additional TextInput props |
| `inputRef` | React.Ref | No | | Ref to underlying TextInput (focus control) |
| `autoCapitalize` | RNTextInputProps['autoCapitalize'] | No | | Text auto-capitalization behavior |
| `autoCorrect` | boolean | No | | Whether to enable auto-correct |
| `autoFocus` | boolean | No | | Whether to auto-focus on mount |
| `returnKeyType` | RNTextInputProps['returnKeyType'] | No | | Return key type for soft keyboard |
| `blurOnSubmit` | boolean | No | | Whether to blur on submit |
| `selectTextOnFocus` | boolean | No | | Select all text on focus |
| `textContentType` | RNTextInputProps['textContentType'] | No | | iOS text content type for autofill |
| `textAlign` | RNTextInputProps['textAlign'] | No | | Text alignment |
| `spellCheck` | boolean | No | | Whether spell check is enabled |
| `inputMode` | RNTextInputProps['inputMode'] | No | | Input mode (modern alternative to keyboardType) |
| `enterKeyHint` | RNTextInputProps['enterKeyHint'] | No | | Hint for the enter key |
| `selectionColor` | string | No | | Color of the text selection handles and highlight |
| `showSoftInputOnFocus` | boolean | No | | Whether to show the soft keyboard on focus |
| `editable` | boolean | No | | Whether the field is read-only (alias for !editable) |
| `variant` | InputVariant | No | | Visual variant of the input. `default` (light surface + border), `filled` (gray fill, no border), `outline` (transparent fill, border only), `unstyled` (no border, no fill). |
| `value` | string | No | | Input value |
| `onChangeText` | (text: string) => void | No | | Change handler |
| `label` | React.ReactNode | No | | Input label (string or component) |
| `disabled` | boolean | No | | Whether input is disabled |
| `required` | boolean | No | | Whether input is required |
| `placeholder` | string | No | | Input placeholder |
| `error` | string | No | | Error message |
| `helperText` | string | No | | Helper text |
| `description` | string | No | | Optional short description displayed directly under the label (above the field) |
| `size` | SizeValue | No | 'md' | Input size |
| `withAsterisk` | boolean | No | | Whether to show required indicator |
| `name` | string | No | | Input name for form integration |
| `startSection` | React.ReactNode | No | | Left section content |
| `endSection` | React.ReactNode | No | | Right section content |
| `style` | any | No | | Additional styling |
| `accessibilityLabel` | string | No | | Accessibility label |
| `accessibilityHint` | string | No | | Accessibility hint |
| `testID` | string | No | | Test ID for testing |
| `debounceMs` | number | No | | Debounce delay for validation in milliseconds |
| `onFocus` | () => void | No | | Focus handler |
| `onBlur` | () => void | No | | Blur handler |
| `onEnter` | () => void | No | | Enter key press handler |
| `clearable` | boolean | No | | Show built-in clear button when input has value |
| `clearButtonLabel` | string | No | | Accessible label for the clear button |
| `onClear` | () => void | No | | Callback when the clear button is pressed |
| `keyboardFocusId` | string | No | | Identifier used with KeyboardManagerProvider to request refocus |
| `labelProps` | Omit | No | | Override props applied to the field label `` (style, weight, ff, etc.) |
| `descriptionProps` | Omit | No | | Override props applied to the field description `` |
| `placeholderTextColor` | string | No | | Color of the placeholder text. Falls back to `theme.text.muted`. |
| `startSectionProps` | Omit | No | | Props applied to the wrapping `` around `startSection` (style, accessibility, etc.). |
| `endSectionProps` | Omit | No | | Props applied to the wrapping `` around `endSection`. |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
## Examples
### Basic
ID: `Input.basic` β’ Tags: basic, input, text β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Basic text input with label, placeholder, and value handling.
```tsx
const [value, setValue] = useState('');
return (
);
}
```
### Variants
ID: `Input.variants` β’ Tags: variants, filled, outline, unstyled β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Four visual variants for the input shell: `default`, `filled`, `outline`, and `unstyled`. The variant only changes the container fill and border β label, sections, and disclaimer stay consistent.
```tsx
return (
);
}
```
### Types
ID: `Input.types` β’ Tags: types, email, password, number, tel β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Set `type` to switch the keyboard and browser behaviour β email, password, number, and tel are all supported.
```tsx
return (
);
}
```
### Validation
ID: `Input.validation` β’ Tags: validation, error, required, helper β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Pass `error` to show a validation message, `required` to mark the field, and `helperText` for guidance. `disabled` blocks editing.
```tsx
const isValidEmail = (email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
const [email, setEmail] = useState('');
return (
0 && !isValidEmail(email) ? 'Please enter a valid email address' : undefined}
helperText="We'll never share your email"
/>
);
}
```
### Multiline Modes
ID: `Input.multiline` β’ Tags: input, multiline β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Pair `multiline` with `minLines`/`maxLines` to auto-expand, or with `numberOfLines` for a fixed height.
```tsx
const [autoText, setAutoText] = useState('');
const [fixedText, setFixedText] = useState('');
return (
);
}
```
### Sections and slot styling
ID: `Input.slot-styling` β’ Tags: startSection, endSection, clearable, placeholderTextColor, slot-props, customization β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Render content inside the field with `startSection` / `endSection`, and add `clearable` for a dismiss button. `startSectionProps` and `endSectionProps` accept any `` props (including `style`) and apply them to the slot wrapper; `placeholderTextColor` overrides the muted default.
```tsx
const [workspace, setWorkspace] = useState('');
const [search, setSearch] = useState('');
return (
https:// }
startSectionProps={{ style: { paddingRight: 8 } }}
/>
}
startSectionProps={{ style: { paddingRight: 8 } }}
/>
);
}
```
--------------------------------------------------------------------------------
# Joystick
Joystick is a two-axis positional input. In its default `circle` shape it behaves like a physical stick β the handle rides the rim at full deflection and springs back to centre when released. As a `square` it becomes an XY pad: each axis clamps on its own so the corners are reachable, and the handle stays where it is left.
Both axes are normalized to β1β¦1. `y` is up-positive by default, matching how a gamepad axis reads; pass `invertY={false}` to follow screen space instead.
`deadZone` zeroes small deflections and rescales what is left, so the value still spans the full range past the threshold rather than jumping to the dead-zone size. `step` snaps each axis, `lockAxis` restricts travel to one direction, and `showCrosshair` adds accent rules that track the handle β the usual XY-pad readout.
The gesture runs on the shared `useDragGesture` hook, which means a drag that leaves the pad keeps tracking the finger instead of handing the touch back to the page. Arrow keys nudge by `keyboardStep` on web, `Home` and `Escape` recentre, and VoiceOver/TalkBack get increment and decrement actions.
## Metadata
- Canonical name: `Joystick`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Joystick } from '@platform-blocks/react-ui-library';`
- Category: input
- Tags: joystick, xy, pad, gesture, two-axis, input
- Docs: https://react-ui-library.com/components/Joystick
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Joystick
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | JoystickValue | No | | Controlled value. Both axes are normalized to β1β¦1. |
| `defaultValue` | JoystickValue | No | | Initial value while uncontrolled. Default `{ x: 0, y: 0 }`. |
| `onChange` | (value: JoystickValue) => void | No | | Fired for every position change, including each frame of a drag. |
| `onChangeEnd` | (value: JoystickValue) => void | No | | Fired once when the gesture ends, with the value it settled on. |
| `onChangeStart` | (value: JoystickValue) => void | No | | Fired when a drag or keyboard interaction begins. |
| `shape` | JoystickShape | No | 'circle' | `circle` clamps the handle to a disc β a stick. `square` clamps each axis on its own so the corners are reachable β an XY pad. Default `circle`. |
| `returnToCenter` | boolean | No | | Spring the handle back to the centre when released, the way a physical stick does. Defaults to `true` for `circle` and `false` for `square`. |
| `lockAxis` | 'x' \| 'y' | No | | Restrict travel to a single axis. |
| `deadZone` | number | No | 0 | Report `0` until the handle travels this far from centre (0β1). Default `0`. |
| `step` | number | No | 0 | Snap each axis to this increment. Default `0` (continuous). |
| `keyboardStep` | number | No | | Increment applied by a single arrow key press. Defaults to `step` or `0.1`. |
| `invertY` | boolean | No | true | Report a positive `y` when the handle is pushed up. Default `true`. |
| `size` | ComponentSizeValue | No | 'md' | Outer size in px, or a size token. Default `'md'`. |
| `handleSize` | number | No | | Handle diameter in px. Defaults to ~32% of `size`. |
| `variant` | JoystickVariant | No | 'default' | Visual preset. Default `'default'`. |
| `color` | string | No | | Accent color: a palette token (`'primary'`), `'primary.6'` shade syntax, or any CSS color. |
| `baseColor` | string | No | | Base surface color override. |
| `handleColor` | string | No | | Handle color override. |
| `showGuides` | boolean | No | true | Draw the static centre guides. Default `true`. |
| `showCrosshair` | boolean | No | false | Draw accent rules that track the handle on each axis β the XY-pad readout. Default `false`. |
| `valueLabel` | boolean \| ((value: JoystickValue) => string) | No | false | Render the current value under the pad. Pass a function to format it. |
| `label` | React.ReactNode | No | | Field label rendered above the pad. |
| `disabled` | boolean | No | false | Ignore all input and dim the control. |
| `readOnly` | boolean | No | false | Ignore all input while keeping full contrast. |
| `transitionDuration` | number | No | | Spring-back / keyboard transition duration in ms. Default `220`. |
| `style` | StyleProp | No | | Root style. |
| `baseStyle` | StyleProp | No | | Style for the pad surface. |
| `handleStyle` | StyleProp | No | | Style for the handle. |
| `valueLabelStyle` | StyleProp | No | | Style for the value label text. |
| `accessibilityLabel` | string | No | | |
| `testID` | string | No | | |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic
ID: `Joystick.basic` β’ Tags: basic, joystick, stick β’ Category: basics β’ Status: stable β’ Since: 1.1.0
A stick that springs back to centre on release. Drag anywhere on the pad β the gesture keeps tracking even when the finger leaves it.
```tsx
const [value, setValue] = useState({ x: 0, y: 0 });
return (
);
}
```
### XY pad
ID: `Joystick.xy-pad` β’ Tags: xy, pad, square, filter β’ Category: variants β’ Status: stable β’ Since: 1.1.0
`shape="square"` clamps each axis independently so the corners are reachable, and the handle holds its position on release β the shape a filter or effect pad wants.
```tsx
const [value, setValue] = useState({ x: -0.4, y: 0.6 });
// Map the pad onto a pair of parameters the way an effect unit would.
const cutoff = Math.round(((value.x + 1) / 2) * 18000 + 200);
const resonance = ((value.y + 1) / 2).toFixed(2);
return (
Cutoff {cutoff} Hz Β· Resonance {resonance}
);
}
```
### Dead zone and steps
ID: `Joystick.dead-zone` β’ Tags: deadZone, step, snapping β’ Category: features β’ Status: stable β’ Since: 1.1.0
`deadZone` ignores small deflections around centre and rescales the rest, so full travel still reports 1. `step` snaps each axis onto a grid.
```tsx
const [free, setFree] = useState({ x: 0, y: 0 });
const [stepped, setStepped] = useState({ x: 0, y: 0 });
return (
);
}
```
### Axis lock
ID: `Joystick.axis-lock` β’ Tags: lockAxis, single-axis, pan β’ Category: features β’ Status: stable β’ Since: 1.1.0
`lockAxis` restricts travel to one direction. A single-axis pad also leaves the perpendicular direction to the page, so vertical scrolling still works over a horizontal control.
```tsx
const [pan, setPan] = useState({ x: 0, y: 0 });
const position = pan.x === 0
? 'Center'
: `${pan.x < 0 ? 'L' : 'R'} ${Math.round(Math.abs(pan.x) * 100)}`;
return (
{position}
);
}
```
--------------------------------------------------------------------------------
# KeyCap
A visual component for displaying keyboard keys, shortcuts, and key combinations with proper styling.
## Metadata
- Canonical name: `KeyCap`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { KeyCap } from '@platform-blocks/react-ui-library';`
- Category: typography
- Tags: keycap, keyboard, shortcut, key, hotkey
- Docs: https://react-ui-library.com/components/KeyCap
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/KeyCap
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | ReactNode | Yes | | The key or text to display |
| `size` | ComponentSizeValue | No | 'md' | Size variant of the key cap |
| `variant` | 'default' \| 'minimal' \| 'outline' \| 'filled' | No | 'default' | Visual variant of the key cap |
| `color` | 'primary' \| 'secondary' \| 'gray' \| 'success' \| 'warning' \| 'error' | No | 'gray' | Color scheme for the key cap |
| `animateOnPress` | boolean | No | true | Whether the key should animate when the actual key is pressed Only works on web platforms |
| `transitionDuration` | number | No | 250 | Length of the press-down/up animation in ms; both legs scale against a 250ms baseline. `0` leaves the cap at rest. Always 0 under reduced motion. |
| `keyCode` | string | No | | The actual key code to listen for (e.g., 'Enter', 'Space', 'Escape') If provided, the component will animate when this key is pressed |
| `modifiers` | Array<'ctrl' \| 'cmd' \| 'alt' \| 'shift' \| 'meta'> | No | | Modifier keys that must be pressed along with the main key |
| `pressed` | boolean | No | | Whether the key cap should appear pressed |
| `onKeyPress` | () => void | No | | Callback when the key combination is pressed |
| `testID` | string | No | | Custom test ID for testing |
| `fontFamily` | string | No | | Custom font family (overrides the default monospace stack) |
| `ff` | string | No | | Shorthand alias for `fontFamily` |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
## Examples
### Basic Usage
ID: `KeyCap.basic` β’ Tags: basic, keyboard β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Basic keyboard key display for showing shortcuts and key combinations.
```tsx
return (
A
Enter
Space
β
Ctrl
β§
);
}
```
### Sizes
ID: `KeyCap.sizes` β’ Tags: sizes β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Different sizes for KeyCap components from extra small to extra large.
```tsx
return (
XS
SM
MD
LG
XL
);
}
```
### Variants
ID: `KeyCap.variants` β’ Tags: variants, styles β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Different visual variants for KeyCap components including default, filled, minimal, and outline styles.
```tsx
return (
Default
Filled
Minimal
Outline
);
}
```
### Modifiers
ID: `KeyCap.modifiers` β’ Tags: modifiers, shortcuts, combinations β’ Category: usage β’ Status: stable β’ Since: 1.0.0
KeyCap components with modifier keys for displaying keyboard shortcuts and key combinations.
```tsx
return (
Copy
β
+
C
Save
β
+
S
Undo
β
+
Z
);
}
```
--------------------------------------------------------------------------------
# Knob
The Knob component provides a rotary control for adjusting values with touch, mouse, and keyboard input. It supports snapping to marks, internal value labels, and accessible field headers for external labels.
## Metadata
- Canonical name: `Knob`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Knob } from '@platform-blocks/react-ui-library';`
- Category: input
- Tags: knob, dial, rotary, gesture, input
- Docs: https://react-ui-library.com/components/Knob
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Knob
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `behavior` | KnobBehavior | No | 'level' | What kind of control this is: how it behaves and what it reads out. @default 'level' |
| `variant` | KnobVariant | No | 'default' | Visual style preset. Merged under `appearance`, so single properties stay overridable. Behavior values (`level`, `stepped`, β¦) still work here at runtime but are deprecated β pass them to `behavior` instead. @default 'default' |
| `mode` | 'bounded' \| 'endless' | No | | Interaction mode for bounded or endless rotary behavior |
| `value` | number | No | | Controlled value |
| `defaultValue` | number | No | | Uncontrolled initial value |
| `min` | number | No | | Minimum selectable value |
| `max` | number | No | | Maximum selectable value |
| `step` | number | No | | Step increment applied when interacting |
| `onChange` | (value: number) => void | No | | Called on every value change |
| `onChangeEnd` | (value: number) => void | No | | Called after interaction completes |
| `onScrubStart` | () => void | No | | Fired when the user begins dragging |
| `onScrubEnd` | () => void | No | | Fired when the user ends dragging |
| `size` | ComponentSizeValue | No | | Size token (`xs`β`3xl`) or an explicit diameter in pixels |
| `thumbSize` | number | No | | Diameter of the thumb indicator, in pixels. Defaults to a ratio of the resolved size. |
| `disabled` | boolean | No | | Disable all user interaction |
| `readOnly` | boolean | No | | Prevent interaction but keep visual state |
| `formatLabel` | (value: number) => ReactNode | No | | Custom formatter for the value label |
| `withLabel` | boolean | No | | Render the value label inside the knob |
| `valueLabel` | KnobValueLabelConfig \| false | No | | Structured configuration for the value label block |
| `marks` | KnobMark[] | No | | Optional marks rendered around the control |
| `restrictToMarks` | boolean | No | | Restrict interaction to the supplied marks |
| `label` | ReactNode | No | | Optional visual label rendered outside the knob |
| `description` | ReactNode | No | | Optional helper text rendered with the label |
| `labelPosition` | 'left' \| 'right' \| 'top' \| 'bottom' | No | | Placement for the external label |
| `style` | StyleProp | No | | Style overrides for the outer container |
| `trackStyle` | StyleProp | No | | Style overrides for the circular track |
| `thumbStyle` | StyleProp | No | | Style overrides for the thumb |
| `markLabelStyle` | StyleProp | No | | Style overrides for mark labels |
| `testID` | string | No | | Accessibility identifier |
| `accessibilityLabel` | string | No | | Screen reader label |
| `appearance` | KnobAppearance | No | | Unified surface styling and interaction overrides |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
## Examples
### Basic
ID: `Knob.basic` β’ Tags: basic, knob, control β’ Category: basics β’ Status: experimental β’ Since: 1.0.0
Controlled knob with percentage formatting and a mirrored readout below the dial.
```tsx
const [value, setValue] = useState(90);
return (
Math.round(current),
suffix: 'Β°',
}}
/>
);
}
```
### Events
ID: `Knob.events` β’ Tags: events, scrubbing, callbacks β’ Category: behavior β’ Status: experimental β’ Since: 1.0.0
Demonstrates live value, committed value, and scrubbing lifecycle callbacks.
```tsx
const [value, setValue] = useState(32);
const [isScrubbing, setIsScrubbing] = useState(false);
const [committed, setCommitted] = useState(value);
return (
setIsScrubbing(true)}
onScrubEnd={() => setIsScrubbing(false)}
/>
);
}
```
### Visual Variants
ID: `Knob.variants` β’ Tags: variant, style, preset β’ Category: variants β’ Status: experimental β’ Since: 1.0.0
`variant` picks the dial's look, independent of `behavior`. Each preset sets stroke weights, caps, body fill, and which indicator carries the value, taking its colors from the theme so the same knob reads correctly in light and dark. Presets are merged *under* `appearance`, so any single property stays overridable.
```tsx
const VARIANTS: { variant: KnobVariant; blurb: string }[] = [
{ variant: 'default', blurb: 'Stock dial' },
{ variant: 'minimal', blurb: 'Hairline, dense UIs' },
{ variant: 'digital', blurb: 'Hard edges, lit marker' },
{ variant: 'retro', blurb: 'Solid body, indicator arm' },
{ variant: 'studio', blurb: 'Plugin rack' },
];
const [value, setValue] = useState(62);
return (
{VARIANTS.map(({ variant, blurb }) => (
{variant}
))}
);
}
```
### Endless
ID: `Knob.endless` β’ Tags: endless, encoder, rotation β’ Category: variants β’ Status: experimental β’ Since: 1.0.0
Endless mode resets the dial each turn while tracking the cumulative rotation value.
```tsx
const [value, setValue] = useState(0);
const normalizedAngle = useMemo(() => ((value % 360) + 360) % 360, [value]);
const rotations = useMemo(() => value / 360, [value]);
return (
`${Math.round(normalizedAngle)}Β°`,
secondary: {
formatter: () => `${rotations.toFixed(2)} turns`,
},
}}
/>
);
}
```
### Dual Readout
ID: `Knob.dual-readout` β’ Tags: dual, valueLabel β’ Category: variants β’ Status: experimental β’ Since: 1.1.0
Demonstrates the `dual` behavior with a center frequency label and a derived percentage below the knob.
```tsx
const [cutoff, setCutoff] = useState(3200);
const percent = useMemo(() => Math.round(((cutoff - 200) / (8000 - 200)) * 100), [cutoff]);
return (
`${Math.round(val)} Hz`,
secondary: {
position: 'bottom',
formatter: () => `${percent}% span`,
},
}}
marks={[
{ value: 400, label: 'Warm' },
{ value: 1200, label: 'Neutral' },
{ value: 6400, label: 'Bright' },
]}
/>
);
}
```
### Status Selector
ID: `Knob.status-selector` β’ Tags: status, behavior β’ Category: variants β’ Status: experimental β’ Since: 1.1.0
Highlights the `status` behavior with icon-enhanced marks, accent colors, and the active scene named directly beneath the icon in the center slot.
```tsx
const [value, setValue] = useState(0);
const statusMarks = useMemo(
() => STATUS_SCENES.map(scene => ({
...scene,
icon: ,
})),
[]
);
const activeStatus = useMemo(
() => statusMarks.reduce((closest, mark) => (
Math.abs(mark.value - value) < Math.abs(closest.value - value) ? mark : closest
), statusMarks[0]),
[statusMarks, value]
);
return (
activeStatus.label,
}}
/>
);
}
```
### Segmented Progress
ID: `Knob.segment-progress` β’ Tags: segments, progress, ring, gauge β’ Category: features β’ Status: experimental β’ Since: 1.0.0
The same bands with `ring.segmentMode: 'progress'`, which makes them the progress arc itself: they stop at the current value and nothing is drawn beyond it, so the fill runs through each color in turn.
```tsx
const ZONES = [
{ value: 60, color: '#22c55e' },
{ value: 25, color: '#f59e0b' },
{ value: 15, color: '#ef4444' },
];
const [load, setLoad] = useState(72);
return (
`${Math.round(val)}%` }}
/>
);
}
```
### Tick Selector
ID: `Knob.tick-selector` β’ Tags: ticks, selector, detent, active β’ Category: features β’ Status: experimental β’ Since: 1.0.0
A twelve-position rotary switch using `activeMode: 'nearest'`, which lights only the tick the pointer is aimed at. The default `'fill'` instead lights every tick up to the value, the way a meter fills.
```tsx
// Twelve detents on a full circle. `max` is 12 rather than 11 so position 11 sits one step
// short of the top instead of overlapping position 0. Each detent carries its own
const POSITIONS = POSITION_COLORS.map((accentColor, index) => ({ value: index, accentColor }));
const [position, setPosition] = useState(3);
return (
(mark?.accentColor ? `${mark.accentColor}44` : '#475569'),
},
],
}}
valueLabel={{ formatter: (val) => `${Math.round(val) + 1}` }}
/>
);
}
```
### Compound Panning
ID: `Knob.compound-panning` β’ Category: general
Stereo-style panning knob composed with `Knob.Root`, split progress, and custom tick labels to highlight the compound sub-component API.
```tsx
// The split arc reads the same on both sides of center: direction is carried by which way
// the arc grows and by the L/R label, not by a color change.
const PAN_COLOR = '#4ade80';
const [pan, setPan] = useState(-18);
const readout = useMemo(() => {
if (pan === 0) return 'Center';
return pan > 0 ? `Right ${Math.abs(pan)}` : `Left ${Math.abs(pan)}`;
}, [pan]);
return (
`${value > 0 ? 'R' : value < 0 ? 'L' : ''}${Math.abs(Math.round(value))}`}
textStyle={{ fontSize: 30, fontWeight: '700', color: '#f8fafc' }}
/>
Stereo balance Β· {readout}
);
}
```
### Pointer Clock
ID: `Knob.pointer-clock` β’ Category: general
Read-only analog clock face built with the compound `Knob.Root` API: a bezel ring, 60 minute marks with bolder hour marks, hour numerals, and `Knob.Pointer` as the hour hand driven by the value (minutes past 12). The minute and second hands are plain rotated views composed over the same center, synced to the system clock each second.
```tsx
const SIZE = 240;
const CENTER = SIZE / 2;
const MINUTES_PER_TURN = 12 * 60;
const HOUR_VALUES = Array.from({ length: 12 }, (_, index) => index * 60);
const HOUR_LABELS = ['12', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'];
// 60 minute marks around the dial; hour positions are drawn by the layer above.
const MINUTE_VALUES = Array.from({ length: 60 }, (_, index) => index * 12).filter(
(value) => value % 60 !== 0
);
const formatTime = (minutes: number) => {
const hour = Math.floor(minutes / 60);
return `${hour === 0 ? 12 : hour}:${(minutes % 60).toString().padStart(2, '0')}`;
};
/** Hand pivoting on the dial center: the wrapper is twice the hand length, so it rotates around it. */
const Hand = ({
angle,
length,
width,
color,
tail = 0,
}: {
angle: number;
length: number;
width: number;
color: string;
tail?: number;
}) => (
);
const theme = useTheme();
const face = theme.backgrounds.surface;
const ink = theme.text.primary;
const accent = theme.colors.primary[6];
// Start on a fixed time so server-rendered and client markup match, then sync on mount.
const [time, setTime] = useState({ minutes: 10 * 60 + 10, seconds: 0 });
useEffect(() => {
const tick = () => {
const now = new Date();
setTime({
minutes: (now.getHours() % 12) * 60 + now.getMinutes(),
seconds: now.getSeconds(),
});
};
tick();
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []);
return (
{/* Chapter ring just inside the minute marks */}
HOUR_LABELS[index],
position: 'inner',
offset: -26,
style: { color: ink, fontSize: 15, fontWeight: '600' },
}}
/>
{/* Hour hand β the knob value is minutes past 12, so it advances gradually. */}
{formatTime(time.minutes)}
);
}
```
### Semicircle Gauge
ID: `Knob.semicircle-gauge` β’ Category: general
Semicircle gauge layout that uses `Knob.Root` with custom ring thickness, contiguous progress, and a pointer hand for instrumentation-style readouts.
```tsx
const TEMPERATURE_STOPS = [0, 25, 50, 75, 100];
const [level, setLevel] = useState(62);
const status = useMemo(() => {
if (level >= 85) return 'Critical';
if (level >= 60) return 'Elevated';
if (level >= 35) return 'Nominal';
return 'Idle';
}, [level]);
return (
`${TEMPERATURE_STOPS[index]}%`,
offset: 24,
style: { color: '#cbd5f5', fontSize: 12, fontWeight: '600' },
}}
/>
`${Math.round(value)}% capacity`}
textStyle={{ fontSize: 18, fontWeight: '600', color: '#f8fafc' }}
secondary={{
formatter: () => status,
position: 'bottom',
textStyle: { fontSize: 14, color: '#94a3b8', marginTop: 4 },
}}
/>
Thermal headroom Β· {status}
);
}
```
### Tick Layers
ID: `Knob.tick-layers` β’ Category: general
Stacks two tick layers on one dial: labelled lines driven by `marks`, over a finer dot scale from an explicit step list.
```tsx
const LEVEL_MARKS = [
{ value: 0, label: 'Mute' },
{ value: 25, label: 'Low' },
{ value: 50, label: 'Mid' },
{ value: 75, label: 'High' },
{ value: 100, label: 'Max' },
];
const [level, setLevel] = useState(48);
return (
`${Math.round(val)}%` }}
/>
);
}
```
### Interaction modes
ID: `Knob.interaction-modes` β’ Tags: interaction, gestures, scroll β’ Category: behavior β’ Status: experimental β’ Since: 1.0.0
Showcases spin, vertical-slide, horizontal-slide, and scroll gestures enabled through `appearance.interaction`, updating the label as each mode locks in.
```tsx
const MODES = [
{
key: 'spin',
name: 'Spin',
detail: 'Drag in a circular path. Move away from the thumb for finer adjustments.',
},
{
key: 'vertical-slide',
name: 'Vertical slide',
detail: 'Grab either side of the knob and drag up or down for mixer-style throws.',
},
{
key: 'horizontal-slide',
name: 'Horizontal slide',
detail: 'Start above or below the center, then drag left or right for sideways sweeps.',
},
{
key: 'scroll',
name: 'Scroll',
detail: 'Hover with a mouse or trackpad and use the wheel/two-finger scroll.',
},
] as const;
type ModeName = (typeof MODES)[number]['key'];
const MODE_LABELS: Record = MODES.reduce((acc, mode) => {
acc[mode.key] = mode.name;
return acc;
}, {} as Record);
const theme = useTheme();
const [value, setValue] = useState(12);
const [activeMode, setActiveMode] = useState(null);
return (
Multimodal control
`${current > 0 ? '+' : ''}${Math.round(current)}`,
secondary: {
position: 'bottom',
formatter: () => (activeMode ? `${MODE_LABELS[activeMode]} mode` : 'Try a gesture'),
},
}}
appearance={{
arc: { startAngle: -135, sweepAngle: 270, clampInput: true },
ring: { thickness: 16, color: '#0f172a', trailColor: '#1e293b' },
fill: { color: '#020617', radiusOffset: -14 },
progress: {
mode: 'split',
roundedCaps: true,
thickness: 10,
color: '#38bdf8',
trailColor: '#475569',
},
interaction: {
modes: MODES.map((mode) => mode.key),
lockThresholdPx: 32,
slideRatio: 1.5,
variancePx: 6,
spinPrecisionRadius: 80,
respectStartSide: true,
scroll: { enabled: true, ratio: 0.8, preventPageScroll: true },
onModeChange: setActiveMode,
},
}}
/>
{MODES.map((mode) => (
{/* The mode currently driving the knob is pulled up to full-contrast text. */}
{mode.name}
{mode.detail}
))}
);
}
```
--------------------------------------------------------------------------------
# Link
A versatile component for creating styled hyperlinks and navigation elements with hover states and accessibility features.
## Metadata
- Canonical name: `Link`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Link } from '@platform-blocks/react-ui-library';`
- Category: navigation
- Tags: link, anchor, navigation, url, href
- Docs: https://react-ui-library.com/components/Link
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Link
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | React.ReactNode | Yes | | Link text content |
| `href` | string | No | | URL or handler for the link |
| `onPress` | () => void | No | | Custom onPress handler (overrides href) |
| `size` | SizeValue | No | 'lg' | Size of the link text (default: 'lg' = 16px to match Text component) |
| `color` | 'primary' \| 'secondary' \| 'success' \| 'warning' \| 'error' \| 'gray' \| 'inherit' \| string | No | | Color variant or custom color string |
| `variant` | 'default' \| 'subtle' \| 'hover-underline' | No | 'default' | Link variant |
| `disabled` | boolean | No | false | Whether the link is disabled |
| `external` | boolean | No | false | Whether to show external link indicator |
| `style` | ViewStyle | No | | Custom style for container |
| `textStyle` | TextStyle | No | | Custom style for text |
| `accessibilityLabel` | string | No | | Accessibility label |
| `target` | '_blank' \| '_self' | No | '_self' | Whether this link opens in a new tab/window (web only) |
| `fontFamily` | string | No | | Custom font family (overrides theme font) |
| `ff` | string | No | | Shorthand alias for `fontFamily` |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Inline Links
ID: `Link.basic` β’ Tags: link β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Embed links directly inside supporting copy to guide readers toward related resources.
```tsx
const resources = [
{ href: '#brand', label: 'brand guidelines' },
{ href: '#voice', label: 'voice and tone guide' },
{ href: '#releases', label: 'release checklist' },
];
const [brandGuide, voiceGuide, releaseChecklist] = resources;
return (
Use `Link` inline with body copy to direct readers to additional guidance without breaking the flow of text.
Before publishing, review the{' '}
{brandGuide.label}, consult our{' '}
{voiceGuide.label}, and confirm each launch in the{' '}
{releaseChecklist.label}.
);
}
```
### External Destinations
ID: `Link.external` β’ Tags: link, external β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Use the `external` prop when pointing to destinations outside the current shell.
```tsx
const references = [
{ href: 'https://reactnative.dev', label: 'React Native documentation', color: 'primary' },
{ href: 'https://expo.dev', label: 'Expo documentation', color: 'secondary' },
{ href: 'mailto:support@example.com', label: 'Email support', color: 'gray' },
];
return (
Set `external` to ensure the link opens outside the app shell and receives the proper accessibility attributes.
{references.map((resource) => (
{resource.label}
))}
);
}
```
### Size Options
ID: `Link.sizes` β’ Tags: link, sizing β’ Category: appearance β’ Status: stable β’ Since: 1.0.0
Demonstrate how the `size` token scales link typography and spacing.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const;
return (
{SIZES.map((size) => (
Link
{size}
))}
);
}
```
### Visual Variants
ID: `Link.variants` β’ Tags: link, appearance β’ Category: appearance β’ Status: stable β’ Since: 1.0.0
Compare persistent and hover-only underlines alongside subtle variants.
```tsx
const linkVariants = [
{ label: 'Default underline', variant: 'default' as const, description: 'Underline is always visible for maximum affordance.' },
{ label: 'Hover underline', variant: 'hover-underline' as const, description: 'Underline appears on hover for denser layouts.' },
{ label: 'Subtle primary', variant: 'subtle' as const, color: 'primary', description: 'Muted style that still matches the brand palette.' },
{ label: 'Subtle gray', variant: 'subtle' as const, color: 'gray', description: 'Pair with neutral layouts or footers.' },
];
return (
Choose a `variant` that matches the surrounding density while keeping the link discoverable.
{linkVariants.map((entry) => (
{entry.label}
{entry.description}
))}
);
}
```
--------------------------------------------------------------------------------
# ListGroup
The ListGroup component provides an organized list structure with items, dividers, and sections for displaying grouped content.
## Metadata
- Canonical name: `ListGroup`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { ListGroup } from '@platform-blocks/react-ui-library';`
- Category: display
- Tags: list, group, items, divider, sections
- Docs: https://react-ui-library.com/components/ListGroup
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/ListGroup
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | React.ReactNode | Yes | | |
| `variant` | 'default' \| 'bordered' \| 'flush' | No | 'default' | |
| `size` | ComponentSizeValue | No | 'md' | |
| `radius` | 'sm' \| 'md' \| 'lg' \| number | No | 'md' | |
| `dividers` | boolean | No | true | |
| `insetDividers` | boolean | No | false | |
| `style` | StyleProp | No | | |
## Examples
### Basic Usage
ID: `ListGroup.basic` β’ Tags: list, items β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Compose a vertical list by nesting `ListGroupItem` elements inside a `ListGroup`. Use the `variant` prop to switch between `default`, `bordered`, and `flush` styles.
```tsx
return (
Overview
Analytics
Reports
Settings
);
}
```
### Two-line rows
ID: `ListGroup.two-line` β’ Tags: list, label, description, settings β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Pass `label` and `description` for a stacked row. These take precedence over `children`, which renders as a single line of text and so cannot hold a layout block. `description` is optional β a `label` on its own reads the same as `children`, and mixing both row shapes in one group stays aligned.
```tsx
return (
);
}
```
### Trailing value
ID: `ListGroup.trailing-value` β’ Tags: list, value, alignment, sections β’ Category: composition β’ Status: stable β’ Since: 1.0.0
`value` renders muted text at the end of the row, before `endSection`. A two-line row already claims the free space, so its value sits flush right on its own; a single-line row only takes its natural width, so the value is what gets pushed to the edge and `endSection` follows it.
```tsx
return (
New}>
Inbox
);
}
```
--------------------------------------------------------------------------------
# Loader
A animated loading component for indicating ongoing processes and loading states with various sizes and styles.
## Metadata
- Canonical name: `Loader`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Loader } from '@platform-blocks/react-ui-library';`
- Category: feedback
- Tags: loader, loading, progress, indicator, animation
- Docs: https://react-ui-library.com/components/Loader
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Loader
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `size` | SizeValue | No | 'md' | Size of the loader - can be a size token or number |
| `color` | string | No | | Color of the loader |
| `variant` | LoaderVariant | No | 'oval' | Variant of the loader |
| `speed` | number | No | 1000 | Animation speed in milliseconds |
| `style` | StyleProp | No | | Container style |
| `testID` | string | No | | Test ID for testing |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic Usage
ID: `Loader.basic` β’ Tags: variant β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Pick a loader `variant` to match the type of busy indicator you need for a loading state.
```tsx
return (
);
}
```
### Sizes
ID: `Loader.sizes` β’ Tags: size β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Set the `size` token to align loaders with other controls, from `xs` indicators up to `3xl` spinners.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const;
return (
{SIZES.map((size) => (
{size}
))}
);
}
```
### Colors
ID: `Loader.colors` β’ Tags: color, theme β’ Category: theming β’ Status: stable β’ Since: 1.0.0
Pull palette values from `useTheme()` and pass them to the `color` prop to align loaders with your semantic colors.
```tsx
interface LoaderSwatch {
label: string;
color: string;
}
const theme = useTheme();
const swatches: LoaderSwatch[] = [
{ label: 'Primary', color: theme.colors.primary[5] },
{ label: 'Success', color: theme.colors.success[5] },
{ label: 'Warning', color: theme.colors.warning[5] },
{ label: 'Error', color: theme.colors.error[5] }
];
return (
{swatches.map(({ label, color }) => (
{label}
))}
);
}
```
### Speed
ID: `Loader.speed` β’ Category: general
```tsx
// `speed` is the duration of one full animation cycle in milliseconds β
// lower is faster. Default is 1000ms.
const SPEEDS = [
{ label: 'Fast', value: 400 },
{ label: 'Default', value: 1000 },
{ label: 'Slow', value: 2000 },
];
return (
{SPEEDS.map(({ label, value }) => (
{label}
{value}ms
))}
);
}
```
--------------------------------------------------------------------------------
# LoadingOverlay
`LoadingOverlay` composits the core `Overlay` and `Loader` primitives to create a convenient helper for blocking interactions with a visual indicator during asynchronous operations. Render it inside a relatively positioned container, toggle `visible` during asynchronous work, and customize appearance by passing `overlayProps` or `loaderProps`.
## Metadata
- Canonical name: `LoadingOverlay`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { LoadingOverlay } from '@platform-blocks/react-ui-library';`
- Status: beta
- Category: feedback
- Docs: https://react-ui-library.com/components/LoadingOverlay
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/LoadingOverlay
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `visible` | boolean | No | false | Controls visibility of the loading overlay. |
| `zIndex` | number | No | | z-index applied to the overlay container. Overrides value defined in overlayProps when provided. |
| `overlayProps` | OverlayProps | No | | Props forwarded to the underlying Overlay component. |
| `loaderProps` | LoaderProps | No | | Props forwarded to the Loader component. |
| `loader` | ReactNode | No | | Custom loader content. When provided, Loader component is not rendered. |
## Examples
### Form blocking
ID: `LoadingOverlay.basic` β’ Tags: overlays, loading β’ Category: feedback β’ Status: stable β’ Since: 1.0.0
Locks a simple form while background work finishes and keeps the loader aligned with the card container.
```tsx
type TextFieldConfig = {
key: string;
} & Pick, 'label' | 'placeholder' | 'keyboardType' | 'secureTextEntry'>;
const TEXT_FIELDS: TextFieldConfig[] = [
{ key: 'first-name', label: 'First name', placeholder: 'Jane' },
{ key: 'last-name', label: 'Last name', placeholder: 'Doe' },
{
key: 'email',
label: 'Email',
placeholder: 'jane@react-ui-library.com',
keyboardType: 'email-address',
},
{
key: 'password',
label: 'Password',
placeholder: 'β’β’β’β’β’β’β’β’',
secureTextEntry: true,
},
];
const [visible, setVisible] = useState(false);
return (
Account details
Pause form interaction while requests finish and keep the layout intact.
{TEXT_FIELDS.map(({ key, ...field }) => (
))}
setVisible((current) => !current)}>
{visible ? 'Stop loading' : 'Simulate loading'}
LoadingOverlay anchors to a relative container and dims the content while the loader animates.
);
}
const styles = StyleSheet.create({
wrapper: {
width: '100%',
},
section: {
width: '100%',
maxWidth: 480,
alignSelf: 'center',
},
card: {
width: '100%',
},
});
```
--------------------------------------------------------------------------------
# Markdown
Markdown component provides a way to render Markdown content with custom styling and component mapping. It supports standard Markdown syntax including headers, lists, code blocks, and more.
## Metadata
- Canonical name: `Markdown`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Markdown } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: data
- Docs: https://react-ui-library.com/components/Markdown
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Markdown
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | string | Yes | | |
| `defaultCodeLanguage` | string | No | | Override default code block language guess |
| `maxHeadingLevel` | number | No | | Max heading level to render (others downgraded) |
| `allowHtml` | boolean | No | | Whether to render inline HTML literally (ignored for now) |
| `components` | Partial | No | | Custom renderer overrides |
| `onLinkPress` | (href: string) => void | No | | Optional handler invoked when a markdown link is pressed |
| `fontFamily` | string | No | | Custom font family applied to all rendered text (overrides the theme font) |
| `ff` | string | No | | Shorthand alias for `fontFamily` |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic Usage
ID: `Markdown.basic` β’ Category: general
Simple markdown rendering with headers, lists, and text formatting.
```tsx
const CONTENT = `# Hello Markdown
This is a **bold** statement and this is _italic_.
- Item one
- Item two
- Item three
> Blockquote with *inline emphasis* and **strong** text.
Inline code: \`const x = 42;\``;
return (
{CONTENT}
Rendered using the default Markdown renderer
);
}
```
### Code Blocks
ID: `Markdown.code` β’ Category: general
Markdown rendering with syntax-highlighted code blocks.
```tsx
const CONTENT = `# Code examples
Here's some JavaScript:
\`\`\`javascript
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
\`\`\`
And some TypeScript:
\`\`\`typescript
interface User {
id: number;
name: string;
email: string;
}
const user: User = {
id: 1,
name: "John Doe",
email: "john@example.com"
};
\`\`\`
Inline code: \`const result = fibonacci(10);\``;
return (
{CONTENT}
Showcases fenced code blocks with syntax highlighting
);
}
```
### Custom Components
ID: `Markdown.custom` β’ Category: general
Custom component mapping for markdown elements.
```tsx
const CUSTOM_COMPONENTS = {
h1: ({ children, ...props }: any) => (
{children}
),
h2: ({ children, ...props }: any) => (
{children}
),
p: ({ children, ...props }: any) => (
{children}
),
blockquote: ({ children, ...props }: any) => (
{children}
),
};
const CONTENT = `# Custom styled Markdown
## This is a subtitle
This paragraph uses custom styling and components.
> This blockquote is rendered with a custom Card component and muted background.
Regular paragraph text with default styling.
`;
return (
{CONTENT}
Headings, paragraphs, and quotes use custom renderers
);
}
```
### Inline Usage
ID: `Markdown.inline` β’ Category: general
Using markdown inline within other text content.
```tsx
const inlineContent = 'This is **bold text** and this is *italic text* with `inline code`.';
return (
Inline markdown: {inlineContent}
Mix with regular text: Here's some regular text, then **markdown formatting** and
back to regular.
Code in context: Use `const x = 42;` to declare a variable.
);
}
```
### Media & Tables
ID: `Markdown.media` β’ Category: general
Markdown with images, links, tables, and horizontal rules.
```tsx
const CONTENT = `# Media in Markdown
## Images

## Links
Visit the [PlatformBlocks Documentation](https://react-ui-library.com) for more examples.
## Tables
| Feature | Status | Notes |
|---------|--------|-------|
| **Text Formatting** | β
| Bold, _italic_, \`code\` |
| Code Blocks | β
| Syntax highlighting |
| Tables | β
| Responsive layout |
| Images | β
| Auto-sizing |
| [Links](https://react-ui-library.com) | β
| External navigation |
## Horizontal Rule
Content above the line.
---
Content below the line.`;
return (
{CONTENT}
Images, links, tables, and horizontal rules render inline
);
}
```
### Table Support
ID: `Markdown.table` β’ Category: general
Markdown tables with proper formatting and styling.
```tsx
const CONTENT = `# Table examples
## Basic table
| Name | Age | City |
|------|-----|------|
| John Doe | 30 | New York |
| Jane Smith | 25 | Los Angeles |
| Bob Johnson | 35 | Chicago |
## Table with formatting
| Feature | Status | **Priority** | Notes |
|---------|--------|-------------|--------|
| Authentication | β
| **High** | _Complete_ |
| User Management | π | **Medium** | In progress |
| Analytics | β | **Low** | \`Not started\` |
| API Integration | β
| **High** | [Documentation](https://example.com) |
## Table with code
| Language | Extension | Sample code |
|----------|-----------|-------------|
| TypeScript | \`.tsx\` | \`const x: string = "hello";\` |
| JavaScript | \`.js\` | \`function hello() { return "world"; }\` |
| Python | \`.py\` | \`def hello(): return "world"\` |
## Complex table
| Component | **Props** | _Description_ | Example |
|-----------|----------|-------------|---------|
| Button | \`variant\`, \`size\`, \`disabled\` | Interactive button element | \`Click me \` |
| Input | \`placeholder\`, \`value\`, \`onChange\` | Text input field | \` \` |
| Card | \`variant\`, \`padding\` | Container component | \`Content \` |`;
return (
{CONTENT}
Multiple table layouts rendered with Markdown
);
}
```
--------------------------------------------------------------------------------
# Masonry
Masonry provides an efficient way to create Pinterest-style layouts where items are arranged in columns with varying heights. Built on FlashList for optimal performance with large datasets, it automatically handles item positioning and provides smooth scrolling even with hundreds of items. The component supports dynamic heights through the heightRatio property on items, custom renderers, and responsive column counts. Perfect for image galleries, card layouts, or any scenario where you need an organic, space-efficient arrangement of content.
## Metadata
- Canonical name: `Masonry`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Masonry } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: layout
- Docs: https://react-ui-library.com/components/Masonry
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Masonry
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `data` | MasonryItem[] | Yes | | Array of items to display in masonry layout |
| `numColumns` | number | No | | Number of columns (default: 2) |
| `gap` | SizeValue | No | | Spacing between items |
| `optimizeItemArrangement` | boolean | No | | Whether to optimize for staggered grid layout |
| `renderItem` | (item: MasonryItem, index: number) => ReactNode | No | | Custom item renderer - receives item and index |
| `contentContainerStyle` | StyleProp | No | | Content container style |
| `style` | StyleProp | No | | Custom styles |
| `testID` | string | No | | Test ID for testing |
| `loading` | boolean | No | | Loading state |
| `emptyContent` | ReactNode | No | | Empty state content |
| `flashListProps` | MasonryFlashListProps | No | | Flash list props to pass through |
| `onEndReached` | ((info: { distanceFromEnd: number }) => void) \| null | No | | Callback when the end of the list is reached (for pagination / infinite scroll) |
| `onEndReachedThreshold` | number | No | | Distance from end (in pixels) to trigger onEndReached (default: FlashList default) |
| `onViewableItemsChanged` | MasonryViewabilityCallback | No | | Callback when viewable items change |
| `scrollEnabled` | boolean | No | | Whether scrolling is enabled |
| `ListEmptyComponent` | React.ComponentType \| React.ReactElement \| null | No | | Component rendered when the list is empty |
| `ListFooterComponent` | React.ComponentType \| React.ReactElement \| null | No | | Component rendered at the bottom of the list |
| `ListHeaderComponent` | React.ComponentType \| React.ReactElement \| null | No | | Component rendered at the top of the list |
| `estimatedItemSize` | number | No | | Estimated size of each item (performance hint) |
| `refreshControl` | React.ReactElement | No | | Pull-to-refresh control |
| `onScroll` | ScrollViewProps['onScroll'] | No | | Scroll event callback |
| `scrollEventThrottle` | number | No | | Throttle interval for scroll events in ms |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic Masonry
ID: `Masonry.basic` β’ Tags: basic, layout, grid, columns, simple β’ Category: general
Simple masonry layout with uniform item heights arranged in a two-column grid.
```tsx
const theme = useTheme();
const masonryItems: MasonryItem[] = [
{
id: '1',
content: (
Card 1
This is a basic card item in the masonry layout.
),
},
{
id: '2',
content: (
Card 2
Short content.
),
},
{
id: '3',
content: (
Card 3
A third card showing how items are arranged in the masonry grid with longer content that will make this card taller than the others.
),
},
{
id: '4',
content: (
Card 4
Medium length content here.
),
},
{
id: '5',
content: (
Card 5
Fifth card in the masonry layout grid.
),
},
{
id: '6',
content: (
Card 6
Sixth card showing the two-column arrangement.
),
},
];
return (
);
}
```
### Custom Columns
ID: `Masonry.custom-columns` β’ Tags: columns, responsive, grid, arrangement, configurable β’ Category: general
Masonry layout with configurable number of columns demonstrating different grid arrangements.
```tsx
const theme = useTheme();
const [numColumns, setNumColumns] = useState(3);
const masonryItems: MasonryItem[] = [
{
id: '1',
heightRatio: 1.1,
content: (
Item 1
Content for first item with some extra text.
),
},
{
id: '2',
heightRatio: 0.8,
content: (
Item 2
Short content.
),
},
{
id: '3',
heightRatio: 1.3,
content: (
Item 3
Longer content to demonstrate height variation in different column layouts.
),
},
{
id: '4',
heightRatio: 0.9,
content: (
Item 4
Medium length content.
),
},
{
id: '5',
heightRatio: 1.5,
content: (
Item 5
Extended content that takes up more space to show how columns adapt.
),
},
{
id: '6',
heightRatio: 0.7,
content: (
Item 6
Compact.
),
},
{
id: '7',
heightRatio: 1.2,
content: (
Item 7
Another item with moderate content length for testing.
),
},
{
id: '8',
heightRatio: 0.9,
content: (
Item 8
Standard content item.
),
},
{
id: '9',
heightRatio: 1.4,
content: (
Item 9
Taller content to fill out the grid and show column distribution effects.
),
},
];
return (
<>
setNumColumns(1)}
/>
setNumColumns(2)}
/>
setNumColumns(3)}
/>
setNumColumns(4)}
/>
>
);
}
```
### Variable Heights
ID: `Masonry.variable-heights` β’ Tags: heights, staggered, organic, pinterest, variable β’ Category: general
Masonry layout with items of different heights creating an organic, Pinterest-style staggered appearance.
```tsx
const theme = useTheme();
const masonryItems: MasonryItem[] = [
{
id: '1',
heightRatio: 1.2,
content: (
Tall Card
This is a taller card with more content to demonstrate the variable height
functionality. It shows how items with different heights are arranged in
the masonry layout to create an organic, staggered appearance.
),
},
{
id: '2',
heightRatio: 0.7,
content: (
Short Card
A shorter card with minimal content.
),
},
{
id: '3',
heightRatio: 1.8,
content: (
Very Tall Card
This card is extra tall to showcase the masonry layout's ability to handle
significant height variations. Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation.
Additional paragraph to make it even taller and show how the layout adapts
to content of different sizes naturally.
),
},
{
id: '4',
heightRatio: 1.0,
content: (
Regular Card
A standard height card with regular content length.
),
},
{
id: '5',
heightRatio: 0.9,
content: (
Medium Card
Medium height card with moderate content.
),
},
{
id: '6',
heightRatio: 1.5,
content: (
Extended Card
An extended card that demonstrates how the masonry layout handles
items that are taller than average, creating natural flow patterns.
),
},
{
id: '7',
heightRatio: 0.6,
content: (
Compact
Compact card.
),
},
{
id: '8',
heightRatio: 2.0,
content: (
Extra Tall Card
This is the tallest card in the set, demonstrating the maximum height
variation supported by the masonry layout. It shows how very tall items
are positioned while maintaining good visual balance.
The masonry layout algorithm ensures that even with extreme height
differences, the overall composition remains visually pleasing and
well-balanced across columns.
This extra content makes the card significantly taller than others to
really showcase the variable height capabilities.
),
},
];
return (
);
}
```
--------------------------------------------------------------------------------
# Menu
The Menu component provides a dropdown interface for navigation links, actions, and contextual options. It supports flexible positioning, keyboard navigation, and customizable triggers.
## Metadata
- Canonical name: `Menu`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Menu } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: navigation
- Docs: https://react-ui-library.com/components/Menu
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Menu
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `opened` | boolean | No | | Whether the menu is open |
| `trigger` | 'click' \| 'hover' \| 'contextmenu' | No | 'click' | Menu trigger event type |
| `position` | 'top' \| 'bottom' \| 'left' \| 'right' \| 'auto' \| 'top-start' \| 'top-end' \| 'bottom-start' \| 'bottom-end' \| 'left-start' \| 'left-end' \| 'right-start' \| 'right-end' | No | 'auto' | Position relative to trigger |
| `offset` | number | No | 4 | Offset from trigger element |
| `closeOnClickOutside` | boolean | No | true | Whether to close when clicking outside |
| `closeOnEscape` | boolean | No | true | Whether to close when pressing escape |
| `onOpen` | () => void | No | | Callback when menu opens |
| `onClose` | () => void | No | | Callback when menu closes |
| `w` | number \| 'target' \| 'auto' | No | 'auto' | Menu content width |
| `maxH` | number | No | 300 | Maximum height for scrollable content |
| `shadow` | 'none' \| 'sm' \| 'md' \| 'lg' \| 'xl' | No | 'md' | Menu content shadow |
| `radius` | 'none' \| 'sm' \| 'md' \| 'lg' \| 'xl' | No | 'md' | Border radius |
| `children` | React.ReactNode | Yes | | Menu trigger element and dropdown |
| `testID` | string | No | | Test ID for testing |
| `disabled` | boolean | No | false | Whether menu is disabled |
| `strategy` | 'absolute' \| 'fixed' \| 'portal' | No | Platform.OS === 'web' ? 'fixed' : 'portal' | Menu placement strategy |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic Usage
ID: `Menu.basic` β’ Tags: menu β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Pair a trigger with `MenuDropdown` to show primary actions and separators in a compact surface.
```tsx
Button,
Card,
Icon,
Menu,
MenuDivider,
MenuDropdown,
MenuItem,
Text,
} from '@platform-blocks/react-ui-library';
return (
Open menu
}>
Profile
}>
Settings
}>
Help & Support
}>
Logout
);
}
```
### Context Trigger
ID: `Menu.context` β’ Tags: menu, contextmenu β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Enable `trigger="contextmenu"` to surface a menu when users right-click or long-press a target.
```tsx
Block,
Card,
Icon,
Menu,
MenuDivider,
MenuDropdown,
MenuItem,
Text,
} from '@platform-blocks/react-ui-library';
return (
Right-click or long-press this area
}>
Copy link
}>
Rename
}>
Share
}>
Delete
);
}
```
### Placement Presets
ID: `Menu.positioning` β’ Tags: menu, position β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Adjust the `position` prop to pin dropdown content to any edge of the trigger.
```tsx
const POSITIONS = [
{ label: 'Bottom start', position: 'bottom-start' },
{ label: 'Bottom', position: 'bottom' },
{ label: 'Bottom end', position: 'bottom-end' },
{ label: 'Top start', position: 'top-start' },
{ label: 'Top', position: 'top' },
{ label: 'Top end', position: 'top-end' },
] as const;
return (
{POSITIONS.map(({ label, position }) => (
{label}
Duplicate
Archive
))}
);
}
```
### Submenu
ID: `Menu.submenu` β’ Category: general
```tsx
Button,
Icon,
Menu,
MenuDivider,
MenuDropdown,
MenuItem,
MenuLabel,
MenuSub,
} from '@platform-blocks/react-ui-library';
return (
Actions
Document
}>Rename
{/* Flyout submenu β opens to the side on hover (web) or tap */}
}>
}>Copy link
}>Email
{/* Submenus nest arbitrarily deep */}
Twitter / X
LinkedIn
Reddit
}>
Projects
Archive
Trash
}>
Delete
);
}
```
--------------------------------------------------------------------------------
# MenuItemButton
A row button used inside menus and command palettes. The inner label `` accepts the full Text-prop API via `labelProps` (`ff`, `weight`, `tracking`, `uppercase`, `color`, `style`).
## Metadata
- Canonical name: `MenuItemButton`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { MenuItemButton } from '@platform-blocks/react-ui-library';`
- Category: navigation
- Tags: menu, dropdown, command, item, button
- Docs: https://react-ui-library.com/components/MenuItemButton
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/MenuItemButton
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `title` | string | No | | Text label (alternative to children) |
| `children` | React.ReactNode | No | | Custom content |
| `startIcon` | React.ReactNode | No | | Leading icon |
| `endIcon` | React.ReactNode | No | | Trailing icon / shortcut hint |
| `onPress` | () => void | No | | Click handler |
| `disabled` | boolean | No | | Whether the button is disabled |
| `active` | boolean | No | | Whether the button is active (selected) |
| `danger` | boolean | No | | Whether the button has destructive styling |
| `fullWidth` | boolean | No | | Whether the button should take up full width |
| `size` | ComponentSizeValue | No | | Size of the button |
| `compact` | boolean | No | | Whether to use compact styling |
| `rounded` | boolean | No | | Whether to use fully rounded corners |
| `style` | any | No | | Custom styles override |
| `onPressIn` | (event: GestureResponderEvent) => void | No | | Callback fired when press starts |
| `onPressOut` | (event: GestureResponderEvent) => void | No | | Callback fired when press ends |
| `onMouseDown` | (event: any) => void | No | | Web-only mouse down handler |
| `onMouseEnter` | (event: any) => void | No | | Web-only mouse enter handler |
| `onMouseLeave` | (event: any) => void | No | | Web-only mouse leave handler |
| `onHoverIn` | PressableProps['onHoverIn'] | No | | Pointer hover start handler (web) |
| `onHoverOut` | PressableProps['onHoverOut'] | No | | Pointer hover end handler (web) |
| `onFocus` | PressableProps['onFocus'] | No | | Focus handler |
| `onBlur` | PressableProps['onBlur'] | No | | Blur handler |
| `color` | MenuItemColor | No | | Semantic color for menu styling |
| `hoverColor` | MenuItemColor | No | | Color to apply when hovered |
| `activeColor` | MenuItemColor | No | | Color to apply when active/pressed |
| `textColor` | string | No | | Override text color for base state |
| `hoverTextColor` | string | No | | Override text color when hovered |
| `activeTextColor` | string | No | | Override text color when active |
| `testID` | string | No | | Test identifier forwarded to Pressable |
| `labelProps` | Omit | No | | Override props applied to the inner label `` (style, weight, ff, size, color). |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic Menu
ID: `MenuItemButton.basic` β’ Category: general
Simple dropdown menu with icons and dividers.
```tsx
return (
Open Menu
}>
Profile
}>
Settings
}>
Help & Support
}>
Logout
)
}
```
--------------------------------------------------------------------------------
# MiniCalendar
A compact calendar component for displaying a month view with selectable dates.
## Metadata
- Canonical name: `MiniCalendar`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { MiniCalendar } from '@platform-blocks/react-ui-library';`
- Category: dates
- Docs: https://react-ui-library.com/components/MiniCalendar
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/MiniCalendar
## Props
_No documented props yet._
## Examples
### Basic MiniCalendar
ID: `MiniCalendar.basic` β’ Category: general
Compact calendar showing a week view with date selection.
```tsx
const [selectedDate, setSelectedDate] = useState(new Date());
return (
setSelectedDate(date)}
numberOfDays={7}
/>
{selectedDate ? `Selected: ${selectedDate.toLocaleDateString()}` : 'No date selected'}
);
}
```
### Custom Day Count
ID: `MiniCalendar.customDays` β’ Category: general
MiniCalendar with configurable number of days displayed.
```tsx
const DAY_OPTIONS = [3, 5, 7];
const [selectedDate, setSelectedDate] = useState(new Date());
const [numberOfDays, setNumberOfDays] = useState(5);
return (
{DAY_OPTIONS.map((days) => (
setNumberOfDays(days)}
>
{days} days
))}
setSelectedDate(date)}
numberOfDays={numberOfDays}
/>
{selectedDate ? `Selected: ${selectedDate.toLocaleDateString()}` : 'No date selected'}
);
}
```
### Date Constraints
ID: `MiniCalendar.constrained` β’ Category: general
MiniCalendar with minimum and maximum date restrictions.
```tsx
const [selectedDate, setSelectedDate] = useState(new Date());
const { minDate, maxDate } = useMemo(() => {
const today = new Date();
const nextWeek = new Date();
nextWeek.setDate(today.getDate() + 7);
return { minDate: today, maxDate: nextWeek };
}, []);
return (
setSelectedDate(date)}
numberOfDays={7}
minDate={minDate}
maxDate={maxDate}
/>
{selectedDate ? `Selected: ${selectedDate.toLocaleDateString()}` : 'No date selected'}
Only the next seven days are enabled
);
}
```
--------------------------------------------------------------------------------
# MonthPicker
Interactive grid for selecting a month within a given year. Renders a responsive layout that adapts to screen width and respects locale formatting as well as min/max date constraints.
## Metadata
- Canonical name: `MonthPicker`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { MonthPicker } from '@platform-blocks/react-ui-library';`
- Category: dates
- Tags: date, month, picker, calendar
- Docs: https://react-ui-library.com/components/MonthPicker
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/MonthPicker
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | Date \| null | No | | Currently selected date (uses the first day of the month) |
| `onChange` | (date: Date \| null) => void | No | | Called when user picks a new month |
| `year` | number | No | | Force a specific year to render |
| `onYearChange` | (year: number) => void | No | | Called when the visible year changes |
| `minDate` | Date | No | | Minimum selectable date (inclusive) |
| `maxDate` | Date | No | | Maximum selectable date (inclusive) |
| `locale` | string | No | | Locale used for month labels |
| `size` | ComponentSizeValue | No | | Size token that influences typography weight |
| `monthLabelFormat` | 'short' \| 'long' | No | | Format of month labels |
| `hideHeader` | boolean | No | | Hide navigation header (used when embedded in Calendar) |
| `monthsPerRow` | ResponsiveProp | No | | Responsive override for the number of months rendered per row |
| `fullWidth` | boolean | No | | Stretch to fill the container instead of sizing to the natural grid width. Default `false`. |
## Examples
### Basic
ID: `MonthPicker.basic` β’ Category: general
```tsx
const [value, setValue] = useState(new Date());
return (
{value
? value.toLocaleDateString(undefined, { month: 'long', year: 'numeric' })
: 'No month selected'}
);
}
```
--------------------------------------------------------------------------------
# MonthPickerInput
Form-friendly wrapper around `MonthPicker` that renders an input field and opens the picker in a modal dialog. Mirrors the `DatePickerInput` API for consistency while focusing on month-level selection workflows.
## Metadata
- Canonical name: `MonthPickerInput`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { MonthPickerInput } from '@platform-blocks/react-ui-library';`
- Status: stable
- Since: 0.1.0
- Category: dates
- Docs: https://react-ui-library.com/components/MonthPickerInput
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/MonthPickerInput
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | Date \| null | No | | Controlled value for the selected month |
| `defaultValue` | Date \| null | No | | Default month when uncontrolled |
| `onChange` | (value: Date \| null) => void | No | | Called when the month selection changes |
| `locale` | string | No | | Locale used for formatting the input value |
| `formatOptions` | Intl.DateTimeFormatOptions | No | | Intl format options for rendering the selected month |
| `formatValue` | (value: Date) => string | No | | Custom formatter for the input value; overrides locale/formatOptions |
| `placeholder` | string | No | | Placeholder text when no month is selected |
| `clearable` | boolean | No | | Show a clear button when a month is selected |
| `closeOnSelect` | boolean | No | | Close the picker after selecting a month |
| `monthPickerProps` | Partial> | No | | Additional props forwarded to MonthPicker (except value) |
| `modalTitle` | string | No | | Dialog title text |
| `onOpen` | () => void | No | | Called when the picker dialog opens |
| `onClose` | () => void | No | | Called when the picker dialog closes |
| `variant` | InputVariant | No | | Visual variant of the input. `default` (light surface + border), `filled` (gray fill, no border), `outline` (transparent fill, border only), `unstyled` (no border, no fill). |
| `label` | React.ReactNode | No | | Input label (string or component) |
| `disabled` | boolean | No | | Whether input is disabled |
| `required` | boolean | No | | Whether input is required |
| `error` | string | No | | Error message |
| `helperText` | string | No | | Helper text |
| `description` | string | No | | Optional short description displayed directly under the label (above the field) |
| `size` | SizeValue | No | | Input size |
| `withAsterisk` | boolean | No | | Whether to show required indicator |
| `name` | string | No | | Input name for form integration |
| `startSection` | React.ReactNode | No | | Left section content |
| `endSection` | React.ReactNode | No | | Right section content |
| `style` | any | No | | Additional styling |
| `accessibilityLabel` | string | No | | Accessibility label |
| `accessibilityHint` | string | No | | Accessibility hint |
| `testID` | string | No | | Test ID for testing |
| `debounceMs` | number | No | | Debounce delay for validation in milliseconds |
| `onFocus` | () => void | No | | Focus handler |
| `onBlur` | () => void | No | | Blur handler |
| `onEnter` | () => void | No | | Enter key press handler |
| `clearButtonLabel` | string | No | | Accessible label for the clear button |
| `onClear` | () => void | No | | Callback when the clear button is pressed |
| `keyboardFocusId` | string | No | | Identifier used with KeyboardManagerProvider to request refocus |
| `labelProps` | Omit | No | | Override props applied to the field label `` (style, weight, ff, etc.) |
| `descriptionProps` | Omit | No | | Override props applied to the field description `` |
| `placeholderTextColor` | string | No | | Color of the placeholder text. Falls back to `theme.text.muted`. |
| `startSectionProps` | Omit | No | | Props applied to the wrapping `` around `startSection` (style, accessibility, etc.). |
| `endSectionProps` | Omit | No | | Props applied to the wrapping `` around `endSection`. |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
## Examples
### Basic
ID: `MonthPickerInput.basic` β’ Category: general
```tsx
const [value, setValue] = useState(null);
return (
{value
? value.toLocaleDateString(undefined, { month: 'long', year: 'numeric' })
: 'No month selected'}
);
}
```
--------------------------------------------------------------------------------
# NavTree
A sidebar that nests itself.
Hand it the flat list of routes an app already has β with a category on each β and it groups, orders and renders them as a tree. The branches above the current page open on their own, the row for that page is marked and scrolled to, and which branches are open survives a reload.
Rows carrying an `href` render as real `` elements on web, so cmd-click, middle-click, "copy link address" and crawlers all work; a plain left-click goes to `onNavigate` for client-side routing. Omit `onNavigate` and the rows stay ordinary links the browser follows.
Built on [Tree](/components/Tree), so keyboard navigation, guide lines, filtering and the ARIA `tree`/`treeitem` roles come along with it.
## Metadata
- Canonical name: `NavTree`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { NavTree } from '@platform-blocks/react-ui-library';`
- Since: 1.1.0
- Category: navigation
- Tags: navigation, sidebar, tree, menu, routes
- Docs: https://react-ui-library.com/components/NavTree
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/NavTree
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `items` | NavTreeItem[] | Yes | | The destinations, flat. Grouped and nested by `buildNavTree`. |
| `activeHref` | string | No | | Current route. Marks its row and opens the groups above it. |
| `onNavigate` | (item: NavTreeItem, node: TreeNode) => void | No | | Where a row press goes. Supply it to route client-side; without it the rows stay plain links and the browser navigates. |
| `size` | ComponentSizeValue | No | 'sm' | Row density. @default 'sm' |
| `collapsed` | boolean | No | | Rail mode: only the top level renders, as icons. For a sidebar that collapses to a strip β the full tree is one hover away, and a column of every leaf's icon is not navigation, it is noise. |
| `searchable` | boolean | No | | Show a filter field above the tree, wired to `filterQuery`. Past a certain length no amount of nesting beats typing three letters, and every sidebar that needs one would otherwise wire the same input and the same state. Pass `filterQuery` as well to drive it from outside; on its own the field keeps its own query. Hidden in `collapsed` mode, where there is no room. |
| `searchPlaceholder` | string | No | 'Filterβ¦' | Placeholder for the filter field. @default 'Filterβ¦' |
| `highlightMatches` | boolean | No | true | Matched substrings are marked in the row labels. @default true |
| `style` | StyleProp | No | | |
| `groupOrder` | string[] | No | | Curated order for group labels, checked at every level. Groups not listed follow, alphabetically β so a partial order is enough, and a new group appears in a sensible place without touching this. Entries are bare labels (`'Input'`) or full paths (`'Hooks/Navigation'`). A path wins over a bare label, which is how the same name can rank differently in two branches. |
| `groupIcons` | Record | No | | Leading icon per group label. |
| `sortLeaves` | 'alpha' \| 'none' | No | 'alpha' | How leaves inside a group are ordered. - `'alpha'` β by `order` then label. The default: a long list is easier to scan alphabetically than in whatever order the array happened to be in. - `'none'` β keep the order given. |
| `openDepth` | number | No | 1 | Groups shallower than this start open. `1` opens the top level and leaves everything below it closed, which is the shape a docs sidebar wants: the sections are visible, the long category lists are not. |
| `openGroups` | string[] | No | | Group labels (or full `A/B` paths) to open regardless of `openDepth`. |
| `getGroupNode` | (context: { label: string; path: string[]; depth: number; items: NavTreeItem[]; }) => Partial | No | | Decorates each group row β a count, a badge, an icon. Receives the group's path from the root and the items beneath it, at every level. |
## Examples
### Grouped routes
ID: `NavTree.basic` β’ Tags: navtree, sidebar β’ Category: navigation β’ Status: stable β’ Since: 1.1.0
A flat list of routes becomes a nested sidebar. The group above the active route is already open, because `activeHref` opened it.
```tsx
// The whole input: a flat list, with a category on each row. Nothing here
// describes the tree β `NavTree` derives it.
const ROUTES: NavTreeItem[] = [
{ label: 'Getting Started', href: '/getting-started' },
{ label: 'Button', href: '/components/Button', group: ['Components', 'Input'] },
{ label: 'Select', href: '/components/Select', group: ['Components', 'Input'] },
{ label: 'Checkbox', href: '/components/Checkbox', group: ['Components', 'Input'] },
{ label: 'Card', href: '/components/Card', group: ['Components', 'Display'] },
{ label: 'Badge', href: '/components/Badge', group: ['Components', 'Display'] },
{ label: 'Tabs', href: '/components/Tabs', group: ['Components', 'Navigation'] },
];
const [route, setRoute] = useState('/components/Select');
return (
setRoute(item.href)}
showGuides
/>
);
}
```
### Counts and order
ID: `NavTree.counts` β’ Tags: navtree, sidebar β’ Category: navigation β’ Status: stable β’ Since: 1.1.0
`groupOrder` curates the sections that matter and leaves the rest alphabetical. `renderEndSection` hangs a count off each branch, and `openDepth={0}` starts everything closed.
```tsx
const ROUTES: NavTreeItem[] = [
{ label: 'Button', href: '/components/Button', group: 'Input' },
{ label: 'Select', href: '/components/Select', group: 'Input' },
{ label: 'Checkbox', href: '/components/Checkbox', group: 'Input' },
{ label: 'Card', href: '/components/Card', group: 'Display' },
{ label: 'Badge', href: '/components/Badge', group: 'Display' },
{ label: 'Tabs', href: '/components/Tabs', group: 'Navigation' },
];
const [route, setRoute] = useState('/components/Card');
return (
setRoute(item.href)}
// Curate the order that matters and let the rest sort themselves.
groupOrder={['Input', 'Display']}
openDepth={0}
renderEndSection={node =>
node.children ? {node.children.length} : null
}
/>
);
}
```
### Filtering
ID: `NavTree.search` β’ Tags: navtree, sidebar, search, filter β’ Category: navigation β’ Status: stable β’ Since: 1.1.0
`searchable` adds a filter field wired to the tree. Typing hides the rows that do not match, opens the branches above the ones that do, and marks the matched substring β past a certain length, three letters beat any amount of nesting.
```tsx
const ROUTES: NavTreeItem[] = [
{ label: 'Button', href: '/components/Button', group: 'Input' },
{ label: 'Checkbox', href: '/components/Checkbox', group: 'Input' },
{ label: 'Select', href: '/components/Select', group: 'Input' },
{ label: 'TextArea', href: '/components/TextArea', group: 'Input' },
{ label: 'Badge', href: '/components/Badge', group: 'Display' },
{ label: 'Card', href: '/components/Card', group: 'Display' },
{ label: 'Breadcrumbs', href: '/components/Breadcrumbs', group: 'Navigation' },
{ label: 'Tabs', href: '/components/Tabs', group: 'Navigation' },
];
const [route, setRoute] = useState('/components/Card');
return (
setRoute(item.href)}
searchable
searchPlaceholder="Filter componentsβ¦"
/>
);
}
```
### Collapsed rail
ID: `NavTree.collapsed` β’ Tags: navtree, sidebar, rail β’ Category: navigation β’ Status: stable β’ Since: 1.1.0
`collapsed` drops the sidebar to a strip of top-level icons β the group holding the current route stays marked, and pressing one lands on the first page inside it. A sidebar with a hundred routes shows a handful of icons here, not a hundred.
```tsx
const ROUTES: NavTreeItem[] = [
{ label: 'Button', href: '/components/Button', group: 'Components' },
{ label: 'Card', href: '/components/Card', group: 'Components' },
{ label: 'LineChart', href: '/components/LineChart', group: 'Charts' },
{ label: 'BarChart', href: '/components/BarChart', group: 'Charts' },
{ label: 'useHover', href: '/hooks/useHover', group: 'Hooks' },
];
const GROUP_ICONS = {
Components: ,
Charts: ,
Hooks: ,
};
const [collapsed, setCollapsed] = useState(true);
const [route, setRoute] = useState('/components/Card');
return (
setRoute(item.href)}
groupIcons={GROUP_ICONS}
collapsed={collapsed}
/>
);
}
```
--------------------------------------------------------------------------------
# NumberInput
The `NumberInput` component is a numeric text input field that provides built-in step controls for incrementing and decrementing the value. It supports custom formatting and parsing functions, allowing you to display numbers in various formats (e.g., currency, percentages) while maintaining a numeric value internally.
## Metadata
- Canonical name: `NumberInput`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { NumberInput } from '@platform-blocks/react-ui-library';`
- Category: input
- Tags: input, numeric, stepper, formatter
- Docs: https://react-ui-library.com/components/NumberInput
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/NumberInput
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | number | No | | Number value |
| `onChange` | (value: number \| undefined) => void | No | | Change handler |
| `allowDecimal` | boolean | No | | Allow decimal values |
| `allowNegative` | boolean | No | | Allow negative values |
| `allowLeadingZeros` | boolean | No | | Allow leading zeros while editing |
| `allowedDecimalSeparators` | string[] | No | | Additional characters that should be treated as decimal separators |
| `decimalSeparator` | string | No | | Decimal separator character |
| `decimalScale` | number | No | | Maximum number of digits after the decimal point |
| `fixedDecimalScale` | boolean | No | | When true, pads the decimal part with trailing zeros to match decimalScale |
| `min` | number | No | | Minimum value |
| `max` | number | No | | Maximum value |
| `step` | number | No | | Step increment |
| `shiftMultiplier` | number | No | | Multiplier applied to the step when using modifier keys |
| `precision` | number | No | | Number of decimal places |
| `thousandSeparator` | string \| boolean | No | | Thousand separator character or boolean to enable default separator |
| `thousandsGroupStyle` | 'none' \| 'thousand' \| 'lakh' \| 'wan' | No | | Thousand grouping strategy |
| `prefix` | string | No | | Prefix string appended before the value when displayed |
| `suffix` | string | No | | Suffix string appended after the value when displayed |
| `format` | 'integer' \| 'decimal' \| 'currency' \| 'percentage' | No | | Number format |
| `currency` | string | No | | Currency code for currency format |
| `isAllowed` | (values: { floatValue?: number; formattedValue: string; value: string }) => boolean | No | | Optional guard executed before value is committed |
| `startValue` | number | No | | Value applied when stepping from an empty state |
| `stepHoldDelay` | number | No | | Delay before step-hold behaviour kicks in (ms) |
| `stepHoldInterval` | number \| ((stepCount: number) => number) | No | | Interval or function controlling step-hold cadence |
| `withKeyboardEvents` | boolean | No | | Enable keyboard arrow interactions |
| `withControls` | boolean | No | | Show increment/decrement buttons |
| `withSideButtons` | boolean | No | | Render horizontal decrement/increment buttons flanking the input |
| `hideControlsOnMobile` | boolean | No | | Whether to hide step controls on mobile |
| `withDragGesture` | boolean | No | | Enable press-drag gesture to adjust value |
| `dragAxis` | 'horizontal' \| 'vertical' | No | | Axis that determines how drag gestures adjust the value |
| `dragStepDistance` | number | No | | Pixel distance required to trigger a single step while dragging |
| `dragStepMultiplier` | number | No | | Multiplier applied to the configured step while dragging |
| `onDragStateChange` | (isDragging: boolean) => void | No | | Callback fired when the drag gesture activation state changes |
| `formatter` | (value: number) => string | No | | Custom formatter function |
| `parser` | (value: string) => number | No | | Custom parser function |
| `clampBehavior` | 'strict' \| 'blur' \| 'none' | No | | Clamp value to min/max bounds |
| `allowEmpty` | boolean | No | | Allow empty value |
| `textInputProps` | ExtendedTextInputProps | No | | Additional TextInput props |
| `autoCapitalize` | RNTextInputProps['autoCapitalize'] | No | | Text auto-capitalization behavior |
| `autoCorrect` | boolean | No | | Whether to enable auto-correct |
| `autoFocus` | boolean | No | | Whether to auto-focus on mount |
| `returnKeyType` | RNTextInputProps['returnKeyType'] | No | | Return key type for soft keyboard |
| `blurOnSubmit` | boolean | No | | Whether to blur on submit |
| `selectTextOnFocus` | boolean | No | | Select all text on focus |
| `textContentType` | RNTextInputProps['textContentType'] | No | | iOS text content type for autofill |
| `textAlign` | RNTextInputProps['textAlign'] | No | | Text alignment |
| `spellCheck` | boolean | No | | Whether spell check is enabled |
| `inputMode` | RNTextInputProps['inputMode'] | No | | Input mode (modern alternative to keyboardType) |
| `enterKeyHint` | RNTextInputProps['enterKeyHint'] | No | | Enter key hint |
| `selectionColor` | string | No | | Color of the text selection handles and highlight |
| `showSoftInputOnFocus` | boolean | No | | Whether to show the soft keyboard on focus |
| `editable` | boolean | No | | Whether the field is editable |
| `variant` | InputVariant | No | | Visual variant of the input. `default` (light surface + border), `filled` (gray fill, no border), `outline` (transparent fill, border only), `unstyled` (no border, no fill). |
| `label` | React.ReactNode | No | | Input label (string or component) |
| `disabled` | boolean | No | | Whether input is disabled |
| `required` | boolean | No | | Whether input is required |
| `placeholder` | string | No | | Input placeholder |
| `error` | string | No | | Error message |
| `helperText` | string | No | | Helper text |
| `description` | string | No | | Optional short description displayed directly under the label (above the field) |
| `size` | SizeValue | No | | Input size |
| `withAsterisk` | boolean | No | | Whether to show required indicator |
| `name` | string | No | | Input name for form integration |
| `startSection` | React.ReactNode | No | | Left section content |
| `endSection` | React.ReactNode | No | | Right section content |
| `style` | any | No | | Additional styling |
| `accessibilityLabel` | string | No | | Accessibility label |
| `accessibilityHint` | string | No | | Accessibility hint |
| `testID` | string | No | | Test ID for testing |
| `debounceMs` | number | No | | Debounce delay for validation in milliseconds |
| `onFocus` | () => void | No | | Focus handler |
| `onBlur` | () => void | No | | Blur handler |
| `onEnter` | () => void | No | | Enter key press handler |
| `clearable` | boolean | No | | Show built-in clear button when input has value |
| `clearButtonLabel` | string | No | | Accessible label for the clear button |
| `onClear` | () => void | No | | Callback when the clear button is pressed |
| `keyboardFocusId` | string | No | | Identifier used with KeyboardManagerProvider to request refocus |
| `labelProps` | Omit | No | | Override props applied to the field label `` (style, weight, ff, etc.) |
| `descriptionProps` | Omit | No | | Override props applied to the field description `` |
| `placeholderTextColor` | string | No | | Color of the placeholder text. Falls back to `theme.text.muted`. |
| `startSectionProps` | Omit | No | | Props applied to the wrapping `` around `startSection` (style, accessibility, etc.). |
| `endSectionProps` | Omit | No | | Props applied to the wrapping `` around `endSection`. |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
## Examples
### Basic
ID: `NumberInput.basic` β’ Tags: basic, numeric, step β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Controlled number input with simple step controls and live value preview.
```tsx
const [quantity, setQuantity] = useState(2);
return (
);
}
```
### Formats
ID: `NumberInput.formats` β’ Tags: currency, percent, formatting β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Showcases currency formatting, percentage suffixes, and a derived total.
```tsx
const [price, setPrice] = useState(249.99);
const [discount, setDiscount] = useState(10);
const finalPrice = price != null && discount != null
? price * (1 - discount / 100)
: undefined;
return (
Final price: {finalPrice != null ? `$${finalPrice.toFixed(2)}` : 'β'}
);
}
```
### Side buttons
ID: `NumberInput.side-buttons` β’ Tags: controls, step, buttons β’ Category: interaction β’ Status: stable β’ Since: 1.0.0
Side button controls with shift multipliers for both fine and coarse adjustments.
```tsx
const EnhancedNumberInput = NumberInput as any;
const [value, setValue] = useState(32);
const [step, setStep] = useState(1);
const effectiveStep = useMemo(() => step || 1, [step]);
return (
Side buttons and shift multiplier
Combine side buttons with the default controls to support coarse and fine adjustments.
{
if (typeof next === 'number') {
setValue(next);
}
}}
/>
Current speed: {value}%
Shift-click = Β±{effectiveStep * 10}
Adjust the base step
Update the increment to see how the multiplier scales.
{
if (typeof next === 'number') {
setStep(next);
}
}}
/>
);
}
```
### Drag gesture
ID: `NumberInput.drag-gesture` β’ Tags: drag, gesture, adjustment β’ Category: interaction β’ Status: stable β’ Since: 1.0.0
Press-and-drag interactions for horizontal and vertical number adjustments.
```tsx
const [horizontalValue, setHorizontalValue] = useState(32);
const [verticalValue, setVerticalValue] = useState(120);
const [dragging, setDragging] = useState(false);
const handleDragStateChange = (state: boolean) => {
setDragging(state);
};
return (
Press-and-drag adjustment
Drag across the input to nudge values without lifting your pointer. The status below reflects the current drag state.
Dragging: {dragging ? 'active' : 'idle'}
Horizontal drag
Step every 14px drag movement with a multiplier for faster adjustments.
Vertical drag
Drag up or down to adjust between 0 and 200 with built-in controls.
);
}
```
--------------------------------------------------------------------------------
# Overlay
The Overlay component provides a utility for dimming background content or drawing focus to foreground elements. It supports theme-aware colors, configurable opacity, gradients, and blur to achieve anything from subtle scrims to dramatic glassmorphism. Because Overlay is non-interactive by default, pair it with focus traps or dismiss controls when building dialogs, sheets, or other blocking surfaces.
## Metadata
- Canonical name: `Overlay`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Overlay } from '@platform-blocks/react-ui-library';`
- Status: beta
- Category: overlay
- Docs: https://react-ui-library.com/components/Overlay
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Overlay
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `color` | string | No | | Background color for the overlay. Accepts raw colors or theme tokens like `primary.6`. |
| `opacity` | number | No | | Opacity applied to the background color. Defaults to 0.6. |
| `backgroundOpacity` | number | No | | Opacity applied to the entire overlay, including gradients and blur effects. Defaults to 1. |
| `gradient` | string | No | | Web-only CSS gradient string. Falls back to `color` on native platforms. |
| `blur` | number \| string | No | | Amount of backdrop blur (in pixels). Supported on web. |
| `radius` | SizeValue \| number | No | | Corner radius for the overlay surface. |
| `zIndex` | number | No | | z-index applied to the overlay container. |
| `fixed` | boolean | No | false | Use viewport-fixed positioning instead of absolute positioning (web only). |
| `center` | boolean | No | false | Center children horizontally and vertically. |
| `style` | StyleProp | No | | Optional style overrides applied after computed styles. |
| `children` | ReactNode | No | | Overlay content rendered on top of the dimmed background. |
## Examples
### Overlay patterns
ID: `Overlay.basic` β’ Tags: overlays, effects β’ Category: surfaces β’ Status: stable β’ Since: 1.0.0
Showcases dimming, gradient, and blurred overlays that inherit their parent size for spotlights and modal scrims.
```tsx
const HERO_IMAGE = require('../../../../assets/images/scene-city.png');
const GRADIENT_IMAGE = require('../../../../assets/images/scene-aurora.png');
const BLUR_IMAGE = require('../../../../assets/images/scene-desert.png');
type OverlayExample = {
key: string;
image: ImageSourcePropType;
title: string;
description: string;
align?: 'flex-start' | 'center';
overlayProps: Omit, 'children'>;
};
const STATIC_EXAMPLES: OverlayExample[] = [
{
key: 'gradient',
image: GRADIENT_IMAGE,
title: 'Gradient spotlight',
description: 'When `gradient` is provided, the overlay renders a vivid fade instead of a solid tint.',
overlayProps: {
gradient: 'linear-gradient(145deg, rgba(0, 0, 0, 0.95) 0%, rgba(0, 0, 0, 0) 75%)',
radius: 'xl',
},
},
{
key: 'blurred',
image: BLUR_IMAGE,
title: 'Glass overlay',
description: 'Blend blur with partial opacity to achieve a glassmorphism effect (blur is web-only).',
align: 'center',
overlayProps: {
color: '#000',
backgroundOpacity: 0.35,
blur: 18,
radius: 'xl',
center: true,
},
},
];
const [visible, setVisible] = useState(true);
return (
{visible ? : null}
Toggle overlay
Overlay fills its parent. Use `backgroundOpacity` to dim the background without affecting children.
setVisible((current) => !current)}>
{visible ? 'Hide overlay' : 'Show overlay'}
{STATIC_EXAMPLES.map(({ key, image, overlayProps, align = 'flex-start', title, description }) => (
{title}
{description}
))}
Overlay inherits the size of its container, making it ideal for dimming media, spotlights, and modal scrims.
);
}
const styles = StyleSheet.create({
wrapper: {
width: '100%',
},
section: {
width: '100%',
maxWidth: 520,
alignSelf: 'center',
},
image: {
width: '100%',
aspectRatio: 16 / 9,
borderRadius: 24,
overflow: 'hidden',
justifyContent: 'flex-end',
},
imageInner: {
borderRadius: 24,
},
overlayContent: {
padding: 24,
},
});
```
--------------------------------------------------------------------------------
# Pagination
A comprehensive pagination component that provides intuitive navigation through large datasets. The component offers flexible configuration options and consistent styling across different use cases.
## Metadata
- Canonical name: `Pagination`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Pagination } from '@platform-blocks/react-ui-library';`
- Status: stable
- Since: 1.0.0
- Category: navigation
- Tags: pagination, navigation, pages, data
- Docs: https://react-ui-library.com/components/Pagination
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Pagination
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `current` | number | Yes | | Current page number (1-indexed) |
| `total` | number | Yes | | Total number of pages |
| `siblings` | number | No | 1 | Number of page items to show on each side of current page |
| `boundaries` | number | No | 1 | Number of page items to show at the boundaries |
| `onChange` | (page: number) => void | Yes | | Page change handler |
| `size` | ComponentSizeValue | No | 'md' | Size of pagination controls |
| `variant` | 'default' \| 'outline' \| 'subtle' | No | 'default' | Variant style |
| `color` | 'primary' \| 'secondary' \| 'gray' | No | 'primary' | Color scheme |
| `showFirst` | boolean | No | true | Show first/last page buttons |
| `showPrevNext` | boolean | No | true | Show previous/next buttons |
| `labels` | { first?: ReactNode; previous?: ReactNode; next?: ReactNode; last?: ReactNode; } | No | {} | Custom labels for navigation buttons |
| `disabled` | boolean | No | false | Whether pagination is disabled |
| `style` | StyleProp | No | | Custom styles |
| `buttonStyle` | StyleProp | No | | Custom button styles |
| `activeButtonStyle` | StyleProp | No | | Custom active button styles |
| `textStyle` | StyleProp | No | | Custom text styles |
| `activeTextStyle` | StyleProp | No | | Custom active text styles |
| `hideOnSinglePage` | boolean | No | false | Hide pagination when there's only one page |
| `showSizeChanger` | boolean | No | false | Show page size selector |
| `pageSizeOptions` | number[] | No | [10, 20, 50, 100] | Available page sizes |
| `pageSize` | number | No | 10 | Current page size |
| `onPageSizeChange` | (size: number) => void | No | | Page size change handler |
| `showTotal` | boolean \| ((total: number, range: [number, number]) => ReactNode) | No | false | Show total count |
| `totalItems` | number | No | | Total number of items |
| `labelProps` | Omit | No | | Override props applied to every page-button label `` (style, weight, ff, size, color). |
| `activeLabelProps` | Omit | No | | Override props applied to the active page-button label `` (merged on top of `labelProps`). |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic
ID: `Pagination.basic` β’ Tags: basic, pagination, navigation β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Provide `current`, `total`, and an `onChange` handler to keep numbered pagination in sync with surrounding state.
```tsx
const [currentPage, setCurrentPage] = useState(1);
const totalPages = 10;
return (
Page {currentPage} of {totalPages}
);
}
```
### Variants
ID: `Pagination.variants` β’ Tags: variants, style, appearance β’ Category: styling β’ Status: stable β’ Since: 1.0.0
Switch the `variant` prop between `default`, `outline`, and `subtle` to align pagination with the surrounding surface treatment.
```tsx
const [defaultPage, setDefaultPage] = useState(5);
const [outlinePage, setOutlinePage] = useState(5);
const [subtlePage, setSubtlePage] = useState(5);
return (
Default variant keeps the control fully filled. Page {defaultPage} of 15.
Outline keeps the surface quiet while the active page gets a stroke. Page {outlinePage} of 15.
Subtle removes backgrounds for tinted surfaces. Page {subtlePage} of 15.
);
}
```
### Sizes
ID: `Pagination.sizes` β’ Tags: sizes, scale, responsive β’ Category: styling β’ Status: stable β’ Since: 1.0.0
Use the `size` prop (`xs` through `3xl`) to match pagination density to its container without changing behavior.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const;
const [page, setPage] = useState(3);
return (
{SIZES.map((size) => (
{size}
))}
);
}
```
### Advanced
ID: `Pagination.advanced` β’ Tags: advanced, controls, boundaries, siblings β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Combine `showFirst`, `showPrevNext`, `siblings`, and `boundaries` to reveal the right amount of context for long result sets.
```tsx
const [page1, setPage1] = useState(10);
const [page2, setPage2] = useState(15);
const [page3, setPage3] = useState(25);
return (
Includes first and last buttons. Page {page1} of 30.
Minimal navigation with prev/next only. Page {page2} of 40.
Compact layout with tight siblings. Page {page3} of 50.
);
}
```
### Total & size changer
ID: `Pagination.size-changer` β’ Tags: pagination, page-size, total β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Set `showTotal` with `totalItems` to render an "X-Y of N" summary, and `showSizeChanger` with `pageSizeOptions` / `onPageSizeChange` to let users change the rows-per-page. This is the same footer the `DataTable` renders internally.
```tsx
const totalItems = 248;
const [pageSize, setPageSize] = useState(10);
const [current, setCurrent] = useState(1);
const total = Math.max(1, Math.ceil(totalItems / pageSize));
return (
{
setPageSize(size);
setCurrent(1);
}}
/>
Page {current} of {total} Β· {pageSize} rows per page
);
}
```
--------------------------------------------------------------------------------
# PhoneInput
The `PhoneInput` component provides a flexible way to capture telephone numbers with built-in masking and formatting.
## Metadata
- Canonical name: `PhoneInput`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { PhoneInput } from '@platform-blocks/react-ui-library';`
- Category: input
- Tags: phone, input, mask, formatting, international
- Docs: https://react-ui-library.com/components/PhoneInput
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/PhoneInput
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | string | No | | Phone number value (digits only). Omit for an uncontrolled field. |
| `defaultValue` | string | No | | Initial value while uncontrolled. |
| `onChange` | (raw: string, formatted: string, meta: PhoneChangeMeta) => void | No | | Change handler receiving (nationalDigits, formattedDisplay, meta) |
| `country` | PhoneCountryCode | No | | Country preset to format against. Controlled when provided. |
| `defaultCountry` | PhoneCountryCode | No | 'US' | Initial country while uncontrolled. Defaults to 'US'. |
| `onCountryChange` | (country: PhoneCountryCode) => void | No | | Called when the country changes (via the picker, or `autoDetect`). |
| `selectableCountry` | boolean | No | false | Render the dial code as a dropdown so the user can change country. |
| `autoDetect` | boolean | No | false | Switch country when the user types or pastes an explicit `+` prefix. Off by default: it changes the mask out from under the caller's `country` prop. The active country's own dial code is stripped either way, so pasting a full local number never truncates it. A *foreign* dial code is only stripped when `autoDetect` lets us switch to that country β otherwise the digits would be re-filed under the active country, turning `+447911123456` into `+17911123456`. |
| `showCountryCode` | boolean | No | true | Show the dial code prefix ahead of the field |
| `mask` | string | No | | Custom mask pattern (overrides the country mask). Use '0' for digits, any other character as a literal. Avoid literal digits β see `PhoneFormat.mask`. |
| `textInputProps` | ExtendedTextInputProps | No | | Additional props forwarded to the underlying TextInput. |
| `variant` | InputVariant | No | | Visual variant of the input. `default` (light surface + border), `filled` (gray fill, no border), `outline` (transparent fill, border only), `unstyled` (no border, no fill). |
| `label` | React.ReactNode | No | | Input label (string or component) |
| `disabled` | boolean | No | | Whether input is disabled |
| `required` | boolean | No | | Whether input is required |
| `placeholder` | string | No | | Input placeholder |
| `error` | string | No | | Error message |
| `helperText` | string | No | | Helper text |
| `description` | string | No | | Optional short description displayed directly under the label (above the field) |
| `size` | SizeValue | No | 'md' | Input size |
| `withAsterisk` | boolean | No | | Whether to show required indicator |
| `name` | string | No | | Input name for form integration |
| `startSection` | React.ReactNode | No | | Left section content |
| `endSection` | React.ReactNode | No | | Right section content |
| `style` | any | No | | Additional styling |
| `accessibilityLabel` | string | No | | Accessibility label |
| `accessibilityHint` | string | No | | Accessibility hint |
| `testID` | string | No | | Test ID for testing |
| `debounceMs` | number | No | | Debounce delay for validation in milliseconds |
| `onFocus` | () => void | No | | Focus handler |
| `onBlur` | () => void | No | | Blur handler |
| `onEnter` | () => void | No | | Enter key press handler |
| `clearable` | boolean | No | | Show built-in clear button when input has value |
| `clearButtonLabel` | string | No | | Accessible label for the clear button |
| `onClear` | () => void | No | | Callback when the clear button is pressed |
| `keyboardFocusId` | string | No | | Identifier used with KeyboardManagerProvider to request refocus |
| `labelProps` | Omit | No | | Override props applied to the field label `` (style, weight, ff, etc.) |
| `descriptionProps` | Omit | No | | Override props applied to the field description `` |
| `placeholderTextColor` | string | No | | Color of the placeholder text. Falls back to `theme.text.muted`. |
| `startSectionProps` | Omit | No | | Props applied to the wrapping `` around `startSection` (style, accessibility, etc.). |
| `endSectionProps` | Omit | No | | Props applied to the wrapping `` around `endSection`. |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
## Examples
### Basic
ID: `PhoneInput.basic` β’ Tags: basic, phone, input β’ Category: basics β’ Status: experimental β’ Since: 1.0.0
Controlled PhoneInput example that surfaces both raw digits and the formatted display.
```tsx
const [raw, setRaw] = useState('');
const [formatted, setFormatted] = useState('');
const [e164, setE164] = useState('');
const [complete, setComplete] = useState(false);
return (
Basic phone input
Controlled phone field showing the raw national digits, the formatted display
value, and the submittable E.164 form.
{
setRaw(rawDigits);
setFormatted(formattedDisplay);
setE164(meta.e164);
setComplete(meta.isComplete);
}}
country="US"
showCountryCode
/>
Current values
{JSON.stringify({ raw, formatted, e164, complete }, null, 2)}
);
}
```
### International
ID: `PhoneInput.international` β’ Tags: international, auto-detect, phone β’ Category: features β’ Status: experimental β’ Since: 1.0.0
Compare auto-detected formats with a manual international configuration.
```tsx
const [autoDetectValue, setAutoDetectValue] = useState('');
const [autoDetectE164, setAutoDetectE164] = useState('');
const [autoDetectCountry, setAutoDetectCountry] = useState('US');
const [intlValue, setIntlValue] = useState('');
const [intlFormatted, setIntlFormatted] = useState('');
return (
International detection
With autoDetect, an explicit + prefix picks the country: type or paste
+447911123456 and the mask, dial code and E.164 output follow along. A
recognized dial code is stripped on paste either way, so a full international
number never overflows the national mask.
{
setAutoDetectValue(raw);
setAutoDetectE164(meta.e164);
}}
defaultCountry="US"
onCountryChange={setAutoDetectCountry}
autoDetect
showCountryCode
placeholder="Try +447911123456 or +33123456789"
/>
{
setIntlValue(raw);
setIntlFormatted(formatted);
}}
showCountryCode={false}
placeholder="Enter any international number"
/>
Values
{JSON.stringify(
{
autoDetect: {
country: autoDetectCountry,
raw: autoDetectValue,
e164: autoDetectE164
},
international: { raw: intlValue, formatted: intlFormatted }
},
null,
2
)}
);
}
```
### Country Picker
ID: `PhoneInput.country-select` β’ Tags: country, picker, dial-code, phone β’ Category: features β’ Status: experimental β’ Since: 1.0.0
Let the user pick the country from the dial-code prefix, remasking the number in place.
```tsx
const [country, setCountry] = useState('US');
const [raw, setRaw] = useState('');
const [e164, setE164] = useState('');
return (
Country picker
With selectableCountry the dial-code prefix becomes a dropdown. Changing the
country remasks the digits already entered instead of clearing them, and the
E.164 value is rebuilt against the new dial code.
{
setRaw(rawDigits);
setE164(meta.e164);
}}
/>
Current values
{JSON.stringify({ country, raw, e164 }, null, 2)}
);
}
```
### Country Formats
ID: `PhoneInput.formats` β’ Tags: formatting, country, phone β’ Category: features β’ Status: experimental β’ Since: 1.0.0
Showcase of built-in country presets with their localized masks and raw digit output.
```tsx
const [us, setUs] = useState('');
const [uk, setUk] = useState('');
const [fr, setFr] = useState('');
const [br, setBr] = useState('');
return (
Country formatting
Compare built-in masks for several countries. Each input stores digits only while rendering a localized format.
setUs(raw)}
showCountryCode
/>
setUk(raw)}
showCountryCode
/>
setFr(raw)}
showCountryCode
/>
setBr(raw)}
showCountryCode
/>
Raw digit values
{JSON.stringify({ us, uk, fr, br }, null, 2)}
);
}
```
### Mask Visibility
ID: `PhoneInput.mask-visibility` β’ Tags: country-code, placeholder β’ Category: features β’ Status: experimental β’ Since: 1.0.0
Demonstrates showing or hiding the country code prefix while preserving raw digits.
```tsx
const [withCountryCode, setWithCountryCode] = useState('');
const [withoutCountryCode, setWithoutCountryCode] = useState('');
return (
Country code visibility
Toggle the country prefix while keeping the same underlying digits.
setWithCountryCode(raw)}
country="US"
showCountryCode
/>
Raw digits: {withCountryCode || 'β'}
setWithoutCountryCode(raw)}
country="US"
showCountryCode={false}
/>
Raw digits: {withoutCountryCode || 'β'}
);
}
```
### Validation
ID: `PhoneInput.validation` β’ Tags: validation, feedback, phone β’ Category: features β’ Status: experimental β’ Since: 1.0.0
Length-based validation for US and international formats with inline messaging.
```tsx
const [usRaw, setUsRaw] = useState('');
const [usFormatted, setUsFormatted] = useState('');
const [internationalRaw, setInternationalRaw] = useState('');
const [internationalFormatted, setInternationalFormatted] = useState('');
const isValidUs = useMemo(() => usRaw.length === 10, [usRaw]);
const isValidInternational = useMemo(
() => internationalRaw.length >= 7 && internationalRaw.length <= 15,
[internationalRaw]
);
return (
Validation states
Surface validation messages based on raw digit counts for domestic and international numbers.
{
setUsRaw(raw);
setUsFormatted(formatted);
}}
country="US"
showCountryCode
error={usRaw.length > 0 && !isValidUs ? 'Enter a 10-digit US phone number' : undefined}
/>
{usRaw.length === 0
? 'Enter a phone number'
: isValidUs
? `β ${usFormatted}`
: `${usRaw.length}/10 digits entered`}
{
setInternationalRaw(raw);
setInternationalFormatted(formatted);
}}
defaultCountry="INTL"
autoDetect
showCountryCode
error={
internationalRaw.length > 0 && !isValidInternational
? 'International numbers should be 7-15 digits'
: undefined
}
/>
{internationalRaw.length === 0
? 'Enter an international phone number'
: isValidInternational
? `β ${internationalFormatted}`
: 'Adjust to 7-15 digits'}
);
}
```
### Advanced Masking
ID: `PhoneInput.advanced-masking` β’ Tags: mask, formatting, advanced β’ Category: features β’ Status: experimental β’ Since: 1.0.0
Custom mask patterns for international formats and extension fields.
```tsx
const [intlRaw, setIntlRaw] = useState('');
const [intlFormatted, setIntlFormatted] = useState('');
const [extensionRaw, setExtensionRaw] = useState('');
const [extensionFormatted, setExtensionFormatted] = useState('');
return (
Advanced masking
Apply custom mask patterns to control formatting for international numbers and extension fields.
{
setIntlRaw(raw);
setIntlFormatted(formatted);
}}
autoDetect={false}
showCountryCode={false}
mask="+00 (000) 000-0000"
placeholder="+44 (7911) 123-456"
/>
Raw digits: {intlRaw || 'β'}
Formatted: {intlFormatted || 'β'}
{
setExtensionRaw(raw);
setExtensionFormatted(formatted);
}}
autoDetect={false}
showCountryCode={false}
mask="000-000-0000 x0000"
placeholder="555-123-4567 x1234"
/>
Raw digits: {extensionRaw || 'β'}
Formatted: {extensionFormatted || 'β'}
);
}
```
--------------------------------------------------------------------------------
# PinInput
A specialized input component designed for entering PIN codes, one-time passwords (OTP), verification codes, and other sequential character inputs. The component provides an intuitive interface with automatic focus management.
## Metadata
- Canonical name: `PinInput`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { PinInput } from '@platform-blocks/react-ui-library';`
- Status: stable
- Since: 1.0.0
- Category: input
- Tags: pin, otp, security, input, verification
- Docs: https://react-ui-library.com/components/PinInput
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/PinInput
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `length` | number | No | 4 | Number of PIN digits |
| `keyboardFocusId` | string | No | | Stable id used by KeyboardManager to restore focus |
| `value` | string | No | | PIN value (controlled) |
| `defaultValue` | string | No | '' | Uncontrolled initial value (used when `value` is not provided) |
| `onChange` | (pin: string) => void | No | | Change handler |
| `mask` | boolean | No | false | Whether to mask PIN |
| `maskChar` | string | No | 'β’' | Character to use for masking |
| `manageFocus` | boolean | No | true | Whether to focus next input automatically |
| `enforceOrderInitialOnly` | boolean | No | | Enforce sequential entry (forces focus to first empty). If false, user can edit any position after complete |
| `type` | 'alphanumeric' \| 'numeric' | No | 'numeric' | Type of input |
| `placeholder` | string | No | '' | Placeholder for each input |
| `allowPaste` | boolean | No | true | Whether to allow paste |
| `oneTimeCode` | boolean | No | false | One-time code auto-complete |
| `spacing` | number | No | 8 | Input spacing |
| `borderRadius` | number | No | | Input border radius |
| `onComplete` | (pin: string) => void | No | | Complete handler - called when all digits are filled |
| `textInputProps` | Omit | No | | Additional TextInput props for each input |
| `autoCapitalize` | RNTextInputProps['autoCapitalize'] | No | | Text auto-capitalization behavior |
| `autoCorrect` | boolean | No | | Whether to enable auto-correct |
| `autoFocus` | boolean | No | | Whether to auto-focus on first input on mount |
| `selectTextOnFocus` | boolean | No | | Select all text on focus |
| `textContentType` | RNTextInputProps['textContentType'] | No | | iOS text content type for autofill |
| `textAlign` | RNTextInputProps['textAlign'] | No | | Text alignment |
| `spellCheck` | boolean | No | | Whether spell check is enabled |
| `selectionColor` | string | No | | Color of the text selection handles and highlight |
| `showSoftInputOnFocus` | boolean | No | | Whether to show the soft keyboard on focus |
| `variant` | InputVariant | No | | Visual variant of the input. `default` (light surface + border), `filled` (gray fill, no border), `outline` (transparent fill, border only), `unstyled` (no border, no fill). |
| `label` | React.ReactNode | No | | Input label (string or component) |
| `disabled` | boolean | No | false | Whether input is disabled |
| `required` | boolean | No | | Whether input is required |
| `error` | string | No | | Error message |
| `helperText` | string | No | | Helper text |
| `description` | string | No | | Optional short description displayed directly under the label (above the field) |
| `size` | SizeValue | No | 'md' | Input size |
| `withAsterisk` | boolean | No | | Whether to show required indicator |
| `name` | string | No | | Input name for form integration |
| `startSection` | React.ReactNode | No | | Left section content |
| `endSection` | React.ReactNode | No | | Right section content |
| `style` | any | No | | Additional styling |
| `accessibilityLabel` | string | No | | Accessibility label |
| `accessibilityHint` | string | No | | Accessibility hint |
| `testID` | string | No | | Test ID for testing |
| `debounceMs` | number | No | | Debounce delay for validation in milliseconds |
| `onFocus` | () => void | No | | Focus handler |
| `onBlur` | () => void | No | | Blur handler |
| `onEnter` | () => void | No | | Enter key press handler |
| `clearable` | boolean | No | | Show built-in clear button when input has value |
| `clearButtonLabel` | string | No | | Accessible label for the clear button |
| `onClear` | () => void | No | | Callback when the clear button is pressed |
| `labelProps` | Omit | No | | Override props applied to the field label `` (style, weight, ff, etc.) |
| `descriptionProps` | Omit | No | | Override props applied to the field description `` |
| `placeholderTextColor` | string | No | | Color of the placeholder text. Falls back to `theme.text.muted`. |
| `startSectionProps` | Omit | No | | Props applied to the wrapping `` around `startSection` (style, accessibility, etc.). |
| `endSectionProps` | Omit | No | | Props applied to the wrapping `` around `endSection`. |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
## Examples
### Basic
ID: `PinInput.basic` β’ Tags: basic, pin, code β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Controlled 4-digit PIN input with automatic focus handoff and live preview.
```tsx
const [value, setValue] = useState('');
return (
);
}
```
### Types
ID: `PinInput.types` β’ Tags: types, numeric, alphanumeric β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Contrast numeric-only PIN entry with an alphanumeric option for recovery codes.
```tsx
const [numericValue, setNumericValue] = useState('');
const [alphanumericValue, setAlphanumericValue] = useState('');
return (
PIN input types
Numeric (default)
Restricts entry to digits 0-9 for PIN and OTP flows.
Alphanumeric
Allow letters and numbers for recovery or backup codes.
);
}
```
### Sizes
ID: `PinInput.sizes` β’ Tags: sizes, scale, responsive β’ Category: styling β’ Status: stable β’ Since: 1.0.0
Demonstrates xs through lg PIN inputs and when each token fits best.
```tsx
type SizeToken = 'xs' | 'sm' | 'md' | 'lg';
const [xsValue, setXsValue] = useState('');
const [smValue, setSmValue] = useState('');
const [mdValue, setMdValue] = useState('');
const [lgValue, setLgValue] = useState('');
const sizeExamples: Array<{
id: SizeToken;
label: string;
helper: string;
size: SizeToken;
value: string;
setValue: (value: string) => void;
}> = [
{
id: 'xs',
label: 'Extra small (xs)',
helper: 'Use for dense layouts or compact verification prompts.',
size: 'xs',
value: xsValue,
setValue: setXsValue,
},
{
id: 'sm',
label: 'Small (sm)',
helper: 'Pairs well with mobile forms and inline flows.',
size: 'sm',
value: smValue,
setValue: setSmValue,
},
{
id: 'md',
label: 'Medium (md)',
helper: 'Default size for most experiences.',
size: 'md',
value: mdValue,
setValue: setMdValue,
},
{
id: 'lg',
label: 'Large (lg)',
helper: 'Highlight critical actions with spacious fields.',
size: 'lg',
value: lgValue,
setValue: setLgValue,
},
];
return (
PIN input sizes
{sizeExamples.map((example) => (
{example.label}
{example.helper}
))}
);
}
```
### Lengths
ID: `PinInput.lengths` β’ Tags: length, digits, fields β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Compare 4, 6, and 8-digit PIN inputs tailored for common authentication flows.
```tsx
const [fourDigit, setFourDigit] = useState('');
const [sixDigit, setSixDigit] = useState('');
const [eightDigit, setEightDigit] = useState('');
const lengthExamples = [
{
length: 4,
title: '4-digit PIN (default)',
helper: 'Common for ATM and device security codes.',
label: '4-digit PIN',
value: fourDigit,
setValue: setFourDigit,
},
{
length: 6,
title: '6-digit verification',
helper: 'Typical for SMS-based one-time codes.',
label: 'Verification code',
value: sixDigit,
setValue: setSixDigit,
},
{
length: 8,
title: '8-digit code',
helper: 'Use for longer recovery or backup codes.',
label: 'Security code',
value: eightDigit,
setValue: setEightDigit,
},
];
return (
PIN input lengths
{lengthExamples.map((example) => (
{example.title}
{example.helper}
))}
);
}
```
### Security
ID: `PinInput.security` β’ Tags: security, mask, validation, otp β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Highlights masking, automatic OTP completion, and validation flows with inline messaging.
```tsx
const [maskedValue, setMaskedValue] = useState('');
const [otpValue, setOtpValue] = useState('');
const [otpStatus, setOtpStatus] = useState('');
const [validationValue, setValidationValue] = useState('');
const [validationMessage, setValidationMessage] = useState('');
const [error, setError] = useState('');
const [disabled, setDisabled] = useState(false);
const correctPin = '1234';
const handleValidate = () => {
if (validationValue !== correctPin) {
setError('Incorrect PIN. Try again.');
setValidationMessage('');
return;
}
setError('');
setValidationMessage('PIN verified successfully.');
};
const handleOtpComplete = (value: string) => {
setOtpStatus(`OTP entered: ${value}`);
};
const handleToggleDisabled = () => {
setDisabled((prev) => !prev);
setError('');
setValidationMessage('');
};
const handleClear = () => {
setValidationValue('');
setError('');
setValidationMessage('');
};
return (
Security-focused PIN inputs
Masked PIN input
Conceal characters as they are typed.
OTP with auto-complete
Automatically completes once all digits are entered.
{
setOtpValue(value);
if (otpStatus) setOtpStatus('');
}}
onComplete={handleOtpComplete}
oneTimeCode
length={6}
label="One-time password"
/>
{otpStatus ? (
{otpStatus}
) : null}
PIN validation state
Enter the correct PIN: 1234
{
setValidationValue(newValue);
if (error) setError('');
if (validationMessage) setValidationMessage('');
}}
label="Enter PIN"
error={error}
disabled={disabled}
helperText={!error ? 'Enter the correct 4-digit PIN' : undefined}
/>
Validate
{disabled ? 'Enable input' : 'Disable input'}
Clear
{validationMessage ? (
{validationMessage}
) : null}
);
}
```
--------------------------------------------------------------------------------
# Popover
Popover sits on the same overlay primitives as Menu and Tooltip, making it suitable for interactive content like forms, lists, and quick action menus while keeping focus management predictable.
## Metadata
- Canonical name: `Popover`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Popover } from '@platform-blocks/react-ui-library';`
- Status: beta
- Category: overlay
- Docs: https://react-ui-library.com/components/Popover
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Popover
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | ReactNode | Yes | | |
| `opened` | boolean | No | | Controlled open state |
| `defaultOpened` | boolean | No | false | Initial open state in uncontrolled mode |
| `onChange` | (opened: boolean) => void | No | | Called when open state changes |
| `onOpen` | () => void | No | | Called when popover opens |
| `onClose` | () => void | No | | Called when popover closes |
| `onDismiss` | () => void | No | | Called when popover is dismissed via outside click or escape |
| `trigger` | 'click' \| 'hover' | No | 'click' | How the popover is triggered: 'click' (default) or 'hover' (mostly useful for devices with a mouse) |
| `disabled` | boolean | No | false | Disable popover entirely |
| `closeOnClickOutside` | boolean | No | true | Close when clicking outside |
| `closeOnEscape` | boolean | No | true | Close when pressing Escape |
| `clickOutsideEvents` | string[] | No | | Events considered for outside click detection (web only) |
| `trapFocus` | boolean | No | | Trap focus within dropdown (web only) |
| `keepMounted` | boolean | No | false | Keep dropdown mounted when hidden |
| `returnFocus` | boolean | No | false | Return focus to target after close |
| `withinPortal` | boolean | No | true | Render dropdown within portal |
| `withOverlay` | boolean | No | false | Render overlay/backdrop |
| `overlayProps` | Record | No | | Overlay component props |
| `w` | number \| 'target' | No | | Dropdown width, number or 'target' to match target width |
| `maxW` | number | No | | Dropdown max-width |
| `maxH` | number | No | | Dropdown max-height |
| `minW` | number | No | | Dropdown min-width |
| `minH` | number | No | | Dropdown min-height |
| `radius` | RadiusValue \| number | No | | Border radius |
| `shadow` | ShadowValue | No | | Box shadow |
| `zIndex` | number | No | 300 | Dropdown z-index |
| `position` | PlacementType | No | 'bottom' | Popover position relative to target |
| `offset` | number \| { mainAxis?: number; crossAxis?: number } | No | 8 | Offset from target |
| `floatingStrategy` | FloatingStrategy | No | 'fixed' | Floating strategy for positioning |
| `middlewares` | PopoverMiddlewares | No | | Custom positioning options |
| `preventPositionChangeWhenVisible` | boolean | No | false | Prevent flipping/shifting when visible |
| `hideDetached` | boolean | No | true | Hide dropdown when target becomes detached |
| `viewport` | PositioningOptions['viewport'] | No | | Override viewport padding |
| `keyboardAvoidance` | boolean | No | true | Whether positioning should avoid the on-screen keyboard |
| `fallbackPlacements` | PlacementType[] | No | | Override fallback placements |
| `boundary` | number | No | | Override boundary padding |
| `withRoles` | boolean | No | true | Render ARIA roles |
| `id` | string | No | | Unique id base for accessibility |
| `withArrow` | boolean | No | false | Render arrow |
| `arrowSize` | number | No | DEFAULT_ARROW_SIZE | Arrow size |
| `arrowRadius` | number | No | 0 | Arrow border radius |
| `arrowOffset` | number | No | 5 | Arrow offset |
| `arrowPosition` | ArrowPosition | No | 'center' | Arrow position for start/end placements |
| `onPositionChange` | (placement: PlacementType) => void | No | | Called when dropdown position changes |
| `testID` | string | No | | Test identifier |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic Usage
ID: `Popover.basic` β’ Tags: popover β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Popover targets wrap an interactive element and render dropdown content within `Popover.Dropdown`.
```tsx
return (
Toggle popover
Quick actions
Popovers expose more content than tooltips without leaving the page.
Create new entry
View documentation
);
}
```
### Hover Trigger
ID: `Popover.hover` β’ Tags: popover, hover, trigger β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Set `trigger="hover"` to open the popover when the user hovers over the target element. This is useful for mouse users who want quick access to additional content without clicking.
```tsx
return (
Hover over me
Hover popover
This popover opens on hover, ideal for mouse users who want quick access to additional content.
);
}
```
### Controlled State
ID: `Popover.controlled` β’ Tags: popover, state β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Control the `opened` prop and respond to `onChange` when the popover needs to sync with surrounding form state.
```tsx
const [opened, setOpened] = useState(false);
const [email, setEmail] = useState('team@example.com');
return (
Invite teammate
Invite team member
setOpened(false)}>
Send invite
);
}
```
### Placement Options
ID: `Popover.placements` β’ Tags: popover, position β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Set the `position` prop to control where the dropdown renders relative to its trigger.
```tsx
const OPTIONS = [
{ label: 'Top', position: 'top', description: 'Appears above the trigger.' },
{ label: 'Right', position: 'right', description: 'Anchors to the right edge.' },
{ label: 'Bottom', position: 'bottom', description: 'Drops below the trigger.' },
{ label: 'Left', position: 'left', description: 'Anchors to the left edge.' },
] as const;
return (
{OPTIONS.map(({ label, position, description }) => (
{label}
{label} placement
{description}
))}
);
}
```
--------------------------------------------------------------------------------
# Progress
The Progress component displays the completion progress of a task or process. Supports different variants, colors, and animations.
## Label and description
Progress accepts the same field props as the input components, rendered outside the track:
```tsx
```
`description` is the sublabel beneath the label, `error` replaces it and renders below the bar, `required` adds an asterisk (suppress it with `withAsterisk={false}`), and `labelPosition` accepts `top` (default), `bottom`, `left`, or `right`. `labelGap` tunes the space between the block and the bar, and `labelProps` / `descriptionProps` pass through to the underlying `` elements. `Progress.Root` takes the same props, so a segmented bar can be labelled the same way.
Don't confuse this with `Progress.Label`, which renders text *inside* a filled section.
## Compound components
For multi-part bars, compose `Progress.Root` with one `Progress.Section` per segment, and optionally a `Progress.Label` inside each section:
```tsx
35%
28%
```
Each section takes its `value` as a percentage of the whole track, so sections may sum to less than 100 and leave the remainder unfilled. Sections support `color`, `striped`, `animate`, `radius`, and `transitionDuration` (inherited from `Progress.Root` when omitted).
## Tooltips
Use the section's own `tooltip` prop β a string, or a config object for full `Tooltip` props:
```tsx
```
Do not wrap a section in `Tooltip` yourself. `Tooltip` renders a wrapper view, which then becomes the flex item inside `Progress.Root` and sizes itself to its content β collapsing the section's percentage width. The `tooltip` prop renders the tooltip *inside* the already-sized section instead. If you do need a manual wrapper, give it the width explicitly: ``.
Sections also forward `onPress` and hover/focus handlers, so they can be made interactive directly.
## Vertical orientation
Pass `orientation="vertical"` to `Progress` or `Progress.Root` to fill from the bottom up. Vertical bars have no intrinsic length, so they default to 160 β set `length` (or the `h` layout prop) to size them, and `size` controls the thickness.
```tsx
```
## Metadata
- Canonical name: `Progress`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Progress } from '@platform-blocks/react-ui-library';`
- Category: feedback
- Tags: progress, loading, status, indicator, completion, segments, vertical
- Docs: https://react-ui-library.com/components/Progress
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Progress
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | number | Yes | | 0-100 |
| `size` | SizeValue | No | 'md' | |
| `color` | ThemeColor | No | 'primary' | |
| `radius` | SizeValue | No | 'md' | |
| `striped` | boolean | No | false | |
| `animate` | boolean | No | false | |
| `transitionDuration` | number | No | 0 | ms |
| `orientation` | ProgressOrientation | No | 'horizontal' | Axis the bar fills along. Vertical bars fill bottom-up. @default 'horizontal' |
| `length` | number \| `${number}%` | No | | Length along the main axis. Vertical bars default to 160. |
| `trackColor` | string | No | | Track (unfilled) color. Defaults to the theme's `gray[1]`. |
| `style` | StyleProp | No | | Styles applied to the track. Spacing/layout props stay on the outermost element. |
| `testID` | string | No | | |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `label` | React.ReactNode | No | | Label rendered outside the track. Strings are styled; nodes render as-is. |
| `description` | React.ReactNode | No | | Helper text ("sublabel") rendered directly beneath the label. Hidden while `error` is set. |
| `error` | React.ReactNode | No | | Error message rendered below the bar. Replaces `description` when present. |
| `required` | boolean | No | false | Marks the field as required, rendering an asterisk beside the label. @default false |
| `withAsterisk` | boolean | No | true | Whether the required marker is drawn. @default true |
| `labelPosition` | ProgressLabelPosition | No | 'top' | Placement of the label block relative to the bar. @default 'top' |
| `labelGap` | SizeValue \| number | No | 'xs' | Gap between the label block and the bar β a theme size token or pixel value. @default 'xs' |
| `labelProps` | Omit | No | | Override props applied to the label `` |
| `descriptionProps` | Omit | No | | Override props applied to the description `` |
## Examples
### Basics
ID: `Progress.basic` β’ Tags: progress β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Track a single completion percentage. Set `transitionDuration` so the bar animates its width whenever `value` changes instead of snapping to it.
```tsx
const TRANSITION_MS = 400;
const [completion, setCompletion] = useState(50);
return (
setCompletion(randomValue)}>
randomize value
);
}
```
### Label and description
ID: `Progress.label` β’ Tags: label, description, error, field β’ Category: basics β’ Status: stable β’ Since: 0.11.0
Progress takes the same field props as the input components: `label`, `description` (the sublabel beneath it), `error`, `required`, and `labelPosition`. The block renders outside the track β use `Progress.Label` for text drawn *inside* a filled section.
```tsx
return (
);
}
```
### Advanced
ID: `Progress.advanced` β’ Tags: animation, striped β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Combine `striped` and `animate` to represent indeterminate work.
```tsx
const [value, setValue] = useState(0);
useEffect(() => {
// Hold on the completed state for a beat, then start the run over.
if (value >= 100) {
const restart = setTimeout(() => setValue(0), 1800);
return () => clearTimeout(restart);
}
const tick = setTimeout(() => {
setValue((current) => Math.min(100, current + stageFor(current).speed));
}, TICK_MS);
return () => clearTimeout(tick);
}, [value]);
const done = value >= 100;
const stage = stageFor(value);
return (
);
}
```
### Compound sections
ID: `Progress.compound` β’ Tags: compound, sections, label β’ Category: composition β’ Status: stable β’ Since: 0.10.2
Compose a multi-part bar from `Progress.Root`, `Progress.Section`, and `Progress.Label`. Each section is sized as a percentage of the track, so sections may sum to less than 100% and leave the remainder unfilled.
```tsx
return (
Sections with inline labels
Docs
Media
Other
Sections take a share of the track, so the remaining 22% stays unfilled.
Striped and animated sections
`striped` and `animate` work per section, marking in-flight work.
);
}
```
### With tooltips
ID: `Progress.tooltips` β’ Tags: tooltip, sections, hover β’ Category: composition β’ Status: stable β’ Since: 0.10.2
```tsx
const SECTIONS = [
{ label: 'Documents', value: 34, color: 'primary' as const },
{ label: 'Photos', value: 26, color: 'success' as const },
{ label: 'Backups', value: 18, color: 'warning' as const }
];
return (
{SECTIONS.map((section) => (
{section.value}%
))}
);
}
```
### Example β segments with legend
ID: `Progress.segments` β’ Tags: segments, legend, storage β’ Category: composition β’ Status: stable β’ Since: 0.10.2
Custom-colored segments with tooltips and legend.
```tsx
const USAGE = [
{ label: 'Documents', value: 32, color: '#4c6ef5' },
{ label: 'Music', value: 24, color: '#12b886' },
{ label: 'Code', value: 14, color: '#fab005' },
{ label: 'Video Games', value: 9, color: '#fa5252' }
];
const TOTAL_GB = 500;
const used = USAGE.reduce((sum, segment) => sum + segment.value, 0);
const formatSize = (percent: number) => {
const gb = (percent / 100) * TOTAL_GB;
return gb < 1 ? `${Math.round(gb * 1024)} MB` : `${Math.round(gb)} GB`;
};
return (
Project storage
{formatSize(used)} of {TOTAL_GB} GB used
{USAGE.map((segment) => (
{formatSize(segment.value)}
))}
{USAGE.map((segment) => (
{segment.label}
{segment.value}%
))}
);
}
```
### Vertical orientation
ID: `Progress.vertical` β’ Tags: orientation, vertical β’ Category: composition β’ Status: stable β’ Since: 0.10.2
Set `orientation="vertical"` to fill from the bottom up. Vertical bars have no intrinsic length, so they default to 160 β use `length` (or `h`) to size them.
```tsx
const CHANNELS = [
{ label: 'Kick', value: 82, color: 'primary' as const },
{ label: 'Snare', value: 64, color: 'success' as const },
{ label: 'Bass', value: 91, color: 'warning' as const },
{ label: 'Vox', value: 47, color: 'error' as const }
];
return (
Vertical bars fill from the bottom up
{CHANNELS.map((channel) => (
{channel.label}
))}
);
}
```
--------------------------------------------------------------------------------
# QRCode
The QRCode component generates QR codes for encoding text, URLs, or other data. Supports customization of size, colors, quiet zones, error correction, and various rendering options.
## Metadata
- Canonical name: `QRCode`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { QRCode } from '@platform-blocks/react-ui-library';`
- Category: data
- Tags: qrcode, barcode, scan, data, encoding
- Docs: https://react-ui-library.com/components/QRCode
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/QRCode
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | string | Yes | | The data/text to encode in the QR code |
| `label` | React.ReactNode | No | | Caption rendered with the code β what the user is being asked to scan. Also supplies the accessibility label when `accessibilityLabel` is unset. |
| `description` | React.ReactNode | No | | Secondary line rendered under the label, for the longer explanation. |
| `labelPosition` | 'top' \| 'bottom' | No | 'bottom' | Which side of the code the caption sits on. @default 'bottom' |
| `labelProps` | Omit | No | | Override props applied to the label `` |
| `descriptionProps` | Omit | No | | Override props applied to the description `` |
| `size` | ComponentSizeValue | No | | Size of the QR code (both width and height). Accepts a size token (`xs`β`3xl`) or an explicit pixel value. |
| `backgroundColor` | string | No | | Background color of the QR code |
| `color` | string | No | | Foreground color (the QR code pattern color) |
| `moduleShape` | 'square' \| 'rounded' \| 'diamond' | No | | Module shape variant for data modules. Note: Finder patterns (corner anchors) always remain square for optimal scanner compatibility. |
| `finderShape` | 'square' \| 'rounded' | No | | Corner (finder) shape variant - DEPRECATED: Finder patterns always remain square |
| `cornerRadius` | number | No | | Rounded corner radius factor (0-1) applied when moduleShape='rounded' |
| `gradient` | { type?: 'linear' \| 'radial'; from: string; to: string; rotation?: number; } | No | | Gradient fill (overrides color) |
| `errorCorrectionLevel` | 'L' \| 'M' \| 'Q' \| 'H' | No | | Error correction level |
| `quietZone` | number | No | | Quiet zone size (border modules around the QR code). Defaults to 1 for compact layouts. Set to 4 for strict QR code standard compliance. Set to 0 to remove all padding around the code. |
| `logo` | { uri: string \| ImageSourcePropType; element?: React.ReactNode; size?: number; backgroundColor?: string; borderRadius?: number; } | No | | Logo to display in the center of the QR code |
| `style` | StyleProp | No | | Custom container style |
| `testID` | string | No | | Test ID for testing |
| `accessibilityLabel` | string | No | | Accessibility label |
| `onError` | (error: Error) => void | No | | Callback when QR code generation fails |
| `onLoadStart` | () => void | No | | Callback when QR code starts loading |
| `onLoadEnd` | () => void | No | | Callback when QR code finishes loading |
| `copyOnPress` | boolean \| { value?: string } | No | | If true (or object), tapping the QR copies the value (or provided value). |
| `showCopyButton` | boolean | No | | Show a floating copy button overlay |
| `copyToastTitle` | string | No | | Custom toast title when copied |
| `copyToastMessage` | string | No | | Custom toast message when copied |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
## Examples
### Basics
ID: `QRCode.basic` β’ Tags: qr-code β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Render a single QR code for a link or payload and provide helper text for scanning context.
```tsx
return (
);
}
```
### Sizes
ID: `QRCode.sizes` β’ Tags: size β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Size accepts a token (`xs`β`3xl`) or an explicit pixel value, so QR codes line up with the rest of the size system while still allowing a bespoke footprint.
```tsx
return (
{SIZES.map((size) => (
))}
);
}
```
### Spacing
ID: `QRCode.spacing` β’ Tags: quiet-zone β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Compare quiet zone values and pair them with outer spacing props when embedding codes in dense layouts.
```tsx
const theme = useTheme();
return (
{QUIET_ZONES.map(({ label, quietZone }) => (
))}
Use spacing props and container styling to pad the QR code externally.
);
}
```
### Colors
ID: `QRCode.colors` β’ Tags: palette β’ Category: theming β’ Status: stable β’ Since: 1.0.0
Derive QR foreground and background colors from theme palettes to keep scans on brand.
```tsx
const theme = useTheme();
return (
Theme-aligned palettes
{SCHEMES.map(({ key, label }) => {
const palette = theme.colors[key];
const foreground = palette?.[6] ?? theme.colors.primary[6];
const background = palette?.[0] ?? theme.backgrounds.surface;
return (
);
})}
);
}
```
### Shapes
ID: `QRCode.shapes` β’ Tags: modules β’ Category: features β’ Status: stable β’ Since: 1.0.0
Switch between square, rounded, and diamond module shapes while keeping finder patterns scanner-safe.
```tsx
return (
Module geometry
{SHAPES.map(({ label, value, moduleShape, cornerRadius }) => (
))}
);
}
```
### Gradients
ID: `QRCode.gradient` β’ Tags: gradient β’ Category: theming β’ Status: stable β’ Since: 1.0.0
Blend theme colors with linear or radial gradients to add polish without hurting scan reliability.
```tsx
const theme = useTheme();
const gradients = createGradientExamples(theme);
return (
Gradient fills
{gradients.map(({ label, value, gradient, moduleShape, cornerRadius }) => (
))}
);
}
```
### Interactive
ID: `QRCode.interactive` β’ Tags: controls β’ Category: interaction β’ Status: stable β’ Since: 1.0.0
Let editors tweak the payload, size, error correction, and module shape while previewing the QR code live.
```tsx
const [value, setValue] = useState(PRESETS[0].value);
const [size, setSize] = useState<(typeof SIZES)[number]>(SIZES[1]);
const [errorLevel, setErrorLevel] = useState<(typeof ERROR_LEVELS)[number]>('M');
const [moduleShape, setModuleShape] = useState<(typeof MODULE_SHAPES)[number]>('square');
return (
Source content
{PRESETS.map(({ label, value: preset }) => (
setValue(preset)}
>
{label}
))}
{value.length} characters
Size
{SIZES.map((option) => (
setSize(option)}
>
{option}px
))}
Error correction
{ERROR_LEVELS.map((level) => (
setErrorLevel(level)}
>
{level}
))}
Lβ7% β’ Mβ15% β’ Qβ25% β’ Hβ30% recovery
Module shape
{MODULE_SHAPES.map((shape) => (
setModuleShape(shape)}
>
{shape.charAt(0).toUpperCase() + shape.slice(1)}
))}
);
}
```
### Logos
ID: `QRCode.logo` β’ Tags: logo β’ Category: branding β’ Status: stable β’ Since: 1.0.0
Embed brand marks inside the QR code while preserving quiet zones and scanner-friendly contrast.
```tsx
const theme = useTheme();
return (
{LOGO_EXAMPLES.map(({ label, value, moduleShape, cornerRadius, logo }) => (
))}
);
}
```
### QR Code Variants
ID: `QRCode.variants` β’ Tags: variants, error-correction, quiet-zone β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Compare how error correction levels and quiet zone widths influence scannability.
```tsx
return (
Error correction levels
{ERROR_LEVELS.map(({ label, value }) => (
))}
Quiet zone widths
{QUIET_ZONES.map((quietZone) => (
))}
);
}
```
--------------------------------------------------------------------------------
# Radio
Radio buttons allow users to select a single option from a group of mutually exclusive choices.
## Metadata
- Canonical name: `Radio`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Radio } from '@platform-blocks/react-ui-library';`
- Status: stable
- Since: 1.0.0
- Category: input
- Tags: input, form, selection, choice
- Docs: https://react-ui-library.com/components/Radio
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Radio
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | string | Yes | | Radio value |
| `checked` | boolean | No | | Whether radio is selected |
| `onChange` | (value: string) => void | No | | Change handler |
| `name` | string | No | | Radio group name |
| `size` | SizeValue | No | | Radio size |
| `color` | ColorValue | No | | Radio color theme |
| `label` | React.ReactNode | No | | Radio label |
| `disabled` | boolean | No | | Whether radio is disabled |
| `required` | boolean | No | | Whether radio is required |
| `error` | string | No | | Error message |
| `description` | string | No | | Helper text |
| `labelPosition` | 'left' \| 'right' | No | | Label position relative to radio |
| `children` | React.ReactNode | No | | Radio content/children (alternative to label) |
| `icon` | React.ReactNode \| string | No | | Optional icon displayed alongside the label |
| `onKeyDown` | (event: any) => void | No | | Key handler for accessibility/keyboard support |
| `labelProps` | Omit | No | | Override props applied to the label `` |
| `descriptionProps` | Omit | No | | Override props applied to the description `` |
| `transitionDuration` | number | No | 160 | Length of the select/deselect animation in ms; the center dot grows in and shrinks out against it. `0` applies the state instantly. Always 0 under reduced motion. |
| `testID` | string | No | | Component test ID for testing |
| `style` | any | No | | Additional CSS styles |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic Usage
ID: `Radio.basic` β’ Tags: Radio, RadioGroup β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Use standalone `Radio` components for custom layouts or pass an `options` array to `RadioGroup` for quick single-selection forms.
```tsx
const TEAMS = ['Falcons', 'Tigers', 'Sharks'] as const;
const [favoriteTeam, setFavoriteTeam] = useState('Tigers');
const [ticketType, setTicketType] = useState('reserved');
return (
Standalone radios
{TEAMS.map((team) => (
))}
Grouped selection
);
}
```
### Variants
ID: `Radio.variants` β’ Tags: variants, radio, radiogroup, segmented, chip, card β’ Category: usage β’ Status: stable β’ Since: 1.0.0
The `variant` prop on `RadioGroup` selects how the group is laid out and how the selected option is communicated. `default` keeps the classic dot indicators; `card` renders each option as a bordered surface (useful when options have descriptions); `segmented` joins the options into a single iOS-style control; `chip` lays them out as wrap-friendly pills (good for filter UIs).
```tsx
const PLAN_OPTIONS = [
{ label: 'Starter', value: 'starter', description: 'Up to 3 projects, community support' },
{ label: 'Growth', value: 'growth', description: 'Unlimited projects, priority email support' },
{ label: 'Scale', value: 'scale', description: 'Dedicated success manager + SSO' },
];
const FREQUENCY_OPTIONS = [
{ label: 'Daily', value: 'daily' },
{ label: 'Weekly', value: 'weekly' },
{ label: 'Monthly', value: 'monthly' },
];
const FILTER_OPTIONS = [
{ label: 'All', value: 'all' },
{ label: 'Active', value: 'active' },
{ label: 'Archived', value: 'archived' },
{ label: 'Trashed', value: 'trashed' },
];
const [defaultValue, setDefaultValue] = useState('weekly');
const [planValue, setPlanValue] = useState('growth');
const [frequencyValue, setFrequencyValue] = useState('weekly');
const [filterValue, setFilterValue] = useState('active');
return (
default
card
segmented
chip
);
}
```
### Theming
ID: `Radio.theming` β’ Tags: size, color, state β’ Category: theming β’ Status: stable β’ Since: 1.0.0
Combine the `size`, `color`, and validation props to align radios with your UI tokens and state requirements.
```tsx
const COLOR_OPTIONS = ['primary', 'secondary', 'success', 'error'] as const;
const [sizeValue, setSizeValue] = useState('club');
const [colorValue, setColorValue] = useState('primary');
return (
Size tokens
Semantic colors
{COLOR_OPTIONS.map((tone) => (
setColorValue(value as typeof COLOR_OPTIONS[number])}
label={`${tone.charAt(0).toUpperCase()}${tone.slice(1)} tickets`}
color={tone}
/>
))}
Common states
);
}
```
### Orientations
ID: `Radio.orientations` β’ Tags: horizontal, vertical β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Toggle `orientation` between `horizontal` and `vertical` to adapt radio groups to the available space.
```tsx
const [favoriteSport, setFavoriteSport] = useState('soccer');
const [skillLevel, setSkillLevel] = useState('intermediate');
return (
Horizontal layout
Vertical layout
);
}
```
### Forms
ID: `Radio.forms` β’ Tags: form, validation β’ Category: advanced β’ Status: stable β’ Since: 1.0.0
Pair `RadioGroup` with `required` and `error` messaging to validate selections before submitting a form workflow.
```tsx
const PLANS = [
{
label: 'Starter β $9/mo',
value: 'starter',
description: 'Streamline a single project'
},
{
label: 'Team β $19/mo',
value: 'team',
description: 'Collaborate with up to 10 teammates'
},
{
label: 'Club β $39/mo',
value: 'club',
description: 'Unlock advanced analytics'
}
];
const BILLING = [
{ label: 'Monthly', value: 'monthly' },
{ label: 'Annual (save 20%)', value: 'annual' }
];
const [plan, setPlan] = useState('');
const [billingCycle, setBillingCycle] = useState('monthly');
const [planError, setPlanError] = useState();
const [confirmation, setConfirmation] = useState(null);
const handleSubmit = () => {
if (!plan) {
setPlanError('Select a plan to continue');
setConfirmation(null);
return;
}
setPlanError(undefined);
setConfirmation(`Subscribed to the ${plan} plan with ${billingCycle} billing.`);
};
return (
{
setPlan(next);
setPlanError(undefined);
}}
error={planError}
required
/>
Confirm subscription
{confirmation && (
{confirmation}
)}
);
}
```
--------------------------------------------------------------------------------
# Rating
An interactive component for displaying star ratings and allowing users to provide ratings with customizable appearance.
## Metadata
- Canonical name: `Rating`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Rating } from '@platform-blocks/react-ui-library';`
- Category: input
- Tags: rating, stars, review, score, feedback
- Docs: https://react-ui-library.com/components/Rating
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Rating
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | number | No | | Current rating value |
| `defaultValue` | number | No | 0 | Initial rating value for uncontrolled component |
| `count` | number | No | 5 | Number of rating items (stars) to render |
| `readOnly` | boolean | No | false | Disables input β the rating only displays its value |
| `disabled` | boolean | No | false | Disables the rating. Like `readOnly` it blocks input, but it also dims the control and reports a disabled state to assistive technology. |
| `allowFraction` | boolean | No | false | Allows partial values so a star can be filled fractionally |
| `precision` | number | No | 0.1 when `allowFraction`, otherwise 1 | Smallest increment a value is rounded to when `allowFraction` is enabled. Clamped to the `0.01`β`1` range. |
| `size` | SizeValue \| number | No | 'md' | Size of each rating item β a theme size token or an explicit pixel size |
| `color` | string | No | | Color of filled items. Defaults to the theme warning color. |
| `emptyColor` | string | No | | Color of empty items. Defaults to the theme gray color. |
| `hoverColor` | string | No | | Color of items while hovering/dragging. Defaults to a darker theme warning color. |
| `onChange` | (value: number) => void | No | | Called with the new value when the rating changes |
| `onHover` | (value: number) => void | No | | Called with the previewed value while hovering (web only) |
| `clearable` | boolean | No | false | Allows clearing the rating by selecting the value that is already set |
| `required` | boolean | No | false | Marks the field as required. Renders an asterisk beside the label and reports the requirement to assistive technology on web. |
| `error` | React.ReactNode | No | | Error message rendered below the rating |
| `description` | React.ReactNode | No | | Helper text rendered below the rating |
| `showTooltip` | boolean | No | false | Shows a tooltip with the current value out of `count` while hovering |
| `getTooltipLabel` | (value: number, count: number) => string | No | | Formats the tooltip text. Receives the previewed value and `count`; defaults to `4.5 / 5`. |
| `icon` | RatingIcon | No | | Icon rendered for each item instead of the default star. Accepts an icon registry name (`'heart'`), an icon library component, or an element. Takes precedence over `character`. |
| `emptyIcon` | RatingIcon | No | | Icon rendered for empty items. Defaults to `icon`, so the same glyph is drawn in `emptyColor` unless a different empty icon is supplied. |
| `character` | string \| React.ReactNode | No | 'β
' | Character or node rendered for filled items. Custom strings render as text glyphs, a React element is cloned with `size` and `color`, and the default star character renders the built-in star icon. Ignored when `icon` is set. |
| `emptyCharacter` | string \| React.ReactNode | No | 'β' | Character or node rendered for empty items. Ignored when `icon` or `emptyIcon` is set. |
| `gap` | SizeValue \| number | No | 'xs' | Spacing between rating items β a theme size token or an explicit pixel value |
| `style` | StyleProp | No | | Additional styles applied to the root element |
| `testID` | string | No | | Test ID for testing |
| `accessibilityLabel` | string | No | | Custom accessibility label. Defaults to `Rating: {value} out of {count} stars`. |
| `accessibilityHint` | string | No | | Custom accessibility hint. Defaults to an adjust hint unless `readOnly`. |
| `label` | React.ReactNode | No | | Label rendered next to the rating. Strings are wrapped in a secondary `Text`. |
| `labelPosition` | 'left' \| 'right' \| 'above' \| 'below' | No | 'above' | Placement of the label relative to the rating |
| `labelGap` | SizeValue \| number | No | 'xs' | Spacing between the label and the rating β a theme size token or pixel value |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basics
ID: `Rating.basic` β’ Tags: interactive β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Capture a single rating value with an interactive control and mirror the current score in helper text.
```tsx
const [score, setScore] = useState(3);
return (
Current score: {score} out of 5.
);
}
```
### Sizes
ID: `Rating.sizes` β’ Tags: size β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Compare the available `size` tokens side by side to pick the right scale for your scene.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const;
return (
{SIZES.map((size) => (
))}
);
}
```
### Colors
ID: `Rating.colors` β’ Tags: palette β’ Category: theming β’ Status: stable β’ Since: 1.0.0
Derive filled, hover, and empty colors from the theme palette to align ratings with product semantics.
```tsx
const COLOR_CONFIG = [
{
key: 'primary',
label: 'Primary accent',
getColors: (palette: string[]) => ({
color: palette[5],
emptyColor: palette[1],
hoverColor: palette[6]
})
},
{
key: 'success',
label: 'Success feedback',
getColors: (palette: string[]) => ({
color: palette[5],
emptyColor: palette[1],
hoverColor: palette[6]
})
},
{
key: 'warning',
label: 'Warning feedback',
getColors: (palette: string[]) => ({
color: palette[5],
emptyColor: palette[1],
hoverColor: palette[6]
})
}
] as const;
type PaletteKey = (typeof COLOR_CONFIG)[number]['key'];
const theme = useTheme();
const [values, setValues] = useState>({
primary: 4,
success: 3.5,
warning: 2.5
});
return (
{COLOR_CONFIG.map(({ key, label, getColors }) => {
const palette = theme.colors[key as keyof typeof theme.colors] ?? theme.colors.gray;
const { color, emptyColor, hoverColor } = getColors(palette);
return (
setValues((prev) => ({ ...prev, [key]: next }))
}
color={color}
emptyColor={emptyColor}
hoverColor={hoverColor}
size="lg"
labelPosition="right"
label={
{label}
}
/>
);
})}
);
}
```
### Fractions
ID: `Rating.fractions` β’ Tags: precision β’ Category: features β’ Status: stable β’ Since: 1.0.0
Enable fractional ratings with configurable `precision` values to capture nuanced feedback.
```tsx
const FRACTION_SETTINGS = [
{
key: 'match',
label: 'Match excitement',
precision: 0.1,
helper: 'Set scores in 0.1 increments to capture precise fan sentiment.'
},
{
key: 'broadcast',
label: 'Broadcast quality',
precision: 0.5,
helper: 'Use half-star increments when quick feedback is enough.'
}
] as const;
type FractionKey = (typeof FRACTION_SETTINGS)[number]['key'];
const theme = useTheme();
const [values, setValues] = useState>({
match: 4.2,
broadcast: 3.5
});
return (
{FRACTION_SETTINGS.map(({ key, label, precision, helper }) => (
{label}
setValues((prev) => ({ ...prev, [key]: next }))}
allowFraction
precision={precision}
size="lg"
color={theme.colors.highlight[5]}
emptyColor={theme.colors.highlight[1]}
hoverColor={theme.colors.highlight[6]}
showTooltip
/>
{helper}
))}
);
}
```
### Custom Icons
ID: `Rating.icons` β’ Tags: icon, character β’ Category: theming β’ Status: stable β’ Since: 0.11.0
Swap the default star for any registry icon with `icon`, pair it with a different `emptyIcon` for the unfilled state, or fall back to plain text glyphs through `character` and `emptyCharacter`.
```tsx
const theme = useTheme();
const [hearts, setHearts] = useState(4);
const [bolts, setBolts] = useState(3);
return (
);
}
```
### Variants
ID: `Rating.variants` β’ Tags: interactive, read-only β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Contrast interactive, read-only, and tooltip-enabled ratings to decide which fits your feedback flow.
```tsx
const [interactiveValue, setInteractiveValue] = useState(4);
return (
`${value} out of ${count} stars`}
size="lg"
label="Custom tooltip text"
disclaimer="Pass `getTooltipLabel` to format the tooltip."
/>
);
}
```
### Form Field
ID: `Rating.form-field` β’ Tags: validation, required, clearable β’ Category: features β’ Status: stable β’ Since: 0.11.0
Use `required`, `description`, and `error` to drop a rating into a form like any other field, and `clearable` to let people undo a score by selecting it again.
```tsx
const [score, setScore] = useState(0);
const [submitted, setSubmitted] = useState(false);
const error = submitted && score === 0 ? 'Please choose a rating' : undefined;
return (
setSubmitted(true)}>Submit
{score === 0 ? 'No rating selected.' : `You rated ${score} out of 5.`}
);
}
```
--------------------------------------------------------------------------------
# Ring
The Ring component displays progress or status using a radial indicator. It supports custom labels, color stops, neutral states, and fully customized center content.
## Metadata
- Canonical name: `Ring`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Ring } from '@platform-blocks/react-ui-library';`
- Category: feedback
- Tags: ring, progress, indicator, radial
- Docs: https://react-ui-library.com/components/Ring
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Ring
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | number | Yes | | Current value represented by the ring |
| `min` | number | No | 0 | Lower bound for normalization. Defaults to 0. |
| `max` | number | No | 100 | Upper bound for normalization. Defaults to 100. |
| `size` | number | No | 100 | Diameter of the ring in pixels. Defaults to 100. |
| `thickness` | number | No | 12 | Stroke thickness in pixels. Defaults to 12. |
| `caption` | React.ReactNode | No | | Optional caption rendered beneath the ring |
| `label` | React.ReactNode | No | | Main label rendered in the ring center |
| `subLabel` | React.ReactNode | No | | Secondary label rendered below the main label |
| `showValue` | boolean | No | true | Displays the computed percentage when no label/subLabel is provided. Defaults to true. |
| `valueFormatter` | (value: number, percent: number) => React.ReactNode | No | | Formats the displayed value or percentage |
| `trackColor` | string | No | | Track color behind the progress stroke |
| `progressColor` | string \| ((value: number, percent: number) => string) | No | | Progress stroke color or resolver |
| `colorStops` | RingColorStop[] | No | | Optional color stops evaluated against the computed percent |
| `neutral` | boolean | No | false | Forces the ring into a neutral state, disabling the progress stroke |
| `roundedCaps` | boolean | No | true | Controls whether the progress stroke has rounded caps. Defaults to true. |
| `style` | StyleProp | No | | Container style for the outer wrapper |
| `ringStyle` | StyleProp | No | | Style applied to the ring wrapper |
| `contentStyle` | StyleProp | No | | Style applied to the center content container |
| `labelStyle` | StyleProp | No | | Style overrides for the main label |
| `subLabelStyle` | StyleProp | No | | Style overrides for the secondary label |
| `captionStyle` | StyleProp | No | | Style overrides for the caption |
| `labelColor` | string | No | | Color override for the main label |
| `subLabelColor` | string | No | | Color override for the secondary label |
| `captionColor` | string | No | | Color override for the caption |
| `children` | React.ReactNode \| ((context: RingRenderContext) => React.ReactNode) | No | | Custom center content. Receives value info when passed as a function |
| `testID` | string | No | | Test identifier for end-to-end tests |
| `accessibilityLabel` | string | No | | Accessibility label describing the ring |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Interactive Presets
ID: `Ring.basic` β’ Tags: ring β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Drive multiple ring presentations from a single stateful value and expose how sizing and labels adapt.
```tsx
const [value, setValue] = useState(72);
return (
setValue(Math.max(0, value - 10))}>
-10%
setValue(Math.min(100, value + 10))}>+10%
);
}
```
### Dynamic Color Stops
ID: `Ring.color-stops` β’ Tags: ring β’ Category: styling β’ Status: stable β’ Since: 1.0.0
Display how `colorStops` shift the progress color as values cross threshold ranges.
```tsx
const colorStops = [
{ value: 0, color: '#f87171' },
{ value: 60, color: '#f59e0b' },
{ value: 90, color: '#14b8a6' },
];
return (
{[48, 72, 97].map((value) => (
))}
);
}
```
### Custom Center Content
ID: `Ring.custom-content` β’ Tags: ring β’ Category: customization β’ Status: stable β’ Since: 1.0.0
Showcase the render-prop API for injecting icons, text, or status badges inside the ring.
```tsx
return (
{({ percent }) => (
{Math.round(percent)}%
)}
On hold
);
}
```
--------------------------------------------------------------------------------
# RollingNumber
RollingNumber displays a number and animates every digit that changes, rolling it to its new position. Use it for counters, live totals, prices and metric readouts where the change itself is part of the information.
## Metadata
- Canonical name: `RollingNumber`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { RollingNumber } from '@platform-blocks/react-ui-library';`
- Category: display
- Tags: number, counter, animation, odometer, metric
- Docs: https://react-ui-library.com/components/RollingNumber
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/RollingNumber
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | number | Yes | | Value to display. Each digit that changes rolls to its new position. |
| `prefix` | string | No | | Static text rendered before the number (e.g. `"$ "`). |
| `suffix` | string | No | | Static text rendered after the number (e.g. `" USD"`). |
| `thousandSeparator` | boolean \| string | No | false | `true` for `,`, or an explicit separator string. |
| `decimalSeparator` | string | No | '.' | Character between the integer and decimal parts. Default `.`. |
| `decimalScale` | number | No | | Number of decimal places to render. |
| `fixedDecimalScale` | boolean | No | false | Pad the decimal part with zeros up to `decimalScale`. |
| `transitionDuration` | number | No | | Roll duration in ms. Default `600`. `0` β and an active reduced-motion preference β snap straight to the new digits. |
| `animationDuration` | number | No | | alias for `transitionDuration`. |
| `timingFunction` | RollingNumberTimingFunction | No | 'ease' | Easing curve for the roll. Default `ease`. |
| `stagger` | number | No | 0 | Per-column delay in ms, applied right-to-left so the least significant digit leads. Default `0` (all columns move together). |
| `animateOnMount` | boolean | No | false | Animate from zero on first render instead of appearing settled. Default `false`. |
| `size` | SizeValue | No | 'md' | Font size token or explicit number. Default `'md'`. |
| `color` | string | No | | Text color. Accepts theme palette syntax (`'primary.6'`, `'dimmed'`) or any CSS color. |
| `c` | string | No | | Shorthand alias for `color`, resolved identically. `color` wins when both are set. |
| `weight` | TextStyle['fontWeight'] \| 'normal' \| 'medium' \| 'semibold' \| 'bold' | No | | Font weight. |
| `fontFamily` | string | No | | Custom font family. |
| `ff` | string | No | | Shorthand alias for `fontFamily`. |
| `tabularNums` | boolean | No | true | Use tabular (fixed-width) figures so columns do not shift width as digits change. Default `true`. |
| `style` | StyleProp | No | | Style for the row that wraps prefix, digits and suffix. |
| `textStyle` | StyleProp | No | | Style applied to every glyph β digits, separators, prefix and suffix. |
| `digitStyle` | StyleProp | No | | Style applied to digit glyphs only. |
| `accessibilityLabel` | string | No | | Screen-reader label. Defaults to the formatted value including prefix and suffix, so the rolling columns never have to be read digit by digit. |
| `testID` | string | No | | |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic
ID: `RollingNumber.basic` β’ Tags: basic, counter, number β’ Category: basics β’ Status: stable β’ Since: 1.1.0
A counter whose digits roll to their new positions. Only the columns that changed move.
```tsx
const [value, setValue] = useState(1234);
return (
setValue((current) => current - 1)}>-1
setValue((current) => current + 1)}>+1
setValue(Math.floor(Math.random() * 100000))}>Random
);
}
```
### Currency
ID: `RollingNumber.currency` β’ Tags: currency, prefix, suffix, decimals β’ Category: features β’ Status: stable β’ Since: 1.1.0
`prefix`, `suffix` and the decimal options cover currency formatting without an external formatter. Copying the value on web yields the formatted string, not the digit strips.
```tsx
const [total, setTotal] = useState(1299.99);
return (
setTotal((current) => current + 149.5)}>
Add item
);
}
```
### Timing
ID: `RollingNumber.timing` β’ Tags: animation, duration, easing, stagger β’ Category: features β’ Status: stable β’ Since: 1.1.0
`transitionDuration`, `timingFunction` and `stagger` shape the roll. Stagger delays each column right-to-left, so the carries trail the ones place the way an odometer does.
```tsx
const [value, setValue] = useState(407219);
return (
Snappy β 200ms, no stagger
Odometer β 900ms, 60ms stagger
setValue(Math.floor(Math.random() * 999999))}>
Shuffle
);
}
```
### Live metric
ID: `RollingNumber.live-metric` β’ Tags: metric, dashboard, live β’ Category: examples β’ Status: stable β’ Since: 1.1.0
A ticking metric tile. Values that change faster than the roll retarget mid-flight rather than snapping.
```tsx
const [requests, setRequests] = useState(84213);
useEffect(() => {
const timer = setInterval(() => {
setRequests((current) => current + Math.floor(Math.random() * 40));
}, 1200);
return () => clearInterval(timer);
}, []);
return (
Requests today
);
}
```
--------------------------------------------------------------------------------
# Search
The Search component provides a search input with debouncing, loading states, and customizable clear functionality.
## Metadata
- Canonical name: `Search`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Search } from '@platform-blocks/react-ui-library';`
- Category: input
- Tags: search, input, filter, debounce
- Docs: https://react-ui-library.com/components/Search
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Search
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | string | No | | |
| `defaultValue` | string | No | | |
| `onChange` | (value: string) => void | No | | |
| `onSubmit` | (value: string) => void | No | | |
| `placeholder` | string | No | | |
| `size` | SizeValue | No | | |
| `radius` | any | No | | |
| `autoFocus` | boolean | No | | |
| `debounce` | number | No | | |
| `clearButton` | boolean | No | | |
| `loading` | boolean | No | | |
| `endSection` | React.ReactNode | No | | |
| `accessibilityLabel` | string | No | | |
| `style` | any | No | | |
| `buttonMode` | boolean | No | | When true, renders as a button that opens the spotlight instead of a typeable input |
| `onPress` | () => void | No | | Callback when search button is pressed (only used in buttonMode) |
| `rightComponent` | React.ReactNode | No | | Component to render on the right side (useful for button mode to show shortcuts like CMD+K) |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic search
ID: `Search.basic` β’ Tags: controlled, input β’ Category: basics β’ Status: stable β’ Since: 0.3.0
Control the `Search` value with local state so you can react to user input and mirror the query elsewhere in your UI.
```tsx
const [query, setQuery] = useState('');
return (
Current query: {query || 'β'}
);
}
```
### Button mode
ID: `Search.button-mode` β’ Tags: spotlight, shortcuts β’ Category: behavior β’ Status: stable β’ Since: 0.3.0
Set `buttonMode` to turn `Search` into a pressable launcher and pass a `rightComponent` with `KeyCap` shortcuts so users discover keyboard access.
```tsx
const toast = useToast();
const handleCustomPress = () => {
toast.show({ message: 'Launching saved searchβ¦' });
};
return (
Default Spotlight launcher
β
K
)}
/>
Custom handler with shortcut hint
Ctrl
F
)}
/>
);
}
```
--------------------------------------------------------------------------------
# SegmentedControl
Segmented controls present a small set of exclusive options. The indicator animates between segments with support for horizontal and vertical layouts, optional auto contrast for filled variants, and reduced motion awareness for accessibility.
## Metadata
- Canonical name: `SegmentedControl`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { SegmentedControl } from '@platform-blocks/react-ui-library';`
- Status: beta
- Since: 1.0.0
- Category: input
- Tags: input, segmentation, toggle, selection
- Docs: https://react-ui-library.com/components/SegmentedControl
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/SegmentedControl
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `data` | SegmentedControlData[] | Yes | | Data that defines the segments |
| `value` | string | No | | Controlled value |
| `defaultValue` | string | No | | Uncontrolled initial value |
| `onChange` | (value: string) => void | No | | Called when value changes |
| `size` | SizeValue | No | | Control size, maps to height and font size |
| `color` | string | No | | Indicator color token or hex |
| `orientation` | 'horizontal' \| 'vertical' | No | | Layout orientation |
| `fullWidth` | boolean | No | | Stretch across available width |
| `disabled` | boolean | No | | Disable entire control |
| `readOnly` | boolean | No | | Prevent user interaction but keep visual state |
| `autoContrast` | boolean | No | | Adjust text color automatically for filled/outline variants |
| `withItemsBorders` | boolean | No | | Render dividers between items |
| `transitionDuration` | number | No | | Indicator transition duration (ms) |
| `transitionTimingFunction` | string | No | | Indicator transition easing |
| `name` | string | No | | Optional radio group name hint |
| `variant` | 'default' \| 'filled' \| 'outline' \| 'ghost' | No | | Visual style variant |
| `indicatorStyle` | StyleProp | No | | Custom style for indicator |
| `itemStyle` | StyleProp | No | | Custom style applied to every item |
| `style` | StyleProp | No | | Style applied to the container |
| `testID` | string | No | | Test identifier applied to container |
| `accessibilityLabel` | string | No | | Accessibility label for the entire control |
| `label` | ReactNode | No | | Optional label rendered alongside the control |
| `description` | ReactNode | No | | Supplementary description text rendered with the label |
| `labelPosition` | 'left' \| 'right' \| 'top' \| 'bottom' | No | | Placement of the label relative to the control |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
## Examples
### Basic Usage
ID: `SegmentedControl.basic` β’ Tags: segmented-control, selection, uncontrolled β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Set `defaultValue` to preselect a segment and let the control manage focus and selection state internally.
```tsx
return (
);
}
```
### Controlled Value
ID: `SegmentedControl.controlled` β’ Tags: segmented-control, controlled, state β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Provide `value` and `onChange` to synchronize the selected segment with external state or companion controls.
```tsx
const [value, setValue] = useState('react');
return (
Drive the segmented control from external state to synchronize its value with other inputs.
Selected value: {value}
setValue('react')}>
Select React
setValue('angular')}>
Select Angular
setValue('vue')}>
Select Vue
);
}
```
### Sizes
ID: `SegmentedControl.sizes` β’ Tags: segmented-control, sizes, density β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Use the `size` prop to match dense toolbars or spacious layouts without changing the underlying data.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const;
return (
{SIZES.map((size) => (
{size}
))}
);
}
```
### Full Width
ID: `SegmentedControl.full-width` β’ Tags: segmented-control, layout, full-width β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Apply `fullWidth` to let segments expand and distribute evenly across the available horizontal space.
```tsx
return (
);
}
```
### Orientation
ID: `SegmentedControl.orientation` β’ Tags: segmented-control, layout, orientation β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Toggle the `orientation` prop to rotate the control vertically for sidebars or keep it horizontal for toolbars.
```tsx
return (
);
}
```
### Custom Colors
ID: `SegmentedControl.colors` β’ Tags: segmented-control, colors, theming β’ Category: theming β’ Status: stable β’ Since: 1.0.0
Set the `color` prop to pull semantic tokens or pass custom values, and enable `autoContrast` when you need readable labels on vivid fills.
```tsx
const palettes = [
{ key: 'primary', color: 'primary', defaultValue: 'react', data: frameworks },
{ key: 'success', color: 'success', defaultValue: 'code', data: panes },
{ key: 'purple', color: 'purple', defaultValue: 'settings', data: accountSections },
{ key: 'custom', color: '#FF6B6B', defaultValue: 'medium', data: priorities },
];
return (
{palettes.map((palette) => (
))}
);
}
```
### Interaction States
ID: `SegmentedControl.states` β’ Tags: segmented-control, states, disabled, readonly β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Combine `disabled`, `readOnly`, or per-item `disabled` flags to signal availability without changing layout or selection rules.
```tsx
const scenarios = [
{ key: 'default', label: 'Interactive', props: {}, defaultValue: 'react', data: frameworks },
{ key: 'disabled', label: 'Disabled', props: { disabled: true }, defaultValue: 'code', data: panes },
{ key: 'readOnly', label: 'Read only', props: { readOnly: true }, defaultValue: 'medium', data: priorities },
// `languages` carries the disabled flag on its last item.
{ key: 'itemDisabled', label: 'Single option disabled', props: {}, defaultValue: 'typescript', data: languages },
];
return (
{scenarios.map((scenario) => (
))}
);
}
```
### Visual Variants
ID: `SegmentedControl.variants` β’ Tags: segmented-control, variants, styling β’ Category: theming β’ Status: stable β’ Since: 1.0.0
Choose between `default`, `filled`, `outline`, or `ghost` variants and pair them with semantic `color` tokens to match the surrounding surface.
```tsx
const variants = [
{
key: 'default',
label: 'Default',
props: { variant: 'default' as const },
defaultValue: 'react',
description: 'Baseline segmented control with tonal contrast.',
data: frameworks,
},
{
key: 'filledPrimary',
label: 'Filled',
props: { variant: 'filled' as const, color: 'primary' as const },
defaultValue: 'code',
description: 'Solid background that matches the selected color token.',
data: panes,
},
{
key: 'filledContrast',
label: 'Filled with auto-contrast',
props: {
variant: 'filled' as const,
color: 'warning' as const,
autoContrast: true,
},
defaultValue: 'medium',
description: 'Enable autoContrast when using vivid palettes to keep labels legible.',
data: priorities,
},
{
key: 'outline',
label: 'Outline',
props: { variant: 'outline' as const, color: 'secondary' as const },
defaultValue: 'weekly',
description: 'Focus on outlining the chosen tab while keeping the surface quiet.',
data: cadences,
},
{
key: 'ghost',
label: 'Ghost',
props: { variant: 'ghost' as const, color: 'success' as const },
defaultValue: 'published',
description: 'Ghost removes the segment background until selection, ideal on tinted surfaces.',
data: publishStates,
},
];
return (
Change the variant to match the surface and emphasis level of the surrounding layout.
{variants.map((variant) => (
{variant.label}
{variant.description}
))}
);
}
```
--------------------------------------------------------------------------------
# Select
Select provides a dropdown interface for choosing from predefined options. It supports single and multi-selection modes, disabled states, validation, and customizable styling.
## Metadata
- Canonical name: `Select`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Select } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: input
- Docs: https://react-ui-library.com/components/Select
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Select
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | T \| null | No | | Current value when the component is controlled. |
| `defaultValue` | T \| null | No | | Initial value when the component manages its own state. |
| `onChange` | (value: T \| null, option?: SelectOption \| null) => void | No | | Callback fired whenever the selection changes. |
| `options` | SelectOption[] | Yes | | Collection of options available to choose from. |
| `placeholder` | string | No | | Placeholder text shown when no value is selected. |
| `size` | SizeValue | No | | Size token controlling trigger height and typography. |
| `radius` | any | No | | Corner radius token applied to the trigger and dropdown. |
| `disabled` | boolean | No | | Disables the control when set to true. |
| `label` | string | No | | Optional label rendered above the trigger. |
| `description` | string | No | | Optional short descriptive text shown directly under the label (above the field). |
| `helperText` | string | No | | Helper copy displayed beneath the control. |
| `error` | string | No | | Error message shown beneath the control in error state. |
| `searchable` | boolean | No | | Enables client-side filtering of options. |
| `renderOption` | (opt: SelectOption, active: boolean, selected: boolean) => React.ReactNode | No | | Custom renderer for an individual option row. |
| `fullWidth` | boolean | No | | Stretches the trigger to occupy the full width of its container. |
| `maxH` | number | No | | Maximum height the dropdown may reach before it scrolls. |
| `closeOnSelect` | boolean | No | | Whether the dropdown should close immediately after selection. |
| `clearable` | boolean | No | | Allows the user to clear the current selection. |
| `clearButtonLabel` | string | No | | Accessible label announced for the clear button when present. |
| `onClear` | () => void | No | | Handler invoked after the selection is cleared. |
| `refocusAfterSelect` | boolean | No | | Controls whether the trigger regains focus after selecting an option. |
| `keyboardAvoidance` | boolean | No | | Whether dropdown positioning should avoid the on-screen keyboard. |
| `labelProps` | Omit | No | | Override props applied to the label `` |
| `descriptionProps` | Omit | No | | Override props applied to the description `` |
| `variant` | InputVariant | No | | Visual variant of the trigger shell β `'default' \| 'filled' \| 'outline' \| 'unstyled'`. Mirrors ` `. |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `minH` | DimensionValue | No | | Sets the minimum height |
## Examples
### Basic
ID: `Select.basic` β’ Tags: basic, label, placeholder, single β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Simple single-value select with helper copy and live selection feedback.
```tsx
return (
)
}
```
### Variants
ID: `Select.variants` β’ Tags: variants, filled, outline, unstyled β’ Category: general β’ Status: stable β’ Since: 1.0.0
`Select` accepts the same `variant` prop as ` ` β `default`, `filled`, `outline`, `unstyled` β and shares the underlying input styles, so the trigger reads consistently with text inputs in the same form.
```tsx
const variants = [
{ variant: 'default', label: 'Default' },
{ variant: 'filled', label: 'Filled' },
{ variant: 'outline', label: 'Outline' },
{ variant: 'unstyled', label: 'Unstyled' },
] as const
const [value, setValue] = useState(null)
return (
{variants.map(({ variant, label }) => (
setValue(v as string | null)}
/>
))}
)
}
```
### Custom rendering
ID: `Select.custom` β’ Tags: render, option, custom β’ Category: customization β’ Status: stable β’ Since: 1.0.0
Render each option with additional detail and selection styling using `renderOption`.
```tsx
const theme = useTheme()
const [value, setValue] = useState(detailedSports[0].value)
const accent = theme.colorScheme === 'dark' ? theme.colors.primary[5] : theme.colors.primary[6]
return (
setValue(selected as string)}
renderOption={(option, active, selected) => {
const { emoji, name, description } = option as DetailedSport
return (
{emoji}
{name}
{description}
{selected ? : null}
)
}}
/>
)
}
```
### Disabled states
ID: `Select.disabled` β’ Tags: disabled, options, state β’ Category: states β’ Status: stable β’ Since: 1.0.0
Disable individual options or the full control to reflect availability.
```tsx
// One option is taken out of play to show the per-option disabled state next to
// the whole-field one.
const options = sports.map((option) =>
option.value === 'basketball' ? { ...option, label: 'Basketball (disabled)', disabled: true } : option,
)
const [value, setValue] = useState(sports[0].value)
return (
setValue(val as string)}
/>
)
}
```
### Persistent menu
ID: `Select.noCloseOnSelect` β’ Tags: persistent, close-on-select, comparison β’ Category: interaction β’ Status: stable β’ Since: 1.0.0
Keep the dropdown open after each choice for quick comparisons.
```tsx
const [value, setValue] = useState(null)
return (
setValue(val as string)}
closeOnSelect={false}
/>
)
}
```
--------------------------------------------------------------------------------
# ShimmerText
Animated text highlight that sweeps a configurable gradient across the content. Ideal for loading states, premium callouts, and attention-grabbing text accents.
## Metadata
- Canonical name: `ShimmerText`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { ShimmerText } from '@platform-blocks/react-ui-library';`
- Category: typography
- Tags: text, shimmer, animation, gradient
- Docs: https://react-ui-library.com/components/ShimmerText
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/ShimmerText
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | ReactNode | No | | Text node children. Overrides `text` when provided |
| `text` | string | No | | Text content to render when not using children |
| `color` | string | No | | Base text color rendered underneath the shimmer |
| `colors` | string[] | No | | Optional gradient stops override |
| `shimmerColor` | string | No | | Highlight color used for the shimmer pass |
| `spread` | number | No | | Width of the highlight band as a multiple of the text width (higher = wider highlight). The band always travels from fully clear of one edge to fully clear of the other, so this also sets how far it moves per cycle. |
| `duration` | number | No | | Duration of a single shimmer cycle in seconds |
| `delay` | number | No | | Delay before the shimmer starts (seconds) |
| `repeatDelay` | number | No | | Pause held at the end of each cycle, with the band off screen (seconds) |
| `repeat` | boolean | No | | Whether the shimmer should repeat indefinitely |
| `once` | boolean | No | | Animate only once after becoming visible |
| `direction` | ShimmerDirection | No | | Direction of shimmer movement |
| `debug` | boolean | No | | Enable verbose logging for debugging |
| `onLayout` | TextProps['onLayout'] | No | | Called with the layout of the shimmer container |
| `startOnView` | boolean | No | | Start shimmering once the component enters the viewport (web only) |
| `inViewMargin` | string | No | | `rootMargin` for the `startOnView` IntersectionObserver (web only) |
| `containerStyle` | any | No | | Optional container style |
| `testID` | string | No | | Optional test identifier |
## Examples
### Basic shimmer
ID: `ShimmerText.basic` β’ Tags: animation, text β’ Category: basics β’ Status: stable β’ Since: 0.3.0
Wrap text in `ShimmerText` to add the default looping highlight with no additional configuration.
```tsx
return (
Weekly highlights go live
New arrivals shimmer into view every Friday at noon.
);
}
```
### Interactive controls
ID: `ShimmerText.controls` β’ Tags: animation, controls β’ Category: behavior β’ Status: stable β’ Since: 0.3.0
Expose `spread`, `repeat`, and `once` controls to let readers tune the shimmer loop at runtime.
```tsx
const MIN_SPREAD = 1;
const MAX_SPREAD = 4;
const SPREAD_STEP = 0.1;
const [spread, setSpread] = useState(2);
const [repeat, setRepeat] = useState(true);
const [once, setOnce] = useState(false);
const handleRepeatChange = (value: boolean) => {
setRepeat(value);
if (value) {
setOnce(false);
}
};
const handleOnceChange = (value: boolean) => {
setOnce(value);
if (value) {
setRepeat(false);
}
};
return (
Interactive shimmer headline
Spread: {spread.toFixed(1)}
Repeat animation
Run once
);
}
```
### Customization options
ID: `ShimmerText.customization` β’ Tags: color, timing β’ Category: theming β’ Status: stable β’ Since: 0.3.0
Combine custom color stops, timing tweaks, and direction to align the shimmer with your brand voice.
```tsx
return (
Golden spotlight offer
Fast pulse notification
Shimmer sweeps from right to left
Single pass announcement
);
}
```
--------------------------------------------------------------------------------
# Skeleton
Skeleton components provide visual placeholders for text, avatars, and blocks to reduce perceived loading time.
## Metadata
- Canonical name: `Skeleton`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Skeleton } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: feedback
- Tags: loading, placeholder, skeleton
- Docs: https://react-ui-library.com/components/Skeleton
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Skeleton
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `shape` | SkeletonShape | No | 'rectangle' | Shape of the skeleton placeholder |
| `w` | DimensionValue | No | | Width of the skeleton component |
| `h` | DimensionValue | No | | Height of the skeleton component |
| `size` | SizeValue | No | 'md' | Size of the skeleton component (overrides width/height) |
| `radius` | SizeValue \| number | No | | Border radius for rectangle/rounded shapes |
| `animate` | boolean | No | true | Whether to show the loading animation |
| `animationDuration` | number | No | 1500 | Duration of the loading animation in milliseconds |
| `colors` | [string, string] | No | | Gradient colors for the shimmer effect |
| `style` | StyleProp | No | | Style overrides for the skeleton container |
| `testID` | string | No | | Optional test identifier |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basics
ID: `Skeleton.basic` β’ Tags: loading, placeholder β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Stack text, avatar, and block placeholders to preview the structure of incoming content while data loads.
```tsx
return (
);
}
```
### Shapes
ID: `Skeleton.shapes` β’ Tags: avatar, text, button β’ Category: features β’ Status: stable β’ Since: 1.0.0
Preview the available skeleton shapes for avatars, typography lines, actions, and media blocks.
```tsx
return (
);
}
```
### Card Layout
ID: `Skeleton.card` β’ Tags: card, layout β’ Category: composition β’ Status: stable β’ Since: 1.0.0
Combine avatar, text, and action placeholders to preview a rich card layout ahead of remote content.
```tsx
const theme = useTheme();
return (
);
}
```
--------------------------------------------------------------------------------
# Slider
The Slider component allows users to select a value or range of values by moving a handle along a track. Supports single values, ranges, vertical layouts, and rich customization hooks for the value-label tooltip.
## Metadata
- Canonical name: `Slider`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Slider } from '@platform-blocks/react-ui-library';`
- Category: input
- Tags: slider, range, input, numeric, control
- Docs: https://react-ui-library.com/components/Slider
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Slider
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | number | No | | Slider value |
| `defaultValue` | number | No | 0 | Uncontrolled initial value |
| `onChange` | (value: number) => void | No | | Change handler |
| `min` | number | No | 0 | Minimum value |
| `max` | number | No | 100 | Maximum value |
| `step` | number | No | 1 | Step increment |
| `orientation` | 'horizontal' \| 'vertical' | No | 'horizontal' | Slider orientation |
| `trackColor` | ColorValue | No | | Track color |
| `activeTrackColor` | ColorValue | No | | Active track color |
| `thumbColor` | ColorValue | No | | Thumb color |
| `trackSize` | number | No | | Track height/width |
| `thumbSize` | number | No | | Thumb size |
| `color` | ThemeColor | No | | Color driving the active track, thumb, and active ticks. A palette token, `'primary.6'` shade syntax, or any CSS color. |
| `variant` | SliderVariant | No | 'default' | Visual variant of the slider track + thumb. Defaults to `'default'`. |
| `trackStyle` | StyleProp | No | | Additional styling for the inactive track |
| `activeTrackStyle` | StyleProp | No | | Additional styling for the active track |
| `thumbStyle` | StyleProp | No | | Additional styling for the thumb |
| `tickColor` | ColorValue | No | | Override inactive tick color |
| `activeTickColor` | ColorValue | No | | Override active tick color |
| `tickStyle` | StyleProp | No | | Style applied to inactive tick marks (merged on top of color/size defaults). |
| `activeTickStyle` | StyleProp | No | | Style applied to active tick marks. |
| `tickLabelProps` | Omit | No | | Props applied to the `` rendered for each tick label (style, ff, weight, size, color). |
| `label` | React.ReactNode | No | | Input label (above the slider) |
| `valueLabel` | ((value: number) => string) \| null | No | | Value label formatter function, set to null to disable value label |
| `valueLabelAlwaysOn` | boolean | No | false | If true, value label will always be displayed |
| `valueLabelPosition` | 'top' \| 'bottom' \| 'left' \| 'right' | No | | Where the value label sits relative to the thumb. For horizontal sliders: 'top' (above) or 'bottom' (below). For vertical: 'left' or 'right'. Defaults to 'top' / 'left'. |
| `valueLabelOffset` | number | No | | Pixel gap between the thumb and the value label (default: 6 for top/bottom, 16 for left/right). |
| `valueLabelStyle` | StyleProp | No | | Style applied to the value label wrapper (Card/View). |
| `valueLabelProps` | Omit | No | | Props applied to the value label `` (style, weight, ff, size, color). |
| `valueLabelAsCard` | boolean | No | true | When true (default) the value label is wrapped in a ``. Set to false to render only the bare `` for a flat tooltip. |
| `showMarks` | boolean | No | | Show min/max labels |
| `ticks` | SliderTick[] | No | | Custom ticks/marks to display on the slider |
| `containerSize` | number | No | | Slider container size (width for horizontal, height for vertical) |
| `showTicks` | boolean | No | false | Whether to show automatic tick marks based on step |
| `restrictToTicks` | boolean | No | false | Restrict value changes to only tick positions |
| `inverted` | boolean | No | | Inverted slider (right-to-left or top-to-bottom) |
| `precision` | number | No | | Precision for value display |
| `tooltip` | 'always' \| 'hover' \| 'never' | No | | Tooltip visibility |
| `fullWidth` | boolean | No | true | Make slider stretch to fill parent width/height |
| `disabled` | boolean | No | false | Whether input is disabled |
| `required` | boolean | No | | Whether input is required |
| `placeholder` | string | No | | Input placeholder |
| `error` | string | No | | Error message |
| `helperText` | string | No | | Helper text |
| `description` | string | No | | Optional short description displayed directly under the label (above the field) |
| `size` | SizeValue | No | 'md' | Input size |
| `withAsterisk` | boolean | No | | Whether to show required indicator |
| `name` | string | No | | Input name for form integration |
| `startSection` | React.ReactNode | No | | Left section content |
| `endSection` | React.ReactNode | No | | Right section content |
| `style` | any | No | | Additional styling |
| `accessibilityLabel` | string | No | | Accessibility label |
| `accessibilityHint` | string | No | | Accessibility hint |
| `testID` | string | No | | Test ID for testing |
| `debounceMs` | number | No | | Debounce delay for validation in milliseconds |
| `onFocus` | () => void | No | | Focus handler |
| `onBlur` | () => void | No | | Blur handler |
| `onEnter` | () => void | No | | Enter key press handler |
| `clearable` | boolean | No | | Show built-in clear button when input has value |
| `clearButtonLabel` | string | No | | Accessible label for the clear button |
| `onClear` | () => void | No | | Callback when the clear button is pressed |
| `keyboardFocusId` | string | No | | Identifier used with KeyboardManagerProvider to request refocus |
| `labelProps` | Omit | No | | Override props applied to the field label `` (style, weight, ff, etc.) |
| `descriptionProps` | Omit | No | | Override props applied to the field description `` |
| `placeholderTextColor` | string | No | | Color of the placeholder text. Falls back to `theme.text.muted`. |
| `startSectionProps` | Omit | No | | Props applied to the wrapping `` around `startSection` (style, accessibility, etc.). |
| `endSectionProps` | Omit | No | | Props applied to the wrapping `` around `endSection`. |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
## Examples
### Basic Usage
ID: `Slider.basic` β’ Tags: basic, slider β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Basic slider usage for selecting numeric values within a range.
```tsx
const [value, setValue] = useState(25);
return (
);
}
```
### Slider variants
ID: `Slider.variants` β’ Tags: variants, slider, styling β’ Category: usage β’ Status: stable β’ Since: 1.0.0
```tsx
const VARIANTS = ['default', 'filled', 'outline', 'minimal', 'segmented', 'unstyled'] as const;
const [value, setValue] = useState(40);
return (
{VARIANTS.map((variant) => (
))}
);
}
```
### Ticks and Marks
ID: `Slider.ticks` β’ Tags: ticks, marks, scale β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Slider with visible tick marks and labeled values for better precision.
```tsx
const [value, setValue] = useState(50);
return (
);
}
```
### Range Slider
ID: `Slider.range` β’ Tags: range, multiple, dual-thumb β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Range slider for selecting a range of values with two handles on a single track. Perfect for filtering, price ranges, and any scenario where you need to select minimum and maximum values. Features demonstrated: - Basic range selection with two thumbs - Custom value formatting and labels - Tick marks and step increments - Minimum range constraints - Real-time range span calculation
```tsx
const [priceRange, setPriceRange] = useState<[number, number]>([25, 75]);
const [temperatureRange, setTemperatureRange] = useState<[number, number]>([18, 24]);
const [scoreRange, setScoreRange] = useState<[number, number]>([60, 90]);
return (
Price Range
Min: ${priceRange[0]}
Max: ${priceRange[1]}
Budget range: ${priceRange[1] - priceRange[0]}
Temperature Range
`${value}Β°C`}
valueLabelAlwaysOn
/>
Range: {temperatureRange[0]}Β°C - {temperatureRange[1]}Β°C
);
}
```
### Value label customization
ID: `Slider.valueLabel` β’ Tags: tooltip, valueLabel, valueLabelProps, valueLabelPosition, customization β’ Category: general β’ Status: stable β’ Since: 1.0.0
`valueLabelPosition` chooses where the thumb tooltip sits (`top` / `bottom` for horizontal, `left` / `right` for vertical). `valueLabelOffset` tunes the gap from the thumb. `valueLabelProps` accepts any `` props β `ff`, `weight`, `size`, `color`, `style` β and `valueLabelStyle` overrides the wrapper Card's style. Set `valueLabelAsCard={false}` for a flat tooltip with no Card chrome. Both `` and `` accept the same set of props.
```tsx
const [a, setA] = useState(40);
const [b, setB] = useState(60);
const [c, setC] = useState(72);
const [d, setD] = useState<[number, number]>([20, 80]);
return (
Value label position & styling
Default β tooltip above the thumb
valueLabelPosition="bottom"
Custom Text styling β `ff`, `weight`, `size`, `color`
`${Math.round(v)}%`}
/>
Flat tooltip (no Card) with custom wrapper style
RangeSlider β both thumbs share placement + styling
(i === 0 ? `min ${Math.round(v)}` : `max ${Math.round(v)}`)}
/>
right
);
}
```
### Decimal steps & precision
ID: `Slider.precision` β’ Tags: precision, step, decimal, valueLabel, tooltip β’ Category: general β’ Status: stable β’ Since: 1.0.0
The thumb tooltip formats its value from the `step`: a fractional step like `0.01` shows two decimals, while an integer step rounds to a whole number (trailing zeros are trimmed, so `0.10` reads as `0.1`). Pass `precision` to force a fixed number of decimals regardless of the step.
```tsx
const [position, setPosition] = useState(0.25);
const [temp, setTemp] = useState(21.5);
const [ratio, setRatio] = useState(0.5);
return (
Fractional `step={0.01}` β decimals are inferred from the step
`step={0.5}` β half steps
Force decimals with `precision={2}` (independent of step)
);
}
```
### Slot styling
ID: `Slider.slotStyling` β’ Tags: trackStyle, activeTrackStyle, thumbStyle, tickStyle, slot-props, customization β’ Category: general β’ Status: stable β’ Since: 1.0.0
Each visual layer of the slider is independently customizable: `trackStyle` and `activeTrackStyle` for the track halves, `thumbStyle` for the handle, `tickStyle` / `activeTickStyle` for tick marks, and `tickLabelProps` for tick labels. Per-tick `style` overrides on individual `ticks[i].style` win over the global tick styles. Combined with the value-label slot props (`valueLabelStyle`, `valueLabelProps`) you can fully reskin the slider without forking it.
```tsx
const milestoneTicks = [
{ value: 0, label: '0' },
{ value: 25, label: '25' },
{ value: 50, label: '50' },
{ value: 75, label: '75' },
{ value: 100, label: '100' },
];
const milestoneTicksWithHighlight = milestoneTicks.map((t) =>
t.value === 50
? {
...t,
// per-tick override wins over the global tickStyle / activeTickStyle
style: {
width: 4,
height: 14,
backgroundColor: '#facc15',
borderRadius: 2,
top: 11,
},
}
: t,
);
const [a, setA] = useState(35);
const [b, setB] = useState(60);
const [c, setC] = useState(50);
const [d, setD] = useState<[number, number]>([20, 80]);
return (
Slot styling
Track + thumb overrides β taller track, square thumb
Branded thumb β gradient-style fill via solid color + shadow
Tick + label styling (`tickStyle`, `activeTickStyle`, `tickLabelProps`)
Per-tick override β the tick at 50 is taller and yellow
RangeSlider β same slot props work on both thumbs and the active band
);
}
```
### Vertical Orientation
ID: `Slider.vertical` β’ Tags: vertical, orientation β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Vertical slider orientation for space-efficient layouts and different use cases.
```tsx
const [value, setValue] = useState(60);
return (
Vertical Slider
Value: {value}
);
}
```
### CustomStyles
ID: `Slider.customStyles` β’ Category: general
Showcases how to restyle the Slider using the new color scheme, sizing, and style override props for both single-value and range scenarios.
```tsx
const [volume, setVolume] = useState(65);
const [range, setRange] = useState<[number, number]>([20, 80]);
return (
Palette-driven slider
`colorScheme`, sizing overrides, and style props let the slider carry product branding.
`${Math.round(val)}%`}
valueLabelAlwaysOn
label="Success palette"
/>
Volume: {Math.round(volume)}%
Styled thumb + ticks
Custom range slider
RangeSlider shares the same overrides, making it easy to mix palettes per context.
`${idx === 0 ? 'Min' : 'Max'} ${val}`}
valueLabelAlwaysOn
label="Warning palette"
/>
Range: {range[0]} β {range[1]}
Custom palette + styles
);
}
```
--------------------------------------------------------------------------------
# Space
Use the Space component to insert fixed spacing between elements when margin props are not available or would make layouts harder to reason about.
## Metadata
- Canonical name: `Space`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Space } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: layout
- Tags: spacing, layout, utility
- Docs: https://react-ui-library.com/components/Space
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Space
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `h` | SizeValue | No | | Height of the spacer. Accepts theme spacing tokens or raw numbers. |
| `w` | SizeValue | No | | Width of the spacer. Accepts theme spacing tokens or raw numbers. |
| `size` | SizeValue | No | 'md' | Fallback size when neither `h` nor `w` is provided. Defaults to `md` so the component always occupies some space. |
| `style` | StyleProp | No | | Optional style overrides. |
| `children` | never | No | | Space is presentational only, so children are not supported. |
## Examples
### Vertical spacing
ID: `Space.basic` β’ Tags: spacing, layout β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Compare token-based and numeric vertical gaps between stacked content blocks.
```tsx
const EXAMPLES = [
{
label: 'Token spacing (md)',
gap: 'md' as const,
helper: 'Use theme tokens for consistent rhythm between related content.'
},
{
label: 'Token spacing (xl)',
gap: 'xl' as const,
helper: 'Larger tokens create breathing room for grouped sections.'
},
{
label: 'Numeric spacing (24px)',
gap: 24,
helper: 'Fallback to numeric values when a token does not fit the layout.'
}
] as const;
const theme = useTheme();
return (
{EXAMPLES.map(({ label, gap, helper }) => (
{label}
First line
Second line
{helper}
))}
);
}
```
### Horizontal spacing
ID: `Space.horizontal` β’ Tags: spacing, layout β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Use `Space` to control gutters between inline buttons with tokens or fixed widths.
```tsx
const GROUPS = [
{
label: 'Token spacing (lg)',
gap: 'lg' as const,
helper: 'Theme tokens keep button gutters aligned with the spacing scale.'
},
{
label: 'Numeric spacing (18px)',
gap: 18,
helper: 'Use a numeric width when exact measurements are required.'
}
] as const;
const theme = useTheme();
return (
{GROUPS.map(({ label, gap, helper }) => (
{label}
Primary
Secondary
Ghost
{helper}
))}
);
}
```
--------------------------------------------------------------------------------
# Spoiler
The Spoiler component automatically collapses content that exceeds a specified height, providing a show/hide toggle to reveal the full content.
## Metadata
- Canonical name: `Spoiler`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Spoiler } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: display
- Docs: https://react-ui-library.com/components/Spoiler
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Spoiler
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | React.ReactNode | Yes | | Content to hide/show |
| `maxHeight` | number | No | | Max height (in px) when collapsed |
| `initiallyOpen` | boolean | No | | Whether component starts initially opened |
| `showLabel` | string | No | | Label for show more |
| `hideLabel` | string | No | | Label for hide |
| `transitionDuration` | number | No | | Transition duration ms |
| `size` | SizeValue | No | | Size token for the show/hide control font size |
| `opened` | boolean | No | | Optional controlled open state |
| `onToggle` | (opened: boolean) => void | No | | Callback when toggle |
| `disabled` | boolean | No | | Disable toggle |
| `style` | any | No | | Optional style |
| `renderControl` | (args: { opened: boolean; toggle: () => void; showLabel: string; hideLabel: string }) => React.ReactNode | No | | Render custom control |
| `transparentFade` | boolean | No | | If true (default) fade bottom of clamped content to transparent using CSS mask on web |
| `fadeColor` | string | No | | Fallback overlay gradient end color (used only when transparentFade=false) |
| `disableFadeAnimation` | boolean | No | | Disable gradient fade animation (debug / perf). Default false (animation enabled). |
| `controlProps` | Omit | No | | Override props applied to the show/hide control `` (style, weight, ff, size, color). |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic Usage
ID: `Spoiler.basic` β’ Tags: spoiler, collapse, content β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Set `maxHeight` to reveal a preview of long copy while the rest stays accessible behind the built-in toggle.
```tsx
const paragraphs = [
'Spoilers collapse long sections of copy while keeping the content accessible to screen readers and keyboard users.',
'Use them for optional detail or secondary information that might distract from a primary task. They expand inline, so the surrounding layout stays stable.',
];
return (
Keep the initial height short to hint that more detail is available without overwhelming the layout.
{paragraphs.map((paragraph) => (
{paragraph}
))}
);
}
```
### Initial State
ID: `Spoiler.initiallyOpen` β’ Tags: spoiler, state, initially-open β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Flip the `initiallyOpen` prop to choose whether content renders expanded on mount or waits for user interaction.
```tsx
const examples = [
{
key: 'open',
label: 'Initially open',
description:
'Starts expanded by default so the reader sees the full content on first render.',
props: { initiallyOpen: true },
},
{
key: 'closed',
label: 'Initially closed',
description:
'Keeps the section compact to emphasize surrounding UI until the user opts in.',
props: {},
},
];
const bodyCopy =
'Vivamus fermentum orci eget tortor facilisis, eu egestas eros maximus. Fusce vitae semper libero. Pellentesque habitant morbi tristique senectus et netus.';
return (
Control whether the content renders expanded on mount or waits for user input. Both states remain accessible to assistive tech.
{examples.map((example) => (
{example.label}
{bodyCopy}
{example.description}
))}
);
}
```
### Max Heights
ID: `Spoiler.sizes` β’ Tags: spoiler, layout, sizes β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Dial the `maxHeight` value up or down to control how much content stays visible before the toggle appears.
```tsx
const longText =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer tincidunt condimentum risus, sit amet cursus massa fermentum non.';
const examples = [
{ key: 'small', label: '60px height', maxHeight: 60 },
{ key: 'medium', label: '100px height', maxHeight: 100 },
{ key: 'large', label: '150px height', maxHeight: 150 },
];
return (
Adjust maxHeight to control how much text stays visible before the rest collapses behind the toggle.
{examples.map((example) => (
{example.label}
{longText}
))}
);
}
```
### Custom Control
ID: `Spoiler.customControl` β’ Tags: spoiler, render-control, controlled β’ Category: advanced β’ Status: stable β’ Since: 1.0.0
Use the `renderControl` callback alongside `opened` and `onToggle` to drive expansion with your own button or analytics hooks.
```tsx
const [isOpen, setIsOpen] = useState(false);
return (
Drive the spoiler state yourself to sync analytics or a sibling component. Use renderControl when you need a bespoke trigger.
(
{opened ? 'Collapse content' : 'Expand content'}
)}
>
Open state: {String(isOpen)}
You can render any React node as the control.
Because the component is controlled, you can track expansion analytics or sync other UI elements when content is revealed.
);
}
```
### Control customization
ID: `Spoiler.control-customization` β’ Tags: controlProps, customization, slot-props β’ Category: general β’ Status: stable β’ Since: 1.0.0
`controlProps` accepts any `` props (`ff`, `weight`, `tracking`, `uppercase`, `size`, `color`) and applies them to the show/hide control text β useful when the rest of your design system uses a particular weight or tracking style.
```tsx
const longText =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.';
return (
Default control
{longText}
Uppercase tracked control
{longText}
Monospace control
{longText}
);
}
```
### Newspaper
ID: `Spoiler.newspaper` β’ Category: general
Combine imagery with long-form copy to mimic a newspaper-style reveal where the reader can expand to see the full story.
```tsx
const paragraphs = [
'The coastal morning edition arrived with stories about record tides and neighborhoods working together to reinforce their seawalls. Photographers captured gulls darting between the waves as volunteers stacked sandbags along the promenade.',
'Hidden inside the fold was a feature about an amateur archivist who discovered a crate of glass negatives documenting life on the waterfront a century ago. Each plate is being digitized so classrooms can study the evolution of the shoreline.',
'City gardeners are also experimenting with hardy dune grasses to keep wind-swept sand in place during the colder months. The pilot plots stretch for blocks and bring a warm beige tone to an otherwise grey season.',
];
const isWeb = Platform.OS === 'web';
return (
{paragraphs.map((paragraph) => (
{paragraph}
))}
);
}
```
--------------------------------------------------------------------------------
# Spotlight
The Spotlight component provides a fast, keyboard-driven interface for searching commands, routes, and entities. Supports arrow key navigation, Enter to select, and Escape to dismiss.
## Metadata
- Canonical name: `Spotlight`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Spotlight } from '@platform-blocks/react-ui-library';`
- Status: experimental
- Category: navigation
- Tags: command-palette, search, quick actions
- Docs: https://react-ui-library.com/components/Spotlight
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Spotlight
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `actions` | SpotlightItem[] | Yes | | |
| `nothingFound` | string | No | | |
| `highlightQuery` | boolean \| HighlightComponentProps['highlight'] | No | | |
| `limit` | number | No | | |
| `scrollable` | boolean | No | | |
| `maxHeight` | number | No | | |
| `shortcut` | string \| string[] \| null | No | | |
| `searchProps` | any | No | | |
| `store` | any | No | | |
| `variant` | 'modal' \| 'bottomsheet' \| 'fullscreen' | No | | |
| `width` | number | No | | |
| `height` | number | No | | |
## Examples
### Keyboard Palette
ID: `Spotlight.basic` β’ Tags: spotlight β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Open the Spotlight command palette through the shared store and a primary trigger button.
```tsx
const actions: SpotlightProps['actions'] = [
{
id: 'home',
label: 'Go to home',
description: 'Navigate to the home screen',
icon: 'home',
onPress: () => console.log('navigate: home'),
},
{
id: 'profile',
label: 'Open profile',
description: 'View your account details',
icon: 'user',
onPress: () => console.log('navigate: profile'),
},
{
id: 'settings',
label: 'Adjust settings',
description: 'Update application preferences',
icon: 'settings',
onPress: () => console.log('navigate: settings'),
},
];
const [store] = useSpotlightStoreInstance();
return (
Spotlight provides a keyboard-driven command palette. Open it with `βK` or `Ctrl+K`, or trigger it imperatively from a button.
store.open()}>Open spotlight
You can reuse the same store across multiple triggers.
);
}
```
### Custom Icons
ID: `Spotlight.custom-icons` β’ Tags: spotlight β’ Category: customization β’ Status: stable β’ Since: 1.0.0
Swap `icon` definitions with custom React nodes to render richer action affordances.
```tsx
const actions: SpotlightProps['actions'] = [
{
id: 'deploy',
label: 'Deploy service',
description: 'Trigger the CI/CD pipeline',
icon: ,
onPress: () => console.log('deploy service'),
},
{
id: 'logs',
label: 'Inspect logs',
description: 'Open the latest runtime logs',
icon: ,
onPress: () => console.log('view logs'),
},
{
id: 'alerts',
label: 'Review alerts',
description: 'Check active incidents',
icon: ,
onPress: () => console.log('open alerts'),
},
];
const [store] = useSpotlightStoreInstance();
return (
Icons accept full React nodes, so you can swap in composable UI like `Icon`, avatars, or status badges for richer visuals.
store.open()}>
Open spotlight
);
}
```
### Grouped Actions
ID: `Spotlight.groups` β’ Tags: spotlight β’ Category: organization β’ Status: stable β’ Since: 1.0.0
Organize actions into named groups so related commands render under semantic headers.
```tsx
const actions: SpotlightProps['actions'] = [
{
group: 'Navigation',
actions: [
{ id: 'home', label: 'Home', icon: 'home', onPress: () => console.log('navigate: home') },
{
id: 'dashboard',
label: 'Dashboard',
description: 'Jump to the analytics overview',
icon: 'star',
onPress: () => console.log('navigate: dashboard'),
},
],
},
{
group: 'Settings',
actions: [
{ id: 'profile', label: 'Profile', icon: 'user', onPress: () => console.log('navigate: profile') },
{
id: 'billing',
label: 'Billing settings',
description: 'Manage payment methods',
icon: 'settings',
onPress: () => console.log('navigate: billing'),
},
],
},
];
const [store] = useSpotlightStoreInstance();
return (
Group actions to create semantic sections inside the results list. Each group renders a header before its nested actions.
store.open()}>
Open spotlight
);
}
```
### Highlight Matches
ID: `Spotlight.highlight-query` β’ Tags: spotlight β’ Category: feedback β’ Status: stable β’ Since: 1.0.0
Enable `highlightQuery` so matching substrings glow while you refine command searches.
```tsx
// Actions intentionally share overlapping substrings to show highlighting effect
const actions: SpotlightProps['actions'] = [
{
id: 'create-project',
label: 'Create project',
description: 'Start a new project workspace',
icon: 'plus',
onPress: () => console.log('action: create project'),
},
{
id: 'create-branch',
label: 'Create branch',
description: 'Open branch creation workflow',
icon: 'code',
onPress: () => console.log('action: create branch'),
},
{
id: 'open-recent',
label: 'Open recent project',
description: 'Choose from recently opened projects',
icon: 'folder',
onPress: () => console.log('action: open recent'),
},
{
id: 'project-settings',
label: 'Project settings',
description: 'Configure repository options',
icon: 'settings',
onPress: () => console.log('action: project settings'),
},
];
const [store] = useSpotlightStoreInstance();
return (
Passing `highlightQuery` emphasizes matching substrings across labels and descriptions, reinforcing why a result surfaced.
store.open()}>Open spotlight
Try typing βprojβ or βcreateβ to see the inline highlights.
);
}
```
### Limit Results
ID: `Spotlight.limit-results` β’ Tags: spotlight β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Restrict how many matching actions render by applying the `limit` prop.
```tsx
const actions: SpotlightProps['actions'] = Array.from({ length: 25 }).map((_, index) => ({
id: `command-${index}`,
label: `Command ${index + 1}`,
description: `Example action #${index + 1}`,
icon: 'star',
onPress: () => console.log('command', index + 1),
}));
const [store] = useSpotlightStoreInstance();
return (
Set the `limit` prop to constrain how many results render, even if more actions match the query.
store.open()}>Open spotlight
This demo caps the list at 8 items.
);
}
```
### Fullscreen Mobile
ID: `Spotlight.fullscreen-mobile` β’ Tags: spotlight β’ Category: layouts β’ Status: stable β’ Since: 1.0.0
Pin the `fullscreen` variant to mimic native command palettes on handheld devices.
```tsx
// Reuse a moderate list to showcase vertical scroll in fullscreen
const actions: SpotlightProps['actions'] = Array.from({ length: 18 }).map((_, index) => ({
id: `mobile-action-${index}`,
label: `Mobile action ${index + 1}`,
description: 'Available on every screen',
icon: 'star',
onPress: () => console.log('mobile action', index + 1),
}));
const [store] = useSpotlightStoreInstance();
const isMobile = Platform.OS !== 'web';
return (
Force the `fullscreen` variant to mimic a native sheet on touch devices while keeping the modal layout on web for comparison.
store.open()}>
{isMobile ? 'Open fullscreen spotlight' : 'Open spotlight'}
The component already auto-detects mobile surfaces; this demo pins the variant for clarity.
);
}
```
### Programmatic Stores
ID: `Spotlight.programmatic` β’ Tags: spotlight β’ Category: advanced β’ Status: stable β’ Since: 1.0.0
Showcase scoped Spotlight stores, dynamic actions, and the global `spotlight` helper working together.
```tsx
Block,
Button,
Card,
Row,
spotlight,
Spotlight,
SpotlightProvider,
Text,
type SpotlightProps,
useSpotlightStoreInstance,
} from '@platform-blocks/react-ui-library';
const baseActions: SpotlightProps['actions'] = [
{
id: 'ping',
label: 'Ping server',
description: 'Send a ping to the backend',
icon: 'bolt',
onPress: () => console.log('ping'),
},
{
id: 'refresh',
label: 'Refresh data',
description: 'Reload cached domain data',
icon: 'refresh',
onPress: () => console.log('refresh'),
},
];
const globalActions: SpotlightProps['actions'] = [
{
id: 'global-home',
label: 'Global home',
description: 'Navigate home via the shared store',
icon: 'home',
onPress: () => console.log('global home'),
},
{
id: 'global-settings',
label: 'Global settings',
description: 'Open the account-wide preferences',
icon: 'settings',
onPress: () => console.log('global settings'),
},
];
const [store] = useSpotlightStoreInstance();
const [dynamicCount, setDynamicCount] = useState(0);
const actions = useMemo(
() => [
...baseActions,
{
id: 'add-dynamic',
label: 'Add dynamic action',
icon: 'plus',
onPress: () => setDynamicCount((count) => count + 1),
},
...Array.from({ length: dynamicCount }).map((_, index) => ({
id: `dynamic-${index}`,
label: `Dynamic action ${index + 1}`,
description: 'Added at runtime to the local store',
icon: 'star',
onPress: () => console.log('dynamic', index + 1),
})),
],
[dynamicCount]
);
return (
Combine local stores with the global `spotlight` helper. This demo adds actions to its scoped store while still toggling the shared palette.
store.open()}>Open demo store
spotlight.toggle()}>
Toggle global spotlight
Select βAdd dynamic actionβ to append more commands on the fly.
);
}
```
--------------------------------------------------------------------------------
# Stepper
The Stepper component provides a step-by-step navigation interface, perfect for multi-step forms, wizards, and progress tracking.
## Metadata
- Canonical name: `Stepper`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Stepper } from '@platform-blocks/react-ui-library';`
- Status: stable
- Category: navigation
- Docs: https://react-ui-library.com/components/Stepper
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Stepper
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `active` | number | Yes | | Active step index |
| `onStepClick` | (stepIndex: number) => void | No | | Called when step is clicked |
| `orientation` | 'horizontal' \| 'vertical' | No | | Step orientation |
| `iconPosition` | 'left' \| 'right' | No | | Icon position relative to step body |
| `iconSize` | number | No | | Icon size |
| `size` | ComponentSizeValue | No | | Component size |
| `color` | string | No | | Component color |
| `completedIcon` | ReactNode | No | | Icon to display when step is completed |
| `allowNextStepsSelect` | boolean | No | | Whether next steps (steps with higher index) can be selected |
| `children` | ReactNode | Yes | | Step content |
| `ref` | React.Ref | No | | Accessibility label Component reference |
## Examples
### Controlled Flow
ID: `Stepper.basic` β’ Tags: stepper β’ Category: usage β’ Status: stable β’ Since: 1.0.0
Control the active step with local state and show completion messaging once the flow finishes.
```tsx
const totalSteps = steps.length;
const [activeStep, setActiveStep] = useState(1);
const handleStepChange = (nextIndex: number) => {
if (nextIndex < 0 || nextIndex > totalSteps) {
return;
}
setActiveStep(nextIndex);
};
const goPrevious = () => handleStepChange(activeStep - 1);
const goNext = () => handleStepChange(activeStep + 1);
return (
Control the current step with the `active` prop and provide completion content with `Stepper.Completed`.
{steps.map((step) => (
{step.details}
))}
All onboarding tasks are complete. You can continue to the dashboard.
Back
{activeStep === totalSteps - 1 ? 'Finish' : 'Next step'}
);
}
```
### Visited Step Selection
ID: `Stepper.allowSelect` β’ Tags: stepper, selection β’ Category: behavior β’ Status: stable β’ Since: 1.0.0
Limit navigation to completed steps using `allowStepSelect`, while preserving forward progress through the flow.
```tsx
const totalSteps = steps.length;
const [activeStep, setActiveStep] = useState(0);
const [highestVisitedStep, setHighestVisitedStep] = useState(0);
const handleStepChange = (nextIndex: number) => {
if (nextIndex < 0 || nextIndex > totalSteps) {
return;
}
setActiveStep(nextIndex);
setHighestVisitedStep((previous) => Math.max(previous, Math.min(nextIndex, totalSteps - 1)));
};
const canSelectStep = (stepIndex: number) => highestVisitedStep >= stepIndex && activeStep !== stepIndex;
const goPrevious = () => handleStepChange(activeStep - 1);
const goNext = () => handleStepChange(activeStep + 1);
return (
Gate step selection with `allowStepSelect` so people can revisit completed steps without skipping ahead.
{steps.map((step, index) => (
{step.details}
))}
Setup is complete. You can always return to earlier steps from the navigation.
Back
{activeStep === totalSteps - 1 ? 'Finish' : 'Next step'}
);
}
```
### Icon Overrides
ID: `Stepper.customIcons` β’ Tags: stepper, icons β’ Category: appearance β’ Status: stable β’ Since: 1.0.0
Swap icons for both active steps and the completed state to reinforce the status of each milestone.
```tsx
const totalSteps = steps.length;
const [activeStep, setActiveStep] = useState(1);
const handleStepChange = (nextIndex: number) => {
if (nextIndex < 0 || nextIndex > totalSteps) {
return;
}
setActiveStep(nextIndex);
};
const goPrevious = () => handleStepChange(activeStep - 1);
const goNext = () => handleStepChange(activeStep + 1);
return (
Provide `icon` and `completedIcon` overrides to visually align each step with its stage.
}
>
{steps.map((step) => (
}
>
{step.details}
))}
All setup tasks are complete with custom indicators for each stage.
Back
{activeStep === totalSteps - 1 ? 'Finish' : 'Next step'}
);
}
```
### Vertical Orientation
ID: `Stepper.vertical` β’ Tags: stepper, layout β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Switch the orientation to vertical when you need more room for descriptive copy under each step.
```tsx
const totalSteps = steps.length;
const [activeStep, setActiveStep] = useState(1);
const handleStepChange = (nextIndex: number) => {
if (nextIndex < 0 || nextIndex >= totalSteps) {
return;
}
setActiveStep(nextIndex);
};
const goPrevious = () => handleStepChange(activeStep - 1);
const goNext = () => handleStepChange(activeStep + 1);
return (
Switch to `orientation="vertical"` when steps need additional room for supporting copy.
{steps.map((step, index) => (
{step.details}
))}
Back
Next step
);
}
```
### Loading Indicator
ID: `Stepper.loading` β’ Tags: stepper, feedback β’ Category: feedback β’ Status: stable β’ Since: 1.0.0
Replace the step icon with a spinner during long-running work by toggling the `loading` prop.
```tsx
const totalSteps = 3;
const [activeStep, setActiveStep] = useState(1);
const [isLoading, setIsLoading] = useState(false);
const timeoutRef = useRef | null>(null);
const handleStepChange = (nextIndex: number) => {
if (nextIndex < 0 || nextIndex >= totalSteps) {
return;
}
setActiveStep(nextIndex);
};
const simulateProcessing = () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
setIsLoading(true);
timeoutRef.current = setTimeout(() => {
setIsLoading(false);
handleStepChange(2);
}, 2000);
};
useEffect(() => () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
}, []);
return (
Apply the `loading` prop to a step when a background task is running to replace the icon with a spinner.
Share your project information to kick off the workflow.
We are syncing your data and preparing the workspace.
Setup is complete and the workspace is ready for collaborators.
{isLoading ? 'Processing...' : 'Simulate processing'}
);
}
```
### Size Variants
ID: `Stepper.sizes` β’ Tags: stepper, sizing β’ Category: appearance β’ Status: stable β’ Since: 1.0.0
Compare the `size` prop across the full `xs`β`3xl` scale while reusing the same steps.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const;
return (
{SIZES.map((size) => (
{size}
))}
);
}
```
--------------------------------------------------------------------------------
# Surface
The base container the rest of the library's elevated components are built from
β what other libraries call "paper".
A Surface owns exactly one decision: **how far off the page it sits.** `level`
resolves background, border color and default shadow as a set from
`theme.surfaces`, so a component can't end up with a level-3 shadow over a
level-0 fill, and no component has to pick a background color itself.
## Why a numeric ladder
Elevation is ordered and nestable, so the scale is numeric rather than a set of
named slots (`secondary`, `tertiary`, β¦). Two things fall out of that:
- **Nesting composes.** `raised` reads the enclosing Surface's level and adds
one, so a popover opened inside a card lands a step above the card without
either one knowing the other's number.
- **Scheme differences live in one place.** Light mode conveys elevation mostly
with shadow; dark mode can't β shadows are invisible on a near-black page β so
it uses a progressively lighter fill plus a hairline border. Encoding that in
the ladder means call sites never branch on `colorScheme`.
## Theming
Override `surfaces` on your theme to retune every elevated component at once:
```ts
createTheme({
surfaces: {
0: { background: '#0B0B0F', border: '#1A1A20', shadow: 'none' },
1: { background: '#16161C', border: '#26262E', shadow: 'xs' },
2: { background: '#1E1E26', border: '#2E2E38', shadow: 'md' },
3: { background: '#26262F', border: '#363642', shadow: 'xl' },
},
})
```
Themes that don't define `surfaces` get a ladder derived from `backgrounds`
(`base` β `surface` β `elevated`), so existing custom themes keep working.
## Used by
Card sits at level 1 (2 for `variant="elevated"`); Menu, Select dropdowns and
Popover at level 2; Dialog at level 3. `useSurfaceLevel()` reports the level of
the surface the caller is rendered on, and `surfaceInteractionTint()` returns
hover/pressed/selected overlays that read correctly at any level.
## Metadata
- Canonical name: `Surface`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Surface } from '@platform-blocks/react-ui-library';`
- Category: layout
- Tags: surface, paper, elevation, container, layout
- Docs: https://react-ui-library.com/components/Surface
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Surface
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | React.ReactNode | No | | |
| `level` | SurfaceLevel | No | | Elevation step (`0`β`3`). Drives background, border color and the default shadow together, so a surface can't end up with a level-3 shadow over a level-0 fill. Omit it to derive the level from the enclosing Surface β see `raised`. |
| `raised` | boolean | No | | Take the enclosing Surface's level and add one (clamped at 3). This is what makes nesting work: a popover inside a card lands a step above the card without either one hard-coding a number. |
| `withBorder` | boolean \| 'auto' | No | | Show the level's hairline border. Defaults to `'auto'`, which draws it only in dark mode β light mode conveys elevation with shadow, dark mode can't. |
| `borderColor` | string | No | | Border color override. Implies a border. |
| `borderWidth` | number | No | | Border width override in px. Implies a border. |
| `bg` | string | No | | Background override β CSS color, a `theme.backgrounds` key, or a palette name/`palette.shade`. Wins over the level's fill. |
| `padding` | SizeValue | No | | Internal padding β size token or px. Surfaces have none by default. |
| `style` | StyleProp | No | | |
| `testID` | string | No | | |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
| `fullWidth` | boolean | No | | Makes the component fill the full width of its parent |
| `w` | DimensionValue | No | | Sets a specific width |
| `h` | DimensionValue | No | | Sets a specific height |
| `maxW` | DimensionValue | No | | Sets the maximum width |
| `minW` | DimensionValue | No | | Sets the minimum width |
| `maxH` | DimensionValue | No | | Sets the maximum height |
| `minH` | DimensionValue | No | | Sets the minimum height |
| `radius` | RadiusValue | No | | Border radius value - supports size tokens, numbers, and special values |
| `shadow` | ShadowValue | No | | Shadow value - supports size tokens and 'none' |
## Examples
### Elevation levels
ID: `Surface.levels` β’ Tags: elevation, levels β’ Category: basics β’ Status: stable β’ Since: 0.11.0
Each level resolves its own background, border and shadow from `theme.surfaces`. Toggle the color scheme to see the ladder switch from shadow-led to fill-led.
```tsx
const LEVELS = [
{ level: 0 as const, label: 'Level 0 β the page' },
{ level: 1 as const, label: 'Level 1 β resting content' },
{ level: 2 as const, label: 'Level 2 β floating over content' },
{ level: 3 as const, label: 'Level 3 β takes over the screen' },
];
return (
{LEVELS.map(({ level, label }) => (
{label}
))}
);
}
```
### Nesting with raised
ID: `Surface.nesting` β’ Tags: nesting, raised, elevation β’ Category: basics β’ Status: stable β’ Since: 0.11.0
`raised` takes the enclosing Surface's level and adds one, so nested containers stack correctly without any of them hard-coding a number. Move the outer Surface to a different level and everything inside follows.
```tsx
return (
Level 0 β the page
Raised once β level 1
Raised again β level 2
);
}
```
### Surface vs Card
ID: `Surface.as-card` β’ Tags: card, composition β’ Category: usage β’ Status: stable β’ Since: 0.11.0
Card is a Surface with padding and section semantics on top. Reach for Surface directly when you want the elevation without Card's structure β a toolbar, a sheet, a custom panel.
```tsx
return (
Surface β elevation only
level 1
Card β Surface + padding + sections
level 2
);
}
```
--------------------------------------------------------------------------------
# Switch
Switch components provide a way to toggle between two states, typically representing on/off or enabled/disabled states.
## Metadata
- Canonical name: `Switch`
- Package: `@platform-blocks/react-ui-library`
- Import: `import { Switch } from '@platform-blocks/react-ui-library';`
- Status: stable
- Since: 1.0.0
- Category: input
- Tags: input, form, toggle, switch, boolean
- Docs: https://react-ui-library.com/components/Switch
- Source: https://github.com/platform-blocks/react-ui-library/tree/main/packages/ui/src/components/Switch
## Props
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `checked` | boolean | No | | Whether switch is on |
| `defaultChecked` | boolean | No | false | Initial checked state for uncontrolled usage |
| `onChange` | (checked: boolean) => void | No | | Change handler |
| `size` | SizeValue | No | 'md' | Switch size |
| `variant` | 'filled' \| 'outline' \| 'ios' \| 'android' | No | 'filled' | Visual style of the switch. - `filled` (default): solid track that fills with `color` when on, white thumb. - `outline`: transparent track with a colored border and a colored thumb when on. - `ios`: iOS-style pill β a large white thumb that nearly fills a rounded track. - `android`: Material-3-style β an outlined track with a small dot thumb that grows and turns white as the switch turns on. |
| `color` | ThemeColor | No | 'primary' | Switch color when on. A palette token, `'primary.6'` shade syntax, or any CSS color. |
| `transitionDuration` | number | No | | Length of the on/off transition in ms. `0` moves the thumb instantly. When omitted the switch keeps its spring animation; any explicit value (including 0) swaps it for a timing curve. Always 0 under reduced motion. |
| `label` | React.ReactNode | No | | Switch label |
| `disabled` | boolean | No | false | Whether switch is disabled |
| `required` | boolean | No | false | Whether switch is required |
| `error` | string | No | | Error message |
| `description` | string | No | | Helper text |
| `labelPosition` | 'left' \| 'right' \| 'top' \| 'bottom' | No | 'right' | Label position relative to switch |
| `children` | React.ReactNode | No | | Switch content/children (alternative to label) |
| `onIcon` | React.ReactNode | No | | Icon to show when on |
| `offIcon` | React.ReactNode | No | | Icon to show when off |
| `onLabel` | string | No | 'On' | Labels for on/off states |
| `offLabel` | string | No | 'Off' | |
| `controls` | string | No | | Controlled component to show/hide |
| `accessibilityLabel` | string | No | | Custom accessibility label (overrides label-based default) |
| `accessibilityHint` | string | No | | Accessibility hint to describe what happens |
| `labelProps` | Omit | No | | Override props applied to the label `` |
| `descriptionProps` | Omit | No | | Override props applied to the description `` |
| `testID` | string | No | | Component test ID for testing |
| `style` | any | No | | Additional CSS styles |
| `m` | number | No | | Margin applied to all sides |
| `mt` | number | No | | Margin applied to the top side |
| `mr` | number | No | | Margin applied to the right side |
| `mb` | number | No | | Margin applied to the bottom side |
| `ml` | number | No | | Margin applied to the left side |
| `mx` | number | No | | Horizontal margin applied to left and right sides |
| `my` | number | No | | Vertical margin applied to top and bottom sides |
| `p` | number | No | | Padding applied to all sides |
| `pt` | number | No | | Padding applied to the top side |
| `pr` | number | No | | Padding applied to the right side |
| `pb` | number | No | | Padding applied to the bottom side |
| `pl` | number | No | | Padding applied to the left side |
| `px` | number | No | | Horizontal padding applied to left and right sides |
| `py` | number | No | | Vertical padding applied to top and bottom sides |
## Examples
### Basic Usage
ID: `Switch.basic` β’ Tags: controlled, label β’ Category: basics β’ Status: stable β’ Since: 1.0.0
Control a `Switch` with local state and surface its status with supporting text and the `description` prop.
```tsx
const [enabled, setEnabled] = useState(true);
return (
Notices are {enabled ? 'enabled' : 'disabled'}.
);
}
```
### Sizes
ID: `Switch.sizes` β’ Tags: size β’ Category: layout β’ Status: stable β’ Since: 1.0.0
Choose a `size` token to scale the switch track and thumb; `defaultChecked` seeds uncontrolled switches.
```tsx
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const;
return (
{SIZES.map((size) => (
{size}
))}
);
}
```
### Variants
ID: `Switch.variants` β’ Tags: variant, filled, outline β’ Category: appearance β’ Status: stable β’ Since: 1.0.0
Choose between a solid `filled` track (default) and an `outline` track whose border and thumb take on the active color.
```tsx
const VARIANTS = [
{ variant: 'filled', hint: 'filled (default) β solid track fills with the active color' },
{ variant: 'outline', hint: 'outline β bordered track with a colored thumb' },
{ variant: 'ios', hint: 'ios β large white thumb in a rounded pill track' },
{ variant: 'android', hint: 'android β Material dot thumb that grows and whitens when on' },
] as const;
const [on, setOn] = useState>({
filled: true,
outline: true,
ios: true,
android: true,
});
return (
{VARIANTS.map(({ variant, hint }) => (
{hint}