# Platform Blocks UI Knowledge Base
Generated: 2026-04-02T22:56:16.105Z
## Package Overview
- Name: @platform-blocks/ui
- Version: 0.8.2
- Description: A comprehensive React Native UI component library — 80+ accessible, themeable, cross-platform components for iOS, Android, and Web
- Main: lib/cjs/index.js
- Module: lib/esm/index.js
- Types: lib/index.d.ts
- React Native Entry: ./lib/esm/index.js
### Scripts (top commands)
- build: npm run clean && rollup -c && npm run build:types
- build:cjs: tsc -p tsconfig.cjs.json
- build:esm: tsc -p tsconfig.esm.json
- build:types: tsc -p tsconfig.json --emitDeclarationOnly
- clean: rm -rf lib || true
- format: eslint src --fix && echo 'Code formatted successfully!'
- generate:llms: tsx ../../scripts/generate-llms.ts
- lint: eslint src
- lint:check: eslint src --max-warnings 0
- lint:fix: eslint src --fix
- precommit: npm run lint:check && npm run test
- prepare: sh -c 'if [ "$BUILD_ON_PREPARE" = "1" ]; then npm run build; else echo "ui: skipping build on prepare (set BUILD_ON_PREPARE=1 to enable)"; fi'
- … 6 more
### Peer Dependencies
- @react-native-masked-view/masked-view: >=0.3.0
- @shopify/flash-list: >=1.6.0
- expo-audio: >=1.0.0
- expo-document-picker: >=13.0.0
- expo-haptics: >=14.0.0
- expo-linear-gradient: >=12.0.0
- expo-status-bar: >=1.12.0
- react: >=18.0.0
- react-native: >=0.73.0
- react-native-reanimated: >=3.4.0
- react-native-reanimated-carousel: >=3.4.0
- react-native-safe-area-context: >=4.5.0
- … 3 more
### Dev Dependencies (not exhaustive)
- @babel/core: ^7.28.0
- @babel/preset-env: ^7.28.0
- @babel/preset-flow: ^7.27.0
- @babel/preset-react: ^7.28.0
- @babel/preset-typescript: ^7.28.0
- @react-native-masked-view/masked-view: ^0.3.2
- @react-native/babel-preset: ^0.83.0
- @rollup/plugin-commonjs: ^29.0.0
- @rollup/plugin-json: ^6.1.0
- @rollup/plugin-node-resolve: ^16.0.0
- @rollup/plugin-typescript: ^12.3.0
- @shopify/flash-list: ^2.2.0
- … 32 more
## UI README
@platform-blocks/ui
[](https://github.com/joshstovall/platform-blocks/blob/HEAD/LICENSE)
[](https://www.npmjs.com/package/@platform-blocks/ui)
[](https://discord.gg/kbHjwzgXbc)
A comprehensive React Native UI component library for building accessible, themeable, and cross-platform mobile and web applications. Part of the [Platform Blocks](https://platform-blocks.com/) ecosystem.
## Features
- **80+ components** — Inputs, navigation, data display, overlays, media, and more
- **Cross-platform** — iOS, Android, and Web from a single codebase
- **Themeable** — Built-in light and dark themes with full customization via `createTheme`
- **Accessible** — Screen reader, keyboard navigation, and RTL support out of the box
- **Animated** — Smooth interactions powered by `react-native-reanimated`
- **Haptics & sound** — Optional feedback via `expo-haptics` and `expo-audio`
- **i18n ready** — Built-in internationalization with `I18nProvider`
- **Tree-shakeable** — ESM and CJS builds with no side effects
## Installation
```bash
npm install @platform-blocks/ui
```
### Peer dependencies
| Package | Version |
| --- | --- |
| `react` | `>=18.0.0 <20.0.0` |
| `react-native` | `>=0.73.0` |
| `react-native-reanimated` | `>=3.4.0` |
| `react-native-safe-area-context` | `>=4.5.0` |
| `react-native-svg` | `>=13.0.0` |
### Optional integrations
These are lazily required and only needed when you use features that depend on them:
`expo-audio` · `expo-document-picker` · `expo-haptics` · `expo-linear-gradient` · `expo-status-bar` · `react-native-worklets` · `react-syntax-highlighter` · `@react-native-masked-view/masked-view` · `@shopify/flash-list` · `react-native-reanimated-carousel`
## Quick start
```tsx
import { PlatformBlocksProvider, Button } from '@platform-blocks/ui';
export function App() {
return (
);
}
```
## Components
### Layout
`AppShell` · `Flex` · `Grid` · `Row` · `Column` · `Masonry` · `KeyboardAwareLayout` · `BottomAppBar`
### Typography
`Text` · `H1`–`H6` · `P` · `Strong` · `Code` · `Kbd` · `Mark` · `Highlight` · `Markdown` · `ShimmerText`
### Forms & inputs
`Input` · `TextArea` · `NumberInput` · `PasswordInput` · `PinInput` · `PhoneInput` · `Search` · `Select` · `AutoComplete` · `Checkbox` · `Radio` · `Switch` · `Slider` · `RangeSlider` · `Knob` · `ToggleButton` · `SegmentedControl` · `DatePicker` · `TimePicker` · `Calendar` · `ColorPicker` · `EmojiPicker` · `FileInput` · `Rating` · `Form`
### Navigation
`Tabs` · `Breadcrumbs` · `Menu` · `Pagination` · `Stepper`
### Data display
`DataTable` · `Table` · `Card` · `Avatar` · `Badge` · `Chip` · `Timeline` · `Tree` · `ListGroup` · `Blockquote` · `TableOfContents` · `Accordion`
### Feedback
`Toast` · `Notice` · `Progress` · `Skeleton` · `Loader` · `Gauge` · `Ring`
### Overlays
`Dialog` · `Tooltip` · `Popover` · `ContextMenu` · `Overlay` · `LoadingOverlay` · `Spotlight` · `FloatingActions`
### Media
`Icon` · `IconButton` · `Image` · `BrandIcon` · `Carousel` · `Gallery` · `Video` · `Waveform`
### Utilities
`Collapse` · `Divider` · `Link` · `CodeBlock` · `CopyButton` · `QRCode` · `Spoiler`
### App store & marketplace badges
Ready-made buttons and badges for App Store, Google Play, Microsoft Store, Amazon, Spotify, Apple Music, YouTube, Discord, GitHub, and 20+ more.
## Hooks
| Hook | Description |
| --- | --- |
| `useClipboard` | Copy text to clipboard |
| `useDeviceInfo` | Device and platform information |
| `useEscapeKey` | Escape key handler |
| `useGlobalHotkeys` | Global keyboard shortcuts |
| `useHotkeys` | Scoped keyboard shortcuts |
| `useHaptics` | Haptic feedback control |
| `useHapticsSettings` | Haptics configuration |
| `useMaskedInput` | Input masking |
| `useOverlayMode` | Overlay UI state |
| `useScrollSpy` | Scroll position tracking |
| `useSpotlightToggle` | Spotlight tutorial control |
| `useToggleColorScheme` | Dark / light mode toggle |
## Theming
Create custom themes or extend the defaults:
```tsx
import { PlatformBlocksProvider, createTheme } from '@platform-blocks/ui';
const theme = createTheme({
colors: { primary: '#6366f1' },
});
export function App() {
return (
{/* ... */}
);
}
```
## Documentation
Full documentation, interactive examples, and component playground are available at [platform-blocks.com](https://platform-blocks.com).
- [Getting started](https://platform-blocks.com/getting-started)
- [Component gallery](https://platform-blocks.com/components)
- [Interactive examples](https://platform-blocks.com/examples)
- [Theming guide](https://platform-blocks.com/theming)
- [Accessibility](https://platform-blocks.com/accessibility)
- [llms.txt](https://platform-blocks.com/llms.txt) — Full API reference for LLMs and AI assistants
## Contributing
See the [contributing guide](https://github.com/joshstovall/platform-blocks/blob/main/CONTRIBUTING.md) for setup instructions.
## License
[MIT](https://github.com/joshstovall/platform-blocks/blob/main/LICENSE) © [Josh Stovall](https://github.com/joshstovall)
## Component:
The Accordion component groups related content into expandable sections.
### Props
`items` (AccordionItem[]) [required]
Ordered list of items to render. The `key` for each item must be unique.
`type` (AccordionType) [optional, default: 'single']
Expansion behavior. 'single' ensures only one item can be expanded at a time; 'multiple' allows independent expansion.
`defaultExpanded` (string[]) [optional, default: [] as string[]]
Initial expanded item keys (uncontrolled). Ignored when `expanded` is provided. For `type="single"` only the first key is used at initialization.
`expanded` (string[]) [optional]
Controlled set of expanded item keys. Provide alongside `onExpandedChange`.
`onExpandedChange` ((expanded: string[]) => void) [optional]
Called when the expanded keys change (both controlled & uncontrolled flows).
`onItemToggle` (OnAccordionToggle) [optional]
Per-item toggle event with rich metadata. Fires after state resolution.
`variant` (AccordionVariant) [optional, default: 'default']
Visual variant style preset.
`size` (SizeValue) [optional, default: 'md']
Size scale controlling paddings, font sizes, and icon dimensions.
`color` (AccordionColor) [optional, default: 'primary']
Color theme accent applied to active headers & focus states.
`showChevron` (boolean) [optional, default: true]
Whether to render the chevron affordance.
`chevronPosition` ('start' | 'end') [optional, default: 'end']
Chevron placement relative to the header text.
`density` ('comfortable' | 'compact' | 'spacious') [optional, default: 'comfortable']
Space efficiency / vertical density preset.
- … 7 more props documented
### Demos
- Single Expansion | (id: Accordion.basic) | tags: accordion
Allow only one item to open at a time by setting `type="single"` and passing an items array.
```tsx
import { Accordion, Card, Column, Text } from '@platform-blocks/ui';
const faqItems = [
{
key: 'foundation',
title: 'What is Platform Blocks?',
content: (
Platform Blocks is a cross-platform design system that helps teams ship polished React Native apps faster.
),
},
{
key: 'benefits',
title: 'Why use an accordion?',
content: (
Accordions keep dense guidance scannable while letting readers expand only the sections they care about.
),
},
{
key: 'next-steps',
title: 'How do I get started?',
content: (
Install the package, drop the provider at the root, and follow the onboarding checklist in the documentation.
),
},
];
return (
Use `type="single"` to ensure only one panel stays open at a time.
);
}
```
- Multiple Expansion | (id: Accordion.multiple) | tags: accordion, controlled
Control the `expanded` keys to keep several accordion items open at the same time.
```tsx
import { useState } from 'react';
import { Accordion, Card, Column, Text } from '@platform-blocks/ui';
const knowledgeBase = [
{
key: 'collaboration',
title: 'Invite collaborators',
content: (
Share the project with teammates to co-author documentation and keep decisions centralized.
),
},
{
key: 'appearance',
title: 'Customize the theme',
content: (
Extend the default theme tokens with your brand colors and typography system before shipping.
),
},
{
key: 'automation',
title: 'Automate release notes',
content: (
Connect the changelog generator to auto-publish updates whenever you tag a new version.
),
},
];
const [expandedKeys, setExpandedKeys] = useState(['collaboration']);
return (
Switch to `type="multiple"` when readers need to reference several answers at once.
Expanded: {expandedKeys.length ? expandedKeys.join(', ') : 'none'}
);
}
```
## Component:
# AppShell
### Props
`layout` ('default' | 'alt') [optional]
`header` (HeaderConfig) [optional]
`navbar` (NavbarConfig) [optional]
`aside` (AsideConfig) [optional]
`footer` (FooterConfig) [optional]
`bottomNav` (BottomNavConfig) [optional]
`showHeader` (boolean) [optional, default: true]
`layoutSections` (LayoutVisibilityConfig) [optional]
Toggle rendering of individual autoLayout sections
`autoLayout` (boolean) [optional]
Enable AppShell auto-composition. When true, AppShell will render its own Header/Navbar/Main/Footer/BottomBar using the provided content props instead of relying on children.
`headerContent` (React.ReactNode | (() => React.ReactNode)) [optional]
Content to render inside AppShell.Header when autoLayout is enabled
`navbarContent` (React.ReactNode | (() => React.ReactNode)) [optional]
Content to render inside AppShell.Navbar when autoLayout is enabled
`asideContent` (React.ReactNode | (() => React.ReactNode)) [optional]
Content to render inside AppShell.Aside when autoLayout is enabled
- … 22 more props documented
### Demos
- Enhanced | (id: AppShell.enhanced)
```tsx
import { Text, AppShell, Column } from '@platform-blocks/ui';
const sampleTOC = (
Contents
Introduction
Getting Started
Installation
NPM Package
Yarn Setup
Configuration
Components
AppShell
Layout System
Examples
);
return (
(
Documentation
)}
navbarContent={() => (
Navigation
Getting Started
Components
Examples
API Reference
)}
maxContentWidth={960}
tableOfContents={sampleTOC}
tableOfContentsWidth={280}
hideTableOfContentsOnMobile
centerContent
>
Main Content with TOC
This demonstrates the enhanced AppShell with max width constraints
and a table of contents sidebar. The main content area has a maximum
width and is centered, while the table of contents appears on the right
on desktop screens.
The layout is fully responsive - on mobile devices, the table of contents
is hidden by default to preserve screen space.
Features
• Max width constraint for better readability on wide screens
• Table of contents sidebar with responsive behavior
• Configurable through AppShell or AppShellMain props
• Seamless integration with existing AppShell layout system
);
}
```
## Component:
The AppStoreBadge component displays a badge for app store ratings or downloads. Supports different sizes, colors, and can be customized with icons or text.
### Props
`brand` (BrandName | 'galaxy-store' | 'amazonAppstore' | 'applePodcasts' | 'amazonMusic' | 'chromeWebStore') [required]
The brand/platform for the badge (uses BrandIcon)
`primaryText` (string) [required]
The primary text (e.g., "Download on the", "Listen on", "Watch on")
`secondaryText` (string) [required]
The secondary text (e.g., "App Store", "Spotify", "Netflix")
`size` (AppStoreBadgeSize) [optional, default: 'md']
Badge size
`backgroundColor` (string) [optional]
Background color override
`textColor` (string) [optional]
Text color override
`borderColor` (string) [optional]
Border color override
`style` (any) [optional]
Custom styles
`onPress` (() => void) [optional]
Press handler
`disabled` (boolean) [optional, default: false]
Whether badge is disabled
`testID` (string) [optional]
Test ID for testing
`darkMode` (boolean) [optional]
Whether to use dark mode styling (auto-detected if not specified)
### Demos
- Basic usage | (id: AppStoreBadge.basic) | tags: basic, getting-started, badge, app-store
Basic usage of AppStoreBadge with different brands and custom styling.
```tsx
import { AppStoreBadge, Column, Row } from '@platform-blocks/ui';
return (
console.log('App Store pressed')}
/>
console.log('Google Play pressed')}
/>
console.log('Spotify pressed')}
/>
);
}
```
- Convenience components | (id: AppStoreBadge.convenience-components) | tags: basic, getting-started, badge, app-store
Pre-built convenience components for popular app stores and platforms. These components have predefined text and styling for common use cases.
```tsx
import {
AppStoreDownloadBadge,
GalaxyStoreDownloadBadge,
GooglePlayDownloadBadge,
HuaweiAppGalleryBadge,
AmazonAppstoreBadge,
SpotifyListenBadge,
ApplePodcastsListenBadge,
YouTubeWatchBadge,
AppleMusicListenBadge,
AmazonMusicListenBadge,
SoundCloudListenBadge,
TwitchWatchBadge,
GitHubViewBadge,
DiscordJoinBadge,
Block,
MicrosoftStoreDownloadBadge,
AmazonPrimeVideoBadge,
AmazonStoreBadge,
ChromeWebStoreBadge,
} from '@platform-blocks/ui';
return (
console.log('App Store pressed')} />
console.log('Galaxy Store pressed')} />
console.log('Google Play pressed')} />
console.log('AppGallery pressed')} />
console.log('Amazon Appstore pressed')} />
console.log('Spotify pressed')} />
console.log('Apple Podcasts pressed')} />
console.log('YouTube pressed')} />
console.log('Apple Music pressed')} />
console.log('Amazon Music pressed')} />
console.log('SoundCloud pressed')} />
console.log('Twitch pressed')} />
console.log('GitHub pressed')} />
console.log('Discord pressed')} />
console.log('Microsoft Store pressed')} />
console.log('Amazon Prime Video pressed')} />
console.log('Amazon Store pressed')} />
console.log('Chrome Web Store pressed')} />
);
}
```
## Component:
# AppStoreButton (deprecated)
### Props
`store` (AppStoreType) [required]
The app store type
`size` (AppStoreButtonSize) [optional, default: 'md']
Button size
`locale` (SupportedLocale) [optional, default: 'en']
Locale for button text
`style` (any) [optional]
Custom styles
`onPress` (() => void) [optional]
Press handler
`disabled` (boolean) [optional, default: false]
Whether button is disabled
`testID` (string) [optional]
Test ID for testing
### Demos
- Basic usage | (id: AppStoreButton.basic) | tags: app store, download, buttons
Use `AppStoreButton` when you need a brand-safe download CTA—set the `store` prop to any supported storefront and drop the component directly into your layout without extra wrappers.
```tsx
import type { AppStoreButtonProps } from '@platform-blocks/ui';
import { AppStoreButton, Column, Row, Text } from '@platform-blocks/ui';
type StoreName = AppStoreButtonProps['store'];
const storeGroups: { title: string; stores: { store: StoreName; label: string }[] }[] = [
{
title: 'Popular app stores',
stores: [
{ store: 'app-store', label: 'App Store' },
{ store: 'google-play', label: 'Google Play' },
{ store: 'microsoft-store', label: 'Microsoft Store' },
],
},
{
title: 'Alternative storefronts',
stores: [
{ store: 'amazon-appstore', label: 'Amazon Appstore' },
{ store: 'mac-app-store', label: 'Mac App Store' },
{ store: 'f-droid', label: 'F-Droid' },
],
},
];
const handlePress = (label: string) => {
console.log(`Open ${label}`);
};
return (
{storeGroups.map(({ title, stores }) => (
{title}
{stores.map(({ store, label }) => (
handlePress(label)}
/>
))}
))}
);
}
```
- Sizes | (id: AppStoreButton.sizes) | tags: sizes, responsiveness
Adjust the `size` prop (`sm` through `xl`) to align the badge density with its container—smaller tokens fit compact toolbars while larger ones headline marketing sections.
```tsx
import type { AppStoreButtonProps } from '@platform-blocks/ui';
import { AppStoreButton, Column, Row, Text } from '@platform-blocks/ui';
type ButtonSize = NonNullable;
const sizeSections: { label: string; token: ButtonSize }[] = [
{ label: 'Small', token: 'sm' },
{ label: 'Medium (default)', token: 'md' },
{ label: 'Large', token: 'lg' },
{ label: 'Extra large', token: 'xl' },
];
return (
{sizeSections.map(({ label, token }) => (
{label}
))}
);
}
```
## Component:
Area (filled) chart for visualizing cumulative or stacked trends.
### Props
`layout` (AreaChartLayout) [optional, default: 'overlap']
Controls how multiple series are rendered. - `overlap` leaves each area independent (default). - `stacked` cumulatively stacks values and renders using stacked layers.
`areaOpacity` (number) [optional]
Opacity used when rendering stacked layers (if not provided defaults to fillOpacity).
`stackOrder` ('normal' | 'reverse') [optional]
Adjust the stacking order when using stacked layouts.
### Demos
- Basic | (id: AreaChart.basic)
Simple random data area chart with title.
```tsx
import { AreaChart } from '@platform-blocks/charts';
const WEEKLY_SIGNUPS = [
{ x: 0, y: 42 },
{ x: 1, y: 68 },
{ x: 2, y: 83 },
{ x: 3, y: 97 },
{ x: 4, y: 124 },
{ x: 5, y: 138 },
{ x: 6, y: 152 },
{ x: 7, y: 167 },
];
return (
`Week ${value + 1}`,
}}
yAxis={{
show: true,
labelFormatter: (value: number) => `${value} users`,
}}
grid={{ show: true, style: 'dashed', color: '#CBD5F0' }}
tooltip={{ show: true }}
enableCrosshair
liveTooltip
/>
);
}
```
- Inventory Levels Warehouses | (id: AreaChart.inventory-levels-warehouses)
**Story focus**
```tsx
import { AreaChart } from '@platform-blocks/charts';
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];
const INVENTORY_SEGMENTS = [
{
id: 'north',
name: 'North DC',
values: [420, 432, 446, 438, 412, 398],
fillColor: '#3B82F6',
smooth: true,
},
{
id: 'central',
name: 'Central Hub',
values: [506, 498, 473, 452, 438, 421],
fillColor: '#0EA5E9',
smooth: false,
},
{
id: 'south',
name: 'South Cross-dock',
values: [318, 332, 347, 352, 366, 371],
fillColor: '#22D3EE',
smooth: true,
},
];
const formatMonth = (index: number) => MONTHS[index] ?? `M${index + 1}`;
const INVENTORY_SERIES = INVENTORY_SEGMENTS.map(({ id, name, values, fillColor, smooth }) => ({
id,
name,
fillColor,
smooth,
data: values.map((units, index) => ({
x: index,
y: units,
data: { warehouse: name, month: formatMonth(index), units },
})),
}));
return (
{
const label = formatMonth(Math.round(point.x));
return `${label} • ${point.data?.warehouse ?? 'Warehouse'}: ${Math.round(point.y)}k units`;
},
}}
xAxis={{
show: true,
title: '2025 timeline',
labelFormatter: (value: number) => formatMonth(Math.round(value)),
}}
yAxis={{
show: true,
title: 'Inventory on hand (thousands)',
labelFormatter: (value: number) => `${Math.round(value)}k`,
}}
enableCrosshair
liveTooltip
/>
);
}
```
## Component:
The AutoComplete component provides search functionality with suggestions, supporting single/multi-select, async data loading, and rich content display.
### Props
`label` (string) [optional]
Input label
`description` (string) [optional]
Description text below the input
`helperText` (string) [optional]
Helper text displayed below the field when no error is present
`required` (boolean) [optional]
Whether the field is required
`error` (string) [optional]
Error message
`value` (string) [optional]
Input value
`onChangeText` ((text: string) => void) [optional]
Change handler
`placeholder` (string) [optional]
Placeholder text
`disabled` (boolean) [optional]
Whether the input is disabled
`size` (SizeValue) [optional]
Controls input size (affects padding and height)
`radius` (RadiusValue) [optional]
Controls border radius; accepts size tokens or numeric value
`clearable` (boolean) [optional]
Show built-in clear button when there is text
- … 61 more props documented
### Demos
- Basic | (id: AutoComplete.basic) | tags: basic, getting-started, search
Simple auto-complete with a sports dataset, helper copy, and selection feedback.
```tsx
import { useMemo, useState } from 'react';
import { AutoComplete, Column, Text } from '@platform-blocks/ui';
import type { AutoCompleteOption } from '../../types';
const sports: AutoCompleteOption[] = [
{ label: 'Football', value: 'football' },
{ label: 'Basketball', value: 'basketball' },
{ label: 'Soccer', value: 'soccer' },
{ label: 'Baseball', value: 'baseball' },
{ label: 'Tennis', value: 'tennis' },
{ label: 'Golf', value: 'golf' },
{ label: 'Swimming', value: 'swimming' },
{ label: 'Volleyball', value: 'volleyball' },
{ label: 'Cricket', value: 'cricket' },
{ label: 'Rugby', value: 'rugby' },
{ label: 'Softball', value: 'softball' },
{ label: 'Hockey', value: 'hockey' },
];
const [inputValue, setInputValue] = useState('');
const [selectedSport, setSelectedSport] = useState(null);
const displayValue = useMemo(() => selectedSport?.label ?? inputValue, [selectedSport, inputValue]);
return (
Basic auto-complete
Start typing to filter the list of sports. Selecting an option fills the input.
{
setInputValue(value);
if (!value) setSelectedSport(null);
}}
onSelect={(item) => {
setSelectedSport(item);
setInputValue(item.label);
}}
displayProperty="label"
minSearchLength={1}
fullWidth
/>
{selectedSport && (
Selected: {selectedSport.label}
)}
);
}
```
- Multi Select | (id: AutoComplete.multi) | tags: multi-select, multiple, selection
Multi-select AutoComplete that toggles genres and renders removable chips.
```tsx
import { useState } from 'react'
import { AutoComplete, Chip, Column, Icon, Text } from '@platform-blocks/ui'
import type { AutoCompleteOption } from '../../types'
const musicGenres: AutoCompleteOption[] = [
{ label: 'Pop', value: 'pop' },
{ label: 'Rock', value: 'rock' },
{ label: 'Hip Hop', value: 'hiphop' },
{ label: 'Jazz', value: 'jazz' },
{ label: 'Classical', value: 'classical' },
{ label: 'Electronic', value: 'electronic' },
{ label: 'Country', value: 'country' },
{ label: 'R&B', value: 'rnb' },
]
const [inputValue, setInputValue] = useState('')
const [selectedGenres, setSelectedGenres] = useState([])
const handleToggle = (option: AutoCompleteOption) => {
const isSelected = selectedGenres.some((genre) => genre.value === option.value)
setSelectedGenres((current) =>
isSelected
? current.filter((genre) => genre.value !== option.value)
: [...current, option],
)
}
return (
Multi-select tags
Tap an item to add or remove it. Selected genres render as removable chips.
{
setSelectedGenres([])
setInputValue('')
}}
selectedValuesContainerStyle={{ flexWrap: 'wrap', gap: 6 }}
renderSelectedValue={(item, _index, helpers) => (
}
onRemove={helpers.onRemove}
>
{item.label}
)}
fullWidth
/>
Selected {selectedGenres.length} genre{selectedGenres.length === 1 ? '' : 's'}.
)
}
```
## Component:
The Avatar component displays user profile images, initials, or icons. Supports different sizes, colors, and can be grouped together in an AvatarGroup.
### Props
`size` (ComponentSizeValue) [optional]
Size of the avatar
`src` (string) [optional]
Image source URL for the avatar
`fallback` (string) [optional]
Initials to show as fallback when no image is provided
`backgroundColor` (string) [optional]
Background color for the fallback initials
`textColor` (string) [optional]
Text color for the fallback initials
`online` (boolean) [optional]
Whether to show online status indicator
`indicatorColor` (string) [optional]
Color override for the status indicator
`style` (StyleProp) [optional]
Style override for the avatar container
`accessibilityLabel` (string) [optional]
Accessibility label for the avatar image
`label` (React.ReactNode) [optional]
Primary label displayed to the right of the avatar (string or custom React node)
`description` (React.ReactNode) [optional]
Secondary description/subtext under the label
`gap` (number) [optional]
Spacing between avatar and text block
- … 1 more props documented
### Demos
- Basics | (id: Avatar.basic) | tags: avatars
Illustrates loading an avatar image with a reliable initials fallback for offline scenarios.
```tsx
import { Avatar } from '@platform-blocks/ui'
)
```
- Sizes | (id: Avatar.sizes) | tags: avatars, sizes
Display every avatar size token with guidance on when to use each scale.
```tsx
import { Avatar, Column } from '@platform-blocks/ui'
return (
)
}
```
## Component:
The Badge component displays compact elements that represent an input, attribute, or action. Supports different colors, sizes, and interactive features like removal.
### Props
`children` (React.ReactNode) [required]
`size` (ComponentSizeValue) [optional, default: 'md']
`variant` ('filled' | 'outline' | 'light' | 'subtle' | 'gradient') [optional]
`v` ('filled' | 'outline' | 'light' | 'subtle' | 'gradient') [optional]
Alias for variant
`color` ('primary' | 'secondary' | 'success' | 'warning' | 'error' | 'gray' | string) [optional]
`c` ('primary' | 'secondary' | 'success' | 'warning' | 'error' | 'gray' | string) [optional]
Alias for color
`onPress` (() => void) [optional]
`startIcon` (React.ReactNode) [optional]
`endIcon` (React.ReactNode) [optional]
`onRemove` (() => void) [optional]
`removePosition` ('left' | 'right') [optional, default: 'right']
`disabled` (boolean) [optional, default: false]
- … 4 more props documented
### Demos
- Core variants | (id: Badge.basic) | tags: variants, getting-started
Use the `variant` prop to switch between the default, `filled`, `outline`, `light`, and `subtle` Badge styles without touching any other props.
```tsx
import { Badge, Row } from '@platform-blocks/ui'
return (
Default
Filled
Outline
Light
Subtle
)
}
```
- Semantic colors | (id: Badge.colors) | tags: colors, theming
Set the `color` prop to tokens such as `primary`, `success`, `warning`, `error`, or `gray` to align Badges with semantic meaning instead of hard-coded hex values.
```tsx
import { Badge, Row } from '@platform-blocks/ui'
return (
Primary
Success
Warning
Error
Gray
)
}
```
## Component:
Discrete / categorical bar chart visualization component.
### Props
`data` (BarChartDataPoint[]) [required]
Data points
`series` (BarChartSeries[]) [optional]
Optional multi-series data
`barColor` (string) [optional]
Bar color
`barSpacing` (number) [optional, default: 0.2]
Bar spacing (0-1)
`barBorderRadius` (number) [optional, default: 4]
Bar border radius
`orientation` ('vertical' | 'horizontal') [optional, default: 'vertical']
Orientation
`layout` ('single' | 'grouped' | 'stacked') [optional]
Layout strategy for multi-series data
`stackMode` ('normal' | '100%') [optional, default: 'normal']
Stacked layout mode
`valueFormatter` ((value: number, datum: BarChartDataPoint, index: number) => string) [optional]
Optional value formatter for tooltip display
`xAxis` (ChartAxis) [optional]
X-axis configuration
`yAxis` (ChartAxis) [optional]
Y-axis configuration
`grid` (ChartGrid) [optional]
Grid configuration
- … 22 more props documented
### Demos
- BarChart Basic | (id: BarChart.basic) | tags: basic, getting-started
Basic BarChart usage with a title.
```tsx
import { BarChart } from '../../';
const QUARTERLY_REVENUE = [
{ id: 'q1', category: 'Q1', value: 420_000 },
{ id: 'q2', category: 'Q2', value: 515_000 },
{ id: 'q3', category: 'Q3', value: 468_500 },
{ id: 'q4', category: 'Q4', value: 590_200 },
];
return (
`$${(value / 1000).toFixed(0)}k`}
xAxis={{ show: true }}
yAxis={{
show: true,
labelFormatter: (value) => `$${(value / 1000).toFixed(0)}k`,
}}
grid={{ show: true, style: 'dotted', color: '#E1E6F9' }}
tooltip={{
show: true,
formatter: (point) => `${point.category}: $${point.value.toLocaleString()}`,
}}
enableCrosshair
liveTooltip
/>
);
}
```
- Feature Adoption By Tier | (id: BarChart.feature-adoption-by-tier)
```tsx
import { BarChart } from '../../';
const FEATURE_ADOPTION = [
{
id: 'enterprise',
category: 'Enterprise',
value: 1280,
color: '#2563eb',
data: { accounts: 1640, adoptionRate: 0.78 },
},
{
id: 'midmarket',
category: 'Mid-market',
value: 930,
color: '#0ea5e9',
data: { accounts: 1310, adoptionRate: 0.71 },
},
{
id: 'growth',
category: 'Growth',
value: 610,
color: '#22c55e',
data: { accounts: 980, adoptionRate: 0.62 },
},
{
id: 'starter',
category: 'Starter',
value: 340,
color: '#f97316',
data: { accounts: 720, adoptionRate: 0.47 },
},
];
const formatAccounts = (value: number) => `${value.toLocaleString()} accounts`;
return (
{
const segmentTotal = datum.data?.accounts ?? value;
const rate = datum.data?.adoptionRate ?? value / segmentTotal;
const percentage = `${Math.round((rate ?? 0) * 100)}% adoption`;
return `${formatAccounts(value)} (${percentage})`;
}}
valueLabel={{
position: 'inside',
color: '#ffffff',
fontSize: 13,
fontWeight: '600',
formatter: (value) => value.toLocaleString(),
}}
yAxis={{
show: true,
title: 'Activated accounts',
titleFontSize: 12,
labelFormatter: (value) => value.toLocaleString(),
}}
xAxis={{ show: true }}
grid={{ show: true, color: '#EFF6FF' }}
tooltip={{
formatter: (datum) => {
const accounts = datum.data?.accounts ?? datum.value;
const adoptionRate = datum.data?.adoptionRate ?? datum.value / accounts;
return [
datum.category,
`Activated: ${formatAccounts(datum.value)}`,
`Account base: ${accounts.toLocaleString()}`,
`Adoption rate: ${(adoptionRate * 100).toFixed(1)}%`,
].join('\n');
},
}}
/>
);
}
```
## Component:
A polymorphic building block component that serves as a foundational element to replace View components throughout the application. Similar to a `` in web development.
### Props
`children` (React.ReactNode) [optional]
Child elements to render inside the block
`component` (React.ElementType) [optional]
The component to render as
`style` (StyleProp
) [optional]
Custom style object
`testID` (string) [optional]
Test ID for testing purposes
`accessibilityLabel` (string) [optional]
Accessibility label
`accessible` (boolean) [optional]
Whether the element is accessible
`accessibilityRole` (string) [optional]
Accessibility role
`className` (string) [optional]
Custom className (for web)
### Demos
- Basic usage | (id: Block.basic) | tags: layout, polymorphic
Combine spacing, layout, and polymorphic props on `Block` to build cards, responsive rows, and button-style actions without custom wrappers.
```tsx
import { Block, Column, Text } from '@platform-blocks/ui';
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
);
}
```
- BlockBasicDemo | (id: Block.BlockBasicDemo)
## Component:
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.
### Props
`children` (React.ReactNode) [required]
Core content
`variant` ('default' | 'testimonial' | 'featured' | 'minimal') [optional]
Styling
`size` (SizeValue) [optional]
`color` (string) [optional]
`quoteIcon` (string | React.ReactNode) [optional]
Quote icon
`quoteIconPosition` ('top-left' | 'top-center' | 'bottom-right' | 'none') [optional]
`quoteIconSize` (SizeValue) [optional]
`author` (BlockquoteAuthor) [optional]
Author attribution
`links` (BlockquoteLinks) [optional]
Social/profile links
`date` (Date | string) [optional]
Metadata
`rating` (BlockquoteRating) [optional]
`source` (BlockquoteSource) [optional]
Brand/source
- … 7 more props documented
### Demos
- Pull quote | (id: Blockquote.basic) | tags: blockquote, testimonial
Frames a simple pull quote with author details.
```tsx
import { Blockquote } from '@platform-blocks/ui';
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
Full-fidelity testimonial with avatar, organization, rating, verified badge, and shadow.
```tsx
import { Blockquote } from '@platform-blocks/ui';
return (
Platform Blocks helped us ship an entirely new settings experience in a single sprint. The components feel native on every platform.
);
}
```
## Component:
The BrandButton component provides a flexible interactive element supporting variants, sizes, icons, loading state, and full-width layout.
### Props
`brand` (BrandPlatform) [required]
The brand/platform to style the button for
`iconPosition` ('left' | 'right') [optional]
Position of the brand icon
`iconVariant` ('full' | 'mono') [optional]
Icon variant: 'full' for multi-color, 'mono' for single-color outline
`icon` (React.ReactNode) [optional]
Override the default brand icon
`title` (string) [required]
Button text
`color` (string) [optional]
Override icon color (overrides brand default colors)
### Demos
- Basic | (id: BrandButton.basic)
---
```tsx
import { BrandButton } from '../..';
import { useToast } from 'platform-blocks/components/Toast';
const toast = useToast()
return toast.warn({
title: 'What the Zuck!',
message: 'This is a Facebook brand button',
position: 'top-center'
})}
/>
}
```
- ColorOverride | (id: BrandButton.colorOverride)
---
```tsx
import { BrandButton, Card, Flex, Text } from '@platform-blocks/ui';
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:
);
}
```
## Component:
Brand icons for popular platforms (e.g. Apple, Google, Facebook, etc.) with built-in dark mode support.
### Props
`brand` (BrandName) [required]
Brand name from the registry
`size` (SizeValue) [optional]
Size of the icon
`color` (string) [optional]
Override all colors with a single color
`variant` ('full' | 'mono') [optional]
Icon variant - 'full' for multi-color, 'mono' for single-color with clipping
`style` (StyleProp) [optional]
Additional styles
`label` (string) [optional]
Accessibility label
`decorative` (boolean) [optional]
Whether the icon is purely decorative (skip a11y)
`invertInDarkMode` (boolean) [optional]
Whether to automatically invert black colors in dark mode
`colorScheme` ('light' | 'dark') [optional]
Force color scheme for testing (overrides automatic detection)
### Demos
- Original Brand Colors | (id: BrandIcon.colors) | tags: colors, branding
Icons displayed in their authentic brand palettes.
```tsx
import { BrandIcon, Card, Block, Row, Text } from '@platform-blocks/ui';
import { FEATURED_BRANDS } from '../data';
return (
{FEATURED_BRANDS.map((brand) => (
{brand}
))}
);
}
```
- Sizes | (id: BrandIcon.sizes) | tags: sizes, layout
Size presets from small through extra large for consistent placement.
```tsx
import { BrandIcon, Card, Block, Row, Text } from '@platform-blocks/ui';
return (
sm
md
lg
xl
);
}
```
## Component:
The Breadcrumbs component displays hierarchical navigation links to help users understand their current location within the application.
### Props
`items` (BreadcrumbItem[]) [required]
Array of breadcrumb items
`separator` (ReactNode) [optional, default: '/']
Custom separator between breadcrumbs (string, icon, or any React component)
`maxItems` (number) [optional]
Maximum number of items to show (will collapse middle items)
`size` (ComponentSizeValue) [optional, default: 'md']
Size of the breadcrumbs
`showIcons` (boolean) [optional, default: true]
Whether to show icons
`style` (StyleProp) [optional]
Custom styles
`textStyle` (StyleProp) [optional]
Custom text styles
`separatorStyle` (StyleProp) [optional]
Custom separator styles
`accessibilityLabel` (string) [optional, default: 'Breadcrumb navigation']
Accessibility label
### Demos
- Hierarchy | (id: Breadcrumbs.basic) | tags: breadcrumbs
Simple breadcrumb trail showing the current page within a product hierarchy.
```tsx
import { Breadcrumbs } from '@platform-blocks/ui';
const ITEMS = [
{ label: 'Home', href: '/' },
{ label: 'Products', href: '/products' },
{ label: 'Electronics', href: '/products/electronics' },
{ label: 'Smartphones' },
];
return ;
}
```
- Separators | (id: Breadcrumbs.separators) | tags: breadcrumbs
Shows how to replace the default slash with characters or React nodes via the `separator` prop.
```tsx
import { Breadcrumbs, Column, Icon } from '@platform-blocks/ui';
const ITEMS = [
{ label: 'Home', href: '/' },
{ label: 'Category', href: '/category' },
{ label: 'Subcategory', href: '/category/subcategory' },
{ label: 'Product' },
];
return (
} />
);
}
```
## Component:
Scatter-style plot where point radius encodes a third quantitative dimension.
### Demos
- Basic | (id: BubbleChart.basic)
```tsx
import { BubbleChart } from '../../';
const companies = [
{ company: 'Aster Labs', revenue: 320, growth: 28, valuation: 920, color: '#5B8FF9' },
{ company: 'Blue Harbor', revenue: 180, growth: 35, valuation: 620, color: '#61DDAA' },
{ company: 'Canopy', revenue: 250, growth: 22, valuation: 710, color: '#65789B' },
{ company: 'Delta Systems', revenue: 140, growth: 44, valuation: 540, color: '#F6BD16' },
{ company: 'Elevate', revenue: 460, growth: 18, valuation: 1080, color: '#7262fd' },
{ company: 'Fieldstone', revenue: 210, growth: 31, valuation: 680, color: '#78D3F8' },
{ company: 'Glowforge', revenue: 120, growth: 52, valuation: 480, color: '#9661BC' },
{ company: 'Horizon', revenue: 390, growth: 24, valuation: 960, color: '#F6903D' },
];
return (
`${Math.round(value)}m`,
}}
yAxis={{
title: 'YoY growth %',
labelFormatter: (value) => `${Math.round(value)}%`,
}}
valueFormatter={(value) => `$${Math.round(value)}m`}
grid={{ show: true, color: '#E4E7F3' }}
withTooltip
range={[64, 1152]}
/>
);
}
```
- Cities Talent Footprint | (id: BubbleChart.cities-talent-footprint)
```tsx
import { BubbleChart } from '../../';
type Region = 'Americas' | 'EMEA' | 'APAC';
type CityProfile = {
city: string;
talentDepth: number;
costOfLivingIndex: number;
officeFootprintKsqft: number;
remoteReady: number;
averageTenure: number;
region: Region;
anchorUniversity: string;
};
const regionPalette: Record = {
Americas: '#2563eb',
EMEA: '#22c55e',
APAC: '#f97316',
};
const cities: CityProfile[] = [
{ city: 'Austin', talentDepth: 78, costOfLivingIndex: 96, officeFootprintKsqft: 185, remoteReady: 68, averageTenure: 3.4, region: 'Americas', anchorUniversity: 'UT Austin' },
{ city: 'Toronto', talentDepth: 82, costOfLivingIndex: 104, officeFootprintKsqft: 150, remoteReady: 72, averageTenure: 3.1, region: 'Americas', anchorUniversity: 'University of Toronto' },
{ city: 'Berlin', talentDepth: 74, costOfLivingIndex: 88, officeFootprintKsqft: 120, remoteReady: 65, averageTenure: 2.9, region: 'EMEA', anchorUniversity: 'TU Berlin' },
{ city: 'Amsterdam', talentDepth: 76, costOfLivingIndex: 110, officeFootprintKsqft: 135, remoteReady: 70, averageTenure: 3.2, region: 'EMEA', anchorUniversity: 'University of Amsterdam' },
{ city: 'Singapore', talentDepth: 90, costOfLivingIndex: 134, officeFootprintKsqft: 210, remoteReady: 61, averageTenure: 3.8, region: 'APAC', anchorUniversity: 'NUS' },
{ city: 'Sydney', talentDepth: 69, costOfLivingIndex: 118, officeFootprintKsqft: 142, remoteReady: 64, averageTenure: 3.0, region: 'APAC', anchorUniversity: 'UNSW' },
{ city: 'Mexico City', talentDepth: 71, costOfLivingIndex: 74, officeFootprintKsqft: 95, remoteReady: 58, averageTenure: 2.6, region: 'Americas', anchorUniversity: 'UNAM' },
{ city: 'Warsaw', talentDepth: 67, costOfLivingIndex: 72, officeFootprintKsqft: 108, remoteReady: 60, averageTenure: 2.7, region: 'EMEA', anchorUniversity: 'Warsaw University of Technology' },
];
const formatFootprint = (value: number) => `${value.toFixed(0)}k sq ft`;
return (
(value && regionPalette[value as Region]) || regionPalette.Americas}
grid={{ show: true, color: '#E5E7EB' }}
xAxis={{
title: 'Cost of living index (base 100 = SF)',
labelFormatter: (value) => value.toFixed(0),
}}
yAxis={{
title: 'Tech talent depth (0–100 readiness)',
labelFormatter: (value) => value.toFixed(0),
}}
valueFormatter={(value) => formatFootprint(value)}
tooltip={{
formatter: ({ record, value }) => [
`Office footprint: ${formatFootprint(value)}`,
`Remote ready: ${record.remoteReady}% • Avg tenure: ${record.averageTenure.toFixed(1)} yrs`,
`Anchor university: ${record.anchorUniversity}`,
].join('\n'),
}}
range={[81, 1764]}
legend={{ show: true, position: 'right', align: 'start' }}
/>
);
}
```
## Component:
The Button component provides a flexible interactive element supporting variants, sizes, icons, loading state, and full-width layout.
### Props
`key` (React.Key) [optional]
allow React key without complaint in TS where JSX key is forwarded in type checking
`title` (string) [optional]
Button text content - can be provided via title prop or children
`children` (React.ReactNode) [optional]
Button text content - alternative to title prop
`onPress` (() => void) [optional]
Called when the button is pressed
`onPressIn` (() => void) [optional]
Called when the button press starts (for immediate feedback)
`onPressOut` (() => void) [optional]
Called when the button press ends
`onHoverIn` (() => void) [optional]
Called when the button is hovered (web/desktop only)
`onHoverOut` (() => void) [optional]
Called when the button is no longer hovered (web/desktop only)
`onLongPress` (() => void) [optional]
Called when the button is long-pressed
`onLayout` ((event: any) => void) [optional]
Called when the button layout is calculated
`variant` ('filled' | 'secondary' | 'outline' | 'ghost' | 'gradient' | 'link' | 'none') [optional, default: 'primary']
Button visual variant
`size` (SizeValue) [optional, default: 'md']
Button size
- … 15 more props documented
### Demos
- Basics | (id: Button.basic) | tags: buttons
Invokes a primary action and surfaces feedback through a toast helper.
```tsx
import { Button, Column, useToast } from '@platform-blocks/ui';
const toast = useToast();
return (
toast.success('Launch command sent')}>Launch mission
toast.info('Sequence aborted')}>Abort
);
}
```
- Colors | (id: Button.colors) | tags: buttons, colors
Cycle through semantic `colorVariant` values to align actions with brand intent.
```tsx
import { Button, Column, Row } from '@platform-blocks/ui';
return (
Primary
Secondary
Success
Warning
Error
);
}
```
## Component:
A versatile calendar component for selecting dates, months, and years with customizable styles and behaviors.
### Props
`level` (CalendarLevel) [optional]
View control
`defaultLevel` (CalendarLevel) [optional, default: 'month']
`onLevelChange` ((level: CalendarLevel) => void) [optional]
`date` (Date) [optional]
Date management
`defaultDate` (Date) [optional]
`onDateChange` ((date: Date) => void) [optional]
`value` (CalendarValue) [optional]
Value handling (for selection)
`onChange` ((value: CalendarValue) => void) [optional]
`type` (CalendarType) [optional, default: 'single']
`minDate` (Date) [optional]
Constraints
`maxDate` (Date) [optional]
`excludeDate` ((date: Date) => boolean) [optional]
- … 12 more props documented
### Demos
- Basic selection | (id: Calendar.basic) | tags: single, selection
Bind `value` and `onChange` to local state to capture the selected day while `highlightToday` keeps the current date visually distinct.
```tsx
import { useState } from 'react';
import { Calendar, Column, Text } from '@platform-blocks/ui';
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
Set `minDate` and `maxDate` to keep navigation inside the current month while still allowing the calendar to show surrounding weeks.
```tsx
import { useMemo, useState } from 'react';
import { Calendar, Column, Text } from '@platform-blocks/ui';
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.
);
}
```
## Component:
Visualizes financial OHLC price movements over time.
### Props
`series` (CandlestickSeries[]) [required]
One or more candlestick series to render
`barWidth` (number) [optional]
Width of each candle body in pixels
`gap` (number) [optional]
Fractional gap between candles (0-1)
`movingAveragePeriods` (number[]) [optional, default: []]
Periods for moving average overlay lines (e.g. [20,50])
`movingAverageColors` (string[]) [optional, default: []]
Colors for moving average overlays (falls back to series palette)
`showMovingAverages` (boolean) [optional, default: true]
Show moving average overlays (defaults true if periods provided)
`showVolume` (boolean) [optional, default: false]
Show volume bars underneath (reserved)
`volumeHeightRatio` (number) [optional, default: 0.22]
Relative height ratio for volume sub-chart (0-0.5)
`xAxis` (ChartAxis) [optional]
Configuration for the horizontal axis
`yAxis` (ChartAxis) [optional]
Configuration for the vertical axis
`grid` (ChartGrid) [optional]
Background grid configuration
`legend` (ChartLegend) [optional]
Legend display options
- … 17 more props documented
### Demos
- Basic | (id: CandlestickChart.basic)
```tsx
import { CandlestickChart } from '../../';
const PRICE_SERIES = [
{ x: new Date('2023-10-02'), open: 154, high: 158, low: 153, close: 157 },
{ x: new Date('2023-10-03'), open: 157, high: 161, low: 156, close: 160 },
{ x: new Date('2023-10-04'), open: 160, high: 164, low: 159, close: 162 },
{ x: new Date('2023-10-05'), open: 162, high: 166, low: 161, close: 165 },
{ x: new Date('2023-10-06'), open: 165, high: 168, low: 163, close: 164 },
{ x: new Date('2023-10-09'), open: 164, high: 167, low: 162, close: 166 },
{ x: new Date('2023-10-10'), open: 166, high: 170, low: 165, close: 169 },
{ x: new Date('2023-10-11'), open: 169, high: 172, low: 167, close: 168 },
];
return (
new Date(value).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
}),
title: 'Trading day',
}}
yAxis={{
show: true,
title: 'Price (USD)',
labelFormatter: (value) => `$${value.toFixed(0)}`,
}}
grid={{ show: true, color: '#E5EAF7' }}
legend={{ show: true }}
tooltip={{
show: true,
formatter: (candle) =>
`O: $${candle.open.toFixed(2)} • H: $${candle.high.toFixed(2)} • L: $${candle.low.toFixed(2)} • C: $${candle.close.toFixed(2)}`,
}}
enableCrosshair
liveTooltip
animation={{ duration: 400 }}
enablePanZoom
zoomMode="both"
minZoom={0.2}
resetOnDoubleTap
clampToInitialDomain
xScaleType="time"
/>
);
}
```
- Battery Material Pricing | (id: CandlestickChart.battery-material-pricing)
```tsx
import { CandlestickChart } from '../../';
const LITHIUM_SERIES = [
{ x: new Date('2024-09-02'), open: 36200, high: 37180, low: 35420, close: 36840 },
{ x: new Date('2024-09-09'), open: 36840, high: 37510, low: 36080, close: 37200 },
{ x: new Date('2024-09-16'), open: 37200, high: 37840, low: 36520, close: 36610 },
{ x: new Date('2024-09-23'), open: 36610, high: 36950, low: 35840, close: 36020 },
{ x: new Date('2024-09-30'), open: 36020, high: 36480, low: 35260, close: 35510 },
{ x: new Date('2024-10-07'), open: 35510, high: 36040, low: 34800, close: 35160 },
{ x: new Date('2024-10-14'), open: 35160, high: 35590, low: 34420, close: 34780 },
{ x: new Date('2024-10-21'), open: 34780, high: 35200, low: 34060, close: 34920 },
];
const NICKEL_SERIES = [
{ x: new Date('2024-09-02'), open: 18940, high: 19480, low: 18620, close: 19260 },
{ x: new Date('2024-09-09'), open: 19260, high: 19850, low: 19010, close: 19530 },
{ x: new Date('2024-09-16'), open: 19530, high: 20040, low: 19300, close: 19780 },
{ x: new Date('2024-09-23'), open: 19780, high: 20360, low: 19420, close: 19890 },
{ x: new Date('2024-09-30'), open: 19890, high: 20540, low: 19610, close: 20330 },
{ x: new Date('2024-10-07'), open: 20330, high: 20810, low: 20060, close: 20210 },
{ x: new Date('2024-10-14'), open: 20210, high: 20640, low: 19880, close: 20140 },
{ x: new Date('2024-10-21'), open: 20140, high: 20520, low: 19710, close: 19940 },
];
const negotiationMarkers = [
{
id: 'kickoff',
shape: 'vertical-line' as const,
x: new Date('2024-09-16').getTime(),
color: '#f97316',
opacity: 0.55,
},
{
id: 'conclusion',
shape: 'vertical-line' as const,
x: new Date('2024-10-14').getTime(),
color: '#0ea5e9',
opacity: 0.55,
},
];
const formatWeek = (value: number) => new Date(value).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
});
return (
`Open $${candle.open.toLocaleString('en-US')} • Close $${candle.close.toLocaleString('en-US')} \nRange $${candle.low.toLocaleString('en-US')} – $${candle.high.toLocaleString('en-US')}`,
}}
xAxis={{
show: true,
title: 'Week of shipment',
labelFormatter: formatWeek,
}}
yAxis={{
show: true,
title: 'Spot price (USD per metric ton)',
labelFormatter: (value) => `$${value.toLocaleString('en-US')}`,
}}
enableCrosshair
liveTooltip
multiTooltip
xScaleType="time"
/>
);
}
```
## Component:
The Card component provides a flexible container for displaying content with consistent styling, padding, and visual hierarchy.
### Props
`children` (React.ReactNode) [optional]
children optional to reduce noisy TS errors during composition
`variant` ('outline' | 'filled' | 'elevated' | 'subtle' | 'ghost' | 'gradient') [optional]
`padding` (number) [optional]
`style` (any) [optional]
`onPress` (() => void) [optional]
Interactive props
`disabled` (boolean) [optional]
`onContextMenu` ((e: any) => void) [optional]
Web-only events passthrough
### Demos
- Basics | (id: Card.basic) | tags: content
Compose card content with spacing primitives and a primary action for quick scenarios.
```tsx
import { Card, Column, Text, Button } from '@platform-blocks/ui';
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
Tour the available card `variant` treatments to pick the right surface style for your layout.
```tsx
import { Card, Column, Text } from '@platform-blocks/ui';
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.
))}
);
}
```
## Component:
The Carousel component displays a series of content in a horizontal scrollable view with optional navigation dots and controls.
### Props
`children` (React.ReactNode[]) [required]
Array of carousel slide elements
`orientation` ('horizontal' | 'vertical') [optional]
Orientation of the carousel
`showArrows` (boolean) [optional]
Show navigation arrow buttons
`showDots` (boolean) [optional]
Show navigation dots
`autoPlay` (boolean) [optional]
Enable autoplay
`autoPlayInterval` (number) [optional]
Autoplay interval in ms
`autoPlayPauseOnTouch` (boolean) [optional]
Pause autoplay on user interaction
`loop` (boolean) [optional]
Enable looping
`itemsPerPage` (number) [optional]
Number of visible items per page
`slidesToScroll` (number) [optional]
Number of slides to advance per snap (defaults to itemsPerPage for backwards compatibility)
`align` ('start' | 'center' | 'end') [optional]
Align the visible slides within the viewport when there is extra space
`containScroll` (false | 'trimSnaps' | 'keepSnaps') [optional]
Contain leading/trailing space by trimming or keeping snap points
- … 20 more props documented
### Demos
- Basic usage | (id: Carousel.basic) | tags: auto-play, loop
Enable `autoPlay` and `loop` on `Carousel` to rotate a small set of slides without custom pagination controls.
```tsx
import { Block, Carousel, Text } from '@platform-blocks/ui';
return (
Weekly highlights
Rotate through featured stories without building pagination controls.
Product updates
Enable `autoPlay` and `loop` to keep the carousel moving hands-free.
Team spotlights
Add a few slides to share wins, announcements, or campaign promos.
);
}
```
- Vertical orientation | (id: Carousel.vertical) | tags: vertical, navigation
Set `orientation="vertical"` to rotate content along the Y-axis while keeping arrow and dot controls aligned for keyboard and touch users.
```tsx
import { Block, Carousel, Text } from '@platform-blocks/ui';
return (
Breaking news
`orientation="vertical"` stacks stories where horizontal space is tight.
Weather alerts
Pair arrows and dots so keyboard and touch users find the controls.
Schedule changes
Slides rotate every few seconds but still allow manual scrolling.
);
}
```
## Component:
The Checkbox component allows users to select one or more options from a set. Supports different states, colors, and group functionality.
### Props
`checked` (boolean) [optional]
Whether checkbox is checked
`defaultChecked` (boolean) [optional, default: false]
Initial checked value for uncontrolled usage
`onChange` ((checked: boolean) => void) [optional]
Change handler
`indeterminate` (boolean) [optional, default: false]
Indeterminate state for partial selections
`color` (string) [optional]
Custom color override
`colorVariant` ('primary' | 'secondary' | 'success' | 'error' | 'warning') [optional, default: 'primary']
Named color variant from theme
`size` (SizeValue) [optional, default: 'md']
Checkbox size
`label` (React.ReactNode) [optional]
Checkbox label
`disabled` (boolean) [optional, default: false]
Whether checkbox is disabled
`required` (boolean) [optional, default: false]
Whether checkbox is required
`error` (string) [optional]
Error message
`description` (string) [optional]
Helper text
- … 4 more props documented
### Demos
- Basics | (id: Checkbox.basic) | tags: checkboxes
Controlled checkbox example with a helper message that reacts to user selection.
```tsx
import { useState } from 'react';
import { Checkbox, Column, Text } from '@platform-blocks/ui';
const [checked, setChecked] = useState(false);
return (
{checked ? 'Thanks! You can proceed to the next step.' : 'Check the box to continue.'}
);
}
```
- Sizes | (id: Checkbox.sizes) | tags: checkboxes, sizes
Explore available checkbox sizes with guidance on where each fits best.
```tsx
import { useState } from 'react';
import { Checkbox, Column, Text } from '@platform-blocks/ui';
const SIZES = [
{ key: 'xs', label: 'Extra small', description: 'Dense tables and compact lists.' },
{ key: 'sm', label: 'Small', description: 'Standard data tables.' },
{ key: 'md', label: 'Medium', description: 'Default for forms and settings.' },
{ key: 'lg', label: 'Large', description: 'Touch-heavy or marketing layouts.' }
] as const;
const [values, setValues] = useState>({ xs: false, sm: true, md: true, lg: false });
const toggle = (size: string) => {
setValues((current) => ({
...current,
[size]: !current[size]
}));
};
return (
Size tokens
{SIZES.map(({ key, label, description }) => (
toggle(key)}
/>
{description}
))}
);
}
```
## Component:
The Chip component displays compact elements that represent an input, attribute, or action. Supports different colors, sizes, and interactive features like removal.
### Props
`children` (React.ReactNode) [required]
`size` (SizeValue) [optional, default: 'md']
`variant` ('filled' | 'outline' | 'light' | 'subtle' | 'gradient') [optional, default: 'filled']
`color` ('primary' | 'secondary' | 'success' | 'warning' | 'error' | 'gray' | string) [optional, default: 'primary']
`onPress` (() => void) [optional]
`startIcon` (React.ReactNode) [optional]
`endIcon` (React.ReactNode) [optional]
`onRemove` (() => void) [optional]
`removePosition` ('left' | 'right') [optional, default: 'right']
`disabled` (boolean) [optional, default: false]
`style` (StyleProp) [optional]
`textStyle` (StyleProp) [optional]
- … 2 more props documented
### Demos
- Core styles | (id: Chip.basic) | tags: variants, getting-started
Toggle the `variant` prop between the default, `filled`, `outline`, `light`, and `subtle` styles when you need different emphasis levels for the same Chip content.
```tsx
import { Chip, Row } from '@platform-blocks/ui'
return (
Default
Filled
Outline
Light
Subtle
)
}
```
- Semantic colors | (id: Chip.colors) | tags: colors, theming
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
import { Chip, Row } from '@platform-blocks/ui'
return (
Primary
Success
Warning
Error
Gray
)
}
```
## Component:
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…
### Props
`language` (string) [optional, default: 'tsx']
Optional language for syntax highlighting
`children` (string) [required]
`title` (string) [optional]
Optional title displayed above the code block
`fileName` (string) [optional]
Optional filename displayed next to the title
`fileIcon` (React.ReactNode) [optional]
Optional icon displayed next to the filename
`showLineNumbers` (boolean) [optional, default: false]
Show line numbers in the code block
`highlight` (boolean) [optional, default: true]
Enable syntax highlighting
`fullWidth` (boolean) [optional, default: true]
Make the code block take the full width of its container
`showCopyButton` (boolean) [optional, default: true]
Show a copy button to copy the code to clipboard
`onCopy` ((code: string) => void) [optional]
Callback when code is copied
`style` (StyleProp) [optional]
Custom styles for the code block container and text
`textStyle` (StyleProp) [optional]
Custom styles for the code text
- … 10 more props documented
### Demos
- Basic | (id: CodeBlock.basic) | tags: basic, code, snippet
Default CodeBlock showing a single snippet with automatic language detection and copy controls.
```tsx
import { CodeBlock, Column, Text } from '@platform-blocks/ui';
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
Custom onCopy handling with inline feedback and a manual trigger button.
```tsx
import { useEffect, useRef, useState } from 'react';
import { Button, CodeBlock, Column, Text } from '@platform-blocks/ui';
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)} />
);
}
```
## Component:
Displays content that can be revealed or hidden with an animation.
### Props
`isCollapsed` (boolean) [required]
Whether the content is revealed/expanded
`children` (ReactNode) [required]
Content to show/hide
`duration` (number) [optional, default: 300]
Animation duration in milliseconds
`timing` ('linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out') [optional, default: 'ease-out']
Animation timing function
`style` (StyleProp) [optional]
Style for the container
`contentStyle` (StyleProp) [optional]
Style for the content wrapper
`onAnimationStart` (() => void) [optional]
Callback fired when animation starts
`onAnimationEnd` (() => void) [optional]
Callback fired when animation completes
`easing` ((value: number) => number) [optional]
Custom easing function overriding the timing preset
`animateOnMount` (boolean) [optional, default: false]
Whether to animate on initial mount
`collapsedHeight` (number) [optional, default: 0]
Custom height when collapsed (useful for partial reveals)
`fadeContent` (boolean) [optional, default: true]
Whether to fade content in/out along with height animation
### Demos
- Basic | (id: Collapse.basic) | tags: basic, collapse
Basic usage of the Collapse component to show and hide content with animation.
```tsx
import { Block, Button, Collapse, Text } from '@platform-blocks/ui';
import { useState } from 'react';
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.
);
}
```
## Component:
The ColorPicker component provides an intuitive interface for selecting colors through various input methods including color wheel, hex input, and preset swatches.
### Props
`value` (string) [optional]
Current color value in hex format (e.g., "#ff0000")
`defaultValue` (string) [optional, default: '']
Default color value for uncontrolled usage
`onChange` ((color: string) => void) [optional]
Callback when color changes
`label` (string) [optional]
Label for the color picker
`placeholder` (string) [optional, default: 'Select color']
Placeholder text when no color is selected
`disabled` (boolean) [optional, default: false]
Whether the picker is disabled
`required` (boolean) [optional, default: false]
Whether the picker is required
`error` (string) [optional]
Error message to display
`description` (string) [optional]
Help text to display below the picker
`size` (ComponentSizeValue) [optional, default: 'md']
Size of the picker
`variant` ('default' | 'filled' | 'unstyled') [optional, default: 'default']
Variant of the picker
`radius` ('none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl') [optional, default: 'md']
Radius of the picker
- … 20 more props documented
### Demos
- Basic Usage | (id: ColorPicker.basic)
Simple color picker with default settings.
```tsx
import { useState } from 'react';
import { ColorPicker, Column, Text } from '@platform-blocks/ui';
const [color, setColor] = useState('#FF6B6B');
return (
Selected: {color || 'none'}
);
}
```
- Custom Swatches | (id: ColorPicker.swatches)
Color picker with custom swatch palettes.
```tsx
import { useState } from 'react';
import { ColorPicker, Column, Text } from '@platform-blocks/ui';
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
);
}
```
## Component:
A simple square component for displaying colors, designed as a building block for color pickers and palette interfaces.
### Props
`color` (string) [required]
The color value to display (hex, rgb, hsl, etc.)
`size` (number) [optional, default: 32]
Size of the swatch in pixels
`selected` (boolean) [optional, default: false]
Whether the swatch is selected/active
`disabled` (boolean) [optional, default: false]
Whether the swatch is disabled
`onPress` (() => void) [optional]
Callback when swatch is pressed
`showBorder` (boolean) [optional, default: true]
Show a border around the swatch
`borderColor` (string) [optional]
Custom border color (defaults to theme color)
`borderWidth` (number) [optional, default: 1]
Border width in pixels
`borderRadius` (number) [optional, default: 4]
Border radius in pixels
`showCheckmark` (boolean) [optional, default: true]
Show a checkmark when selected
`checkmarkColor` (string) [optional]
Custom checkmark color
`accessibilityLabel` (string) [optional]
Accessibility label
- … 1 more props documented
### Demos
- Advanced | (id: ColorSwatch.advanced)
```tsx
import React, { useState } from 'react';
import { Flex, Text, ColorSwatch } from '@platform-blocks/ui';
const [selectedColor, setSelectedColor] = useState('#E74C3C');
const colors = [
'#E74C3C', '#3498DB', '#2ECC71', '#F39C12', '#9B59B6',
'#1ABC9C', '#34495E', '#95A5A6', '#E67E22', '#16A085'
];
return (
Advanced Color Swatches
Custom styling options:
{/* Rounded swatches */}
setSelectedColor('#E74C3C')}
/>
Rounded
{/* Square with thick border */}
setSelectedColor('#3498DB')}
/>
Square
{/* Custom border color */}
setSelectedColor('#2ECC71')}
/>
Gold border
{/* No checkmark */}
setSelectedColor('#F39C12')}
/>
No checkmark
{/* Custom checkmark color */}
setSelectedColor('#9B59B6')}
/>
Gold check
Color palette grid:
{colors.map(color => (
setSelectedColor(color)}
/>
))}
Selected: {selectedColor}
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)
```tsx
import React, { useState } from 'react';
import { Flex, Text, ColorSwatch } from '@platform-blocks/ui';
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 => (
))}
);
}
```
## Component:
Combines multiple chart types (e.g., bars and line) for richer comparison.
### Props
`layers` (ComboChartLayer[]) [required]
Ordered set of layers to render in the combo chart
`enableCrosshair` (boolean) [optional, default: true]
Enable crosshair indicator across layers
`multiTooltip` (boolean) [optional, default: true]
Enable multi-series tooltip aggregation
`liveTooltip` (boolean) [optional, default: true]
Follow pointer live with tooltip
`xDomain` ([number, number]) [optional]
Explicit override for the shared x-domain
`yDomain` ([number, number]) [optional]
Explicit override for the primary y-domain
`yDomainRight` ([number, number]) [optional]
Explicit override for the secondary y-domain
`xAxis` (ChartAxis) [optional]
Axis configuration for the shared x-axis
`yAxis` (ChartAxis) [optional]
Axis configuration for the primary y-axis
`yAxisRight` (ChartAxis) [optional]
Axis configuration for the secondary y-axis
`grid` (ChartGrid) [optional]
Grid line configuration
`legend` (ChartLegend) [optional]
Legend display options
### Demos
- Basic | (id: ComboChart.basic)
```tsx
import { ComboChart } from '../../';
const LAYERS = [
{
type: 'bar' as const,
id: 'revenue',
name: 'Monthly revenue',
data: [
{ x: 1, y: 420 },
{ x: 2, y: 455 },
{ x: 3, y: 508 },
{ x: 4, y: 480 },
{ x: 5, y: 532 },
{ x: 6, y: 575 },
],
color: '#4C6EF5',
opacity: 0.85,
},
{
type: 'line' as const,
id: 'active-users',
name: 'Active users',
targetAxis: 'right' as const,
data: [
{ x: 1, y: 110 },
{ x: 2, y: 134 },
{ x: 3, y: 149 },
{ x: 4, y: 158 },
{ x: 5, y: 166 },
{ x: 6, y: 172 },
],
color: '#12B880',
thickness: 3,
showPoints: true,
pointSize: 6,
},
];
return (
`M${value}`,
}}
yAxis={{
show: true,
title: 'Revenue (USD thousands)',
labelFormatter: (value) => `$${value}`,
}}
yAxisRight={{
show: true,
title: 'Active users (thousands)',
labelFormatter: (value) => `${value}k`,
}}
yDomain={[0, 650]}
yDomainRight={[80, 200]}
grid={{ show: true, color: '#E3E7F4', style: 'dashed' }}
legend={{ show: true, position: 'bottom' }}
/>
);
}
```
## Component:
# CopyButton
### Props
`value` (string) [required]
The text to copy to clipboard
`onCopy` ((value: string) => void) [optional]
Callback fired after copy action
`iconOnly` (boolean) [optional]
If true, only the icon is shown (no button chrome or label)
`label` (string) [optional]
Accessible label for the button
`toastTitle` (string) [optional]
Title for the toast
`toastMessage` (string) [optional]
Detailed message for the toast
`size` (ComponentSizeValue) [optional]
Visual size token
`style` (StyleProp) [optional]
Style overrides for the button container
`disableToast` (boolean) [optional]
Disable the "copied to clipboard" toast
`tooltip` (string) [optional]
Tooltip text
`tooltipPosition` ('top' | 'bottom' | 'left' | 'right') [optional]
Tooltip position
`mode` ('button' | 'icon') [optional]
Presentation mode: default button (legacy) or bare icon
- … 5 more props documented
### Demos
- Basic | (id: CopyButton.basic)
Icon-only copy button with default toast feedback.
```tsx
import { CopyButton, Card, Flex, Text } from '@platform-blocks/ui';
return (
Invite Code:
ABCD-1234
);
}
```
- Labeled | (id: CopyButton.labeled)
Copy control showing label text instead of icon-only presentation.
```tsx
import { CopyButton, Card, Flex, Text } from '@platform-blocks/ui';
return (
API Key
sk_live_1a2b3c4d5e6f7g8h9i10
);
}
```
## Component:
The DataTable component provides a feature-rich interface for displaying tabular data with sorting, pagination, row selection, and customizable columns.
### Props
`id` (string) [optional]
Stable id for user preference persistence
`data` (T[]) [required]
Data rows
`columns` (DataTableColumn[]) [required]
Column definitions
`loading` (boolean) [optional]
Loading state
`error` (string | null) [optional]
Error message (when defined overrides table body)
`emptyMessage` (string) [optional]
Message to display when there is no data
`searchable` (boolean) [optional]
Enable global search input
`searchPlaceholder` (string) [optional]
Placeholder text for global search
`searchValue` (string) [optional]
Controlled global search value
`onSearchChange` ((value: string) => void) [optional]
Global search change handler
`sortBy` (DataTableSort[]) [optional]
Current sorting state
`onSortChange` ((sort: DataTableSort[]) => void) [optional]
Sorting change callback
- … 66 more props documented
### Demos
- Getting Started | (id: DataTable.basic) | tags: datatable
Define columns, feed the dataset, and let `DataTable` handle search, sorting, and pagination out of the box.
```tsx
import { useState } from 'react';
import { Column, DataTable, Text } from '@platform-blocks/ui';
import type { DataTableColumn, DataTablePagination, DataTableSort } from '@platform-blocks/ui';
type Employee = {
id: number;
name: string;
email: string;
role: string;
status: 'active' | 'inactive' | 'pending';
department: string;
};
const rows: Employee[] = [
{ id: 1, name: 'Aria Chen', email: 'aria@example.com', role: 'Product Manager', status: 'active', department: 'Product' },
{ id: 2, name: 'Ben Ortiz', email: 'ben@example.com', role: 'Engineer', status: 'active', department: 'Engineering' },
{ id: 3, name: 'Carla Singh', email: 'carla@example.com', role: 'Designer', status: 'inactive', department: 'Design' },
{ id: 4, name: 'Diego Price', email: 'diego@example.com', role: 'Support', status: 'pending', department: 'Operations' },
{ id: 5, name: 'Elena Ruiz', email: 'elena@example.com', role: 'Engineer', status: 'active', department: 'Engineering' },
];
const columns: DataTableColumn[] = [
{
key: 'name',
header: 'Name',
accessor: 'name',
sortable: true,
cell: (value) => {value} ,
},
{
key: 'email',
header: 'Email',
accessor: 'email',
sortable: true,
},
{
key: 'role',
header: 'Role',
accessor: 'role',
sortable: true,
},
{
key: 'status',
header: 'Status',
accessor: 'status',
sortable: true,
cell: (value: Employee['status']) => (
{value.toUpperCase()}
),
},
{
key: 'department',
header: 'Department',
accessor: 'department',
sortable: true,
},
];
const [sortBy, setSortBy] = useState([]);
const [pagination, setPagination] = useState({
page: 1,
pageSize: 4,
total: rows.length,
});
return (
Sortable columns with built-in pagination and search
);
}
```
- Column Filters | (id: DataTable.advanced-filtering) | tags: datatable, filters
Toggle `filterable` on each column to surface context-aware controls and provide quick clearing for active filters.
```tsx
import { useMemo, useState } from 'react';
import { Button, Column, DataTable, Row, Text } from '@platform-blocks/ui';
import type { DataTableColumn, DataTableFilter, DataTableSort } from '@platform-blocks/ui';
type Employee = {
id: number;
name: string;
department: 'Engineering' | 'Design' | 'Marketing' | 'Sales';
status: 'active' | 'inactive';
salary: number;
};
const rows: Employee[] = [
{ id: 1, name: 'Avery Knight', department: 'Engineering', status: 'active', salary: 125_000 },
{ id: 2, name: 'Bianca Hall', department: 'Design', status: 'active', salary: 98_500 },
{ id: 3, name: 'Caleb Fox', department: 'Marketing', status: 'inactive', salary: 72_400 },
{ id: 4, name: 'Dana Moss', department: 'Sales', status: 'active', salary: 88_100 },
{ id: 5, name: 'Enzo Reed', department: 'Engineering', status: 'inactive', salary: 112_900 },
{ id: 6, name: 'Farah Li', department: 'Sales', status: 'active', salary: 94_300 },
];
const columns: DataTableColumn[] = [
{ key: 'name', header: 'Name', accessor: 'name', sortable: true, filterable: true, filterType: 'text' },
{
key: 'department',
header: 'Department',
accessor: 'department',
sortable: true,
filterable: true,
filterType: 'select',
filterOptions: [
{ label: 'Engineering', value: 'Engineering' },
{ label: 'Design', value: 'Design' },
{ label: 'Marketing', value: 'Marketing' },
{ label: 'Sales', value: 'Sales' },
],
},
{
key: 'status',
header: 'Status',
accessor: 'status',
sortable: true,
filterable: true,
filterType: 'select',
filterOptions: [
{ label: 'Active', value: 'active' },
{ label: 'Inactive', value: 'inactive' },
],
},
{
key: 'salary',
header: 'Salary',
accessor: 'salary',
sortable: true,
filterable: true,
filterType: 'number',
dataType: 'currency',
align: 'right',
},
];
const [sortBy, setSortBy] = useState([]);
const [filters, setFilters] = useState([]);
const activeFilters = useMemo(() => filters.length, [filters]);
return (
Enable column-level filters for text, select, or numeric operators
{activeFilters ? `${activeFilters} filter${activeFilters > 1 ? 's' : ''} applied` : 'No active filters'}
setFilters([])} disabled={!activeFilters}>
Clear filters
);
}
```
## Component:
---
### Props
`value` (CalendarValue) [optional]
Selected value; type depends on `type` prop
`defaultValue` (CalendarValue) [optional]
Initial value for uncontrolled usage
`onChange` ((value: CalendarValue) => void) [optional]
Called when value changes
`type` (CalendarType) [optional]
Selection behavior
`calendarProps` (Partial) [optional]
Pass-through customization for underlying Calendar
`style` (StyleProp) [optional]
Optional container style for the inline calendar
`testID` (string) [optional]
Test identifier
`accessibilityLabel` (string) [optional]
Accessibility label for the inline calendar region
`accessibilityHint` (string) [optional]
Accessibility hint for the inline calendar region
### Demos
- Basic Date Picker | (id: DatePicker.basic) | tags: basic, single-date, label, placeholder
Standard single date selection with label and placeholder text.
```tsx
import React, { useState } from 'react';
import { Column, DatePicker, Text } from '@platform-blocks/ui';
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
Select a range of dates with start and end date selection.
```tsx
import React, { useState } from 'react';
import { Column, DatePicker, Text } from '@platform-blocks/ui';
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'}
);
}
```
## Component:
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.
### Props
`value` (CalendarValue) [optional]
Selected value; type depends on `type` prop
`defaultValue` (CalendarValue) [optional]
Initial value for uncontrolled usage
`onChange` ((value: CalendarValue) => void) [optional]
Called when value changes
`type` (CalendarType) [optional]
Selection behavior
`calendarProps` (Partial) [optional]
Pass-through customization for underlying Calendar
`placeholder` (string) [optional]
Input placeholder text
`displayFormat` (string) [optional]
Format string for displaying value in the input
`valueFormat` (string) [optional]
Serialization/parsing format (reserved for future)
`clearable` (boolean) [optional]
Show a clear button
`size` (SizeValue) [optional]
Visual size
`disabled` (boolean) [optional]
Disable interaction
`withAsterisk` (boolean) [optional]
Show required indicator
- … 7 more props documented
### Demos
- Basic | (id: DatePickerInput.basic)
```tsx
import React, { useState } from 'react';
import { Column, DatePickerInput, Text } from '@platform-blocks/ui';
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)
```tsx
import React, { useState } from 'react';
import { Column, DatePickerInput, Text } from '@platform-blocks/ui';
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'}
);
}
```
## Component:
The Dialog component presents content above the app, supporting focus trapping, scroll locking, and multiple presentation styles (modal, confirmation, bottom sheet).
### Props
`visible` (boolean) [required]
Controls whether the dialog is visible.
`variant` ('modal' | 'bottomsheet' | 'fullscreen') [optional]
Presentation style of the dialog.
`title` (string | null) [optional]
Optional title text shown in the header area.
`children` (ReactNode) [required]
Dialog body content.
`closable` (boolean) [optional]
Allows the user to close the dialog via UI controls or escape/back.
`backdrop` (boolean) [optional]
Whether to render the dimming backdrop behind the dialog.
`backdropClosable` (boolean) [optional]
Whether tapping the backdrop should close the dialog.
`shouldClose` (boolean) [optional]
Triggers close animation when set to true.
`onClose` (() => void) [optional]
Called when the dialog requests to close.
`w` (number) [optional]
Optional explicit width for the dialog content (modal/bottomsheet).
`h` (number) [optional]
Optional explicit height for the dialog content.
`radius` (number) [optional]
Corner radius for the dialog container (bottom sheet rounds top corners only).
- … 3 more props documented
### Demos
- Basic Modal | (id: Dialog.basic) | tags: dialog, modal, actions
Call `openDialog` with `variant: 'modal'` to show a titled dialog and wire action buttons to `closeDialog` when the user makes a choice.
```tsx
import { Alert } from 'react-native';
import { Button, Column, Row, Text, useDialog } from '@platform-blocks/ui';
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
{
Alert.alert('Action', 'OK button pressed!');
closeDialog(dialogId);
}}
>
OK
)
});
};
return (
Open Basic Dialog
);
}
```
- Bottom Sheet | (id: Dialog.bottomsheet) | tags: dialog, bottomsheet, gestures
Switch the dialog `variant` to `'bottomsheet'` to get swipe-to-dismiss behavior and retain full control with `closeDialog` handlers.
```tsx
import { Button, Column, Input, Text, useDialog } from '@platform-blocks/ui';
const { openDialog, closeDialog } = useDialog();
const showBottomSheetDialog = () => {
const dialogId = openDialog({
variant: 'bottomsheet',
title: 'Bottom Sheet with Gestures',
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
);
}
```
## Component:
The Divider component provides a visual separator between content sections. Supports horizontal and vertical orientations, with optional labels.
### Props
`orientation` (DividerOrientation) [optional, default: 'horizontal']
`variant` (DividerVariant) [optional, default: 'solid']
`color` (string) [optional]
`colorVariant` ('primary' | 'secondary' | 'tertiary' | 'surface' | 'success' | 'warning' | 'error' | 'gray' | 'muted' | 'subtle') [optional]
Semantic color variant (overrides color prop). Supports theme color palettes
`size` (SizeValue | number) [optional, default: 1]
`label` (React.ReactNode) [optional]
`labelPosition` ('left' | 'center' | 'right') [optional, default: 'center']
`style` (StyleProp) [optional]
`testID` (string) [optional]
### Demos
- Basic Usage | (id: Divider.basic) | tags: solid, dashed
Insert horizontal dividers between sections to separate content; switch the `variant` prop to toggle between solid and dashed lines.
```tsx
import { Column, Divider, Text } from '@platform-blocks/ui';
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: colorVariant, label, variant
Select a `colorVariant` to match semantic palettes and combine it with `label` plus `variant` to fit your divider into content sections.
```tsx
import type { DividerProps } from '@platform-blocks/ui';
import { Column, Divider, Text } from '@platform-blocks/ui';
const COLOR_VARIANTS: Array<{ label: string; tone?: DividerProps['colorVariant'] }> = [
{ label: 'Surface (default)' },
{ label: 'Primary', tone: 'primary' },
{ label: 'Success', tone: 'success' },
{ label: 'Warning', tone: 'warning' },
{ label: 'Error', tone: 'error' },
{ label: 'Muted', tone: 'muted' },
{ label: 'Subtle', tone: 'subtle' },
{ label: 'Gray', tone: 'gray' }
];
return (
Semantic color variants
{COLOR_VARIANTS.map(({ label, tone }) => (
{label}
))}
Labeled dividers
Variant styles
);
}
```
## Component:
Circular proportional chart with a hollow center (variant of pie chart).
### Props
`data` (DonutChartDataPoint[]) [optional]
Data points rendered in the donut
`rings` (DonutChartRing[]) [optional]
Optional multi-ring configuration. When provided, the top-level data acts as a fallback
`size` (number) [optional, default: 280]
Convenience size that sets both width and height when explicit values are omitted
`innerRadiusRatio` (number) [optional, default: 0.55]
Ratio (0-1) of the inner radius relative to the outer radius when thickness is not provided
`thickness` (number) [optional]
Explicit ring thickness override (outerRadius - innerRadius)
`ringGap` (number) [optional]
Gap in chart units between concentric rings (defaults to 8 for multi-ring charts)
`padAngle` (number) [optional, default: 1.5]
Padding between slices in degrees
`startAngle` (number) [optional, default: -90]
Starting angle for the first slice (degrees)
`endAngle` (number) [optional, default: 270]
Ending angle for the last slice (degrees)
`primaryRingIndex` (number) [optional, default: 0]
Index of the ring used for center totals and value formatting (defaults to 0)
`legendRingIndex` (number) [optional]
Index of the ring whose slices feed the legend (defaults to primaryRingIndex)
`inheritColorByLabel` (boolean) [optional, default: true]
When true, slices across rings reuse colors based on their label/id
- … 11 more props documented
### Demos
- Annual Expense Allocation | (id: DonutChart.annual-expense-allocation)
```tsx
import { DonutChart } from '../../';
import type { DonutChartDataPoint } from '../../';
const DEPARTMENT_ALLOCATIONS: DonutChartDataPoint[] = [
{ label: 'Product & Engineering', value: 42, color: '#5C7CFA' },
{ label: 'Go-to-Market', value: 24, color: '#FF922B' },
{ label: 'Customer Success', value: 14, color: '#20C997' },
{ label: 'G&A', value: 9, color: '#748FFC' },
{ label: 'R&D Partnerships', value: 7, color: '#F76707' },
{ label: 'Workplace & Ops', value: 4, color: '#15AABF' },
];
const formatBudget = (value: number) => `$${Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(value)}M`;
return (
'Budget'}
centerSubLabel={() => 'Allocation by function'}
centerValueFormatter={(value) => formatBudget(value)}
/>
);
}
```
- Basic | (id: DonutChart.basic)
```tsx
import { DonutChart } from '../../';
const SEGMENTS = [
{ label: 'Design', value: 28, color: '#845EF7' },
{ label: 'Engineering', value: 42, color: '#20C997' },
{ label: 'Marketing', value: 18, color: '#FF922B' },
{ label: 'Support', value: 12, color: '#4C6EF5' },
];
return (
);
}
```
## Component:
The EmojiPicker component
### Props
`variant` (EmojiPickerVariant) [optional, default: 'quick']
Picker variant
`value` (string) [optional]
Controlled value (selected emoji)
`onSelect` ((emoji: string) => void) [optional]
Callback when emoji selected
`disabled` (boolean) [optional]
Whether picker is disabled
`searchPlaceholder` (string) [optional, default: 'Search emoji…']
Placeholder text for search
`emojis` ({ label: string; emoji: string }[]) [optional, default: [{ label: 'Thumbs Up']
Number of frequently used / quick emojis to show in quick variant
`defaultOpened` (boolean) [optional]
Default opened (uncontrolled)
`onOpenChange` ((opened: boolean) => void) [optional]
Callback when open state changes
`onSearchChange` ((query: string) => void) [optional]
Callback whenever search query changes
`showBackground` (boolean) [optional, default: false]
Show background for the whole quick variant component
`testID` (string) [optional]
Test id
### Demos
- Quick | (id: EmojiPicker.quick)
```tsx
import React, { useState } from 'react';
import { Flex, Text, EmojiPicker } from '@platform-blocks/ui';
const [reaction, setReaction] = useState('❤️');
return (
Quick Reaction Emojis
{/* Basic without background */}
Without background:
{/* With background */}
With background:
{reaction && (
Current reaction: {reaction}
)}
);
}
```
## Component:
The FileInput component provides a user-friendly interface for file uploads with drag-and-drop functionality, file validation, and preview capabilities.
### Props
`variant` ('standard' | 'dropzone' | 'compact') [optional]
File input variant
`accept` (string[]) [optional]
Accepted file types (MIME types or extensions)
`multiple` (boolean) [optional]
Multiple file selection
`maxSize` (number) [optional]
Maximum file size in bytes
`maxFiles` (number) [optional]
Maximum number of files
`onUpload` ((files: FileInputFile[]) => Promise) [optional]
Upload handler
`onProgress` ((fileId: string, progress: number) => void) [optional]
Upload progress callback
`onFilesChange` ((files: FileInputFile[]) => void) [optional]
File change handler
`onFileRemove` ((fileId: string) => void) [optional]
File remove handler
`PreviewComponent` (React.ComponentType<{ file: FileInputFile; onRemove: () => void }>) [optional]
File preview component
`children` (React.ReactNode) [optional]
Custom drop zone content
`showFileList` (boolean) [optional]
Whether to show file list
- … 13 more props documented
### Demos
- Basic | (id: FileInput.basic) | tags: basic, upload, files
Simple file input with helper text and multiple file selection.
```tsx
import { useState } from 'react';
import { Column, FileInput, Text } from '@platform-blocks/ui';
import type { FileInputFile } from '@platform-blocks/ui';
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
Drag-and-drop dropzone variant with native fallback instructions and selected file list.
```tsx
import { useState } from 'react';
import { Platform } from 'react-native';
import { Column, FileInput, Text } from '@platform-blocks/ui';
import type { FileInputFile } from '@platform-blocks/ui';
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}
))}
)}
);
}
```
## Component:
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.
### Props
`direction` ('row' | 'column' | 'row-reverse' | 'column-reverse') [optional]
Flex direction
`align` ('flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline') [optional]
Align items on the cross axis
`justify` ('flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly') [optional]
Justify content on the main axis
`wrap` ('nowrap' | 'wrap' | 'wrap-reverse') [optional]
Flex wrap
`gap` (SizeValue) [optional]
Gap between children (applies to both row and column gap)
`rowGap` (SizeValue) [optional]
Row gap between children
`columnGap` (SizeValue) [optional]
Column gap between children
`grow` (number) [optional]
Flex grow
`shrink` (number) [optional]
Flex shrink
`basis` (DimensionValue) [optional]
Flex basis
`children` (React.ReactNode) [optional]
Children elements
`style` (ViewStyle) [optional]
Custom styles
- … 2 more props documented
### Demos
- Align Items | (id: Flex.align) | tags: align, items, cross-axis, alignment, stretch
Item alignment options along the cross axis (flex-start, center, stretch, etc.).
```tsx
import { Flex, Card, Text, Column } from '@platform-blocks/ui';
return (
{[
{ label: 'Start', value: 'flex-start' },
{ label: 'Center', value: 'center' },
{ label: 'End', value: 'flex-end' },
{ label: 'Stretch', value: 'stretch' },
{ label: 'Baseline', value: 'baseline' }
].map(({ label, value }) => (
align="{value}"
{value === 'baseline' ? (
{/* Use Text of varying sizes directly to demonstrate baseline alignment */}
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
Simple flex container with three items and gap spacing.
```tsx
import { Flex, Card, Text } from '@platform-blocks/ui';
return (
Item 1
Item 2
Item 3
);
}
```
## Component:
Shows progressive reduction of data through stages (conversion pipeline).
### Props
`series` (FunnelChartSeries | FunnelChartSeries[]) [required]
Funnel data series to render
`layout` (FunnelLayoutConfig) [optional]
Layout customization options
`valueFormatter` (FunnelValueFormatter) [optional]
Formatter for step values
`tooltip` (ChartTooltip) [optional]
Tooltip configuration
`legend` (ChartLegend) [optional]
Legend configuration
`enableCrosshair` (boolean) [optional, default: true]
Enable crosshair indicator
`multiTooltip` (boolean) [optional, default: true]
Enable aggregated tooltip for multiple series
`liveTooltip` (boolean) [optional, default: false]
Keep tooltip following the pointer
`annotations` (any[]) [optional]
Additional decorations or notes to render
`accessibilityTable` (FunnelAccessibilityTableOptions) [optional]
Render hidden accessibility table
`onDataTable` ((payloads: FunnelDataTablePayload[]) => void) [optional]
Callback invoked with data table payload
### Demos
- Basic | (id: FunnelChart.basic)
```tsx
import { FunnelChart } from '../../';
const SALES_FUNNEL = {
id: 'pipeline',
name: 'Q2 pipeline',
steps: [
{ label: 'Website visits', value: 32_500 },
{ label: 'Sign-ups', value: 9_600 },
{ label: 'Qualified leads', value: 4_350 },
{ label: 'Trials started', value: 2_150 },
{ label: 'Customers', value: 1_120 },
],
};
return (
{
const label = index === 0 ? `${value.toLocaleString()} visitors` : `${value.toLocaleString()} leads`;
const lines = [label];
if (context?.conversion != null) {
lines.push(`Retention ${(context.conversion * 100).toFixed(1)}%`);
}
if (context?.dropValue && context.previousValue) {
lines.push(
`Drop ${(context.dropRate * 100).toFixed(1)}% (${context.dropValue.toLocaleString()} lost)`
);
}
return lines;
}}
legend={{ show: false }}
multiTooltip
enableCrosshair
tooltip={{
show: true,
formatter: (step) => `${step.label}: ${step.value.toLocaleString()}`
}}
/>
);
}
```
- Data Pipeline Quality | (id: FunnelChart.data-pipeline-quality)
```tsx
import { FunnelChart } from '../../';
type PipelineMeta = {
note?: string;
};
const PIPELINE_QUALITY = {
id: 'data-quality-pipeline',
name: 'Data quality checkpoints',
steps: [
{ label: 'Ingested records', value: 12_400_000, color: '#0f766e' },
{ label: 'Schema validated', value: 11_760_000, color: '#0d9488', meta: { note: 'Dropped malformed partner feeds and null timestamps' } as PipelineMeta },
{ label: 'Deduplicated', value: 11_180_000, color: '#14b8a6', meta: { note: 'UserId + sessionId key resolves campaign duplicates' } as PipelineMeta },
{ label: 'Quality checks passed', value: 10_260_000, color: '#2dd4bf', meta: { note: 'Primary blockers: stale reference data & threshold breaches' } as PipelineMeta },
{ label: 'Certified for dashboards', value: 9_940_000, color: '#5eead4', meta: { note: 'Ready for downstream activation' } as PipelineMeta },
],
};
return (
{
const step = PIPELINE_QUALITY.steps[index];
const meta = step.meta as PipelineMeta | undefined;
const lines: string[] = [`${value.toLocaleString()} rows`];
if (context?.previousValue) {
const dropRate = (context.dropRate * 100).toFixed(1);
const drop = context.dropValue.toLocaleString();
lines.push(`Filtered ${drop} rows (${dropRate}%)`);
}
lines.push(`Retention ${((context?.conversion ?? 1) * 100).toFixed(1)}% of ingest`);
if (meta?.note) {
lines.push(meta.note);
}
return lines;
}}
legend={{ show: false }}
tooltip={{
show: true,
formatter: (step) => {
const idx = PIPELINE_QUALITY.steps.findIndex((candidate) => candidate.label === step.label);
const previous = idx > 0 ? PIPELINE_QUALITY.steps[idx - 1] : undefined;
const dropValue = previous ? previous.value - step.value : 0;
const dropRate = previous && previous.value > 0 ? (dropValue / previous.value) * 100 : 0;
const meta = step.meta as PipelineMeta | undefined;
return [
step.label,
`${step.value.toLocaleString()} rows`,
previous ? `Filtered: ${dropValue.toLocaleString()} (${dropRate.toFixed(1)}%)` : 'Ingestion baseline',
meta?.note,
]
.filter(Boolean)
.join('\n');
},
}}
/>
);
}
```
## Component:
The Gallery component displays a collection of images or media with thumbnail navigation, keyboard controls, and optional fullscreen/modal viewing.
### Props
`images` (GalleryItem[]) [required]
`initialIndex` (number) [optional]
`onClose` (() => void) [optional]
`onImageChange` ((index: number, image: GalleryItem) => void) [optional]
`onDownload` ((image: GalleryItem) => void) [optional]
`showMetadata` (boolean) [optional]
`showThumbnails` (boolean) [optional]
`showDownloadButton` (boolean) [optional]
`allowKeyboardNavigation` (boolean) [optional]
`allowSwipeNavigation` (boolean) [optional]
`overlayOpacity` (number) [optional]
`animationDuration` (number) [optional]
### Demos
- Basic | (id: Gallery.basic) | tags: gallery, images, navigation, metadata
Basic image gallery with navigation and metadata.
```tsx
import { useState } from 'react';
import { View, TouchableOpacity, Image } from 'react-native';
import { Gallery, Button, Card, Text, Flex } from '@platform-blocks/ui';
import type { GalleryItem } from '../../types';
// Sample images for the gallery
const SAMPLE_IMAGES: GalleryItem[] = [
{
id: '1',
uri: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800&h=600&fit=crop',
title: 'Mountain Landscape',
description: 'Beautiful mountain view with snow-capped peaks',
metadata: {
size: '2.4 MB',
dimensions: { width: 1920, height: 1080 },
dateCreated: 'March 15, 2024',
camera: 'Canon EOS R5',
location: 'Swiss Alps',
},
},
{
id: '2',
uri: 'https://images.unsplash.com/photo-1469474968028-56623f02e42e?w=800&h=600&fit=crop',
title: 'Forest Path',
description: 'A serene path through the forest',
metadata: {
size: '1.8 MB',
dimensions: { width: 1600, height: 1200 },
dateCreated: 'April 2, 2024',
camera: 'Sony A7 III',
location: 'Pacific Northwest',
},
},
{
id: '3',
uri: 'https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=800&h=600&fit=crop',
title: 'Dense Forest',
description: 'Tall trees reaching toward the sky',
metadata: {
size: '3.1 MB',
dimensions: { width: 2048, height: 1536 },
dateCreated: 'May 18, 2024',
camera: 'Nikon D850',
location: 'Redwood National Park',
},
},
];
const [galleryVisible, setGalleryVisible] = useState(false);
const [currentIndex, setCurrentIndex] = useState(0);
const openGallery = (index: number = 0) => {
setCurrentIndex(index);
setGalleryVisible(true);
};
const handleDownload = (image: GalleryItem) => {
alert(`Downloading: ${image.title}`);
};
return (
Gallery Demo
Tap any image below to open the fullscreen gallery viewer.
{SAMPLE_IMAGES.map((image, index) => (
openGallery(index)}
>
{image.title}
))}
openGallery(0)}
/>
setGalleryVisible(false)}
onImageChange={(index: number, image: GalleryItem) => {
setCurrentIndex(index);
console.log('Changed to:', image.title);
}}
onDownload={handleDownload}
showMetadata={true}
showThumbnails={true}
showDownloadButton={true}
allowKeyboardNavigation={true}
allowSwipeNavigation={true}
/>
);
}
```
- Advanced | (id: Gallery.advanced) | tags: advanced, customization, handlers, minimal
Advanced gallery configurations with custom handlers.
```tsx
import { useState } from 'react';
import { View, Alert } from 'react-native';
import { Gallery, Button, Card, Text } from '@platform-blocks/ui';
import type { GalleryItem } from '../../types';
const PRODUCT_IMAGES: GalleryItem[] = [
{
id: 'prod1',
uri: 'https://images.unsplash.com/photo-1484704849700-f032a568e944?w=800&h=600&fit=crop',
title: 'Wireless Headphones',
description: 'Premium noise-canceling headphones',
metadata: {
size: '1.5 MB',
dimensions: { width: 1600, height: 1200 },
dateCreated: 'March 8, 2024',
camera: 'Studio Setup',
location: 'Product Studio',
},
},
{
id: 'prod2',
uri: 'https://images.unsplash.com/photo-1526170375885-4d8ecf77b99f?w=800&h=600&fit=crop',
title: 'Smartphone',
description: 'Latest model with advanced features',
metadata: {
size: '1.8 MB',
dimensions: { width: 1400, height: 1050 },
dateCreated: 'March 12, 2024',
camera: 'Professional Setup',
location: 'Tech Studio',
},
},
];
const [activeGallery, setActiveGallery] = useState(null);
const [currentIndex, setCurrentIndex] = useState(0);
const openGallery = (galleryType: string, index: number = 0) => {
setCurrentIndex(index);
setActiveGallery(galleryType);
};
const closeGallery = () => {
setActiveGallery(null);
};
const handleDownload = (image: GalleryItem) => {
Alert.alert(
'Download Image',
`Would you like to download "${image.title}"?\\n\\nFile: ${image.metadata?.size || 'Unknown size'}`,
[
{ text: 'Cancel', style: 'cancel' },
{ text: 'Download', onPress: () => console.log('Downloading:', image.title) }
]
);
};
return (
Advanced Gallery Features
📱 Minimal Gallery
Clean interface with no thumbnails or metadata
openGallery('minimal')}
/>
💾 Custom Download Handler
Gallery with custom download confirmation dialog
openGallery('custom')}
/>
{/* Minimal Gallery */}
{/* Custom Gallery */}
);
}
```
## Component:
Visualises progress toward a bounded target using a semicircular gauge with configurable ranges, ticks, labels, and animated needle. Ported from the UI Gauge component to the charts package so it can slot into chart demos, interaction prov…
### Props
`value` (number) [required]
Current value
`min` (number) [optional, default: 0]
Minimum value
`max` (number) [optional, default: 100]
Maximum value
`startAngle` (number) [optional, default: 135]
Gauge start angle (degrees, 0 = top)
`endAngle` (number) [optional, default: 45]
Gauge end angle (degrees, 0 = top)
`rotationOffset` (number) [optional, default: 0]
Rotation offset applied to the entire gauge
`thickness` (number) [optional, default: 12]
Base arc thickness
`innerRadiusRatio` (number) [optional]
Ratio of inner radius to outer radius when rendering donut style gauges
`track` (GaugeChartTrackConfig) [optional]
Track configuration
`ranges` (GaugeChartRange[]) [optional, default: []]
Value ranges displayed on the gauge
`ticks` (GaugeChartTickConfig) [optional]
Tick configuration
`labels` (GaugeChartLabelConfig) [optional]
Label configuration
- … 9 more props documented
### Demos
- Basic | (id: GaugeChart.basic)
```tsx
import { GaugeChart } from '../../';
const RANGES = [
{ from: 0, to: 40, color: '#F87171', label: 'High' },
{ from: 40, to: 70, color: '#FBBF24', label: 'Moderate' },
{ from: 70, to: 100, color: '#34D399', label: 'Optimal' },
];
return (
`${Math.round(v)}%`, offset: 26 }}
needle={{ length: 0.85, centerSize: 6, showCenter: true }}
centerLabel={{ show: true, formatter: (val) => `${Math.round(val)}%`, secondaryText: (_, pct) => `${Math.round(pct * 100)}% of max` }}
legend={{ show: true, position: 'bottom' }}
/>
);
}
```
- Carbon Emissions Progress | (id: GaugeChart.carbon-emissions-progress)
```tsx
import { GaugeChart } from '../../';
const RANGES = [
{ from: 0, to: 8, color: '#F87171', label: 'Behind plan' },
{ from: 8, to: 15, color: '#FBBF24', label: 'On watch' },
{
from: 15,
to: 20,
color: '#34D399',
label: 'Ahead of goal',
gradient: {
angle: 45,
stops: [
{ offset: 0, color: '#34D399' },
{ offset: 1, color: '#059669' },
],
},
},
];
const TARGET_REDUCTION = 15;
const CURRENT_REDUCTION = 12.4;
const LAST_YEAR_REDUCTION = 10.7;
const MARKERS = [
{ value: TARGET_REDUCTION, label: 'Quarter target', color: '#14B8A6', size: 18, labelOffset: 16 },
{
type: 'needle' as const,
value: LAST_YEAR_REDUCTION,
label: 'Last year',
color: '#0EA5E9',
needleLength: 0.78,
labelOffset: 18,
},
];
const goalProgress = Math.min(1, CURRENT_REDUCTION / TARGET_REDUCTION);
return (
`${Math.round(v)}%`, offset: 30 }}
needle={{ color: '#0EA5E9', length: 0.82, width: 3.5, centerSize: 7, showCenter: true }}
centerLabel={{
show: true,
formatter: (val) => `${val.toFixed(1)}% reduction`,
secondaryText: `${Math.round(goalProgress * 100)}% of target`,
}}
legend={{ show: true, position: 'bottom' }}
/>
);
}
```
## Component:
A text component that displays text with gradient colors. Supports customizable gradients with multiple colors, different angles, and animated transitions (web only).
### Props
`colors` (string[]) [required]
Array of colors for the gradient (at least 2 required)
`locations` (number[]) [optional]
Color stops (0-1) for each color. If not provided, colors are evenly distributed
`angle` (number) [optional]
Gradient direction angle in degrees (0 = left to right, 90 = top to bottom, etc.)
`start` ([number, number]) [optional]
Start point [x, y] (0-1). Overrides angle if provided
`end` ([number, number]) [optional]
End point [x, y] (0-1). Overrides angle if provided
`position` (number) [optional]
Gradient position offset (0-1). Moves the gradient along the line
`testID` (string) [optional]
Custom testID for testing
### Demos
- Angles | (id: GradientText.angles)
# Gradient Angles
```tsx
import { View, StyleSheet } from 'react-native';
import { GradientText } from '../..';
import { Text } from '../../../Text';
return (
0° (Left to Right)
Horizontal Gradient
45° (Diagonal)
Diagonal Gradient
90° (Top to Bottom)
Vertical Gradient
135° (Diagonal)
Reverse Diagonal
);
}
const styles = StyleSheet.create({
container: {
gap: 16,
},
spacer: {
height: 20,
},
});
```
- Basic | (id: GradientText.basic)
# Basic Gradient Text
```tsx
import { View, StyleSheet } from 'react-native';
import { GradientText } from '../..';
return (
Hello Gradient World
Bold Gradient Text
Heading with Gradient
);
}
const styles = StyleSheet.create({
container: {
gap: 16,
},
spacer: {
height: 16,
},
});
```
## Component:
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 pr…
### Props
`columns` (ResponsiveProp) [optional]
Number of columns (can be responsive)
`gap` (SizeValue) [optional, default: 0]
Gap between items
`rowGap` (SizeValue) [optional]
Row gap between items
`columnGap` (SizeValue) [optional]
Column gap between items
`fullWidth` (boolean) [optional, default: false]
Make the grid take full width (100%)
`children` (React.ReactNode) [optional]
Children elements
`style` (StyleProp) [optional]
Custom styles
`testID` (string) [optional]
Test ID for testing
### Demos
- Basic | (id: Grid.basic)
Basic 12-column grid with equal width items.
```tsx
import { Grid, GridItem, Card, Column, Text } from '@platform-blocks/ui';
return (
{Array.from({ length: 12 }).map((_, index) => (
{index + 1}
))}
Twelve even columns, each spanning a single track
);
}
```
- Gaps | (id: Grid.gaps)
Custom row and column gaps using gap, rowGap, and columnGap props.
```tsx
import { Grid, GridItem, Card, Column, Text } from '@platform-blocks/ui';
const sections = [
{
label: 'Compact gap (xs)',
columns: 6,
items: 12,
props: { gap: 'xs' as const, rowGap: 6, columnGap: 6 },
},
{
label: 'Roomy gap (24px)',
columns: 6,
items: 6,
props: { gap: 24, rowGap: 24, columnGap: 12 },
},
];
return (
{sections.map(({ label, columns, items, props }) => (
{label}
{Array.from({ length: items }).map((_, index) => (
Item {index + 1}
))}
))}
);
}
```
## Component:
Bar chart grouping multiple series side-by-side for comparison.
### Props
`series` (StackedBarSeries[]) [required]
Grouped series data to render
`barSpacing` (number) [optional, default: 0.2]
Gap between grouped categories
`innerBarSpacing` (number) [optional, default: 0.1]
Gap between bars inside a group
`legend` (ChartLegend) [optional, default: { show: true]
Legend configuration
`xAxis` (ChartAxis) [optional]
X-axis configuration
`yAxis` (ChartAxis) [optional]
Y-axis configuration
`grid` (ChartGrid) [optional]
Grid line configuration
`animation` (ChartAnimation) [optional]
Animation configuration
`colorOptions` (GroupedBarColorOptions) [optional]
Palette and hashing options for bar colors
`valueLabels` (GroupedBarValueLabelConfig) [optional]
Optional bar value label configuration
`multiTooltip` (boolean) [optional]
Enable multi-series aggregated tooltip content
`liveTooltip` (boolean) [optional]
Keep tooltip visible while pointer moves within chart
- … 1 more props documented
### Demos
- Basic | (id: GroupedBarChart.basic)
```tsx
import { GroupedBarChart } from '../../';
const SERIES = [
{
id: '2024',
name: '2024',
color: '#4C6EF5',
data: [
{ id: 'analytics-24', category: 'Analytics', value: 420 },
{ id: 'automation-24', category: 'Automation', value: 365 },
{ id: 'integrations-24', category: 'Integrations', value: 298 },
],
},
{
id: '2025',
name: '2025',
color: '#20C997',
data: [
{ id: 'analytics-25', category: 'Analytics', value: 512 },
{ id: 'automation-25', category: 'Automation', value: 418 },
{ id: 'integrations-25', category: 'Integrations', value: 342 },
],
},
{
id: 'target',
name: 'Target',
color: '#FF922B',
data: [
{ id: 'analytics-target', category: 'Analytics', value: 540 },
{ id: 'automation-target', category: 'Automation', value: 440 },
{ id: 'integrations-target', category: 'Integrations', value: 360 },
],
},
];
return (
`$${value}`,
}}
grid={{ show: true, color: '#E6EBFA' }}
legend={{ show: true, position: 'bottom' }}
animation={{ duration: 450 }}
colorOptions={{ hash: false }}
/>
);
}
```
- Experiment Variant By Geo | (id: GroupedBarChart.experiment-variant-by-geo)
```tsx
import { GroupedBarChart } from '../../';
const SERIES = [
{
id: 'control',
name: 'Control',
color: '#ADB5BD',
data: [
{ id: 'na-control', category: 'North America', value: 4.8 },
{ id: 'emea-control', category: 'EMEA', value: 4.2 },
{ id: 'apac-control', category: 'APAC', value: 3.9 },
{ id: 'latam-control', category: 'LATAM', value: 3.6 },
],
},
{
id: 'variant-a',
name: 'Variant A',
color: '#5C7CFA',
data: [
{ id: 'na-var-a', category: 'North America', value: 5.6 },
{ id: 'emea-var-a', category: 'EMEA', value: 4.9 },
{ id: 'apac-var-a', category: 'APAC', value: 4.4 },
{ id: 'latam-var-a', category: 'LATAM', value: 4.1 },
],
},
{
id: 'variant-b',
name: 'Variant B',
color: '#20C997',
data: [
{ id: 'na-var-b', category: 'North America', value: 6.1 },
{ id: 'emea-var-b', category: 'EMEA', value: 5.3 },
{ id: 'apac-var-b', category: 'APAC', value: 4.8 },
{ id: 'latam-var-b', category: 'LATAM', value: 4.5 },
],
},
];
return (
`${value.toFixed(1)}%`,
ticks: [3, 4, 5, 6],
}}
grid={{ show: true, color: '#EEF2FF' }}
legend={{ show: true, position: 'bottom' }}
multiTooltip
liveTooltip
valueLabels={{
show: true,
position: 'outside',
formatter: ({ value }) => `${value.toFixed(1)}%`,
color: '#1F1F24',
fontWeight: '600',
offset: 8,
}}
animation={{ duration: 420 }}
/>
);
}
```
## Component:
Color-coded matrix for intensity visualization across two dimensions.
### Props
`data` (HeatmapCell[] | HeatmapMatrixInput) [required]
Heatmap data points or matrix-style input
`colorScale` (HeatmapColorScaleConfig) [optional]
Color scale configuration
`cellSize` (HeatmapCellSize) [optional]
Explicit cell size overrides
`gap` (number) [optional, default: 2]
Gap between cells in pixels
`xAxis` (ChartAxis) [optional]
X-axis configuration
`yAxis` (ChartAxis) [optional]
Y-axis configuration
`grid` (ChartGrid) [optional]
Grid line configuration
`enablePanZoom` (boolean) [optional]
Enable pan/zoom interaction
`zoomMode` ('both' | 'x' | 'y') [optional]
Which axes can be zoomed
`minZoom` (number) [optional]
Minimum zoom factor relative to original domain
`enableWheelZoom` (boolean) [optional]
Enable wheel-based zooming
`wheelZoomStep` (number) [optional]
Step factor applied to wheel zoom
- … 20 more props documented
### Demos
- Basic Heatmap | (id: HeatmapChart.basic)
```tsx
import { HeatmapChart } from '../../';
const DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'];
const SESSIONS = ['Morning', 'Afternoon', 'Evening'];
const UTILIZATION = [
[12, 18, 25, 22, 28],
[8, 14, 19, 24, 20],
[6, 9, 12, 15, 11],
];
return (
);
}
```
- GitHub Contributions | (id: HeatmapChart.contributions)
GitHub-style contribution calendar generated with a 7x53 grid (weeks x weekdays) and a 5-step color scale.
```tsx
import { useMemo } from 'react';
import { HeatmapChart } from '../../';
const WEEKS = 52;
const WEEKDAY_LABELS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
const PALETTE = ['#EBEDF0', '#C6E48B', '#7BC96F', '#239A3B', '#196127'];
function generateContributionMatrix(): number[][] {
return WEEKDAY_LABELS.map((_, row) =>
Array.from({ length: WEEKS }, (_, col) => {
const wave = Math.sin(col / 4) + Math.cos((row + col) / 3);
const seasonal = Math.cos(col / 12) + row * 0.2;
const score = wave + seasonal + 2;
return Math.max(0, Math.min(4, Math.round(score)));
})
);
}
const COLUMNS = Array.from({ length: WEEKS }, (_, index) => `W${index + 1}`);
const matrix = useMemo(() => generateContributionMatrix(), []);
return (
WEEKDAY_LABELS[value] ?? '',
}}
legend={{
show: true,
position: 'bottom',
items: [
{ label: 'Less', color: PALETTE[0] },
{ label: 'More', color: PALETTE[PALETTE.length - 1] },
],
}}
tooltip={{ show: true }}
/>
);
}
```
## Component:
Highlight emphasizes matching fragments inside longer strings, reusing the Text component so typography settings stay consistent across platforms.
### Props
`highlight` (HighlightValue | HighlightValue[]) [optional]
Substring or substrings to emphasize within the provided children
`highlightStyles` (any | ((theme: PlatformBlocksTheme) => any)) [optional]
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) [optional]
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) [optional]
Toggle case-sensitive matching (defaults to case-insensitive).
`trim` (boolean) [optional]
Trim highlight values before matching to ignore accidental whitespace. Defaults to true.
`highlightProps` (Partial) [optional]
Additional props applied to the highlighted Text nodes.
### Demos
- Basic | (id: Highlight.basic)
Default highlight behavior with a single search term. Matching fragments are wrapped with the theme-aware highlight styles.
```tsx
import { View } from 'react-native';
import { Highlight } from '../..';
import { Text } from '../../../../components/Text';
const PARAGRAPH = 'Highlight This, definitely THIS and also this!';
return (
Case-insensitive match
{PARAGRAPH}
);
}
```
- Multiple | (id: Highlight.multiple)
Pass an array to highlight several distinct substrings. Every match shares the same styles by default.
```tsx
import { View } from 'react-native';
import { Highlight } from '../..';
import { Text } from '../../../../components/Text';
const SENTENCE = 'Platform Blocks brings patterns, blocks, and building tools together.';
return (
Multiple values
{SENTENCE}
);
}
```
## Component:
Distribution of continuous data split into bins.
### Props
`data` (number[]) [required]
Raw values used to build the histogram
`bins` (number) [optional]
Explicit number of bins (overrides method)
`binMethod` ('sturges' | 'sqrt' | 'fd') [optional, default: 'sturges']
Bin selection heuristic
`showDensity` (boolean) [optional, default: true]
Show density (KDE) overlay line
`bandwidth` (number) [optional]
Gaussian kernel bandwidth (auto if not provided)
`density` (boolean) [optional, default: true]
Normalize histogram to probability density (area=1)
`barColor` (string) [optional]
Color for bars
`barOpacity` (number) [optional, default: 0.8]
Opacity for bars (0-1)
`densityColor` (string) [optional, default: '#ef4444']
Density line color
`densityThickness` (number) [optional, default: 2]
Density line thickness
`barRadius` (number) [optional, default: 2]
Rounded bar corners
`barGap` (number) [optional, default: 0.08]
Gap ratio between bars (0-1)
- … 18 more props documented
### Demos
- Basic | (id: HistogramChart.basic)
```tsx
import { HistogramChart } from '../../';
const SESSION_DURATIONS = [
4, 6, 7, 8, 9, 9, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 14, 15, 16, 16,
16, 17, 18, 18, 18, 19, 20, 20, 22, 24, 25,
];
return (
`${bin.count} sessions between ${bin.start}-${bin.end} min`,
}}
valueFormatter={(count, bin) => `${count} • ${bin.density.toFixed(2)} pdf`}
enableCrosshair
liveTooltip
/>
);
}
```
- Contract Length Retention | (id: HistogramChart.contract-length-retention)
```tsx
import { HistogramChart } from '../../';
const CONTRACT_LENGTHS = [
6, 6, 6, 7, 8, 9, 9, 10, 10, 11,
12, 12, 12, 12, 13, 14, 14, 15, 15, 16,
17, 18, 18, 18, 18, 19, 20, 20, 21, 21,
22, 24, 24, 24, 24, 25, 26, 26, 27, 28,
30, 30, 30, 32, 32, 33, 34, 36, 36, 36,
38, 40, 42, 45, 48,
];
const sortedLengths = [...CONTRACT_LENGTHS].sort((a, b) => a - b);
const median = sortedLengths[Math.floor(sortedLengths.length / 2)];
return (
`${value.toFixed(0)}`,
}}
grid={{ show: true, color: '#EEF2FF' }}
tooltip={{
show: true,
formatter: (bin) => `${bin.count} accounts between ${bin.start.toFixed(0)}–${bin.end.toFixed(0)} months`,
}}
valueFormatter={(count) => `${count} customers`}
/>
);
}
```
## Component:
The `Icon` component displays icons with optional captions and overlays, providing a flexible way to present visual content in your application.
### Props
`name` (string) [required]
Icon name from the registry
`size` (IconSize) [optional, default: 'md']
Size of the icon
`color` (string) [optional]
Color of the icon
`stroke` (number) [optional, default: 1.5]
Stroke thickness for outlined icons. Defaults to 1.5.
`variant` (IconVariant) [optional, default: 'outlined']
Icon variant - overrides the default variant from icon definition
`style` (StyleProp) [optional]
Additional styles
`label` (string) [optional]
Accessibility label
`decorative` (boolean) [optional, default: false]
Whether the icon is purely decorative (skip a11y)
`mirrorInRTL` (boolean) [optional]
Whether to mirror this icon in RTL mode. If not specified, uses auto-detection based on icon name
### Demos
- Basic | (id: Icon.basic)
```tsx
import { ScrollView, View } from 'react-native';
import { Icon } from '../../Icon';
import { Text, Card, Flex, Title } from '../../../..';
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)
```tsx
import { Card, Flex, Text } from '../../../..';
import { Icon } from '../../Icon';
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}
))}
);
}
```
## Component:
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.
### Props
`icon` (string) [required]
Icon name from the registry
`onPress` (() => void) [optional]
Called when the button is pressed
`onLayout` ((event: any) => void) [optional]
Called when the button layout is calculated
`variant` ('filled' | 'secondary' | 'outline' | 'ghost' | 'gradient' | 'none') [optional]
Button visual variant
`size` (SizeValue) [optional]
Button size
`disabled` (boolean) [optional]
Whether the button is disabled
`loading` (boolean) [optional]
Whether button is in loading state (shows loader)
`colorVariant` (string) [optional]
Custom color override 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
`iconColor` (string) [optional]
Explicit icon color override (else derived automatically from variant & color)
`iconVariant` (IconProps['variant']) [optional]
Icon variant override
`iconSize` (IconProps['size']) [optional]
Icon size override (defaults to appropriate size for button size)
`tooltip` (string) [optional]
Tooltip text to show on hover/focus - when provided, wraps button in Tooltip component
- … 4 more props documented
### Demos
- Basic | (id: IconButton.basic)
```tsx
import React, { useState } from 'react';
import {
IconButton,
Column,
Row,
Card,
Text,
Divider,
Switch,
} from '@platform-blocks/ui';
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('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
);
}
```
## Component:
The `Image` component displays images with optional captions and overlays, providing a flexible way to present visual content in your application.
### Props
`src` (string) [required]
Image source URI
`source` ({ uri: string } | number) [optional]
Image source object (alternative to src)
`alt` (string) [optional]
Alternative text for accessibility
`accessibilityLabel` (string) [optional]
Accessibility label
`resizeMode` ('cover' | 'contain' | 'stretch' | 'repeat' | 'center') [optional]
Image resize mode
`size` (SizeValue | number) [optional]
Image size preset
`w` (number | string) [optional]
Custom width
`h` (number | string) [optional]
Custom height
`aspectRatio` (number) [optional]
Aspect ratio
`borderWidth` (number) [optional]
Border width
`borderColor` (ColorValue) [optional]
Border color
`rounded` (boolean) [optional]
Whether image should be rounded
- … 9 more props documented
### Demos
- Basic | (id: Image.basic)
```tsx
import { Card, Text, Column } from '@platform-blocks/ui';
import { Image } from '../../Image';
return (
Basic Image Usage
A simple image with specified dimensions
);
}
```
- Fallback | (id: Image.fallback)
```tsx
import { Image, Card, Text, Row, Icon, Block } from '@platform-blocks/ui';
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
);
}
```
## Component:
The Indicator component
### Props
`size` (SizeValue | number) [optional]
`color` (string) [optional]
`borderColor` (string) [optional]
`borderWidth` (number) [optional]
`placement` ('top-left' | 'top-right' | 'bottom-left' | 'bottom-right') [optional]
`offset` (number) [optional]
`style` (StyleProp) [optional]
`children` (React.ReactNode) [optional]
`invisible` (boolean) [optional]
### Demos
- Basic usage | (id: Indicator.basic) | tags: status, notification
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
import { Avatar, Block, Column, Indicator, Text } from '@platform-blocks/ui';
return (
Corner indicator
Panel
Avatar status
Numeric counter
Inbox
5
);
}
```
- Placements | (id: Indicator.placements) | tags: placement, offset
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
import type { ReactNode } from 'react';
import { Block, Column, Indicator, Row, Text } from '@platform-blocks/ui';
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}
))}
);
}
```
## Component:
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.
### Props
`validation` (ValidationRule[]) [optional]
Input type - determines styling and behavior Input validation rules
`keyboardType` (KeyboardTypeOptions) [optional]
Auto-complete type Keyboard type for mobile
`multiline` (boolean) [optional]
Whether input is multiline
`numberOfLines` (number) [optional]
Number of lines for multiline input
`minLines` (number) [optional, default: 1]
Minimum number of lines for multiline input (default: 1)
`maxLines` (number) [optional]
Maximum number of lines for multiline input
`maxLength` (number) [optional]
Maximum length
`secureTextEntry` (boolean) [optional]
Whether to secure text entry
`textInputProps` (ExtendedTextInputProps) [optional]
Additional TextInput props
`inputRef` (React.Ref) [optional]
Ref to underlying TextInput (focus control)
`autoCapitalize` (RNTextInputProps['autoCapitalize']) [optional]
Text auto-capitalization behavior
`autoCorrect` (boolean) [optional]
Whether to enable auto-correct
- … 12 more props documented
### Demos
- Basic | (id: Input.basic) | tags: basic, input, text
Basic text input with label, placeholder, and value handling.
```tsx
import { useState } from 'react';
import { Column, Input, Text } from '@platform-blocks/ui';
const [value, setValue] = useState('');
return (
Basic text input
{value ? (
Current value: {value}
) : null}
);
}
```
- Clearable Inputs | (id: Input.clearable) | tags: input, clearable
Add `clearable` to surface a dismiss icon, works alongside helper text, custom sizes, and right-section content.
```tsx
import { useState } from 'react';
import { Column, Input, Text } from '@platform-blocks/ui';
const [basicValue, setBasicValue] = useState('');
const [prefillValue, setPrefillValue] = useState('Pre-filled text to clear');
const sizeTokens = ['xs', 'sm', 'md', 'lg', 'xl'] as const;
const [sizeValues, setSizeValues] = useState>({
xs: 'Extra small',
sm: 'Small text',
md: 'Medium text',
lg: 'Large text',
xl: 'Extra large text',
});
return (
Clearable inputs
Basic usage
Pre-filled input
Size variants
{sizeTokens.map((token) => (
setSizeValues((previous) => ({
...previous,
[token]: text,
}))
}
clearable
size={token}
/>
))}
With right section
@domain.com}
helperText="Clear button renders alongside custom right content."
/>
);
}
```
## Component:
A visual component for displaying keyboard keys, shortcuts, and key combinations with proper styling.
### Props
`children` (ReactNode) [required]
The key or text to display
`size` (ComponentSizeValue) [optional, default: 'md']
Size variant of the key cap
`variant` ('default' | 'minimal' | 'outline' | 'filled') [optional, default: 'default']
Visual variant of the key cap
`color` ('primary' | 'secondary' | 'gray' | 'success' | 'warning' | 'error') [optional, default: 'gray']
Color scheme for the key cap
`animateOnPress` (boolean) [optional, default: true]
Whether the key should animate when the actual key is pressed Only works on web platforms
`keyCode` (string) [optional]
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'>) [optional]
Modifier keys that must be pressed along with the main key
`pressed` (boolean) [optional]
Whether the key cap should appear pressed
`onKeyPress` (() => void) [optional]
Callback when the key combination is pressed
`testID` (string) [optional]
Custom test ID for testing
### Demos
- Basic Usage | (id: KeyCap.basic) | tags: basic, keyboard
Basic keyboard key display for showing shortcuts and key combinations.
```tsx
import { Column, KeyCap, Row } from '@platform-blocks/ui';
return (
A
Enter
Space
⌘
Ctrl
⇧
);
}
```
- Sizes | (id: KeyCap.sizes) | tags: sizes
Different sizes for KeyCap components from extra small to extra large.
```tsx
import { Column, KeyCap, Row } from '@platform-blocks/ui';
return (
XS
SM
MD
LG
XL
);
}
```
## Component:
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.
### Props
`variant` (KnobVariant) [optional]
Visual preset that tunes defaults for common encoder scenarios
`mode` ('bounded' | 'endless') [optional]
Interaction mode for bounded or endless rotary behavior
`value` (number) [optional]
Controlled value
`defaultValue` (number) [optional]
Uncontrolled initial value
`min` (number) [optional]
Minimum selectable value
`max` (number) [optional]
Maximum selectable value
`step` (number) [optional]
Step increment applied when interacting
`onChange` ((value: number) => void) [optional]
Called on every value change
`onChangeEnd` ((value: number) => void) [optional]
Called after interaction completes
`onScrubStart` (() => void) [optional]
Fired when the user begins dragging
`onScrubEnd` (() => void) [optional]
Fired when the user ends dragging
`size` (number) [optional]
Diameter of the control in pixels
- … 18 more props documented
### Demos
- Basic | (id: Knob.basic) | tags: basic, knob, control
Controlled knob with percentage formatting and a mirrored readout below the dial.
```tsx
import { useState } from 'react';
import { Column, Knob, Text } from '@platform-blocks/ui';
const [value, setValue] = useState(48);
return (
Math.round(current),
suffix: '%',
}}
/>
Current gain: {Math.round(value)}%
);
}
```
- Endless | (id: Knob.endless) | tags: endless, encoder, rotation
Endless mode resets the dial each turn while tracking the cumulative rotation value.
```tsx
import { useMemo, useState } from 'react';
import { Column, Knob, Text } from '@platform-blocks/ui';
const [value, setValue] = useState(0);
const normalizedAngle = useMemo(() => ((value % 360) + 360) % 360, [value]);
const rotations = useMemo(() => value / 360, [value]);
return (
`${Math.round(normalizedAngle)}°`,
secondary: {
position: 'bottom',
formatter: () => `${rotations.toFixed(2)} turns`,
},
}}
/>
Normalized angle: {Math.round(normalizedAngle)}°
Total turns: {rotations.toFixed(2)}
);
}
```
## Component:
Basic line chart visualization component.
### Props
`data` (ChartDataPoint[]) [optional]
Data points (single series) or array of series (multiseries)
`series` (LineChartSeries[]) [optional]
Multiple data series
`lineColor` (string) [optional]
Line color (for single series)
`lineThickness` (number) [optional, default: 2]
Line thickness (for single series)
`lineStyle` ('solid' | 'dashed' | 'dotted') [optional, default: 'solid']
Line style (for single series)
`showPoints` (boolean) [optional, default: true]
Show data points (for single series)
`pointSize` (number) [optional, default: 4]
Point size (for single series)
`pointColor` (string) [optional]
Point color (for single series)
`smooth` (boolean) [optional, default: false]
Smooth curve
`fill` (boolean) [optional, default: false]
Fill area under line
`fillColor` (string) [optional]
Fill color
`fillOpacity` (number) [optional, default: 0.3]
Fill opacity
- … 28 more props documented
### Demos
- Basic | (id: LineChart.basic)
Simple random data line chart with title.
```tsx
import { LineChart } from '../../';
const SERIES = [
{
id: 'north-america',
name: 'North America',
color: '#4C6EF5',
data: [
{ x: 1, y: 120 },
{ x: 2, y: 138 },
{ x: 3, y: 152 },
{ x: 4, y: 167 },
{ x: 5, y: 176 },
{ x: 6, y: 189 },
{ x: 7, y: 205 },
{ x: 8, y: 220 },
{ x: 9, y: 232 },
{ x: 10, y: 246 },
{ x: 11, y: 260 },
{ x: 12, y: 278 },
],
},
{
id: 'emea',
name: 'EMEA',
color: '#20C997',
data: [
{ x: 1, y: 96 },
{ x: 2, y: 108 },
{ x: 3, y: 117 },
{ x: 4, y: 126 },
{ x: 5, y: 134 },
{ x: 6, y: 142 },
{ x: 7, y: 150 },
{ x: 8, y: 158 },
{ x: 9, y: 168 },
{ x: 10, y: 175 },
{ x: 11, y: 182 },
{ x: 12, y: 191 },
],
},
];
return (
`M${value}`,
}}
yAxis={{
show: true,
title: 'Customers (thousands)',
labelFormatter: (value) => `${value}`,
}}
grid={{ show: true, style: 'dashed', color: '#E0E7FF' }}
legend={{ show: true, position: 'bottom' }}
tooltip={{
show: true,
formatter: (point) => `${point.y}k customers in month ${point.x}`,
}}
enableCrosshair
multiTooltip
liveTooltip
enablePanZoom
zoomMode="x"
minZoom={0.3}
/>
);
}
```
- Smooth Area | (id: LineChart.smooth-area)
Smoothed dual-series line chart with gradient fill and custom legend.
```tsx
import { LineChart } from '../../';
const SERIES = [
{
id: 'projected-revenue',
name: 'Projected revenue',
color: '#6366F1',
data: [
{ x: 1, y: 940 },
{ x: 2, y: 980 },
{ x: 3, y: 1020 },
{ x: 4, y: 1090 },
{ x: 5, y: 1175 },
{ x: 6, y: 1260 },
{ x: 7, y: 1340 },
{ x: 8, y: 1415 },
{ x: 9, y: 1490 },
{ x: 10, y: 1575 },
{ x: 11, y: 1660 },
{ x: 12, y: 1740 },
],
},
{
id: 'actual-revenue',
name: 'Actual revenue',
color: '#F97316',
data: [
{ x: 1, y: 910 },
{ x: 2, y: 960 },
{ x: 3, y: 1005 },
{ x: 4, y: 1088 },
{ x: 5, y: 1150 },
{ x: 6, y: 1225 },
{ x: 7, y: 1312 },
{ x: 8, y: 1384 },
{ x: 9, y: 1478 },
{ x: 10, y: 1532 },
{ x: 11, y: 1610 },
{ x: 12, y: 1705 },
],
},
];
return (
`M${value}`,
}}
yAxis={{
show: true,
title: 'Revenue (USD thousands)',
labelFormatter: (value) => `$${Math.round(value)}`,
}}
tooltip={{
show: true,
formatter: (point) => `$${point.y.toLocaleString()}k in month ${point.x}`,
}}
annotations={[
{
id: 'midyear-target',
shape: 'vertical-line',
x: 6,
label: 'Mid-year target',
color: '#6366F1',
dashArray: [6, 6],
},
]}
/>
);
}
```
## Component:
A versatile component for creating styled hyperlinks and navigation elements with hover states and accessibility features.
### Props
`children` (React.ReactNode) [required]
Link text content
`href` (string) [optional]
URL or handler for the link
`onPress` (() => void) [optional]
Custom onPress handler (overrides href)
`size` (SizeValue) [optional, default: 'lg']
Size of the link text (default: 'lg' = 16px to match Text component)
`color` ('primary' | 'secondary' | 'success' | 'warning' | 'error' | 'gray' | 'inherit' | string) [optional]
Color variant or custom color string
`variant` ('default' | 'subtle' | 'hover-underline') [optional, default: 'default']
Link variant
`disabled` (boolean) [optional, default: false]
Whether the link is disabled
`external` (boolean) [optional, default: false]
Whether to show external link indicator
`style` (ViewStyle) [optional]
Custom style for container
`textStyle` (TextStyle) [optional]
Custom style for text
`accessibilityLabel` (string) [optional]
Accessibility label
`target` ('_blank' | '_self') [optional, default: '_self']
Whether this link opens in a new tab/window (web only)
### Demos
- Inline Links | (id: Link.basic) | tags: link
Embed links directly inside supporting copy to guide readers toward related resources.
```tsx
import { Card, Column, Link, Text } from '@platform-blocks/ui';
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
Use the `external` prop when pointing to destinations outside the current shell.
```tsx
import { Card, Column, Link, Text } from '@platform-blocks/ui';
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}
))}
);
}
```
## Component:
A animated loading component for indicating ongoing processes and loading states with various sizes and styles.
### Props
`size` (SizeValue) [optional, default: 'md']
Size of the loader - can be a size token or number
`color` (string) [optional]
Color of the loader
`variant` (LoaderVariant) [optional, default: 'oval']
Variant of the loader
`speed` (number) [optional, default: 1000]
Animation speed in milliseconds
`style` (StyleProp) [optional]
Container style
`testID` (string) [optional]
Test ID for testing
### Demos
- Basic Usage | (id: Loader.basic) | tags: variant
Pick a loader `variant` to match the type of busy indicator you need for a loading state.
```tsx
import { Loader, Row } from '@platform-blocks/ui';
return (
);
}
```
- Sizes | (id: Loader.sizes) | tags: size
Set the `size` token to align loaders with other controls, from `xs` indicators up to `xl` spinners.
```tsx
import type { LoaderProps } from '@platform-blocks/ui';
import { Block, Column, Loader, Row, Text } from '@platform-blocks/ui';
const LOADER_SIZES: Required['size'][] = ['xs', 'sm', 'md', 'lg', 'xl'];
return (
{LOADER_SIZES.map((size) => (
{String(size).toUpperCase()}
))}
);
}
```
## Component:
`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, toggl…
### Props
`visible` (boolean) [optional, default: false]
Controls visibility of the loading overlay.
`zIndex` (number) [optional]
z-index applied to the overlay container. Overrides value defined in overlayProps when provided.
`overlayProps` (OverlayProps) [optional]
Props forwarded to the underlying Overlay component.
`loaderProps` (LoaderProps) [optional]
Props forwarded to the Loader component.
`loader` (ReactNode) [optional]
Custom loader content. When provided, Loader component is not rendered.
### Demos
- Form blocking | (id: LoadingOverlay.basic) | tags: overlays, loading
Locks a simple form while background work finishes and keeps the loader aligned with the card container.
```tsx
import { useState, type ComponentProps } from 'react';
import { StyleSheet } from 'react-native';
import { Button, Card, Column, Input, LoadingOverlay, Switch, Text } from '@platform-blocks/ui';
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@platform-blocks.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%',
},
});
```
## Component:
Visualize categorical mix alongside overall weight with a mosaic-style variable-width column chart.
### Props
`data` (MarimekkoCategory[]) [required]
Categories rendered as variable-width columns.
`legend` (ChartLegend) [optional]
Optional legend configuration.
`xAxis` (ChartAxis) [optional]
Optional x-axis configuration.
`yAxis` (ChartAxis) [optional]
Optional y-axis configuration (defaults to percentage scale).
`grid` (ChartGrid) [optional]
Grid configuration applied to the background.
`columnGap` (number) [optional, default: 12]
Gap (in pixels) inserted between columns.
`segmentBorderRadius` (number) [optional, default: 2]
Corner radius applied to each segment rectangle.
`padding` ({ top: number; right: number; bottom: number; left: number }) [optional]
Override padding around the chart plot area.
`categoryLabelFormatter` ((category: MarimekkoCategory, index: number) => string) [optional]
Formatter applied to categorical labels along the x-axis.
### Demos
- Basic | (id: MarimekkoChart.basic)
Variable-width columns reveal how each pipeline channel and region contributes to total qualified pipeline.
```tsx
import { MarimekkoChart } from '../../';
const PIPELINE_COMPOSITION = [
{
label: 'Inbound',
segments: [
{ label: 'North America', value: 52 },
{ label: 'EMEA', value: 34 },
{ label: 'APAC', value: 24 },
],
},
{
label: 'Outbound',
segments: [
{ label: 'North America', value: 44 },
{ label: 'EMEA', value: 28 },
{ label: 'APAC', value: 18 },
],
},
{
label: 'Partnerships',
segments: [
{ label: 'North America', value: 29 },
{ label: 'EMEA', value: 22 },
{ label: 'APAC', value: 15 },
],
},
{
label: 'Expansion',
segments: [
{ label: 'North America', value: 37 },
{ label: 'EMEA', value: 18 },
{ label: 'APAC', value: 12 },
],
},
];
return (
category.label}
/>
);
}
```
- Budget Allocation | (id: MarimekkoChart.budget-allocation)
Lay out the company plan to see both department budgets and how each team invests within their allocation.
```tsx
import { MarimekkoChart } from '../../';
const BUDGET_PLAN = [
{
label: 'Growth',
segments: [
{ label: 'Paid media', value: 28 },
{ label: 'Field marketing', value: 24 },
{ label: 'Events', value: 18 },
{ label: 'Advocacy', value: 12 },
],
},
{
label: 'Product',
segments: [
{ label: 'Roadmap', value: 26 },
{ label: 'Design', value: 14 },
{ label: 'Research', value: 10 },
{ label: 'Maintenance', value: 22 },
],
},
{
label: 'Customer',
segments: [
{ label: 'Success', value: 18 },
{ label: 'Support', value: 21 },
{ label: 'Education', value: 9 },
{ label: 'Community', value: 7 },
],
},
{
label: 'Operations',
segments: [
{ label: 'Security', value: 15 },
{ label: 'IT', value: 11 },
{ label: 'Finance', value: 14 },
{ label: 'People', value: 13 },
],
},
];
return (
`${category.label} (${category.segments.reduce((sum, seg) => sum + seg.value, 0)}%)`}
/>
);
}
```
## Component:
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.
### Props
`children` (string) [required]
`defaultCodeLanguage` (string) [optional]
Override default code block language guess
`maxHeadingLevel` (number) [optional]
Max heading level to render (others downgraded)
`allowHtml` (boolean) [optional]
Whether to render inline HTML literally (ignored for now)
`components` (Partial) [optional]
Custom renderer overrides
`onLinkPress` ((href: string) => void) [optional]
Optional handler invoked when a markdown link is pressed
### Demos
- Basic Usage | (id: Markdown.basic)
Simple markdown rendering with headers, lists, and text formatting.
```tsx
import { Column, Markdown, Text } from '@platform-blocks/ui';
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)
Markdown rendering with syntax-highlighted code blocks.
```tsx
import { Column, Markdown, Text } from '@platform-blocks/ui';
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
);
}
```
## Component:
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 pro…
### Props
`data` (MasonryItem[]) [required]
Array of items to display in masonry layout
`numColumns` (number) [optional]
Number of columns (default: 2)
`gap` (SizeValue) [optional]
Spacing between items
`optimizeItemArrangement` (boolean) [optional]
Whether to optimize for staggered grid layout
`renderItem` ((item: MasonryItem, index: number) => ReactNode) [optional]
Custom item renderer - receives item and index
`contentContainerStyle` (StyleProp) [optional]
Content container style
`style` (StyleProp) [optional]
Custom styles
`testID` (string) [optional]
Test ID for testing
`loading` (boolean) [optional]
Loading state
`emptyContent` (ReactNode) [optional]
Empty state content
`flashListProps` (Partial, 'data' | 'renderItem' | 'numColumns'>>) [optional]
Flash list props to pass through
`onEndReached` (((info: { distanceFromEnd: number }) => void) | null) [optional]
Callback when the end of the list is reached (for pagination / infinite scroll)
- … 10 more props documented
### Demos
- Basic Masonry | (id: Masonry.basic) | tags: basic, layout, grid, columns, simple
Simple masonry layout with uniform item heights arranged in a two-column grid.
```tsx
import { Masonry } from '../../Masonry';
import type { MasonryItem } from '../../types';
import { Card } from '../../../Card';
import { Text } from '../../../Text';
import { useTheme } from '../../../../core/theme';
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
Masonry layout with configurable number of columns demonstrating different grid arrangements.
```tsx
import { Masonry } from '../../Masonry';
import type { MasonryItem } from '../../types';
import { Card } from '../../../Card';
import { Text } from '../../../Text';
import { Button } from '../../../Button';
import { Row } from '../../../Layout';
import { useTheme } from '../../../../core/theme';
import { useState } from 'react';
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)}
/>
>
);
}
```
## Component:
The Menu component provides a dropdown interface for navigation links, actions, and contextual options. It supports flexible positioning, keyboard navigation, and customizable triggers.
### Props
`opened` (boolean) [optional]
Whether the menu is open
`trigger` ('click' | 'hover' | 'contextmenu') [optional, default: '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') [optional, default: 'auto']
Position relative to trigger
`offset` (number) [optional, default: 4]
Offset from trigger element
`closeOnClickOutside` (boolean) [optional, default: true]
Whether to close when clicking outside
`closeOnEscape` (boolean) [optional, default: true]
Whether to close when pressing escape
`onOpen` (() => void) [optional]
Callback when menu opens
`onClose` (() => void) [optional]
Callback when menu closes
`w` (number | 'target' | 'auto') [optional, default: 'auto']
Menu content width
`maxH` (number) [optional, default: 300]
Maximum height for scrollable content
`shadow` ('none' | 'sm' | 'md' | 'lg' | 'xl') [optional, default: 'md']
Menu content shadow
`radius` ('none' | 'sm' | 'md' | 'lg' | 'xl') [optional, default: 'md']
Border radius
- … 4 more props documented
### Demos
- Basic Usage | (id: Menu.basic) | tags: menu
Pair a trigger with `MenuDropdown` to show primary actions and separators in a compact surface.
```tsx
import {
Button,
Card,
Column,
Icon,
Menu,
MenuDivider,
MenuDropdown,
MenuItem,
Text,
} from '@platform-blocks/ui';
return (
Open menu
}>
Profile
}>
Settings
}>
Help & Support
}>
Logout
);
}
```
- Context Trigger | (id: Menu.context) | tags: menu, contextmenu
Enable `trigger="contextmenu"` to surface a menu when users right-click or long-press a target.
```tsx
import {
Card,
Column,
Icon,
Menu,
MenuDivider,
MenuDropdown,
MenuItem,
Text,
} from '@platform-blocks/ui';
return (
Right-click or long-press this area
}>
Copy link
}>
Rename
}>
Share
}>
Delete
);
}
```
## Component:
A button component that triggers a dropdown menu
### Props
`title` (string) [optional]
Text label (alternative to children)
`children` (React.ReactNode) [optional]
Custom content
`startIcon` (React.ReactNode) [optional]
Leading icon
`endIcon` (React.ReactNode) [optional]
Trailing icon / shortcut hint
`onPress` (() => void) [optional]
Click handler
`disabled` (boolean) [optional]
Whether the button is disabled
`active` (boolean) [optional]
Whether the button is active (selected)
`danger` (boolean) [optional]
Whether the button has destructive styling
`fullWidth` (boolean) [optional]
Whether the button should take up full width
`size` (ComponentSizeValue) [optional]
Size of the button
`compact` (boolean) [optional]
Whether to use compact styling
`rounded` (boolean) [optional]
Whether to use fully rounded corners
- … 17 more props documented
### Demos
- Basic Menu | (id: MenuItemButton.basic)
Simple dropdown menu with icons and dividers.
```tsx
import { Menu, MenuItem, MenuDivider, MenuDropdown, Button, Icon } from '@platform-blocks/ui';
return (
Open Menu
}>
Profile
}>
Settings
}>
Help & Support
}>
Logout
)
}
```
## Component:
A compact calendar component for displaying a month view with selectable dates.
### Demos
- Basic MiniCalendar | (id: MiniCalendar.basic)
Compact calendar showing a week view with date selection.
```tsx
import React, { useState } from 'react';
import { Column, MiniCalendar, Text } from '@platform-blocks/ui';
const [selectedDate, setSelectedDate] = useState(new Date());
return (
setSelectedDate(date)}
numberOfDays={7}
/>
{selectedDate ? `Selected: ${selectedDate.toLocaleDateString()}` : 'No date selected'}
);
}
```
- Custom Day Count | (id: MiniCalendar.customDays)
MiniCalendar with configurable number of days displayed.
```tsx
import React, { useState } from 'react';
import { Button, Column, MiniCalendar, Row, Text } from '@platform-blocks/ui';
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'}
);
}
```
## Component:
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.
### Props
`value` (Date | null) [optional]
Currently selected date (uses the first day of the month)
`onChange` ((date: Date | null) => void) [optional]
Called when user picks a new month
`year` (number) [optional]
Force a specific year to render
`onYearChange` ((year: number) => void) [optional]
Called when the visible year changes
`minDate` (Date) [optional]
Minimum selectable date (inclusive)
`maxDate` (Date) [optional]
Maximum selectable date (inclusive)
`locale` (string) [optional]
Locale used for month labels
`size` (ComponentSizeValue) [optional]
Size token that influences typography weight
`monthLabelFormat` ('short' | 'long') [optional]
Format of month labels
`hideHeader` (boolean) [optional]
Hide navigation header (used when embedded in Calendar)
`monthsPerRow` (ResponsiveProp) [optional]
Responsive override for the number of months rendered per row
### Demos
- Basic | (id: MonthPicker.basic)
```tsx
import React, { useState } from 'react';
import { Column, MonthPicker, Text } from '@platform-blocks/ui';
const [value, setValue] = useState(new Date());
return (
{value
? value.toLocaleDateString(undefined, { month: 'long', year: 'numeric' })
: 'No month selected'}
);
}
```
## Component:
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.
### Props
`value` (Date | null) [optional]
Controlled value for the selected month
`defaultValue` (Date | null) [optional]
Default month when uncontrolled
`onChange` ((value: Date | null) => void) [optional]
Called when the month selection changes
`locale` (string) [optional]
Locale used for formatting the input value
`formatOptions` (Intl.DateTimeFormatOptions) [optional]
Intl format options for rendering the selected month
`formatValue` ((value: Date) => string) [optional]
Custom formatter for the input value; overrides locale/formatOptions
`placeholder` (string) [optional]
Placeholder text when no month is selected
`clearable` (boolean) [optional]
Show a clear button when a month is selected
`closeOnSelect` (boolean) [optional]
Close the picker after selecting a month
`monthPickerProps` (Partial>) [optional]
Additional props forwarded to MonthPicker (except value)
`modalTitle` (string) [optional]
Dialog title text
`onOpen` (() => void) [optional]
Called when the picker dialog opens
- … 1 more props documented
### Demos
- Basic | (id: MonthPickerInput.basic)
```tsx
import React, { useState } from 'react';
import { Column, MonthPickerInput, Text } from '@platform-blocks/ui';
const [value, setValue] = useState(null);
return (
{value
? value.toLocaleDateString(undefined, { month: 'long', year: 'numeric' })
: 'No month selected'}
);
}
```
## Component:
Graph structure visualization (nodes & links) for relationships.
### Props
`nodes` (NetworkNode[]) [required]
Nodes to render in the network
`links` (NetworkLink[]) [required]
Links connecting the nodes
`layout` (NetworkLayoutMode) [optional, default: 'force']
Layout engine mode
`grid` (ChartGrid | boolean) [optional]
Optional grid configuration for coordinate layouts
`xAxis` (ChartAxis) [optional]
X axis configuration when using coordinate layouts
`yAxis` (ChartAxis) [optional]
Y axis configuration when using coordinate layouts
`padding` (Partial<{ top: number; right: number; bottom: number; left: number }>) [optional]
Optional padding overrides
`coordinateAccessor` ({) [optional]
Accessor overrides for coordinate layouts
`x` ((node: NetworkNode, index: number) => number) [optional]
`y` ((node: NetworkNode, index: number) => number) [optional]
`showLabels` (boolean) [optional, default: true]
Show node labels
`nodeRadius` (number) [optional, default: 12]
Radius override for nodes
- … 14 more props documented
### Demos
- Basic | (id: NetworkChart.basic)
```tsx
import { NetworkChart } from '../../';
const NODES = [
{ id: 'product', name: 'Product', group: 'teams', value: 12, color: '#4C6EF5' },
{ id: 'design', name: 'Design', group: 'teams', value: 8, color: '#845EF7' },
{ id: 'engineering', name: 'Engineering', group: 'teams', value: 18, color: '#20C997' },
{ id: 'marketing', name: 'Marketing', group: 'teams', value: 10, color: '#FF922B' },
{ id: 'sales', name: 'Sales', group: 'teams', value: 9, color: '#F03E3E' },
{ id: 'support', name: 'Support', group: 'teams', value: 7, color: '#15AABF' },
{ id: 'platform', name: 'Platform', group: 'initiatives', value: 15, color: '#2F9E44' },
{ id: 'ai', name: 'AI', group: 'initiatives', value: 11, color: '#D9480F' },
];
const LINKS = [
{ source: 'platform', target: 'engineering', value: 6 },
{ source: 'platform', target: 'product', value: 5 },
{ source: 'platform', target: 'support', value: 2 },
{ source: 'ai', target: 'product', value: 4 },
{ source: 'ai', target: 'marketing', value: 3 },
{ source: 'ai', target: 'sales', value: 2 },
{ source: 'product', target: 'design', value: 7 },
{ source: 'product', target: 'engineering', value: 8 },
{ source: 'marketing', target: 'sales', value: 5 },
{ source: 'support', target: 'sales', value: 4 },
];
return (
);
}
```
- Customer Referral Network | (id: NetworkChart.customer-referral-network)
```tsx
import { NetworkChart } from '../../';
import type { NetworkLink, NetworkNode } from '../../types';
const COHORTS: NetworkNode[] = [
{ id: 'seed-advocates', name: 'Seed Advocates', group: 'seed', value: 38, color: '#12B886' },
{ id: 'growth-us', name: 'Growth - US', group: 'growth', value: 44, color: '#228BE6' },
{ id: 'growth-eu', name: 'Growth - EU', group: 'growth', value: 36, color: '#15AABF' },
{ id: 'enterprise-wave', name: 'Enterprise Wave', group: 'enterprise', value: 29, color: '#F76707' },
{ id: 'partner-ecosystem', name: 'Partner Ecosystem', group: 'partners', value: 24, color: '#845EF7' },
{ id: 'freemium-community', name: 'Freemium Community', group: 'seed', value: 50, color: '#5C7CFA' },
{ id: 'latam-expansion', name: 'LATAM Expansion', group: 'growth', value: 31, color: '#FF922B' },
];
const REFERRALS: NetworkLink[] = [
{ source: 'seed-advocates', target: 'freemium-community', weight: 6.4, meta: { wave: 1 } },
{ source: 'seed-advocates', target: 'growth-us', weight: 4.7, meta: { wave: 1 } },
{ source: 'freemium-community', target: 'growth-eu', weight: 3.8, meta: { wave: 2 } },
{ source: 'growth-us', target: 'enterprise-wave', weight: 3.3, meta: { wave: 2 } },
{ source: 'growth-eu', target: 'enterprise-wave', weight: 2.6, meta: { wave: 3 } },
{ source: 'partner-ecosystem', target: 'enterprise-wave', weight: 3.9, meta: { wave: 1 } },
{ source: 'partner-ecosystem', target: 'growth-us', weight: 2.4, meta: { wave: 2 } },
{ source: 'latam-expansion', target: 'growth-eu', weight: 3.2, meta: { wave: 2 } },
{ source: 'latam-expansion', target: 'freemium-community', weight: 2.7, meta: { wave: 3 } },
];
const waveToColor = (wave?: number) => {
if (wave === 1) return '#34C759';
if (wave === 2) return '#4DABF7';
if (wave === 3) return '#FF922B';
return '#ADB5BD';
};
const waveToOpacity = (wave?: number) => {
if (wave === 1) return 0.7;
if (wave === 2) return 0.6;
if (wave === 3) return 0.55;
return 0.45;
};
return (
waveToColor(typeof link.meta?.wave === 'number' ? link.meta.wave : Number(link.meta?.wave))}
linkOpacityAccessor={(link) => waveToOpacity(typeof link.meta?.wave === 'number' ? link.meta.wave : Number(link.meta?.wave))}
/>
);
}
```
## Component:
The Notice component displays important messages to users with different severity levels, variants, and optional actions like dismissal.
### Props
`variant` (NoticeVariant) [optional, default: 'light']
`color` (NoticeColor | string) [optional, default: 'primary']
`sev` (NoticeSeverity) [optional]
`title` (string) [optional]
`children` (React.ReactNode) [optional]
`icon` (React.ReactNode | string | null | false) [optional]
`fullWidth` (boolean) [optional, default: false]
`withCloseButton` (boolean) [optional, default: false]
`closeButtonLabel` (string) [optional]
`onClose` (() => void) [optional]
`style` (StyleProp) [optional]
`testID` (string) [optional]
### Demos
- Basics | (id: Notice.basic) | tags: alerts
Show the default alert alongside common status colors to communicate outcomes and guidance.
```tsx
import type { ComponentProps } from 'react';
import { Notice, Column, Text } from '@platform-blocks/ui';
type NoticeExample = {
key: string;
title?: string;
color?: ComponentProps['color'];
message: string;
};
const ALERTS: NoticeExample[] = [
{
key: 'default',
color: 'secondary',
message: 'Use alerts to highlight contextual information inline with page content.'
},
{
key: 'info',
title: 'Information',
color: 'primary',
message: 'Share helpful tips or next steps without interrupting the flow.'
},
{
key: 'success',
title: 'Success',
color: 'success',
message: 'Confirm when actions complete successfully so users can continue.'
},
{
key: 'warning',
title: 'Warning',
color: 'warning',
message: 'Draw attention to inputs that need review before progressing.'
},
{
key: 'error',
title: 'Error',
color: 'error',
message: 'Describe blocking errors with guidance on how to resolve them.'
}
];
return (
{ALERTS.map(({ key, title, color, message }) => (
{message}
))}
Notices adapt their color palette to match the tone of the underlying message.
);
}
```
- Variants | (id: Notice.variants) | tags: alerts, variants
Compare light, outline, filled, and subtle variants to match alert prominence to the message.
```tsx
import type { ComponentProps } from 'react';
import { Notice, Column, Text } from '@platform-blocks/ui';
type Variant = NonNullable['variant']>;
const VARIANT_EXAMPLES: Array<{
variant: Variant;
color: NonNullable['color']>;
title: string;
body: string;
}> = [
{
variant: 'light',
color: 'primary',
title: 'Light',
body: 'Balanced background and border treatment best suited for inline notes.'
},
{
variant: 'outline',
color: 'success',
title: 'Outline',
body: 'Use outline styling for subtle emphasis without increasing background contrast.'
},
{
variant: 'filled',
color: 'warning',
title: 'Filled',
body: 'High-contrast option for urgent or high-visibility messaging.'
},
{
variant: 'subtle',
color: 'error',
title: 'Subtle',
body: 'Removes background color but keeps iconography and text tinted.'
}
];
return (
{VARIANT_EXAMPLES.map(({ variant, color, title, body }) => (
{body}
))}
Choose a variant that matches the prominence you want without straying from the semantic color.
);
}
```
## Component:
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 for…
### Props
`value` (number) [optional]
Number value
`onChange` ((value: number | undefined) => void) [optional]
Change handler
`allowDecimal` (boolean) [optional]
Allow decimal values
`allowNegative` (boolean) [optional, default: true]
Allow negative values
`allowLeadingZeros` (boolean) [optional, default: true]
Allow leading zeros while editing
`allowedDecimalSeparators` (string[]) [optional]
Additional characters that should be treated as decimal separators
`decimalSeparator` (string) [optional, default: DEFAULT_DECIMAL_SEPARATOR]
Decimal separator character
`decimalScale` (number) [optional]
Maximum number of digits after the decimal point
`fixedDecimalScale` (boolean) [optional, default: false]
When true, pads the decimal part with trailing zeros to match decimalScale
`min` (number) [optional]
Minimum value
`max` (number) [optional]
Maximum value
`step` (number) [optional, default: 1]
Step increment
- … 40 more props documented
### Demos
- Basic | (id: NumberInput.basic) | tags: basic, numeric, step
Controlled number input with simple step controls and live value preview.
```tsx
import { useState } from 'react';
import { Column, NumberInput, Text } from '@platform-blocks/ui';
const [quantity, setQuantity] = useState(2);
return (
Basic number input
Controlled numeric field with increment buttons and a minimum of zero.
Current value: {quantity ?? '—'}
);
}
```
- Formats | (id: NumberInput.formats) | tags: currency, percent, formatting
Showcases currency formatting, percentage suffixes, and a derived total.
```tsx
import { useState } from 'react';
import { Column, NumberInput, Text } from '@platform-blocks/ui';
const [price, setPrice] = useState(249.99);
const [discount, setDiscount] = useState(10);
const finalPrice = price != null && discount != null
? price * (1 - discount / 100)
: undefined;
return (
Formatted values
Pair currency formatting with percentage discounts to display a calculated total.
Final price: {finalPrice != null ? `$${finalPrice.toFixed(2)}` : '—'}
);
}
```
## Component:
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 g…
### Props
`color` (string) [optional]
Background color for the overlay. Accepts raw colors or theme tokens like `primary.6`.
`opacity` (number) [optional]
Opacity applied to the background color. Defaults to 0.6.
`backgroundOpacity` (number) [optional]
Opacity applied to the entire overlay, including gradients and blur effects. Defaults to 1.
`gradient` (string) [optional]
Web-only CSS gradient string. Falls back to `color` on native platforms.
`blur` (number | string) [optional]
Amount of backdrop blur (in pixels). Supported on web.
`radius` (SizeValue | number) [optional]
Corner radius for the overlay surface.
`zIndex` (number) [optional]
z-index applied to the overlay container.
`fixed` (boolean) [optional, default: false]
Use viewport-fixed positioning instead of absolute positioning (web only).
`center` (boolean) [optional, default: false]
Center children horizontally and vertically.
`style` (StyleProp) [optional]
Optional style overrides applied after computed styles.
`children` (ReactNode) [optional]
Overlay content rendered on top of the dimmed background.
### Demos
- Overlay patterns | (id: Overlay.basic) | tags: overlays, effects
Showcases dimming, gradient, and blurred overlays that inherit their parent size for spotlights and modal scrims.
```tsx
import { useState, type ComponentProps } from 'react';
import { ImageBackground, StyleSheet } from 'react-native';
import { Button, Column, Overlay, Text } from '@platform-blocks/ui';
const HERO_IMAGE = 'https://picsum.photos/800/450?random=8';
const GRADIENT_IMAGE = 'https://picsum.photos/800/450?random=2';
const BLUR_IMAGE = 'https://picsum.photos/800/450?random=10';
type OverlayExample = {
key: string;
image: string;
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,
},
});
```
## Component:
A comprehensive pagination component that provides intuitive navigation through large datasets. The component offers flexible configuration options and consistent styling across different use cases.
### Props
`current` (number) [required]
Current page number (1-indexed)
`total` (number) [required]
Total number of pages
`siblings` (number) [optional, default: 1]
Number of page items to show on each side of current page
`boundaries` (number) [optional, default: 1]
Number of page items to show at the boundaries
`onChange` ((page: number) => void) [required]
Page change handler
`size` (ComponentSizeValue) [optional, default: 'md']
Size of pagination controls
`variant` ('default' | 'outline' | 'subtle') [optional, default: 'default']
Variant style
`color` ('primary' | 'secondary' | 'gray') [optional, default: 'primary']
Color scheme
`showFirst` (boolean) [optional, default: true]
Show first/last page buttons
`showPrevNext` (boolean) [optional, default: true]
Show previous/next buttons
`labels` ({) [optional, default: {}]
Custom labels for navigation buttons
`first` (ReactNode) [optional]
- … 16 more props documented
### Demos
- Basic | (id: Pagination.basic) | tags: basic, pagination, navigation
Provide `current`, `total`, and an `onChange` handler to keep numbered pagination in sync with surrounding state.
```tsx
import { useState } from 'react';
import { Column, Pagination, Text } from '@platform-blocks/ui';
const [currentPage, setCurrentPage] = useState(1);
const totalPages = 10;
return (
Page {currentPage} of {totalPages}
);
}
```
- Variants | (id: Pagination.variants) | tags: variants, style, appearance
Switch the `variant` prop between `default`, `outline`, and `subtle` to align pagination with the surrounding surface treatment.
```tsx
import { useState } from 'react';
import { Column, Pagination, Text } from '@platform-blocks/ui';
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.
);
}
```
## Component:
Highlight the small number of categories that drive the majority of impact with a bar plus cumulative line visualization.
### Props
`data` (ParetoChartDatum[]) [required]
Raw categories to render inside the Pareto analysis.
`sortDirection` ('desc' | 'asc' | 'none') [optional, default: 'desc']
Sorting direction applied before calculating cumulative percentages.
`valueSeriesLabel` (string) [optional, default: 'Frequency']
Display label for the bar series.
`cumulativeSeriesLabel` (string) [optional, default: 'Cumulative %']
Display label for the cumulative line series.
`barColor` (string) [optional]
Base color used for the bar series when data points do not provide one.
`lineColor` (string) [optional]
Base color used for the cumulative line series.
`categoryLabelFormatter` ((category: string, index: number) => string) [optional]
Optional formatter applied to the categorical axis labels.
### Demos
- Basic | (id: ParetoChart.basic)
Simple Pareto chart showing how cumulative contribution highlights the dominant defect categories.
```tsx
import { ParetoChart } from '../../';
const DEFECT_BREAKDOWN = [
{ label: 'Authentication', value: 118 },
{ label: 'Checkout', value: 96 },
{ label: 'Notifications', value: 64 },
{ label: 'Analytics', value: 42 },
{ label: 'Billing', value: 31 },
{ label: 'Integrations', value: 27 },
{ label: 'Mobile', value: 19 },
{ label: 'Reporting', value: 17 },
];
return (
);
}
```
- Customer Support Hotspots | (id: ParetoChart.customer-support-hotspots)
Highlight how a handful of ticket categories drive the majority of support backlog volume.
```tsx
import { ParetoChart } from '../../';
const SUPPORT_CASES = [
{ label: 'Login reset', value: 420 },
{ label: 'Billing error', value: 318 },
{ label: 'Delayed shipment', value: 247 },
{ label: 'Missing items', value: 186 },
{ label: 'Promo code', value: 132 },
{ label: 'Damaged product', value: 108 },
{ label: 'Account locked', value: 96 },
{ label: 'Wrong item', value: 74 },
{ label: 'Return label', value: 65 },
{ label: 'Subscription cancel', value: 52 },
];
return (
);
}
```
## Component:
The `PhoneInput` component provides a flexible way to capture telephone numbers with built-in masking and formatting.
### Props
`value` (string) [optional, default: '']
Phone number value (digits only)
`onChange` ((raw: string, formatted: string) => void) [optional]
Change handler receiving (rawDigits, formattedDisplay)
`country` (string) [optional, default: 'US']
Country code for format (US, CA, UK, FR, DE, AU, BR, IN, JP, INTL)
`autoDetect` (boolean) [optional, default: true]
Auto-detect format based on input
`showCountryCode` (boolean) [optional, default: true]
Show country code prefix in display
`mask` (string) [optional]
Custom mask pattern (overrides country-based mask). Use '0' for digits, other characters as literals
### Demos
- Basic | (id: PhoneInput.basic) | tags: basic, phone, input
Controlled PhoneInput example that surfaces both raw digits and the formatted display.
```tsx
import { useState } from 'react';
import { Card, Code, Column, PhoneInput, Text } from '@platform-blocks/ui';
const [raw, setRaw] = useState('');
const [formatted, setFormatted] = useState('');
return (
Basic phone input
Controlled phone field showing both the raw digits and the formatted display value.
{
setRaw(rawDigits);
setFormatted(formattedDisplay);
}}
country="US"
showCountryCode
/>
Current values
{JSON.stringify({ raw, formatted }, null, 2)}
);
}
```
- International | (id: PhoneInput.international) | tags: international, auto-detect, phone
Compare auto-detected formats with a manual international configuration.
```tsx
import { useState } from 'react';
import { Card, Code, Column, PhoneInput, Text } from '@platform-blocks/ui';
const [autoDetectValue, setAutoDetectValue] = useState('');
const [autoDetectFormatted, setAutoDetectFormatted] = useState('');
const [intlValue, setIntlValue] = useState('');
const [intlFormatted, setIntlFormatted] = useState('');
return (
International detection
Compare auto-detected formats against a fully manual international mask.
{
setAutoDetectValue(raw);
setAutoDetectFormatted(formatted);
}}
autoDetect
showCountryCode
placeholder="Try 5551234567, 447911123456, or 33123456789"
/>
{
setIntlValue(raw);
setIntlFormatted(formatted);
}}
autoDetect={false}
showCountryCode={false}
placeholder="Enter any international number"
/>
Values
{JSON.stringify(
{
autoDetect: { raw: autoDetectValue, formatted: autoDetectFormatted },
international: { raw: intlValue, formatted: intlFormatted }
},
null,
2
)}
);
}
```
## Component:
Proportional slice chart for categorical part-to-whole representation.
### Props
`data` (PieChartDataPoint[]) [required]
Data points
`innerRadius` (number) [optional]
Inner radius (for donut chart)
`outerRadius` (number) [optional]
Outer radius
`startAngle` (number) [optional]
Start angle in degrees
`endAngle` (number) [optional]
End angle in degrees
`padAngle` (number) [optional]
Padding between slices
`showLabels` (boolean) [optional]
Show labels
`labelPosition` ('inside' | 'outside' | 'center') [optional]
Label position
`labelStrategy` ('auto' | 'inside' | 'outside' | 'center') [optional]
Automatically choose label placement
`labelAutoSwitchAngle` (number) [optional]
Minimum slice angle (deg) to keep label inside when strategy is auto
`wrapLabels` (boolean) [optional]
Automatically wrap labels that exceed width
`labelMaxCharsPerLine` (number) [optional]
Maximum characters per label line when wrapping
- … 18 more props documented
### Demos
- Basic | (id: PieChart.basic)
```tsx
import { PieChart } from '../../';
const TRAFFIC_SOURCES = [
{ id: 'direct', label: 'Direct', value: 55, color: '#4C6EF5' },
{ id: 'organic', label: 'Organic', value: 25, color: '#51CF66' },
{ id: 'referral', label: 'Referral', value: 15, color: '#FF922B' },
{ id: 'social', label: 'Social', value: 5, color: '#845EF7' },
];
return (
`${value}%`}
legend={{ show: true, position: 'right' }}
tooltip={{
show: true,
formatter: (segment) => `${segment.label}: ${segment.value}%`,
}}
startAngle={-90}
endAngle={270}
/>
);
}
```
- Browser Usage Share | (id: PieChart.browser-usage-share)
```tsx
import { PieChart, type PieChartDataPoint } from '../../';
const BROWSER_USAGE = [
{ id: 'chrome', label: 'Chrome', value: 52, color: '#5C7CFA' },
{ id: 'safari', label: 'Safari', value: 28, color: '#38D9A9' },
{ id: 'edge', label: 'Edge', value: 11, color: '#FF922B' },
{ id: 'firefox', label: 'Firefox', value: 6, color: '#BE4BDB' },
{ id: 'other', label: 'Other', value: 3, color: '#ADB5BD' },
];
const formatLabel = (slice: PieChartDataPoint) => `${slice.label} ${slice.value}%`;
const formatTooltip = (slice: PieChartDataPoint) => `${slice.label}: ${slice.value}% of sessions`;
return (
);
}
```
## Component:
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.
### Props
`length` (number) [optional, default: 4]
Number of PIN digits
`keyboardFocusId` (string) [optional]
Stable id used by KeyboardManager to restore focus
`value` (string) [optional, default: '']
PIN value
`onChange` ((pin: string) => void) [optional]
Change handler
`mask` (boolean) [optional, default: false]
Whether to mask PIN
`maskChar` (string) [optional, default: '•']
Character to use for masking
`manageFocus` (boolean) [optional, default: true]
Whether to focus next input automatically
`enforceOrderInitialOnly` (boolean) [optional]
Enforce sequential entry (forces focus to first empty). If false, user can edit any position after complete
`type` ('alphanumeric' | 'numeric') [optional, default: 'numeric']
Type of input
`placeholder` (string) [optional, default: '']
Placeholder for each input
`allowPaste` (boolean) [optional, default: true]
Whether to allow paste
`oneTimeCode` (boolean) [optional, default: false]
One-time code auto-complete
- … 13 more props documented
### Demos
- Basic | (id: PinInput.basic) | tags: basic, pin, code
Controlled 4-digit PIN input with automatic focus handoff and live preview.
```tsx
import { useState } from 'react';
import { Column, PinInput, Text } from '@platform-blocks/ui';
const [value, setValue] = useState('');
return (
Basic PIN input
Controlled 4-digit PIN field with automatic focus management.
{value ? (
Current value: {value}
) : null}
);
}
```
- Types | (id: PinInput.types) | tags: types, numeric, alphanumeric
Contrast numeric-only PIN entry with an alphanumeric option for recovery codes.
```tsx
import { useState } from 'react';
import { Column, PinInput, Text } from '@platform-blocks/ui';
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.
);
}
```
## Component:
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.
### Props
`children` (ReactNode) [required]
`opened` (boolean) [optional]
Controlled open state
`defaultOpened` (boolean) [optional, default: false]
Initial open state in uncontrolled mode
`onChange` ((opened: boolean) => void) [optional]
Called when open state changes
`onOpen` (() => void) [optional]
Called when popover opens
`onClose` (() => void) [optional]
Called when popover closes
`onDismiss` (() => void) [optional]
Called when popover is dismissed via outside click or escape
`trigger` ('click' | 'hover') [optional, default: 'click']
How the popover is triggered: 'click' (default) or 'hover' (mostly useful for devices with a mouse)
`disabled` (boolean) [optional, default: false]
Disable popover entirely
`closeOnClickOutside` (boolean) [optional, default: true]
Close when clicking outside
`closeOnEscape` (boolean) [optional, default: true]
Close when pressing Escape
`clickOutsideEvents` (string[]) [optional]
Events considered for outside click detection (web only)
- … 33 more props documented
### Demos
- Basic Usage | (id: Popover.basic) | tags: popover
Popover targets wrap an interactive element and render dropdown content within `Popover.Dropdown`.
```tsx
import { Button, Card, Column, Popover, Text } from '@platform-blocks/ui';
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
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
import { Button, Card, Column, Popover, Text } from '@platform-blocks/ui';
return (
Hover over me
Hover popover
This popover opens on hover, ideal for mouse users who want quick access to additional content.
);
}
```
## Component:
The Progress component displays the completion progress of a task or process. Supports different variants, colors, and animations.
### Props
`value` (number) [required]
0-100
`size` (SizeValue) [optional, default: 'md']
`color` (ProgressColor | string) [optional, default: 'primary']
`radius` (SizeValue) [optional, default: 'md']
`striped` (boolean) [optional, default: false]
`animate` (boolean) [optional, default: false]
`transitionDuration` (number) [optional, default: 0]
ms
`fullWidth` (boolean) [optional]
`style` (StyleProp) [optional]
`testID` (string) [optional]
### Demos
- Basics | (id: Progress.basic) | tags: progress
Track a single completion percentage, adjust it with controls, and embed the bar inside constrained layouts.
```tsx
import { useState } from 'react';
import { Block, Button, Column, Progress, Row, Text } from '@platform-blocks/ui';
const STEP = 10;
const [completion, setCompletion] = useState(60);
const handleAdjust = (delta: number) => {
setCompletion((value) => Math.min(100, Math.max(0, value + delta)));
};
return (
Scouting report completion
{completion}% of checklist items submitted.
handleAdjust(-STEP)}>
-10%
handleAdjust(STEP)}>
+10%
Embed inside constrained layouts
);
}
```
- Variants | (id: Progress.variants) | tags: color, stripe
Compare default, semantic, and striped progress states to match the tone of your status updates.
```tsx
import { Column, Progress, Text } from '@platform-blocks/ui';
const VARIANTS = [
{
label: 'Primary status',
value: 60,
props: {}
},
{
label: 'Success status',
value: 82,
props: { color: 'success' as const }
},
{
label: 'Warning status',
value: 45,
props: { color: 'warning' as const }
},
{
label: 'Striped + animated',
value: 70,
props: { striped: true, animate: true, transitionDuration: 600 }
}
] as const;
return (
{VARIANTS.map(({ label, value, props }) => (
{label}
))}
);
}
```
## Component:
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.
### Props
`value` (string) [required]
The data/text to encode in the QR code
`size` (number) [optional]
Size of the QR code (both width and height)
`backgroundColor` (string) [optional]
Background color of the QR code
`color` (string) [optional]
Foreground color (the QR code pattern color)
`moduleShape` ('square' | 'rounded' | 'diamond') [optional]
Module shape variant for data modules. Note: Finder patterns (corner anchors) always remain square for optimal scanner compatibility.
`finderShape` ('square' | 'rounded') [optional]
Corner (finder) shape variant - DEPRECATED: Finder patterns always remain square
`cornerRadius` (number) [optional]
Rounded corner radius factor (0-1) applied when moduleShape='rounded'
`gradient` ({) [optional]
Gradient fill (overrides color)
`type` ('linear' | 'radial') [optional]
`from` (string) [required]
Start color
`to` (string) [required]
End color
`rotation` (number) [optional]
(linear) rotation deg (0=left->right)
- … 16 more props documented
### Demos
- Basics | (id: QRCode.basic) | tags: qr-code
Render a single QR code for a link or payload and provide helper text for scanning context.
```tsx
import { Column, QRCode, Text } from '@platform-blocks/ui';
return (
Scan to open the Platform Blocks docs.
);
}
```
- Sizes | (id: QRCode.sizes) | tags: size
Preview recommended QR code dimensions to pick the right footprint for your canvas.
```tsx
import { Column, QRCode, Row, Text } from '@platform-blocks/ui';
const SIZES = [128, 152, 176, 200] as const;
return (
Available size presets
{SIZES.map((size) => (
{size}px
))}
);
}
```
## Component:
Displays multivariate data across axes starting from the same origin.
### Props
`series` (RadarChartSeries[]) [required]
Radar series to display
`maxValue` (number) [optional]
Maximum value displayed across axes
`radialGrid` (RadarGridConfig) [optional]
Grid styling configuration
`smooth` (boolean | number) [optional]
Smooth the polygon edges; pass a number between 0 and 1 to control tension
`fill` (boolean) [optional, default: true]
Fill the radar area
`colorScheme` (string) [optional]
Color palette identifier
`enableCrosshair` (boolean) [optional]
Enable radial crosshair highlights
`multiTooltip` (boolean) [optional]
Enable multi-series tooltip aggregation
`liveTooltip` (boolean) [optional]
Follow pointer with tooltip
`legend` (ChartLegend) [optional]
Legend configuration
`tooltip` (ChartTooltip) [optional]
Tooltip configuration
`annotations` (any[]) [optional]
Additional annotations to render
### Demos
- Skill Comparison | (id: RadarChart.skill-comparison)
Comparison of three engineering guilds with polygon grid, crosshair, and aggregated tooltip.
```tsx
import { RadarChart } from '../../';
const AXES = [
'Code quality',
'Delivery speed',
'Testing coverage',
'Observability',
'Collaboration',
'Innovation',
];
const buildSeriesData = (values: number[]) =>
AXES.map((axis, index) => ({ axis, value: values[index] }));
const SERIES = [
{
id: 'frontend-guild',
name: 'Frontend guild',
color: 'rgba(59, 130, 246, 0.6)',
showPoints: true,
pointSize: 4,
data: buildSeriesData([92, 84, 78, 86, 90, 74]),
},
{
id: 'platform-guild',
name: 'Platform guild',
color: 'rgba(16, 185, 129, 0.6)',
showPoints: true,
pointSize: 4,
data: buildSeriesData([88, 79, 91, 93, 82, 70]),
},
{
id: 'qa-guild',
name: 'QA guild',
color: 'rgba(249, 115, 22, 0.6)',
showPoints: true,
pointSize: 4,
data: buildSeriesData([80, 72, 95, 88, 76, 68]),
},
];
return (
`${point.axis}: ${Math.round(point.value)}%`,
}}
/>
);
}
```
- Product Health | (id: RadarChart.product-health)
Product health snapshot with circular grid, point markers, and custom tooltip messaging.
```tsx
import { RadarChart } from '../../';
const AXES = [
'Availability',
'Latency',
'NPS',
'Feature velocity',
'Security posture',
'Cost efficiency',
];
const makeSeriesData = (values: number[]) =>
AXES.map((axis, index) => ({ axis, value: values[index] }));
const SERIES = [
{
id: 'current-health',
name: 'Current health',
color: 'rgba(14, 165, 233, 0.55)',
showPoints: true,
pointSize: 3,
data: makeSeriesData([8.4, 7.2, 6.8, 7.5, 8.8, 6.2]),
},
{
id: 'target-health',
name: 'Target health',
color: 'rgba(59, 130, 246, 0.45)',
showPoints: true,
pointSize: 3,
data: makeSeriesData([9.2, 8.6, 8.1, 8.5, 9.0, 7.5]),
},
];
return (
`${point.axis}: ${point.value.toFixed(1)} / 10`,
}}
/>
);
}
```
## Component:
Circular bar segments representing values radially.
### Props
`data` (RadialBarDatum[]) [required]
Radial bar data to render
`radius` (number) [optional]
Radius of outer ring (auto if not provided)
`barThickness` (number) [optional, default: 14]
Thickness of each arc
`gap` (number) [optional, default: 8]
Gap (px) between concentric bars
`minAngle` (number) [optional, default: 0]
Minimum angle span percentage (helps show tiny values)
`startAngle` (number) [optional, default: -90]
Start angle in degrees (default -90 = top)
`endAngle` (number) [optional, default: 270]
End angle in degrees (default 270 for full circle)
`animate` (boolean) [optional, default: true]
Animate value growth
`showValueLabels` (boolean) [optional, default: true]
Show value labels inside arcs
`valueFormatter` ((value: number, datum: RadialBarDatum, index: number) => string) [optional]
Format value for label
`multiTooltip` (boolean) [optional, default: true]
Enable aggregated tooltip across arcs
`liveTooltip` (boolean) [optional, default: true]
Keep tooltip following the pointer
- … 3 more props documented
### Demos
- Basic | (id: RadialBarChart.basic)
```tsx
import { RadialBarChart } from '../../';
const METRICS = [
{ id: 'uptime', label: 'Uptime', value: 99, max: 100, color: '#4C6EF5', trackColor: '#E3E9FF' },
{ id: 'nps', label: 'NPS', value: 72, max: 100, color: '#20C997', trackColor: '#DEF7EE' },
{ id: 'retention', label: 'Retention', value: 86, max: 100, color: '#FF922B', trackColor: '#FFE8D7' },
{ id: 'sla', label: 'SLA', value: 94, max: 100, color: '#845EF7', trackColor: '#EFE6FF' },
];
return (
`${value}% ${datum.label}`}
multiTooltip
liveTooltip
legend={{ show: true, position: 'bottom' }}
/>
);
}
```
## Component:
Radio buttons allow users to select a single option from a group of mutually exclusive choices.
### Props
`value` (string) [required]
Radio value
`checked` (boolean) [optional, default: false]
Whether radio is selected
`onChange` ((value: string) => void) [optional]
Change handler
`name` (string) [optional]
Radio group name
`size` (SizeValue) [optional, default: 'md']
Radio size
`color` (ColorValue) [optional, default: 'primary']
Radio color theme
`label` (React.ReactNode) [optional]
Radio label
`disabled` (boolean) [optional, default: false]
Whether radio is disabled
`required` (boolean) [optional, default: false]
Whether radio is required
`error` (string) [optional]
Error message
`description` (string) [optional]
Helper text
`labelPosition` ('left' | 'right') [optional, default: 'right']
Label position relative to radio
- … 3 more props documented
### Demos
- Basic Usage | (id: Radio.basic) | tags: Radio, RadioGroup
Use standalone `Radio` components for custom layouts or pass an `options` array to `RadioGroup` for quick single-selection forms.
```tsx
import { useState } from 'react';
import { Column, Radio, RadioGroup, Text } from '@platform-blocks/ui';
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: size, color, state
Combine the `size`, `color`, and validation props to align radios with your UI tokens and state requirements.
```tsx
import { useState } from 'react';
import { Column, Radio, RadioGroup, Text } from '@platform-blocks/ui';
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
);
}
```
## Component:
An interactive component for displaying star ratings and allowing users to provide ratings with customizable appearance.
### Props
`value` (number) [optional]
Current rating value
`defaultValue` (number) [optional]
Initial rating value for uncontrolled component
`count` (number) [optional]
`readOnly` (boolean) [optional]
`allowFraction` (boolean) [optional]
`allowHalf` (boolean) [optional]
Deprecated
`precision` (number) [optional]
`size` (SizeValue | number) [optional]
`color` (string) [optional]
`emptyColor` (string) [optional]
`hoverColor` (string) [optional]
`onChange` ((value: number) => void) [optional]
- … 12 more props documented
### Demos
- Basics | (id: Rating.basic) | tags: interactive
Capture a single rating value with an interactive control and mirror the current score in helper text.
```tsx
import { useState } from 'react';
import { Column, Rating, Text } from '@platform-blocks/ui';
const [score, setScore] = useState(3);
return (
Current score: {score} out of 5.
);
}
```
- Sizes | (id: Rating.sizes) | tags: size
Compare the available `size` tokens side by side to pick the right scale for your scene.
```tsx
import { Rating, Block } from '@platform-blocks/ui';
return (
)
}
```
## Component:
Layered density plots (joyplot) comparing distributions across categories.
### Props
`series` (DensitySeries[]) [required]
Density series rendered in the ridge chart
`samples` (number) [optional]
Density sample resolution
`bandwidth` (number) [optional]
Kernel bandwidth override
`bandPadding` (number) [optional]
Fractional padding between ridge bands (0 - 0.8)
`amplitudeScale` (number) [optional]
Amplitude scaling applied to each ridge (0.1 - 1)
`xAxis` (ChartAxis) [optional]
X-axis configuration
`yAxis` (ChartAxis) [optional]
Y-axis configuration
`grid` (ChartGrid) [optional]
Grid line configuration
`statsMarkers` (RidgeStatsMarkersConfig) [optional]
Optional statistic marker configuration
### Demos
- Api Latency Endpoints | (id: RidgeChart.api-latency-endpoints)
```tsx
import { RidgeChart } from '../../';
const SAMPLE_POINTS = 170;
const createLatencyProfile = (base: number, jitter: number, tail: number) =>
Array.from({ length: SAMPLE_POINTS }, (_, index) => {
const diurnal = Math.sin(index / 7) * jitter;
const deployWave = Math.max(0, Math.sin((index - 28) / 16)) * tail;
const background = Math.cos(index / 4.5) * jitter * 0.3;
const heavyTail = Math.pow(Math.sin((index + 12) / 40), 6) * tail * 1.8;
const value = base + diurnal + deployWave + background + heavyTail;
return Number(Math.max(48, value).toFixed(1));
});
const formatLatency = (value: number) => `${Math.round(value)} ms`;
const latencyTooltip = ({ value, density, series }: any) => {
const p90 = series?.stats?.p90;
const p90Label = p90 != null ? ` • p90 ${formatLatency(p90)}` : '';
return `${formatLatency(value)} • density ${(density * 100).toFixed(1)}%${p90Label}`;
};
const SERIES = [
{
id: 'projects',
name: 'GET /projects',
color: '#6366f1',
values: createLatencyProfile(210, 48, 160),
fillOpacity: 0.64,
strokeWidth: 1.2,
valueFormatter: formatLatency,
tooltipFormatter: latencyTooltip,
},
{
id: 'reports',
name: 'GET /reports',
color: '#f43f5e',
values: createLatencyProfile(260, 52, 210),
fillOpacity: 0.64,
strokeWidth: 1.2,
valueFormatter: formatLatency,
tooltipFormatter: latencyTooltip,
},
{
id: 'ingest',
name: 'POST /ingest',
color: '#0ea5e9',
values: createLatencyProfile(340, 60, 280),
fillOpacity: 0.64,
strokeWidth: 1.2,
valueFormatter: formatLatency,
tooltipFormatter: latencyTooltip,
},
{
id: 'search',
name: 'GET /search',
color: '#facc15',
values: createLatencyProfile(180, 44, 150),
fillOpacity: 0.64,
strokeWidth: 1.2,
valueFormatter: formatLatency,
tooltipFormatter: latencyTooltip,
},
{
id: 'billing',
name: 'POST /billing',
color: '#22c55e',
values: createLatencyProfile(400, 66, 320),
fillOpacity: 0.64,
strokeWidth: 1.2,
valueFormatter: formatLatency,
tooltipFormatter: latencyTooltip,
},
];
return (
formatLatency(value as number),
}}
yAxis={{
show: true,
tickLength: 6,
}}
/>
);
}
```
- Basic | (id: RidgeChart.basic)
```tsx
import { RidgeChart } from '../../';
const SERIES = [
{
id: '2019',
name: '2019',
color: '#4C6EF5',
values: [
42, 44, 45, 46, 47, 48, 49, 49, 50, 52, 53, 54, 55, 55, 55, 57, 58, 60, 62,
],
},
{
id: '2020',
name: '2020',
color: '#20C997',
values: [
38, 39, 40, 41, 42, 43, 44, 46, 48, 50, 51, 51, 52, 53, 53, 54, 55, 57, 59,
],
},
{
id: '2021',
name: '2021',
color: '#FF922B',
values: [
45, 47, 48, 49, 50, 51, 52, 53, 55, 56, 57, 58, 59, 59, 60, 61, 62, 63, 64,
],
},
{
id: '2022',
name: '2022',
color: '#845EF7',
values: [
50, 51, 52, 54, 55, 56, 57, 58, 60, 61, 62, 63, 64, 65, 65, 66, 67, 68, 69,
],
},
];
return (
);
}
```
## Component:
The Ring component displays progress or status using a radial indicator. It supports custom labels, color stops, neutral states, and fully customized center content.
### Props
`value` (number) [required]
Current value represented by the ring
`min` (number) [optional, default: 0]
Lower bound for normalization. Defaults to 0.
`max` (number) [optional, default: 100]
Upper bound for normalization. Defaults to 100.
`size` (number) [optional, default: 100]
Diameter of the ring in pixels. Defaults to 100.
`thickness` (number) [optional, default: 12]
Stroke thickness in pixels. Defaults to 12.
`caption` (React.ReactNode) [optional]
Optional caption rendered beneath the ring
`label` (React.ReactNode) [optional]
Main label rendered in the ring center
`subLabel` (React.ReactNode) [optional]
Secondary label rendered below the main label
`showValue` (boolean) [optional, default: true]
Displays the computed percentage when no label/subLabel is provided. Defaults to true.
`valueFormatter` ((value: number, percent: number) => React.ReactNode) [optional]
Formats the displayed value or percentage
`trackColor` (string) [optional]
Track color behind the progress stroke
`progressColor` (string | ((value: number, percent: number) => string)) [optional]
Progress stroke color or resolver
- … 15 more props documented
### Demos
- Interactive Presets | (id: Ring.basic) | tags: ring
Drive multiple ring presentations from a single stateful value and expose how sizing and labels adapt.
```tsx
import { useState } from 'react';
import { Button, Card, Column, Ring, Row, Text } from '@platform-blocks/ui';
const ringPresets = [
{
key: 'default',
label: 'Default presentation',
props: { caption: 'Completion' },
},
{
key: 'compact',
label: 'Compact footprint',
props: { size: 72, thickness: 8, caption: 'Compact' },
},
{
key: 'labeled',
label: 'Labeled preview',
props: { size: 120, thickness: 14, label: 'Release' },
getSubLabel: (value: number) => `${value}%`,
},
];
const [value, setValue] = useState(72);
const updateValue = (delta: number) => {
setValue((current) => Math.min(100, Math.max(0, current + delta)));
};
const displayValue = Math.round(value);
return (
Use rings to surface progress at a glance; adjust `size`, `thickness`, and labeling to fit the surface area.
{ringPresets.map((preset) => {
const subLabel = preset.getSubLabel?.(displayValue);
return (
{preset.label}
);
})}
Current value: {displayValue}%
updateValue(-8)} disabled={value <= 0}>
Decrease 8%
updateValue(8)} disabled={value >= 100}>
Increase 8%
);
}
```
- Dynamic Color Stops | (id: Ring.color-stops) | tags: ring
Display how `colorStops` shift the progress color as values cross threshold ranges.
```tsx
import { Card, Column, Ring, Row, Text } from '@platform-blocks/ui';
const colorStops = [
{ value: 0, color: '#f87171' },
{ value: 60, color: '#fb923c' },
{ value: 75, color: '#f59e0b' },
{ value: 85, color: '#0ea5e9' },
{ value: 92, color: '#14b8a6' },
];
const completionSamples = [48, 72, 97];
return (
Provide ordered `colorStops` to create gradient-style progress rings that shift tone as completion grows.
{completionSamples.map((value) => (
{value}% complete
))}
);
}
```
## Component:
Flow diagram showing volume between nodes.
### Props
`nodes` (SankeyNode[]) [required]
Nodes included in the Sankey diagram
`links` (SankeyLink[]) [required]
Links connecting the nodes
`animationDuration` (number) [optional, default: 1000]
Animation duration in milliseconds
`disabled` (boolean) [optional, default: false]
Disable animations
`nodeWidth` (number) [optional]
Fixed node width in pixels (auto-calculated if omitted)
`nodePadding` (number) [optional]
Vertical gap between nodes (auto-calculated if omitted)
`chartPadding` (Partial>) [optional]
Override chart padding (defaults to 40px all around)
`labelFormatter` ((node: SankeyNode) => string) [optional]
Format display label for a node
`valueFormatter` ((value: number, node: SankeyNode | undefined) => string) [optional]
Format value label for a node
`onNodeHover` ((node: SankeyNode | null) => void) [optional]
Receive callbacks when a node is hovered/focused
`onLinkHover` ((link: SankeyLink | null) => void) [optional]
Receive callbacks when a link is hovered/focused
`highlightOnHover` (boolean) [optional, default: true]
Highlight hovered nodes/links (defaults to true)
- … 1 more props documented
### Demos
- Basic | (id: SankeyChart.basic)
```tsx
import { SankeyChart } from '../../';
const NODES = [
{ id: 'solar', name: 'Solar', color: '#FFC078' },
{ id: 'wind', name: 'Wind', color: '#74C0FC' },
{ id: 'hydro', name: 'Hydro', color: '#69DB7C' },
{ id: 'grid', name: 'Grid', color: '#FFD43B' },
{ id: 'battery', name: 'Battery Storage', color: '#748FFC' },
{ id: 'residential', name: 'Residential', color: '#FF6B6B' },
{ id: 'commercial', name: 'Commercial', color: '#12B886' },
{ id: 'industrial', name: 'Industrial', color: '#5F3DC4' },
];
const LINKS = [
{ source: 'solar', target: 'grid', value: 32 },
{ source: 'wind', target: 'grid', value: 28 },
{ source: 'hydro', target: 'grid', value: 18 },
{ source: 'solar', target: 'battery', value: 6 },
{ source: 'wind', target: 'battery', value: 4 },
{ source: 'battery', target: 'grid', value: 8 },
{ source: 'grid', target: 'residential', value: 30 },
{ source: 'grid', target: 'commercial', value: 24 },
{ source: 'grid', target: 'industrial', value: 16 },
];
return (
);
}
```
- Budget Allocation | (id: SankeyChart.budget-allocation)
```tsx
import { SankeyChart } from '../../';
const NODES = [
{ id: 'corporate-budget', name: 'Corporate Budget', color: '#2563EB' },
{ id: 'gtm', name: 'Go-to-Market', color: '#34D399' },
{ id: 'product', name: 'Product & Engineering', color: '#A855F7' },
{ id: 'operations', name: 'Operations', color: '#F97316' },
{ id: 'paid-media', name: 'Paid Media', color: '#60A5FA' },
{ id: 'events', name: 'Events', color: '#F472B6' },
{ id: 'product-investment', name: 'Product Investment', color: '#C084FC' },
{ id: 'platform-modernization', name: 'Platform Modernization', color: '#8B5CF6' },
{ id: 'customer-success', name: 'Customer Success', color: '#22C55E' },
{ id: 'supply-chain', name: 'Supply Chain', color: '#FB923C' },
];
const LINKS = [
{ source: 'corporate-budget', target: 'gtm', value: 24 },
{ source: 'corporate-budget', target: 'product', value: 32 },
{ source: 'corporate-budget', target: 'operations', value: 18 },
{ source: 'gtm', target: 'paid-media', value: 12 },
{ source: 'gtm', target: 'events', value: 8 },
{ source: 'gtm', target: 'customer-success', value: 4 },
{ source: 'product', target: 'product-investment', value: 14 },
{ source: 'product', target: 'platform-modernization', value: 12 },
{ source: 'operations', target: 'customer-success', value: 6 },
{ source: 'operations', target: 'supply-chain', value: 10 },
];
return (
);
}
```
## Component:
Plots data points in two-dimensional space for correlation analysis.
### Props
`data` (ChartDataPoint[]) [required]
Data points
`series` (ScatterSeries[]) [optional]
Optional multi-series data (overrides top-level data if provided)
`pointSize` (number) [optional, default: 6]
Point size
`pointColor` (string) [optional]
Point color
`pointOpacity` (number) [optional, default: 1]
Point opacity
`allowAddPoints` (boolean) [optional, default: false]
Allow adding points by tapping
`allowDragPoints` (boolean) [optional, default: false]
Allow dragging points
`showTrendline` (boolean | 'overall' | 'per-series') [optional, default: false]
Show trend line. true|'overall' for single combined regression, 'per-series' for one per series
`trendlineColor` (string) [optional]
Trend line color
`enablePanZoom` (boolean) [optional]
Enable pan & zoom interactions
`zoomMode` ('x' | 'y' | 'both') [optional]
Zoom mode (axes constrained)
`minZoom` (number) [optional]
Minimum zoom scale (domain fraction)
- … 18 more props documented
### Demos
- Api Error Rate Volume | (id: ScatterChart.api-error-rate-volume)
```tsx
import { ScatterChart } from '../../';
const SERIES = [
{
id: 'core-apis',
name: 'Core services',
color: '#1E66F5',
pointSize: 8,
data: [
{ id: 'core-auth', x: 520, y: 2.9, label: 'Auth' },
{ id: 'core-catalog', x: 480, y: 2.6, label: 'Catalog' },
{ id: 'core-inventory', x: 410, y: 2.4, label: 'Inventory' },
{ id: 'core-profiles', x: 365, y: 2.1, label: 'Profiles' },
{ id: 'core-checkout', x: 445, y: 2.7, label: 'Checkout' },
],
},
{
id: 'payment-apis',
name: 'Payment services',
color: '#F76707',
pointSize: 8,
data: [
{ id: 'pay-processing', x: 260, y: 4.1, label: 'Processor' },
{ id: 'pay-ledger', x: 195, y: 3.1, label: 'Ledger' },
{ id: 'pay-invoicing', x: 220, y: 3.6, label: 'Invoicing' },
{ id: 'pay-fx', x: 180, y: 3.4, label: 'FX gateway' },
{ id: 'pay-risk', x: 240, y: 3.9, label: 'Risk scoring' },
],
},
{
id: 'edge-apis',
name: 'Edge and experimental',
color: '#15AABF',
pointSize: 7,
data: [
{ id: 'edge-recos', x: 120, y: 4.2, label: 'Recommendations' },
{ id: 'edge-search', x: 95, y: 4.5, label: 'Search beta' },
{ id: 'edge-proto', x: 70, y: 3.9, label: 'Prototype API' },
{ id: 'edge-labs', x: 55, y: 3.6, label: 'Labs checkout' },
{ id: 'edge-content', x: 82, y: 4, label: 'Content sync' },
],
},
];
const QUADRANTS = {
x: 180,
y: 3.5,
fills: {
topLeft: 'rgba(255, 193, 7, 0.1)',
topRight: 'rgba(255, 107, 107, 0.12)',
bottomLeft: 'rgba(63, 142, 252, 0.08)',
bottomRight: 'rgba(34, 197, 247, 0.08)',
},
fillOpacity: 1,
lineColor: '#B0C4FE',
lineWidth: 1,
labels: {
topLeft: 'High error - lower volume',
topRight: 'Critical risk',
bottomLeft: 'Monitor growth',
bottomRight: 'Healthy scale',
},
labelColor: '#1F2933',
labelFontSize: 11,
labelOffset: 12,
};
const describeQuadrant = (x: number, y: number) => {
const labels = QUADRANTS.labels;
if (!labels) return null;
const isRight = x >= QUADRANTS.x;
const isTop = y >= QUADRANTS.y;
if (isRight && isTop) return labels.topRight ?? null;
if (!isRight && isTop) return labels.topLeft ?? null;
if (isRight && !isTop) return labels.bottomRight ?? null;
return labels.bottomLeft ?? null;
};
return (
serie.data)}
series={SERIES}
quadrants={QUADRANTS}
pointOpacity={0.9}
enableCrosshair
multiTooltip
liveTooltip
grid={{ show: true, color: '#E9ECEF' }}
legend={{ show: true, position: 'bottom' }}
xScaleType="log"
xAxis={{
show: true,
title: 'Requests per minute (thousands)',
labelFormatter: (value: number) => `${Math.round(value)}k`,
}}
yAxis={{
show: true,
title: 'Error rate (%)',
labelFormatter: (value: number) => `${value.toFixed(1)}%`,
}}
tooltip={{
show: true,
backgroundColor: '#12263A',
textColor: '#F4F6FB',
formatter: (point) => {
const quadrantNote = describeQuadrant(point.x, point.y);
const lines = [
point.label ?? 'Service',
`Volume ${Math.round(point.x)}k rpm · Errors ${point.y.toFixed(1)}%`,
];
if (quadrantNote) {
lines.push(quadrantNote);
}
return lines.join('\n');
},
}}
/>
);
}
```
- Basic | (id: ScatterChart.basic)
```tsx
import { ScatterChart } from '../../';
const SERIES = [
{
id: 'marketing',
name: 'Marketing spend',
color: '#4C6EF5',
data: [
{ x: 18, y: 42 },
{ x: 22, y: 48 },
{ x: 25, y: 54 },
{ x: 27, y: 58 },
{ x: 30, y: 61 },
{ x: 34, y: 66 },
{ x: 38, y: 70 },
],
pointSize: 8,
},
{
id: 'events',
name: 'Event sponsorship',
color: '#20C997',
data: [
{ x: 12, y: 36 },
{ x: 16, y: 41 },
{ x: 19, y: 44 },
{ x: 23, y: 47 },
{ x: 27, y: 55 },
{ x: 32, y: 62 },
{ x: 35, y: 65 },
],
pointSize: 7,
},
];
return (
serie.data)}
series={SERIES}
showTrendline="per-series"
enableCrosshair
enablePanZoom
zoomMode="both"
multiTooltip
liveTooltip
xAxis={{
show: true,
title: 'Spend (USD thousands)',
labelFormatter: (value) => `$${value}`,
}}
yAxis={{
show: true,
title: 'Qualified leads',
}}
grid={{ show: true, color: '#E7ECFC' }}
legend={{ show: true, position: 'bottom' }}
tooltip={{
show: true,
formatter: (point) => `${point.x}k spend → ${point.y} leads`,
}}
/>
);
}
```
## Component:
The Search component provides a search input with debouncing, loading states, and customizable clear functionality.
### Props
`value` (string) [optional]
`defaultValue` (string) [optional]
`onChange` ((value: string) => void) [optional]
`onSubmit` ((value: string) => void) [optional]
`placeholder` (string) [optional]
`size` (SizeValue) [optional]
`radius` (any) [optional]
`autoFocus` (boolean) [optional]
`debounce` (number) [optional]
`clearButton` (boolean) [optional]
`loading` (boolean) [optional]
`endSection` (React.ReactNode) [optional]
- … 5 more props documented
### Demos
- Basic search | (id: Search.basic) | tags: controlled, input
Control the `Search` value with local state so you can react to user input and mirror the query elsewhere in your UI.
```tsx
import { useState } from 'react';
import { Column, Search, Text } from '@platform-blocks/ui';
const [query, setQuery] = useState('');
return (
Current query: {query || '—'}
);
}
```
- Button mode | (id: Search.button-mode) | tags: spotlight, shortcuts
Set `buttonMode` to turn `Search` into a pressable launcher and pass a `rightComponent` with `KeyCap` shortcuts so users discover keyboard access.
```tsx
import { Column, KeyCap, Row, Search, Text, useToast } from '@platform-blocks/ui';
const toast = useToast();
const handleCustomPress = () => {
toast.show({ message: 'Launching saved search…' });
};
return (
Default Spotlight launcher
⌘
K
)}
/>
Custom handler with shortcut hint
Ctrl
F
)}
/>
);
}
```
## Component:
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 accessibili…
### Props
`data` (SegmentedControlData[]) [required]
Data that defines the segments
`value` (string) [optional]
Controlled value
`defaultValue` (string) [optional]
Uncontrolled initial value
`onChange` ((value: string) => void) [optional]
Called when value changes
`size` (SizeValue) [optional]
Control size, maps to height and font size
`color` (string) [optional]
Indicator color token or hex
`orientation` ('horizontal' | 'vertical') [optional]
Layout orientation
`fullWidth` (boolean) [optional]
Stretch across available width
`disabled` (boolean) [optional]
Disable entire control
`readOnly` (boolean) [optional]
Prevent user interaction but keep visual state
`autoContrast` (boolean) [optional]
Adjust text color automatically for filled/outline variants
`withItemsBorders` (boolean) [optional]
Render dividers between items
- … 12 more props documented
### Demos
- Basic Usage | (id: SegmentedControl.basic) | tags: segmented-control, selection, uncontrolled
Set `defaultValue` to preselect a segment and let the control manage focus and selection state internally.
```tsx
import { Card, Column, SegmentedControl, Text } from '@platform-blocks/ui';
const frameworks = [
{ label: 'React', value: 'react' },
{ label: 'Angular', value: 'angular' },
{ label: 'Vue', value: 'vue' },
];
return (
Segmented controls expose a compact option switcher. This version relies on internal state via `defaultValue`.
);
}
```
- Controlled Value | (id: SegmentedControl.controlled) | tags: segmented-control, controlled, state
Provide `value` and `onChange` to synchronize the selected segment with external state or companion controls.
```tsx
import { useState } from 'react';
import { Button, Card, Column, Row, SegmentedControl, Text } from '@platform-blocks/ui';
const frameworks = [
{ label: 'React', value: 'react' },
{ label: 'Angular', value: 'angular' },
{ label: 'Vue', value: 'vue' },
];
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
);
}
```
## Component:
Select provides a dropdown interface for choosing from predefined options. It supports single and multi-selection modes, disabled states, validation, and customizable styling.
### Props
`value` (T | null) [optional]
Current value when the component is controlled.
`defaultValue` (T | null) [optional]
Initial value when the component manages its own state.
`onChange` ((value: T | null, option?: SelectOption | null) => void) [optional]
Callback fired whenever the selection changes.
`options` (SelectOption[]) [required]
Collection of options available to choose from.
`placeholder` (string) [optional]
Placeholder text shown when no value is selected.
`size` (SizeValue) [optional]
Size token controlling trigger height and typography.
`radius` (any) [optional]
Corner radius token applied to the trigger and dropdown.
`disabled` (boolean) [optional]
Disables the control when set to true.
`label` (string) [optional]
Optional label rendered above the trigger.
`description` (string) [optional]
Optional short descriptive text shown directly under the label (above the field).
`helperText` (string) [optional]
Helper copy displayed beneath the control.
`error` (string) [optional]
Error message shown beneath the control in error state.
- … 10 more props documented
### Demos
- Basic | (id: Select.basic) | tags: basic, label, placeholder, single
Simple single-value select with helper copy and live selection feedback.
```tsx
import { useState } from 'react'
import { Column, Select, Text } from '@platform-blocks/ui'
const sportsOptions = [
{ label: '⚽ Soccer', value: 'soccer' },
{ label: '🏀 Basketball', value: 'basketball' },
{ label: '🎾 Tennis', value: 'tennis' },
]
const [value, setValue] = useState(null)
return (
Basic select
Choose from a simple list of sports to see the current selection below.
setValue(selected as string)}
/>
{value && (
Selected: {value}
)}
)
}
```
- Full width | (id: Select.fullWidth) | tags: full-width, radius, responsive
Stretch the control to the container width and tweak radius tokens for styling.
```tsx
import { useState } from 'react'
import { Column, Select, Text } from '@platform-blocks/ui'
const options = [
{ label: 'Small', value: 'sm' },
{ label: 'Medium', value: 'md' },
{ label: 'Large', value: 'lg' },
]
const [value, setValue] = useState(null)
return (
Full-width variations
Stretch the select to fill its container and adjust radius tokens as needed.
setValue(val as string)}
/>
The trigger matches the parent width while retaining default styling.
setValue(val as string)}
/>
Apply radius tokens to align with your design system.
)
}
```
## Component:
Animated text highlight that sweeps a configurable gradient across the content. Ideal for loading states, premium callouts, and attention-grabbing text accents.
### Props
`children` (ReactNode) [optional]
Text node children. Overrides `text` when provided
`text` (string) [optional]
Text content to render when not using children
`color` (string) [optional]
Base text color rendered underneath the shimmer
`colors` (string[]) [optional]
Optional gradient stops override
`shimmerColor` (string) [optional]
Highlight color used for the shimmer pass
`spread` (number) [optional]
Length multiplier for the shimmer sweep (higher = wider highlight)
`duration` (number) [optional]
Duration of a single shimmer cycle in seconds
`delay` (number) [optional]
Delay before the shimmer starts (seconds)
`repeatDelay` (number) [optional]
Delay between shimmer repetitions (seconds)
`repeat` (boolean) [optional]
Whether the shimmer should repeat indefinitely
`once` (boolean) [optional]
Animate only once after becoming visible
`direction` (ShimmerDirection) [optional]
Direction of shimmer movement
- … 6 more props documented
### Demos
- Basic shimmer | (id: ShimmerText.basic) | tags: animation, text
Wrap text in `ShimmerText` to add the default looping highlight with no additional configuration.
```tsx
import { Column, ShimmerText } from '@platform-blocks/ui';
return (
Weekly highlights go live
New arrivals shimmer into view every Friday at noon.
);
}
```
- Interactive controls | (id: ShimmerText.controls) | tags: animation, controls
Expose `spread`, `repeat`, and `once` controls to let readers tune the shimmer loop at runtime.
```tsx
import { useState } from 'react';
import { Column, Row, ShimmerText, Slider, Switch, Text } from '@platform-blocks/ui';
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
);
}
```
## Component:
Skeleton components provide visual placeholders for text, avatars, and blocks to reduce perceived loading time.
### Props
`shape` (SkeletonShape) [optional, default: 'rectangle']
Shape of the skeleton placeholder
`w` (DimensionValue) [optional]
Width of the skeleton component
`h` (DimensionValue) [optional]
Height of the skeleton component
`size` (SizeValue) [optional, default: 'md']
Size of the skeleton component (overrides width/height)
`radius` (SizeValue | number) [optional]
Border radius for rectangle/rounded shapes
`animate` (boolean) [optional, default: true]
Whether to show the loading animation
`animationDuration` (number) [optional, default: 1500]
Duration of the loading animation in milliseconds
`colors` ([string, string]) [optional]
Gradient colors for the shimmer effect
`style` (StyleProp) [optional]
Style overrides for the skeleton container
`testID` (string) [optional]
Optional test identifier
### Demos
- Basics | (id: Skeleton.basic) | tags: loading, placeholder
Stack text, avatar, and block placeholders to preview the structure of incoming content while data loads.
```tsx
import { Skeleton, Column, Row } from '@platform-blocks/ui';
return (
);
}
```
- Shapes | (id: Skeleton.shapes) | tags: avatar, text, button
Preview the available skeleton shapes for avatars, typography lines, actions, and media blocks.
```tsx
import { Skeleton, Column, Row } from '@platform-blocks/ui';
return (
);
}
```
## Component:
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.
### Props
`value` (number) [optional]
Slider value
`defaultValue` (number) [optional, default: 0]
Uncontrolled initial value
`onChange` ((value: number) => void) [optional]
Change handler
`min` (number) [optional, default: 0]
Minimum value
`max` (number) [optional, default: 100]
Maximum value
`step` (number) [optional, default: 1]
Step increment
`orientation` ('horizontal' | 'vertical') [optional, default: 'horizontal']
Slider orientation
`trackColor` (ColorValue) [optional]
Track color
`activeTrackColor` (ColorValue) [optional]
Active track color
`thumbColor` (ColorValue) [optional]
Thumb color
`trackSize` (number) [optional]
Track height/width
`thumbSize` (number) [optional]
Thumb size
- … 19 more props documented
### Demos
- Basic Usage | (id: Slider.basic) | tags: basic, slider
Basic slider usage for selecting numeric values within a range.
```tsx
import { useState } from 'react';
import { Slider, Text, Column, Card, Block } from '@platform-blocks/ui';
const [value, setValue] = useState(25);
return (
Basic Slider
Value: {value}
);
}
```
- Range Slider | (id: Slider.range) | tags: range, multiple, dual-thumb
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.
```tsx
import { useState } from 'react';
import { RangeSlider, Text, Block, Card, Flex } from '@platform-blocks/ui';
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
Score Range
Score range: {scoreRange[0]} - {scoreRange[1]} (span: {scoreRange[1] - scoreRange[0]})
);
}
```
## Component:
Use the Space component to insert fixed spacing between elements when margin props are not available or would make layouts harder to reason about.
### Props
`h` (SizeValue) [optional]
Height of the spacer. Accepts theme spacing tokens or raw numbers.
`w` (SizeValue) [optional]
Width of the spacer. Accepts theme spacing tokens or raw numbers.
`size` (SizeValue) [optional, default: 'md']
Fallback size when neither `h` nor `w` is provided. Defaults to `md` so the component always occupies some space.
`style` (StyleProp) [optional]
Optional style overrides.
`children` (never) [optional]
Space is presentational only, so children are not supported.
### Demos
- Vertical spacing | (id: Space.basic) | tags: spacing, layout
Compare token-based and numeric vertical gaps between stacked content blocks.
```tsx
import { Block, Column, Space, Text, useTheme } from '@platform-blocks/ui';
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
Use `Space` to control gutters between inline buttons with tokens or fixed widths.
```tsx
import { Block, Button, Column, Row, Space, Text, useTheme } from '@platform-blocks/ui';
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}
))}
);
}
```
## Component:
Compact inline trend indicator for dense data summaries.
### Props
`id` (string | number) [optional]
Optional unique series ID
`name` (string) [optional]
Optional series name (for tooltip)
`data` (number[] | SparklinePoint[]) [required]
Data points (plain y values or explicit {x,y} pairs)
`color` (string) [optional]
Line color
`fill` (boolean) [optional, default: true]
Fill area under line
`fillOpacity` (number) [optional, default: 0.3]
Fill opacity
`strokeWidth` (number) [optional, default: 2]
Stroke width
`smooth` (boolean) [optional, default: true]
Curve smoothing
`showPoints` (boolean) [optional, default: false]
Show data points
`pointSize` (number) [optional, default: 3]
Point size (for individual data points)
`domain` (SparklineDomain) [optional]
Provide min/max to avoid re-scaling jitter across multiple sparklines
`highlightLast` (boolean) [optional, default: true]
Show last value bubble
- … 7 more props documented
### Demos
- Basic | (id: SparklineChart.basic)
```tsx
import { SparklineChart } from '../../';
const DAILY_SIGNUPS = [
32, 36, 31, 40, 44, 47, 46, 52, 58, 60, 64, 67, 70, 72,
];
return (
// TODO: cant see gradient fill on this one, investigate
`${value} signups`}
domain={{ y: [20, 80] }}
/>
);
}
```
- Dashboard Daily Active Users | (id: SparklineChart.dashboard-daily-active-users)
```tsx
import { View, Text } from 'react-native';
import { SparklineChart } from '../../';
const SURFACE_SERIES = [
{
id: 'web',
title: 'Web',
color: '#3B82F6',
data: [1820, 1855, 1880, 1915, 1940, 1975, 2010, 2045, 2070, 2095, 2130, 2165, 2195, 2230],
},
{
id: 'ios',
title: 'iOS',
color: '#6366F1',
data: [940, 955, 968, 984, 1005, 1018, 1042, 1058, 1075, 1098, 1110, 1126, 1148, 1168],
},
{
id: 'android',
title: 'Android',
color: '#10B981',
data: [1280, 1295, 1310, 1335, 1342, 1360, 1385, 1410, 1432, 1455, 1470, 1488, 1512, 1536],
},
];
const formatUsers = (value: number) => `${Math.round(value).toLocaleString()} users`;
const getDeltaLabel = (series: number[]) => {
if (series.length < 2) return 'Stable vs yesterday';
const latest = series[series.length - 1];
const prior = series[series.length - 2];
const delta = latest - prior;
if (delta === 0) return 'Stable vs yesterday';
const prefix = delta > 0 ? '+' : '-';
return `${prefix}${Math.abs(delta).toLocaleString()} vs yesterday`;
};
return (
Daily Active Users
Trailing two weeks, by platform
{SURFACE_SERIES.map((series) => {
const latest = series.data[series.data.length - 1];
return (
{series.title}
{latest.toLocaleString()} · {getDeltaLabel(series.data)}
);
})}
);
}
```
## Component:
The Spoiler component automatically collapses content that exceeds a specified height, providing a show/hide toggle to reveal the full content.
### Props
`children` (React.ReactNode) [required]
Content to hide/show
`maxHeight` (number) [optional]
Max height (in px) when collapsed
`initiallyOpen` (boolean) [optional]
Whether component starts initially opened
`showLabel` (string) [optional]
Label for show more
`hideLabel` (string) [optional]
Label for hide
`transitionDuration` (number) [optional]
Transition duration ms
`size` (SizeValue) [optional]
Size token influences padding / font
`opened` (boolean) [optional]
Optional controlled open state
`onToggle` ((opened: boolean) => void) [optional]
Callback when toggle
`disabled` (boolean) [optional]
Disable toggle
`style` (any) [optional]
Optional style
`renderControl` ((args: { opened: boolean; toggle: () => void; showLabel: string; hideLabel: string }) => React.ReactNode) [optional]
Render custom control
- … 3 more props documented
### Demos
- Basic Usage | (id: Spoiler.basic) | tags: spoiler, collapse, content
Set `maxHeight` to reveal a preview of long copy while the rest stays accessible behind the built-in toggle.
```tsx
import { Card, Column, Spoiler, Text } from '@platform-blocks/ui';
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
Flip the `initiallyOpen` prop to choose whether content renders expanded on mount or waits for user interaction.
```tsx
import { Card, Column, Spoiler, Text } from '@platform-blocks/ui';
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}
))}
);
}
```
## Component:
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.
### Props
`actions` (SpotlightItem[]) [required]
`nothingFound` (string) [optional]
`highlightQuery` (boolean | HighlightComponentProps['highlight']) [optional]
`limit` (number) [optional]
`scrollable` (boolean) [optional]
`maxHeight` (number) [optional]
`shortcut` (string | string[] | null) [optional]
`searchProps` (any) [optional]
`store` (any) [optional]
`variant` ('modal' | 'bottomsheet' | 'fullscreen') [optional]
`width` (number) [optional]
`height` (number) [optional]
### Demos
- Keyboard Palette | (id: Spotlight.basic) | tags: spotlight
Open the Spotlight command palette through the shared store and a primary trigger button.
```tsx
import { Button, Card, Column, Spotlight, Text, useSpotlightStoreInstance, type SpotlightProps } from '@platform-blocks/ui';
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
Swap `icon` definitions with custom React nodes to render richer action affordances.
```tsx
import { Button, Card, Column, Icon, Spotlight, Text, useSpotlightStoreInstance, type SpotlightProps } from '@platform-blocks/ui';
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
);
}
```
## Component:
Area chart with multiple series stacked to show cumulative contributions.
### Props
`series` (LineChartSeries[]) [required]
`stackOrder` ('normal' | 'reverse') [optional, default: 'normal']
`smooth` (boolean) [optional, default: true]
`opacity` (number) [optional, default: 0.55]
base opacity for layers
`stackMode` ('absolute' | 'percentage') [optional, default: 'absolute']
### Demos
- Basic | (id: StackedAreaChart.basic)
```tsx
import { StackedAreaChart } from '../../';
const SERIES = [
{
id: 'mobile',
name: 'Mobile',
color: '#4C6EF5',
data: [
{ x: 1, y: 22 },
{ x: 2, y: 26 },
{ x: 3, y: 28 },
{ x: 4, y: 32 },
{ x: 5, y: 36 },
{ x: 6, y: 38 },
{ x: 7, y: 42 },
{ x: 8, y: 44 },
{ x: 9, y: 47 },
{ x: 10, y: 50 },
{ x: 11, y: 52 },
{ x: 12, y: 55 },
],
},
{
id: 'web',
name: 'Web',
color: '#20C997',
data: [
{ x: 1, y: 18 },
{ x: 2, y: 20 },
{ x: 3, y: 22 },
{ x: 4, y: 25 },
{ x: 5, y: 26 },
{ x: 6, y: 27 },
{ x: 7, y: 28 },
{ x: 8, y: 30 },
{ x: 9, y: 32 },
{ x: 10, y: 33 },
{ x: 11, y: 34 },
{ x: 12, y: 35 },
],
},
{
id: 'api',
name: 'API',
color: '#FF922B',
data: [
{ x: 1, y: 12 },
{ x: 2, y: 14 },
{ x: 3, y: 15 },
{ x: 4, y: 16 },
{ x: 5, y: 18 },
{ x: 6, y: 19 },
{ x: 7, y: 21 },
{ x: 8, y: 22 },
{ x: 9, y: 23 },
{ x: 10, y: 24 },
{ x: 11, y: 25 },
{ x: 12, y: 27 },
],
},
];
return (
`M${value}` }}
yAxis={{
show: true,
title: 'Active users (thousands)',
labelFormatter: (value) => `${value}`,
}}
grid={{ show: true, color: '#E3E8FB' }}
legend={{ show: true, position: 'bottom' }}
enableCrosshair
multiTooltip
liveTooltip
/>
);
}
```
## Component:
Bar chart with segments stacked to show part-to-whole by category.
### Props
`series` (StackedBarSeries[]) [required]
Stacked bar series to render
`barSpacing` (number) [optional, default: 0.25]
Gap between stacked groups
`legend` (ChartLegend) [optional, default: { show: true]
Legend configuration
`xAxis` (ChartAxis) [optional]
X-axis configuration
`yAxis` (ChartAxis) [optional]
Y-axis configuration
`grid` (ChartGrid) [optional]
Grid line configuration
`animation` (ChartAnimation) [optional]
Animation configuration
### Demos
- Basic | (id: StackedBarChart.basic)
```tsx
import { StackedBarChart } from '../../';
const SERIES = [
{
id: 'new-business',
name: 'New business',
color: '#4C6EF5',
data: [
{ id: 'q1-new', category: 'Q1', value: 220 },
{ id: 'q2-new', category: 'Q2', value: 250 },
{ id: 'q3-new', category: 'Q3', value: 240 },
{ id: 'q4-new', category: 'Q4', value: 280 },
],
},
{
id: 'expansion',
name: 'Expansion',
color: '#20C997',
data: [
{ id: 'q1-exp', category: 'Q1', value: 110 },
{ id: 'q2-exp', category: 'Q2', value: 135 },
{ id: 'q3-exp', category: 'Q3', value: 150 },
{ id: 'q4-exp', category: 'Q4', value: 165 },
],
},
{
id: 'renewal',
name: 'Renewal',
color: '#FF922B',
data: [
{ id: 'q1-renew', category: 'Q1', value: 180 },
{ id: 'q2-renew', category: 'Q2', value: 172 },
{ id: 'q3-renew', category: 'Q3', value: 188 },
{ id: 'q4-renew', category: 'Q4', value: 194 },
],
},
];
return (
`$${value}`,
}}
grid={{ show: true, color: '#E5EAF7' }}
legend={{ show: true, position: 'bottom' }}
animation={{ duration: 500 }}
/>
);
}
```
## Component:
The Stepper component provides a step-by-step navigation interface, perfect for multi-step forms, wizards, and progress tracking.
### Props
`active` (number) [required]
Active step index
`onStepClick` ((stepIndex: number) => void) [optional]
Called when step is clicked
`orientation` ('horizontal' | 'vertical') [optional]
Step orientation
`iconPosition` ('left' | 'right') [optional]
Icon position relative to step body
`iconSize` (number) [optional]
Icon size
`size` (ComponentSizeValue) [optional]
Component size
`color` (string) [optional]
Component color
`completedIcon` (ReactNode) [optional]
Icon to display when step is completed
`allowNextStepsSelect` (boolean) [optional]
Whether next steps (steps with higher index) can be selected
`children` (ReactNode) [required]
Step content
`ref` (React.Ref) [optional]
Accessibility label Component reference
### Demos
- Controlled Flow | (id: Stepper.basic) | tags: stepper
Control the active step with local state and show completion messaging once the flow finishes.
```tsx
import { useState } from 'react';
import { Button, Card, Block, Row, Stepper, Text } from '@platform-blocks/ui';
const steps = [
{
label: 'Account',
description: 'Create your credentials',
details: 'Set up your sign-in information and confirm your contact details.',
},
{
label: 'Verification',
description: 'Confirm your email',
details: 'Check your inbox for a verification link to secure the account.',
},
{
label: 'Profile',
description: 'Complete setup',
details: 'Add profile details so the team can find and collaborate with you.',
},
];
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
Limit navigation to completed steps using `allowStepSelect`, while preserving forward progress through the flow.
```tsx
import { useState } from 'react';
import { Button, Card, Block, Row, Stepper, Text } from '@platform-blocks/ui';
const steps = [
{
label: 'Account',
description: 'Create credentials',
details: 'Provide your name, email, and a secure password to get started.',
},
{
label: 'Verification',
description: 'Confirm ownership',
details: 'We email a confirmation link that needs to be opened before continuing.',
},
{
label: 'Preferences',
description: 'Adjust settings',
details: 'Choose notification and privacy defaults for your workspace.',
},
];
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'}
);
}
```
## Component:
Switch components provide a way to toggle between two states, typically representing on/off or enabled/disabled states.
### Props
`checked` (boolean) [optional]
Whether switch is on
`defaultChecked` (boolean) [optional, default: false]
Initial checked state for uncontrolled usage
`onChange` ((checked: boolean) => void) [optional]
Change handler
`size` (SizeValue) [optional, default: 'md']
Switch size
`color` (ColorValue) [optional, default: 'primary']
Switch color theme when on
`label` (React.ReactNode) [optional]
Switch label
`disabled` (boolean) [optional, default: false]
Whether switch is disabled
`required` (boolean) [optional, default: false]
Whether switch is required
`error` (string) [optional]
Error message
`description` (string) [optional]
Helper text
`labelPosition` ('left' | 'right' | 'top' | 'bottom') [optional, default: 'right']
Label position relative to switch
`children` (React.ReactNode) [optional]
Switch content/children (alternative to label)
- … 7 more props documented
### Demos
- Basic Usage | (id: Switch.basic) | tags: controlled, label
Control a `Switch` with local state and surface its status with supporting text and the `description` prop.
```tsx
import { useState } from 'react';
import { Column, Switch, Text } from '@platform-blocks/ui';
const [enabled, setEnabled] = useState(true);
return (
Notices are {enabled ? 'enabled' : 'disabled'}.
);
}
```
- Sizes | (id: Switch.sizes) | tags: size
Choose a `size` token to scale the switch track and thumb; `defaultChecked` seeds uncontrolled switches.
```tsx
import type { SwitchProps } from '@platform-blocks/ui';
import { Column, Row, Switch, Text } from '@platform-blocks/ui';
const SIZES: NonNullable[] = ['sm', 'md', 'lg', 'xl'];
return (
{SIZES.map((size) => (
))}
{SIZES.map((size) => (
))}
);
}
```
## Component:
The Table component offers a minimal semantic wrapper (thead, tbody, tr, th, td) useful for simple static tabular data when the full DataTable is unnecessary.
### Props
`children` (React.ReactNode) [optional]
`data` (TableData) [optional]
Table data for automatic generation of rows
`horizontalSpacing` ('xs' | 'sm' | 'md' | 'lg' | 'xl' | number) [optional]
Horizontal spacing between cells
`verticalSpacing` ('xs' | 'sm' | 'md' | 'lg' | 'xl' | number) [optional]
Vertical spacing between cells
`striped` (boolean) [optional]
Add striped styling to rows
`highlightOnHover` (boolean) [optional]
Highlight rows on hover/press
`withTableBorder` (boolean) [optional]
Add borders around table
`withColumnBorders` (boolean) [optional]
Add borders between columns
`withRowBorders` (boolean) [optional]
Add borders between rows
`captionSide` ('top' | 'bottom') [optional]
Caption position
`layout` ('auto' | 'fixed') [optional]
Table layout mode
`variant` ('default' | 'vertical') [optional]
Variant of table layout
- … 9 more props documented
### Demos
- Data Prop | (id: Table.basic) | tags: table
Pass a dataset to the `data` prop to render the caption, header, and body without composing subcomponents.
```tsx
import { Card, Column, Table, Text } from '@platform-blocks/ui';
const data = {
caption: 'User accounts overview',
head: ['Name', 'Role', 'Status', 'Posts'],
body: [
['Alice', 'Admin', 'Active', 128],
['Bob', 'Editor', 'Invited', 42],
['Carol', 'Viewer', 'Active', 5],
['Dave', 'Editor', 'Suspended', 16],
],
};
return (
Provide the `data` prop to render the caption, header, and body automatically.
);
}
```
- Border Options | (id: Table.borders) | tags: table, borders
Enable `withTableBorder`, `withColumnBorders`, and `withRowBorders` to emphasize cell boundaries.
```tsx
import { Card, Column, Table, Text } from '@platform-blocks/ui';
const data = {
head: ['ID', 'Region', 'Sales', 'Growth %'],
body: [
['#1001', 'NA', '$120,340', '+12.4%'],
['#1002', 'EU', '$98,210', '+4.1%'],
['#1003', 'APAC', '$76,003', '+8.9%'],
['#1004', 'LATAM', '$23,554', '+15.2%'],
],
caption: 'Quarterly regional performance',
};
return (
Combine table, column, and row borders to separate dense numeric data.
);
}
```
## Component:
The `TableOfContents` component automatically discovers headings on the page (h1–h6) and renders a navigable outline with active section tracking. It supports scroll‑spy highlighting, deep-link copying, custom initial data (SSR / virtual d…
### Props
`variant` ('filled' | 'outline' | 'ghost' | 'none') [optional, default: 'none']
Visual style variant for the table of contents
`color` (string) [optional]
Background color for filled variant. Falls back to theme primary color if not specified
`size` (SizeValue) [optional, default: 'sm']
Text size for table of contents items
`radius` (any) [optional]
Border radius value for the container
`scrollSpyOptions` (ScrollSpyOptions) [optional]
Configuration options for scroll spy behavior
`getControlProps` ((payload: { data: TocItem; active: boolean; index: number }) => any) [optional]
Function to customize props for each table of contents item control
`initialData` (TocItem[]) [optional]
Initial data for table of contents items (useful for SSR or pre-rendering)
`minDepthToOffset` (number) [optional, default: 1]
Minimum depth level at which to start applying depth offset indentation
`depthOffset` (number) [optional, default: 20]
Pixel offset to apply for each depth level (indentation amount)
`reinitializeRef` (React.RefObject<() => void>) [optional]
Ref to expose the reinitialize function for manually triggering TOC refresh
`autoContrast` (boolean) [optional, default: false]
Automatically adjust text color for contrast when using filled variant
`style` (any) [optional]
Additional styles to apply to the container
- … 2 more props documented
### Demos
- Basics | (id: TableOfContents.basic) | tags: navigation
Drop a table of contents beside your main article and it will register headings automatically through the shared title registry.
```tsx
import { useRef } from 'react';
import { Block, Column, Row, TableOfContents, Text, Title, TitleRegistryProvider } from '@platform-blocks/ui';
const SECTIONS = [
{ id: 'intro', title: 'Introduction', summary: 'Set the stage for the walkthrough and link to key resources.' },
{ id: 'setup', title: 'Setup', summary: 'Install dependencies and initialize the provider before rendering content.' },
{ id: 'usage', title: 'Usage', summary: 'Render headings inside your main content area so they register automatically.' },
{ id: 'advanced', title: 'Advanced Features', summary: 'Expose the controller or customize the render function when needed.' },
{ id: 'faq', title: 'FAQ', summary: 'Answer the questions you expect to receive most often.' },
];
const contentRef = useRef(null);
return (
{SECTIONS.map((section, index) => (
{section.title}
{section.summary}
))}
);
}
```
- Active callbacks | (id: TableOfContents.active-callback) | tags: navigation
Subscribe to `onActiveChange` to surface the currently highlighted section, perfect for syncing status chips or analytics.
```tsx
import { useRef, useState } from 'react';
import { Block, Chip, Column, Row, TableOfContents, Text, Title, TitleRegistryProvider } from '@platform-blocks/ui';
const SECTIONS = [
{ id: 'overview', title: 'Overview', summary: 'Explain when the progress indicator should appear.' },
{ id: 'loading', title: 'Loading States', summary: 'Describe how to expose feedback while content is fetching.' },
{ id: 'completion', title: 'Completion', summary: 'Document the triggers that finalize navigation progress.' },
{ id: 'error', title: 'Error Recovery', summary: 'Clarify what happens if the data fails to load.' },
];
const [activeId, setActiveId] = useState(null);
const contentRef = useRef(null);
return (
setActiveId(id)}
/>
{SECTIONS.map((section, index) => (
{section.title}
{section.summary}
))}
Active section: {activeId ?? 'None'}
);
}
```
## Component:
Tabs organize content into multiple sections that users can navigate between. The component supports various visual styles, orientations, and interactive behaviors while maintaining accessibility standards.
### Props
`items` (TabItem[]) [required]
Array of tab definitions to render. The first item becomes active by default when uncontrolled.
`activeTab` (string) [optional]
Controlled active tab key. When omitted the component manages internal state.
`onTabChange` ((tabKey: string) => void) [optional]
Called whenever the active tab changes. Fires for both controlled and uncontrolled usage.
`onDisabledTabPress` ((tabKey: string, item: TabItem) => void) [optional]
Invoked when a disabled tab is pressed, allowing custom messaging or recovery flows.
`variant` ('line' | 'chip' | 'card' | 'folder') [optional, default: 'line']
Visual style of the tabs.
`size` (SizeValue) [optional, default: 'sm']
Size token controlling text and padding.
`color` ('primary' | 'secondary' | 'gray' | 'tertiary' | string) [optional, default: 'primary']
Theme color token or custom color used for indicators and active states.
`orientation` ('horizontal' | 'vertical') [optional, default: 'horizontal']
Orientation of the tab list.
`location` ('start' | 'end') [optional, default: 'start']
Placement of the tabs relative to their content. Influences indicator positioning.
`scrollable` (boolean) [optional, default: false]
Enables scrolling when tabs overflow the available axis.
`animated` (boolean) [optional, default: true]
Enables animated indicator transitions between tabs.
`animationDuration` (number) [optional, default: 250]
Duration (ms) for indicator animations when `animated` is true.
- … 16 more props documented
### Demos
- Basics | (id: Tabs.basic) | tags: tabs
Render the default tabs experience with inline content and a short caption explaining the pattern.
```tsx
import { Column, Tabs, Text } from '@platform-blocks/ui';
const ITEMS = [
{
key: 'overview',
label: 'Overview',
content: High-level summary and entry point.
},
{
key: 'details',
label: 'Details',
content: Deeper dive into metrics and configuration.
},
{
key: 'activity',
label: 'Activity',
content: Recent events, tasks, and notifications.
}
];
return (
Tabs render inline content directly below the active trigger by default.
);
}
```
- Animated transitions | (id: Tabs.animated) | tags: animation, tabs
Demonstrates motion-enabled tabs using the `animated` flag and custom duration to smooth the switch between panels.
```tsx
import { Column, Tabs, Text } from '@platform-blocks/ui';
const ITEMS = [
{
key: 'home',
label: 'Home',
content: (
Welcome back
Animated transitions ease between dashboard sections and reinforce context shifts.
)
},
{
key: 'analytics',
label: 'Analytics',
content: (
Analytics overview
Surface key charts and KPIs while the motion guides attention to new content.
)
},
{
key: 'settings',
label: 'Settings',
content: (
Account settings
Manage notifications, billing, and other preferences without abrupt content swaps.
)
}
];
return (
Enable `animated` to add motion and use `animationDuration` to moderate the easing speed.
);
}
```
## Component:
Text component provides consistent typography with various variants, colors, and styling options for displaying content.
### Props
`children` (React.ReactNode) [optional]
Text node children. Optional if using translation via `tx`.
`tx` (string) [optional]
Translation key (if provided, overrides children when found)
`txParams` (Record) [optional]
Params for translation interpolation
`variant` (HTMLTextVariant) [optional]
Text variant (mirrors semantic HTML tags)
`size` (SizeValue) [optional]
Size can be a size token or number (overrides variant fontSize)
`color` (string) [optional]
Text color (overrides theme text color)
`colorVariant` ('primary' | 'secondary' | 'muted' | 'disabled' | 'link' | 'success' | 'warning' | 'error' | 'info') [optional]
Semantic color variant (overrides color prop). Supports text palette plus status colors
`weight` ('normal' | 'medium' | 'semibold' | 'bold' | 'light' | 'black' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' | number) [optional]
Font weight (supports all CSS font-weight values)
`align` ('left' | 'center' | 'right' | 'justify') [optional]
Text alignment
`lineHeight` (number) [optional]
Line height as a multiplier (e.g., 1.5) or absolute value
`tracking` (number) [optional]
Letter spacing (tracking) in pixels or em units
`uppercase` (boolean) [optional]
Convert text to uppercase
- … 9 more props documented
### Demos
- Basic Usage | (id: Text.basic) | tags: basic, typography
Basic text usage with different variants and semantic elements.
```tsx
import { Card, Column, Text } from '@platform-blocks/ui';
return (
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Body text is the default variant for paragraphs.
Caption text keeps supporting details readable.
Small text works well for fine print or metadata.
Default text without a variant falls back to body styling.
This paragraph shows natural wrapping behavior when the content spans multiple lines in a layout.
);
}
```
- Colors | (id: Text.colors) | tags: colors, theming
Text color variants and custom color options for different contexts.
```tsx
import { Card, Column, Text } from '@platform-blocks/ui';
return (
Semantic colors
Primary color text
Secondary color text
Muted color text
Disabled color text
Link color text
Custom palette
Custom red text
Custom teal text
Custom blue text
Custom green text
Custom yellow text
);
}
```
## Component: