# plocks — Complete Documentation Every documentation page concatenated in full: component and chart pages with their props, sub-components and every example, hook pages with their type definitions, the guides, and the FAQ. Nothing here is truncated. For an index of the same content as individually fetchable pages, use https://plocks.dev/llms.txt. For one file sized to a context window, use https://plocks.dev/llms-small.txt. Every example is a complete module that imports from the published packages (@plocks/ui, @plocks/brands, @plocks/carousel, @plocks/charts, @plocks/code, @plocks/dates, @plocks/emoji-picker, @plocks/media, @plocks/qrcode, @plocks/spotlight). Install: `npm install @plocks/ui` — separate packages, installed alongside it: `@plocks/brands`, `@plocks/carousel`, `@plocks/charts`, `@plocks/code`, `@plocks/dates`, `@plocks/emoji-picker`, `@plocks/media`, `@plocks/qrcode`, `@plocks/spotlight`. A component page's Import line names the package it comes from. Version: generated from the `main` branch — `@plocks/ui` 0.2.0, `@plocks/brands` 0.2.0, `@plocks/carousel` 0.2.0, `@plocks/charts` 0.2.0, `@plocks/code` 0.2.0, `@plocks/dates` 0.2.0, `@plocks/emoji-picker` 0.2.0, `@plocks/media` 0.2.0, `@plocks/qrcode` 0.2.0, `@plocks/spotlight` 0.2.0. The branch can be ahead of the latest npm release; if an API here is missing from your installed version, check the changelog: https://github.com/platform-blocks/plocks/tree/main/changelog Website: https://plocks.dev • GitHub: https://github.com/platform-blocks/plocks • npm: https://www.npmjs.com/package/@plocks/ui **Conventions** - Import from the package root: `import { Button, Row, useToast } from '@plocks/ui'`. Some components live in their own package — `@plocks/dates`, `@plocks/charts`, `@plocks/code`, `@plocks/media`, `@plocks/carousel`, `@plocks/spotlight`, `@plocks/brands`, `@plocks/qrcode`, `@plocks/emoji-picker` — installed alongside `@plocks/ui`; each page's Import line names the package. - Wrap the app once in `` (theme, color scheme, overlays, i18n). `useToast()` and `useDialog()` render through `` / `` — mount those inside it before calling them. Theming: https://plocks.dev/llms/guides/theming.md - Lay out with `Block` in place of `View`, `Row` / `Column` for flex stacks (`gap`, `align`, `justify`, `wrap`), and `Grid` + `GridItem` for columns. `Row` and `Column` are documented on the Flex page. - Most components take the shared style props — spacing (`m`, `p`, `mx`, `py`, …), sizing (`w`, `h`, `fullWidth`, …), `radius`, `shadow` — form inputs share the field props (`label`, `description`, `error`, `helperText`, `required`, …) and charts the chart props. Component pages list their own props and name the shared groups they accept; the groups are defined once here: https://plocks.dev/llms/guides/shared-props.md - `size`, spacing, `radius` and `shadow` take the tokens `xs` `sm` `md` `lg` `xl` `2xl` `3xl` (default `md`) or a number. `color` takes a palette name (`primary`, `secondary`, `tertiary`, `success`, `warning`, `error`, `gray`), a shade (`'primary.6'`), or any CSS color. - `Button` is neutral by default — use `variant="filled"` for the primary action. Events use React Native names: `onPress`, `onChange`, never `onClick`. - `` accepts only names in the built-in Tabler registry — do not guess one: https://plocks.dev/llms/guides/icons.md - Every example is a complete module: real imports from the published packages and an exported `Demo` component. Fixtures it imports (`./data`) are shown beneath it. **Choosing between similar components** - On/off or pick-one input: `Switch` for a setting that applies immediately; `Checkbox` for a choice submitted with a form; `ControlField` wraps either (or `Radio`) in a labelled, pressable row; `SegmentedControl` for 2–5 exclusive options shown inline; `RadioGroup` for a vertical list; `Select` for a long list in a dropdown; `AutoComplete` when the list is searched or loaded async; `ToggleGroup` for toolbar-style single or multi selection. - Tabular data: `Table` for static semantic markup; `DataTable` for sorting, filtering, pagination and row selection; `DataList` for label/value pairs. - Dates and times: `DatePickerInput` / `TimePickerInput` for a form field that opens a picker; `DatePicker` / `TimePicker` for an inline panel; `Calendar` for a full month grid; `MiniCalendar` for a compact month view; `MonthPicker` / `YearPicker` for coarser choices. - Colors: `ColorInput` for a hex field plus palette; `ColorPicker` for a swatch button that opens a palette, with no text field; `ColorSwatch` to display a color. - Floating and transient UI: `Tooltip` for a non-interactive hint; `Popover` for interactive content anchored to a trigger; `Menu` for a list of actions; `ContextMenu` for right-click / long-press; `Dialog` for modal, confirmation or bottom-sheet content; `Toast` (via `useToast()`) for a transient notice; `Alert` for a message that stays inline. - Loading states: `Loader` for a spinner; `Skeleton` for placeholders shaped like the content; `Progress` (linear) or `Ring` (radial) for measurable progress; `LoadingOverlay` to block a region while work runs. - Labels and markers: `Badge` for a status, category or count; `Chip` for a tag the user selects, presses or removes; `Indicator` for a dot or count pinned to the corner of another element. - Containers: `Block` for plain layout; `Card` for a content card (six variants); `Surface` for an elevation level without card chrome; `Space` for a fixed gap where margins do not fit. - Text: `Text` for body copy and inline variants; `Title` for semantic headings (`order={1..6}`); `Highlight` to mark matches in a string; `GradientText` / `ShimmerText` for decorative emphasis. - Actions and navigation: `Button` for actions; `IconButton` for icon-only actions (needs `accessibilityLabel`); `Link` for navigation; `Menu` for several related actions behind one trigger; `Tabs`, `Stepper`, `Pagination`, `Breadcrumbs` and `NavTree` for moving between views. ================================================================================ # GUIDES # Getting started Install plocks, wire up the provider, and render your first component. Docs: https://plocks.dev/getting-started **Prerequisites:** - [Node.js 20.19.4 or newer](https://nodejs.org/en/download) — An active LTS release (22.x or 24.x) is the safest choice - [npm 10 or newer](https://www.npmjs.com) — Bundled with Node.js ## Install with npm Add [@plocks/ui](https://www.npmjs.com/package/@plocks/ui) to your React Native or Expo project: ```bash npm install @plocks/ui ``` Date pickers, charts, code blocks, media players, the carousel, Spotlight, brand logos, QR codes and emoji picking are separate packages — @plocks/dates, @plocks/charts, @plocks/code, @plocks/media, @plocks/carousel, @plocks/spotlight, @plocks/brands, @plocks/qrcode and @plocks/emoji-picker. Install the ones you use. ## Install the peer dependencies plocks builds on a handful of packages your app provides. On Expo, install them with expo install so the versions match your SDK: ```bash npx expo install \ react-native-reanimated \ react-native-safe-area-context \ react-native-svg ``` ## Set up the provider Wrap your root component with PlocksProvider to enable theming: `App.tsx` ```tsx import { PlocksProvider } from '@plocks/ui'; import { YourApp } from './YourApp'; export function Demo() { return ( ); } ``` ## Verify the install Render a component to confirm everything is wired up: `TestComponent.tsx` ```tsx import { Text, Button, Block } from '@plocks/ui'; export function Demo() { return ( ; } export default function App() { return ( ); } ``` ## Custom theme Pass `theme` to change tokens. A partial override is merged onto the built-in theme of the current scheme, so light/dark switching keeps working; an override with an explicit `colorScheme` pins that scheme; a `{ light, dark }` pair gives each scheme its own override. Color ramps have ten shades with the base color at index 5 — light ramps run lightest to darkest, dark ramps darkest to lightest — so a brand palette needs one ramp per scheme. Keep the theme object stable (a module constant or `useMemo`): resolved themes are cached per object. `App.tsx` ```tsx import type { ReactNode } from 'react'; import { DARK_THEME, DEFAULT_THEME, PlocksProvider, createTheme } from '@plocks/ui'; const theme = { light: createTheme({ colors: { ...DEFAULT_THEME.colors, primary: ['#F5F3FF', '#EDE9FE', '#DDD6FE', '#C4B5FD', '#A78BFA', '#7C3AED', '#6D28D9', '#5B21B6', '#4C1D95', '#2E1065'], }, }), dark: createTheme({ colors: { ...DARK_THEME.colors, primary: ['#1E1033', '#2E1065', '#4C1D95', '#5B21B6', '#6D28D9', '#7C3AED', '#8B5CF6', '#A78BFA', '#C4B5FD', '#DDD6FE'], }, }), }; export default function App({ children }: { children: ReactNode }) { return {children}; } ``` ## Reading tokens `useTheme()` returns the active `PlocksTheme` — outside a provider it quietly returns `DEFAULT_THEME`. Prefer component props (`color`, `bg`, `p`, `radius`) over reading tokens; reach for the theme when styling your own primitives. `theme.spacing`, `theme.radii` and `theme.fontSizes` hold CSS pixel strings such as `'16px'`, not numbers. `Callout.tsx` ```tsx import { Block, Text, useTheme } from '@plocks/ui'; export function Callout({ children }: { children: string }) { const theme = useTheme(); return ( {children} ); } ``` ## Elevation Express depth with `Surface` rather than hand-picked backgrounds: `level={0..3}` runs page → resting content (cards, panels) → floating content (menus, popovers) → screen-level content (dialogs, sheets, toasts), and `raised` takes the enclosing Surface's level plus one. Light mode shows elevation mostly with shadow, dark mode with a lighter fill and a hairline border. ## Provider props `PlocksProvider` also mounts the overlay layer, i18n, direction, haptics, reduced motion and a safe-area provider; each has an opt-out below. ```ts export interface PlocksProviderProps { /** Your application */ children: React.ReactNode; /** * Custom theme. Either: * - a partial override — merged onto the built-in theme of the CURRENT color * scheme, so it keeps light/dark switching (`colorSchemeMode`, OS setting); * in a nested provider, merged onto the parent's theme (see `inherit`); * - an override with an explicit `colorScheme` — pins that scheme; * - a `{ light, dark }` pair — each side merged onto the matching built-in * theme and picked by the current scheme. * * Keep the object stable (module constant or memoized): resolved themes are * cached per object. */ theme?: PlocksThemeOverride | PlocksThemePair; /** * Nested providers: merge a partial `theme` onto the parent provider's theme * (default) rather than onto the built-in one. */ inherit?: boolean; /** * Whether to inject CSS variables. The root provider writes them to `:root`; * a nested one only when given its own `cssVariablesSelector`. */ withCSSVariables?: boolean; /** * Render `text` / `backgrounds` / `surfaces` as CSS `var()` references instead * of literal colors (web only, opt-in). * * Pair it with `createThemeColorVariablesCss` inlined in the document head: * statically rendered markup then answers to `prefers-color-scheme` on its * own, so a prerendered page is already in the reader's scheme at first paint * rather than after hydration. Off by default — the rewritten values are CSS * strings, so anything that hands a theme color to a non-CSS consumer (SVG * attributes, Animated interpolation) has to read `literalColors` instead. */ colorsAsCssVariables?: boolean; /** CSS selector where variables should be applied */ cssVariablesSelector?: string; /** * Color scheme mode: * - 'auto': automatically follows OS preference * - 'light': force light mode * - 'dark': force dark mode * * Default `'auto'` at the root; a nested provider follows its parent's scheme. */ colorSchemeMode?: 'auto' | 'light' | 'dark'; /** Root provider only. Whether to enable overlay system (menus, tooltips, etc.) */ withOverlays?: boolean; /** Root provider only. Inject the global web CSS (keyboard focus ring, text-input reset). */ withGlobalCSS?: boolean; /** * Root provider only. Mount a `SafeAreaProvider` so Dialog, the dropdown * sheets and AppShell keep clear of the notch and the home indicator. Skipped * when one is already above (Expo Router's, or your own), so you rarely need * to set this — pass `false` to leave the tree without one. */ withSafeAreaProvider?: boolean; /** * Root provider only. Persist and switch the color-scheme mode; read and set * it with `useThemeMode()`. */ themeModeConfig?: ThemeModeConfig; /** Root provider only. i18n: initial active locale */ locale?: string; /** Root provider only. i18n: fallback locale */ fallbackLocale?: string; /** Root provider only. i18n: resources map */ i18nResources?: I18nResources; /** Direction context configuration (pass false to opt out at the root) */ direction?: false | DirectionProviderConfig; /** Root provider only. Haptics context configuration (pass false to opt out) */ haptics?: false | HapticsProviderConfig; /** * Reduced motion for every library animation: * - `'system'` (default at the root): follow the OS setting * - `true` / `false`: force it on / off (e.g. an in-app setting, screenshots) * Omitted in a nested provider: inherit the parent's setting. */ reducedMotion?: ReducedMotionSetting; } ``` ## Theme object `useTheme()` returns a `PlocksTheme` with these top-level groups: - `primaryColor` — Primary color used for buttons, links, etc. - `colorScheme` — Color scheme - `designTokens` — Design tokens for consistent styling - `colors` — Colors palette - `text` — Semantic text colors - `backgrounds` — Semantic background & surface colors - `literalColors` — The literal colors behind `text`, `backgrounds` and `surfaces` when those have been rewritten to CSS `var()` references for the web — see `withCssVariableColors`. - `surfaces` — Elevation ladder consumed by `Surface` (and, through it, Card, Menu, Popover, Dialog…). - `textRoles` — Typography for titles and group labels (see `TextRoleName`), read through `resolveTextRole` and `Text`'s `textRole` prop. - `states` — Semantic interactive state colors - `fontFamily` — Font family - `fontFamilyMono` — Monospace font family (code, kbd, tabular numbers). - `controlSizes` — The control-size table used by Button, IconButton, Input and every other fixed-height control — read it through `getControlSize(theme, size)`. - `zIndices` — Stacking layers for overlays and sticky chrome — read through `getZIndex(theme, layer)`. - `fontSizes` — Font sizes - extended with new size system - `spacing` — Spacing values - extended with new size system - `radii` — Border radius values - extended with new size system - `shadows` — Shadows - `breakpoints` — Breakpoints for responsive design - `motion` — Motion tokens for animations - `components` — Component default props and styles (override point) - `other` — Any additional custom theme properties (see `PlocksThemeOther`) ## Types ```ts export interface PlocksThemePair { light?: PlocksThemeOverride; dark?: PlocksThemeOverride; } export interface ThemeModeConfig { /** Initial color scheme mode */ initialMode?: ColorSchemeMode; /** Custom persistence functions (optional) */ persistence?: { get: () => ColorSchemeMode | null; set: (mode: ColorSchemeMode) => void; }; /** Custom DOM manipulation (web only, optional) */ domConfig?: { selector: string; lightClass: string; darkClass: string; attribute: string; }; } // Returned by useThemeMode() interface ThemeModeContextValue { mode: ColorSchemeMode; setMode: (mode: ColorSchemeMode) => void; cycleMode: () => void; actualColorScheme: 'light' | 'dark'; // resolved value (no 'auto') } export function useTheme(): PlocksTheme; export function useThemeMode(): ThemeModeContextValue; export function createTheme(themeOverride: PlocksThemeOverride): PlocksThemeOverride; ``` -------------------------------------------------------------------------------- # Shared props and tokens Component pages list each component's own props, then name the shared groups it also accepts in one line — "Also accepts the shared props — field (…), spacing (…), …". The groups are defined here once. ## Style props Every component takes the style props — spacing (`m`, `px`, …) and box props (`w`, `h`, `miw`, `maw`, `mih`, `mah`, `bg`, `opacity`) — plus the visibility props and `style` / `testID` (the base group). They apply to the component's root. Many also take `radius` and `shadow`. Values are theme tokens or numbers: `p="md"`, `mt={12}`, `w="full"`, `bg="subtle"`. Horizontal spacing (`ml`, `pl`, …) follows the leading and trailing edges, so it flips in right-to-left layouts. Each prop has only its short name — there is no `maxWidth` or `backgroundColor` spelling. ```tsx import { Card, Text } from '@plocks/ui'; export function Demo() { return ( Spacing, size, background, radius and shadow come from the shared style props. ); } ``` ```ts export interface SpacingProps { /** Margin on all sides */ m?: SpacingValue; /** Margin top */ mt?: SpacingValue; /** Margin on the trailing edge (right in LTR, left in RTL) */ mr?: SpacingValue; /** Margin bottom */ mb?: SpacingValue; /** Margin on the leading edge (left in LTR, right in RTL) */ ml?: SpacingValue; /** Margin horizontal (both inline edges) */ mx?: SpacingValue; /** Margin vertical (top and bottom) */ my?: SpacingValue; /** Padding on all sides */ p?: SpacingValue; /** Padding top */ pt?: SpacingValue; /** Padding on the trailing edge (right in LTR, left in RTL) */ pr?: SpacingValue; /** Padding bottom */ pb?: SpacingValue; /** Padding on the leading edge (left in LTR, right in RTL) */ pl?: SpacingValue; /** Padding horizontal (both inline edges) */ px?: SpacingValue; /** Padding vertical (top and bottom) */ py?: SpacingValue; } export interface BoxProps { /** Width */ w?: DimensionProp; /** Height */ h?: DimensionProp; /** Minimum width */ miw?: DimensionProp; /** Maximum width */ maw?: DimensionProp; /** Minimum height */ mih?: DimensionProp; /** Maximum height */ mah?: DimensionProp; /** * Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, * `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade * syntax, or any CSS color. */ bg?: ThemeColor; /** Opacity, `0`–`1` */ opacity?: number; } export interface BorderRadiusProps { /** Border radius value - supports size tokens, numbers, and special values */ radius?: RadiusValue; } export interface ShadowProps { /** Shadow value - supports size tokens and 'none' */ shadow?: ShadowValue; } export interface VisibilityProps { /** Do not render in the light color scheme. */ lightHidden?: boolean; /** Do not render in the dark color scheme. */ darkHidden?: boolean; /** Do not render when the viewport is at least this breakpoint wide (`width >= theme.breakpoints[bp]`). */ hiddenFrom?: BreakpointToken; /** Render only when the viewport is at least this breakpoint wide. */ visibleFrom?: BreakpointToken; } export type BaseProps = StyleProps & VisibilityProps & { /** Style for the root element — merged last, after the component's own styles. */ style?: StyleProp; /** Test identifier for the root element. */ testID?: string; }; ``` `DimensionProp` is a number (dp / px), a percentage such as `'50%'`, `'auto'`, `'full'` (100%), or on web any CSS length. ## Field props Form inputs — `Input`, `Select`, `Checkbox`, `Radio`, `Switch`, the pickers and the rest — share one field frame: label, description, error and helper text wired to the control for assistive technology. Text inputs add the text-field group (`value`, `onChangeText`, `placeholder`, `clearable`, sections). Inside a `Form.Field`, value, change handler and error are injected for you. ```ts export interface FieldBaseProps extends BaseProps, LayoutProps, DisclaimerSupport { /** Label rendered above (or beside) the control. */ label?: React.ReactNode; /** Short description rendered under the label. */ description?: React.ReactNode; /** Error message. Marks the field invalid and is announced to assistive technology. */ error?: React.ReactNode; /** Helper text rendered under the control when there is no error. */ helperText?: React.ReactNode; /** Marks the field required (announced; shows an asterisk unless `withAsterisk` is false). */ required?: boolean; /** Show the required asterisk. Defaults to `required`. */ withAsterisk?: boolean; disabled?: boolean; readOnly?: boolean; size?: SizeValue; radius?: RadiusValue; variant?: FieldVariant; /** Field name for form integration. */ name?: string; /** Overrides the accessible name composed from `label`. */ accessibilityLabel?: string; accessibilityHint?: string; /** Identifier used with KeyboardManagerProvider to request refocus. */ keyboardFocusId?: string; /** Props applied to the label ``. */ labelProps?: Omit; /** Props applied to the description ``. */ descriptionProps?: Omit; onFocus?: () => void; onBlur?: () => void; } export interface TextFieldBaseProps extends FieldBaseProps { value?: string; defaultValue?: string; onChangeText?: (text: string) => void; placeholder?: string; /** Falls back to `theme.text.muted`. */ placeholderTextColor?: string; /** Show a clear button while the field has a value. */ clearable?: boolean; clearButtonLabel?: string; onClear?: () => void; /** Debounce delay for validation, in milliseconds. */ debounceMs?: number; /** Called when Enter is pressed. */ onEnter?: () => void; startSection?: React.ReactNode; endSection?: React.ReactNode; startSectionProps?: Omit; endSectionProps?: Omit; } export interface DisclaimerSupport { disclaimer?: React.ReactNode; disclaimerProps?: Omit; } ``` ## Chart props Every chart in `@plocks/charts` accepts the chart group (size, title, legend, tooltip, animation and accessibility options) and the chart event callbacks, on top of its own data props. ```ts export interface BaseChartProps extends SpacingProps { /** * Chart width in px. Omit it and the chart fills the box it is placed in, * redrawing when that box changes. A number is honoured up to the width the * container can actually give it — a chart never draws wider than its slot. */ w?: number; /** Chart height in px. Defaults to the chart's resting height, or `width / aspectRatio`. */ h?: number; /** * Height as a fraction of the resolved width (`width / height`), used when * `height` is omitted. `2` keeps the chart twice as wide as it is tall at * every container size. */ aspectRatio?: number; /** Upper bound on the resolved width. Useful for radial charts in wide columns. */ maw?: number; /** Lower bound on the resolved width. */ miw?: number; /** Upper bound on a height derived from `aspectRatio`. */ mah?: number; /** Lower bound on a height derived from `aspectRatio`. */ mih?: number; /** Chart test ID for testing */ testID?: string; /** Additional styles */ style?: any; /** Accessibility label surfaced to assistive tech */ accessibilityLabel?: string; /** Accessibility hint describing chart interaction */ accessibilityHint?: string; /** Accessibility role override */ accessibilityRole?: string; /** Whether the chart container is accessible */ accessible?: boolean; /** Platform specific accessibility importance */ importantForAccessibility?: 'auto' | 'yes' | 'no' | 'no-hide-descendants'; /** Animation duration in ms */ animationDuration?: number; /** Animation easing function */ animationEasing?: string; /** Whether chart is disabled */ disabled?: boolean; /** Chart title */ title?: string; /** Chart subtitle */ subtitle?: string; /** If false, chart expects a parent interaction provider (shared context). */ useOwnInteractionProvider?: boolean; /** Force suppress or show internal popover (auto suppressed when useOwnInteractionProvider=false if undefined). */ suppressPopover?: boolean; } export interface ChartInteractionCallbacks { /** Called when chart is tapped/clicked */ onPress?: (event: ChartInteractionEvent) => void; /** Called when data point is selected */ onDataPointPress?: (dataPoint: TData, event: ChartInteractionEvent) => void; } ``` The option types those props take — a chart's own data types are on its page: ```ts export interface ChartDataPoint { /** Unique identifier for the data point */ id?: string | number; /** X-axis value */ x: number; /** Y-axis value */ y: number; /** Optional label */ label?: string; /** Optional color override */ color?: string; /** Optional size override */ size?: number; /** Custom data for interactions */ data?: any; } export interface ChartAxis { /** Show axis line */ show?: boolean; /** Axis color */ color?: string; /** Axis thickness */ thickness?: number; /** Show tick marks */ showTicks?: boolean; /** Tick positions (auto-calculated if not provided) */ ticks?: number[]; /** Tick color */ tickColor?: string; /** Tick length */ tickLength?: number; /** Show labels */ showLabels?: boolean; /** Label formatter */ labelFormatter?: (value: number) => string; /** Label color */ labelColor?: string; /** Label font size */ labelFontSize?: number; /** Axis title */ title?: string; /** Title color */ titleColor?: string; /** Title font size */ titleFontSize?: number; } export interface ChartGrid { /** Show grid */ show?: boolean; /** Grid color */ color?: string; /** Grid line thickness */ thickness?: number; /** Grid line style */ style?: 'solid' | 'dashed' | 'dotted'; /** Show major grid lines */ showMajor?: boolean; /** Show minor grid lines */ showMinor?: boolean; /** Major grid positions */ majorLines?: number[]; /** Minor grid positions */ minorLines?: number[]; } export interface ChartLegend { /** Show legend */ show?: boolean; /** Legend position */ position?: 'top' | 'bottom' | 'left' | 'right'; /** Legend alignment */ align?: 'start' | 'center' | 'end'; /** Legend items (auto-generated if not provided) */ items?: ChartLegendItem[]; /** Legend item color */ textColor?: string; /** Legend item font size */ fontSize?: number; } export interface ChartTooltip { /** Show tooltip on hover/press */ show?: boolean; /** Tooltip formatter */ formatter?: (dataPoint: TData) => string | React.ReactNode; /** Tooltip background color */ backgroundColor?: string; /** Tooltip text color */ textColor?: string; /** Tooltip font size */ fontSize?: number; /** Tooltip border radius */ borderRadius?: number; /** Tooltip padding */ padding?: number; } export interface ChartAnimation { /** Animation duration */ duration?: number; /** Animation delay */ delay?: number; /** Animation easing */ easing?: string; /** Animation type */ type?: 'fade' | 'scale' | 'slide' | 'draw' | 'drawOn' | 'spiral' | 'bounce' | 'elastic' | 'wave'; /** Stagger animation for multiple elements */ stagger?: number; } export interface ChartAnnotation { /** Unique identifier for the annotation */ id: string | number; /** Visual shape used to render the annotation */ shape: ChartAnnotationShape; /** X coordinate for point or vertical-line annotations */ x?: number; /** Y coordinate for point or horizontal-line annotations */ y?: number; /** Starting x coordinate for range or box annotations */ x1?: number; /** Ending x coordinate for range or box annotations */ x2?: number; /** Starting y coordinate for range or box annotations */ y1?: number; /** Ending y coordinate for range or box annotations */ y2?: number; /** Label displayed near the annotation */ label?: string; /** Stroke or outline color used for the annotation */ color?: string; /** Fill color used for the annotation */ backgroundColor?: string; /** Overall opacity of the annotation */ opacity?: number; /** Stroke width for line-based annotations */ lineWidth?: number; /** Dashed stroke pattern for the annotation */ dashArray?: number[]; /** Font size for any annotation text */ fontSize?: number; /** Text color for the annotation label */ textColor?: string; /** Arbitrary additional data associated with the annotation */ data?: any; } export type ChartFill = string | ChartGradient; ``` ## Token types ```ts /** Size tokens, smallest to largest (also exported as `ComponentSize`). */ export type SizeToken = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl'; /** `SizeValue` is the same type. A number is read in px. */ export type ComponentSizeValue = SizeToken | number; export type SpacingValue = SizeToken | 'auto' | '0' | number; export type DimensionProp = number | 'auto' | 'full' | `${number}%` | (string & {}); export type RadiusValue = SizeToken | 'none' | 'full' | number; export type ShadowValue = | SizeValue | 'none'; export type ThemeColorToken = | 'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'error' | 'gray'; export type ThemeColor = ThemeColorToken | (string & {}); export type BreakpointToken = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; ``` -------------------------------------------------------------------------------- # Icons `@plocks/ui@0.2.0` registers 216 icons by default, using bundled Tabler-derived glyphs. These are the only strings `name` accepts out of the box — anything else renders nothing, so **do not guess an icon name**; pick one from this list or pass a component instead. ```tsx import { Icon, IconButton } from '@plocks/ui'; // Not in the registry? Pass any icon component or element instead: import { IconRocket } from '@tabler/icons-react-native'; ``` There is no public API for registering additional names — `registerIcon` / `registerIcons` exist in the source but are not exported from the package root or from `@plocks/ui/Icon`. Pass a component for anything outside this list. ## Names accordion, alert-circle, alignCenter, alignJustify, alignLeft, alignRight, arrow-down, arrow-left, arrow-right, arrow-up, arrowDown, arrowLeft, arrowRight, arrowUp, autocomplete, avatar, badge, bar-chart, bell, block, bluetooth, bold, bolt, bone, bookmark, breadcrumbs, button, calendar, camera, card, carousel, cart, chart-area, chart-bar, chart-donut, chart-heatmap, chart-line, chart-pie, chart-scatter, chart-sparkline, chat, check, chevron-down, chevron-left, chevron-right, chevron-up, chevronDown, chevronLeft, chevronRight, chevrons-up-down, chevronUp, chip, circle, clock, close, code, cog, color, colors, compress, container, contrast, copy, database, datatable, delete, dialog, divider, dots, download, edit, ellipsis, ellipsis-h, email, emoji, error, exclamation, expand, external-link, eye, eyeOff, file, filter, flag, flex, folder, font, form, funnel, gallery, globe, grid, guitar, heading, headphones, heart, highlight, home, hook, image, image-off, indicator, info, input, italic, keyboard, keycap, knob, knobs, layer-mask, layers, line-chart, linechart, link, list, listOrdered, listUnordered, loader, loading, location, lock, mail, map, markdown, masonry, menu, message, message-circle, mic, microphone, minus, moon, music, number, package, paddingFrame, pagination, palette, paper, pause, person, phone, piano, pin, plan, play, plus, popover, progress, progress-shield, qrcode, question, quote, radio, rating, record, redo, refresh, repeat, richtext, rocket, rotate, save, search, select, selector-vertical, selectorVertical, settings, share, sheild, shop, slider, smile, sort, sparkles, speedometer, splitTrack, spoiler, spotlight, star, stepper, stop, strikethrough, success, sun, support, switch, table, tableofcontents, tabs, target, text, textarea, timeline, times, title, toast, toggle, toggleOff, toggleOn, tooltip, trash, tree, tuning, underline, undo, upload, user, volume-off, volume-up, volumeOff, warning, waveform, web, wifi, x -------------------------------------------------------------------------------- # Accessibility at plocks Inclusive design is a baseline requirement across the plocks library and documentation site. This page gathers the guardrails, utilities, and testing practices that help us meet WCAG 2.1 AA expectations for keyboard, screen reader, low-vision, and motion-sensitive users. Docs: https://plocks.dev/accessibility ## Keyboard & Focus Management plocks is fully operable without a mouse. Focus order mirrors visual layout and shortcuts are discoverable and customizable. - Every interactive component participates in a predictable tab order and preserves visible focus outlines across light and dark themes. - Dialogs, Spotlights, Dropdowns, and Menus trap focus while open and restore it to the triggering control when they close. - Scoped and global keyboard shortcuts are defined with `useHotkeys` / `useGlobalHotkeys`, with optional `aria-live` announcements for critical actions. ## Assistive Technology Semantics Components expose reliable roles, names, and states so VoiceOver, TalkBack, NVDA, and other assistive tools can correctly interpret the UI. - Buttons, links, inputs, and composite widgets expose consistent `accessibilityRole`, `accessibilityLabel`, and `aria-*` attributes on web and native platforms. - Spotlight and navigation lists announce active options while you move with the arrow keys or screen-reader rotor. - Semantic heading structure is enforced through typography variants and `useTitleRegistration`, ensuring the documentation table of contents mirrors the DOM outline. ## Visual Accessibility Color, typography, and spacing tokens are engineered for clarity, even under high contrast, zoom, or theme overrides. - The design tokens maintain WCAG AA contrast ratios (4.5:1 or higher) for body text and interactive states in both color schemes. - Highlight backgrounds adapt automatically to surrounding foreground colors, so inline emphasis remains legible on both dark and light surfaces. - Component spacing, typography scales, and layout primitives support dynamic type and zoom without breaking line wraps or truncating controls. ## Motion, Audio, & Sensory Preferences Micro-interactions respect user comfort and can be tuned or disabled globally. - Animations, shimmer effects, and transitions read the system `prefers-reduced-motion` flag using `usePrefersReducedMotion`, gracefully disabling non-essential motion. - Tactile interactions use the `useHaptics` hook and can be toggled off centrally for users who prefer silent interactions. - Auditory cues route through the `SoundProvider`, allowing global enable/disable controls and volume adjustments. ## Design & QA Workflow Accessibility is woven into the release checklist with dedicated reviews and automated tooling. - WCAG 2.1 AA alignment drives our component acceptance criteria and manual review checklists. - ARIA Authoring Practices are referenced for complex widgets such as accordions, tabs, and tree views. - Testing flows incorporate keyboard-only walkthroughs, VoiceOver / TalkBack smoke tests, and contrast analysis with tooling like Axe and Lighthouse. ## Applying Accessible APIs Components forward platform accessibility props, letting you layer contextual hints or ARIA attributes without losing built-in behaviour. ```tsx import { Button, useSpotlightToggle } from '@plocks/ui'; export function AccessibleSearchTrigger() { const { open } = useSpotlightToggle(); return ( ); } ``` Have an accessibility request or need help auditing a flow? Open an issue on GitHub or ask in Discord so we can collaborate on an inclusive solution. -------------------------------------------------------------------------------- # Localization plocks ships a lightweight i18n layer. Provide locale resource objects, wrap your app with I18nProvider, then translate via the tx prop or the useI18n hook. Docs: https://plocks.dev/localization ## Create resource files Create JSON resource files per locale. `resources.ts` ```tsx import en from './locales/en/common.json'; import fr from './locales/fr/common.json'; import es from './locales/es/common.json'; export const resources = { en: { translation: en }, fr: { translation: fr }, es: { translation: es } }; ``` ## Wrap the app in I18nProvider Wrap your app in the I18nProvider component. `App.tsx` ```tsx import { I18nProvider } from '@plocks/ui'; import { resources } from './resources'; export function App() { return ( ); } ``` ## Translate and switch locales Switch locales by calling setLocale('fr') etc. Components re-render automatically. `Greeting.tsx` ```tsx import { Alert, Text, ToggleButton, ToggleGroup, useI18n } from '@plocks/ui'; const LOCALES = ['en', 'fr', 'es']; function Greeting() { const { t, setLocale, locale } = useI18n(); return ( <> { if (typeof next === 'string') setLocale(next); }} > {LOCALES.map((l) => ( {l.toUpperCase()} ))} {t('localization.current', { locale })} ); } ``` ## Notes - Use to render translated copy. - Or call const { t, setLocale, locale } = useI18n(); then t('localization.exampleGreeting', { name: 'Ada' }). - Use formatDate / formatNumber / formatRelativeTime helpers for localized formatting. - Missing keys fall back to fallbackLocale then return the key name (configurable via onMissingKey). -------------------------------------------------------------------------------- # COMPONENTS # Accordion The Accordion component groups related content into expandable sections. ## Metadata - Import: `import { Accordion } from '@plocks/ui';` - Tags: collapse, expand, panel, ui, content-grouping - Docs: https://plocks.dev/components/Accordion - Source: https://github.com/platform-blocks/plocks/tree/main/packages/ui/src/components/Accordion ## Props - `items` (required): AccordionItem[] — Ordered list of items to render. The `key` for each item must be unique. - `type`: 'single' | 'multiple' = 'single' — Expansion behavior. 'single' ensures only one item can be expanded at a time; 'multiple' allows independent expansion. - `defaultExpanded`: string[] = [] — Initial expanded item keys (uncontrolled). Ignored when `expanded` is provided. For `type="single"` only the first key is used at initialization. - `expanded`: string[] — Controlled set of expanded item keys. Provide alongside `onExpandedChange`. - `onExpandedChange`: (expanded: string[]) => void — Called when the expanded keys change (both controlled & uncontrolled flows). - `onItemToggle`: OnAccordionToggle — Per-item toggle event with rich metadata. Fires after state resolution. - `variant`: 'default' | 'separated' | 'bordered' = 'default' — Visual variant style preset. - `size`: SizeValue = 'md' — Size scale controlling paddings, font sizes, and icon dimensions. - `color`: ThemeColor = undefined — Brand accent applied to the expanded item (title, chevron, and a subtle surface tint). Opt-in — when unset, the open state stays neutral and reads from the bolded title and rotated chevron alone. - `showChevron`: boolean = true — Whether to render the chevron affordance. - `chevronPosition`: 'start' | 'end' = 'end' — Chevron placement relative to the header text. - `density`: 'comfortable' | 'compact' | 'spacious' = 'comfortable' — Space efficiency / vertical density preset. - `radius`: RadiusValue — Corner radius of the `separated` items / `bordered` frame: a radius token, px number, `'none'` or `'full'`. - `headerStyle`: StyleProp — Header row style override applied to each item. - `contentStyle`: StyleProp — Collapsible content container style override. - `headerTextStyle`: StyleProp — Text style applied to the header label. - `titleProps`: Omit — Override props applied to each item's header `` (style, fw, ff, size, c). Applies to every item in the accordion. - `persistKey`: string — Explicit persistence key. If omitted, an automatic hash key will be generated when uncontrolled. - `autoPersist`: boolean = true — Enables persistence of expanded state (uncontrolled only) across remounts in-process. - `animated`: AccordionAnimationProp = true — Enables animation or accepts a config object for custom durations & easing. - `transitionDuration`: number = 220 — Length of the expand/collapse transition (chevron spin + panel height) in ms. Takes precedence over `animated`; `0` renders state changes instantly. Always 0 when the user prefers reduced motion. - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ## Types ```ts export interface AccordionItem { /** * Unique identifier for the item. Must be stable across renders. */ key: string; /** * Header label rendered in the item trigger row. */ title: string; /** * Collapsible body content shown when the item is expanded. */ content: ReactNode; /** * Disables user interaction and visually indicates the item is inactive. */ disabled?: boolean; /** * Optional decorative or status icon rendered alongside the title. */ icon?: ReactNode; /** * Overrides the accordion-level `color` for this item's expanded emphasis * (title, chevron, and surface tint). Lets a single accordion mix accents. */ color?: ThemeColor; } export type OnAccordionToggle = (detail: AccordionToggleDetail) => void; export type AccordionAnimationProp = boolean | { duration?: number; easing?: (t: number) => number }; export interface AccordionToggleDetail { itemKey: string; expanded: boolean; expandedKeys: string[]; type: AccordionType; variant: AccordionVariant | undefined; } ``` ## Examples ### Basics Allow only one item to open at a time by setting `type="single"` and passing an items array. ```tsx import { Accordion } from '@plocks/ui'; import { faqItems } from '../data'; export function Demo() { return ; } ``` `data.ts` ```ts import React from 'react'; import { Text } from '@plocks/ui'; import type { AccordionItemType } from '@plocks/ui'; // Wrap body copy in without JSX so this stays a plain `.ts` data module // (a `.tsx` sibling would be picked up as its own demo by the docs generator). const body = (text: string) => React.createElement(Text, { size: 'sm' }, text); export const faqItems: AccordionItemType[] = [ { key: 'foundation', title: 'What is plocks?', content: body('A cross-platform design system for shipping polished React Native apps faster.'), }, { key: 'benefits', title: 'Why use an accordion?', content: body('Keep dense guidance scannable while letting readers expand only what they need.'), }, { key: 'next-steps', title: 'How do I get started?', content: body('Install the package, drop the provider at the root, and follow the onboarding checklist.'), }, ]; export const knowledgeBase: AccordionItemType[] = [ { key: 'collaboration', title: 'Invite collaborators', content: body('Share the project with teammates to co-author docs and keep decisions centralized.'), }, { key: 'appearance', title: 'Customize the theme', content: body('Extend the default theme tokens with your brand colors and typography.'), }, { key: 'automation', title: 'Automate release notes', content: body('Connect the changelog generator to auto-publish updates on every tag.'), }, ]; export const onboardingSteps: AccordionItemType[] = [ { key: 'create-project', title: 'Create a project', content: body('Spin up a workspace and invite your teammates.'), }, { key: 'import-assets', title: 'Import assets', content: body('Upload icons, typography, and spacing tokens.'), }, ]; export const statusItems: AccordionItemType[] = [ { key: 'info', title: 'Informational', color: 'primary', content: body('Set `color` per item to accent its expanded panel.'), }, { key: 'healthy', title: 'All systems healthy', color: 'success', content: body('The title and chevron pick up the color while open.'), }, { key: 'review', title: 'Needs review', color: 'warning', content: body('Collapsed items stay neutral.'), }, { key: 'failed', title: 'Build failed', color: 'error', content: body('Use error to emphasize failures.'), }, ]; export const setupSteps: AccordionItemType[] = [ { key: 'install', title: 'Install the package', content: body('Run `npm install @plocks/ui` in your workspace.'), }, { key: 'provider', title: 'Wrap your app in providers', content: body('Add ThemeProvider, ToastProvider, and DialogProvider at the root.'), }, { key: 'compose', title: 'Compose your first screen', content: body('Drop in fields, buttons, and feedback components from the library.'), }, ]; ``` ### Multiple Expansion Control the `expanded` keys to keep several accordion items open at the same time. ```tsx import { useState } from 'react'; import { Accordion } from '@plocks/ui'; import { knowledgeBase } from '../data'; export function Demo() { const [expandedKeys, setExpandedKeys] = useState(['collaboration']); return ( ); } ``` `data.ts` is the same file shown under “Basics” above. ### Visual Variants Switch between `default`, `separated`, and `bordered` variants to adjust emphasis. ```tsx import { Accordion, Block, Text } from '@plocks/ui'; import { onboardingSteps } from '../data'; const variants = ['default', 'separated', 'bordered'] as const; export function Demo() { return ( {variants.map((variant) => ( {variant} ))} ); } ``` `data.ts` is the same file shown under “Basics” above. ### Accent Colors Accent each expanded panel with a theme palette — `primary`, `secondary`, `tertiary`, `success`, `warning`, `error`, or `gray`. Set `color` on the accordion for a uniform accent, or per item to mix accents in a single accordion. Collapsed items stay neutral so only the open panel is highlighted. ```tsx import { Accordion } from '@plocks/ui'; import { statusItems } from '../data'; export function Demo() { return ( ); } ``` `data.ts` is the same file shown under “Basics” above. ### Title customization `titleProps` accepts any `` props (`ff`, `fw`, `lts`, `tt`, `size`, `c`, `style`) and applies them to every item header in the accordion. The existing `headerTextStyle` escape hatch still works and can be combined. ```tsx import { Accordion } from '@plocks/ui'; import { setupSteps } from '../data'; export function Demo() { return ( ); } ``` `data.ts` is the same file shown under “Basics” above. -------------------------------------------------------------------------------- # ActionBar Use ActionBar for actions that apply to selected content. It stays pinned while the page scrolls. ## Metadata - Import: `import { ActionBar } from '@plocks/ui';` - Status: beta - Docs: https://plocks.dev/components/ActionBar - Source: https://github.com/platform-blocks/plocks/tree/main/packages/ui/src/components/ActionBar ## Props - `children`: React.ReactNode — Actions rendered in the bar. - `opened` (required): boolean — Controls visibility. - `onClose`: () => void — Called by the close button or Escape when enabled. - `closeOnEscape`: boolean = false — Let Escape dismiss this bar once layers above it close. @default false - `keepMounted`: boolean = false — Preserve the hidden bar in the tree. @default false - `withinPortal`: boolean = true — Render at the application root. @default true - `position`: { top?: number; bottom?: number; start?: number; end?: number } = { bottom: 24 } — Logical viewport insets. @default { bottom: 24 } - `radius`: RadiusValue = 'md' — Corner radius. @default 'md' - `shadow`: ShadowToken = 'md' — Surface shadow. @default 'md' - `withBorder`: boolean = true — Draw a border. @default true - `zIndex`: number — Stack order. - `transition`: 'pop' | 'slide-up' | 'fade' = 'pop' — Entrance effect. @default 'pop' - `transitionDuration`: number = 200 — Animation duration in ms. @default 200 - `aria-label`: string = 'Actions' — Accessible group label. @default 'Actions' - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ## Sub-components `import { ActionBarDivider, ActionBarCloseButton } from '@plocks/ui';` ### ActionBarDivider - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ### ActionBarCloseButton - `accessibilityLabel`: string - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ### ActionBar.CloseButton - `accessibilityLabel`: string - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ### ActionBar.Divider - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ## Examples ### Basics Open the bar when selection is active; CloseButton calls `onClose`. ```tsx import { useState } from 'react'; import { ActionBar, Block, Button, Text } from '@plocks/ui'; export function Demo() { const [opened, setOpened] = useState(false); return ( setOpened(false)}> 3 selected ); } ``` ### Placement and Escape Use logical viewport insets to place the bar and `closeOnEscape` to dismiss it with Escape. ```tsx import { useState } from 'react'; import { ActionBar, Block, Button, Text } from '@plocks/ui'; export function Demo() { const [opened, setOpened] = useState(false); return ( setOpened(false)} position={{ top: 24, end: 24 }} transition="slide-up" closeOnEscape > Top end ); } ``` ### Icon Buttons IconButton actions can be grouped with dividers and tooltips. ```tsx import { useState } from 'react'; import { ActionBar, Block, Button, IconButton, Tooltip } from '@plocks/ui'; export function Demo() { const [opened, setOpened] = useState(false); return ( setOpened(false)}> ); } ``` ### Transitions `transition` changes how the bar enters and leaves. ```tsx import { useState } from 'react'; import { ActionBar, Block, Button, Text } from '@plocks/ui'; export function Demo() { const [opened, setOpened] = useState(false); return ( setOpened(false)} transition="slide-up" transitionDuration={300} > Sliding actions ); } ``` ### Keep Mounted `keepMounted` preserves child state while the bar is hidden. ```tsx import { useState } from 'react'; import { ActionBar, Block, Button, Checkbox } from '@plocks/ui'; export function Demo() { const [opened, setOpened] = useState(false); return ( setOpened(false)} keepMounted> ); } ``` -------------------------------------------------------------------------------- # Alert Alert displays prominent messages with severity styles and optional actions. ## Metadata - Import: `import { Alert } from '@plocks/ui';` - Tags: alert, notice, notification, message, status, feedback, callout - Docs: https://plocks.dev/components/Alert - Source: https://github.com/platform-blocks/plocks/tree/main/packages/ui/src/components/Alert ## Props - `variant`: 'light' | 'filled' | 'outline' | 'subtle' = 'light' - `color`: ThemeColor = 'primary' — Accent color. Without a `severity`, `error` / `warning` colors also make the alert urgent (`role="alert"`); every other color is a polite `role="status"`. - `severity`: 'info' | 'success' | 'warning' | 'error' — Severity helper — sets the color, the default icon, and the live-region urgency: `error` / `warning` render `role="alert"` (and are announced on mount on native), `info` / `success` render `role="status"`. Prefer it over `color` when the alert carries a status. - `title`: string - `children`: React.ReactNode - `icon`: React.ReactNode | string | null | false - `fullWidth`: boolean = false - `withCloseButton`: boolean = false - `closeButtonLabel`: string = 'Close' — Accessible name of the close button. @default 'Close' - `onClose`: () => void - `radius`: RadiusValue = 'md' — Corner radius: theme radius token, px, `'none'` or `'full'`. @default 'md' - `titleProps`: Omit — Override props applied to the title `` (style, fw, ff, size, c). - `bodyProps`: Omit — Override props applied to the body `` (the `children` content). - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ## Examples ### Basics Use `severity` to set it — it picks the matching color and icon automatically. ```tsx import { Alert, Block } from '@plocks/ui'; export function Demo() { return ( Use alerts to highlight contextual information inline with page content. Your changes were stored successfully. Retry the action or check the status page for outages. ); } ``` ### Variants Compare light, outline, filled, and subtle variants to match alert prominence to the message. ```tsx import { Alert, Block } from '@plocks/ui'; export function Demo() { return ( Balanced background and border treatment for inline notes. Subtle emphasis without increasing background contrast. High-contrast option for urgent messaging. No background color, but tinted icon and text. ); } ``` ### Dismissible Add `withCloseButton` and handle `onClose` to let users dismiss an alert. ```tsx import { useState } from 'react'; import { Alert, Button } from '@plocks/ui'; export function Demo() { const [visible, setVisible] = useState(true); if (!visible) { return ( ); } return ( setVisible(false)} > Your draft is missing a title. Resolve before publishing. ); } ``` -------------------------------------------------------------------------------- # AppShell AppShell arranges an app’s header, navigation, aside, footer, and mobile navigation in a responsive frame. ## Metadata - Import: `import { AppShell } from '@plocks/ui';` - Docs: https://plocks.dev/components/AppShell - Source: https://github.com/platform-blocks/plocks/tree/main/packages/ui/src/components/AppShell ## Props - `layout`: 'default' | 'alt' = 'default' — `'default'`: the header spans the full width and the navbar/aside sit below it. `'alt'`: the navbar and aside span the full height and the header sits between them. @default 'default' - `header`: HeaderConfig - `navbar`: NavbarConfig - `aside`: AsideConfig - `footer`: FooterConfig - `bottomNav`: BottomNavConfig - `showHeader`: boolean = true - `layoutSections`: LayoutVisibilityConfig — Toggle rendering of individual autoLayout sections - `autoLayout`: boolean — 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) — Content to render inside AppShell.Header when autoLayout is enabled - `navbarContent`: React.ReactNode | (() => React.ReactNode) — Content to render inside AppShell.Navbar when autoLayout is enabled - `asideContent`: React.ReactNode | (() => React.ReactNode) — Content to render inside AppShell.Aside when autoLayout is enabled - `footerContent`: React.ReactNode | (() => React.ReactNode) — Content to render inside AppShell.Footer when autoLayout is enabled - `bottomNavItems`: BottomAppBarItem[] — Items for a mobile bottom navigation bar when autoLayout is enabled - `bottomNavProps`: Partial — Additional props forwarded to BottomAppBar in autoLayout mode (items overridden by bottomNavItems) - `mobileMenu`: MobileMenuConfig - `cssGeometry`: boolean = false — Take the shell's geometry from CSS custom properties rather than from the breakpoint the JavaScript resolved. Web only, and a contract: the app must inline the stylesheet `createAppShellCss` builds from the same config. It exists for statically rendered apps, where the prerender has no viewport to measure and every guess it makes lands as a layout shift and a hydration mismatch on first paint. See `shellCssVars.ts`. - `statusBar`: StatusBarConfig - `padding`: ResponsiveSize = no padding — Padding inside the main content area: a spacing token (`'md'`), px number, or a per-breakpoint object. @default no padding - `withBorder`: boolean = true — Default `withBorder` for every section. @default true - `zIndex`: number = each section's `theme.zIndices` layer — Stacking order for every section that doesn't set its own. @default each section's `theme.zIndices` layer - `transitionDuration`: number = 200 — Navbar/content transition length in ms (`0` = instant). Reduced motion forces `0`. @default 200 - `transitionTimingFunction`: string = a cubic ease-in-out — CSS timing function for the navbar/content transitions: `'linear'`, `'ease'`, `'ease-in'`, `'ease-out'`, `'ease-in-out'` or `'cubic-bezier(…)'`. - `disabled`: boolean = false - `children` (required): React.ReactNode - `withSafeArea`: boolean = true - `maxContentWidth`: number | string — Maximum width for main content area to prevent stretching on wide screens - `centerContent`: boolean — Center content when maxContentWidth is set - `tableOfContents`: React.ReactNode — Optional table of contents rendered at the end side of the main content - `hideTableOfContentsOnMobile`: boolean = true — Hide the table of contents automatically on mobile breakpoints - `tableOfContentsWidth`: number | string = 280 — Custom width for the table of contents column - `tableOfContentsWithBorder`: boolean = true — Toggle border between content and table of contents - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ## Sub-components `import { AppShellHeader, AppShellNavbar, AppShellAside, AppShellFooter, AppShellBottomNav, AppShellMain, AppShellSection, BottomAppBar, StatusBarManager, AppLayoutProvider, AppLayoutRenderer, MobileMenu } from '@plocks/ui';` ### AppShellHeader - `children` (required): React.ReactNode - `withBorder`: boolean = AppShell `withBorder` — Draw the hairline between this section and the content. @default AppShell `withBorder` - `zIndex`: number — Stacking order; wins over the section config and the theme layer. - `accessibilityLabel`: string — Accessible name of the section's landmark. - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ### AppShellNavbar - `drawerMode`: boolean — Force the overlay drawer (`true`) or the inline rail (`false`). Defaults to the drawer below `navbar.breakpoint`. - `accessibilityLabel`: string = 'Main' — Accessible name of the navigation landmark. @default 'Main' - `children` (required): React.ReactNode - `withBorder`: boolean = AppShell `withBorder` — Draw the hairline between this section and the content. @default AppShell `withBorder` - `zIndex`: number — Stacking order; wins over the section config and the theme layer. - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ### AppShellAside - `children` (required): React.ReactNode - `withBorder`: boolean = AppShell `withBorder` — Draw the hairline between this section and the content. @default AppShell `withBorder` - `zIndex`: number — Stacking order; wins over the section config and the theme layer. - `accessibilityLabel`: string — Accessible name of the section's landmark. - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ### AppShellFooter - `children` (required): React.ReactNode - `withBorder`: boolean = AppShell `withBorder` — Draw the hairline between this section and the content. @default AppShell `withBorder` - `zIndex`: number — Stacking order; wins over the section config and the theme layer. - `accessibilityLabel`: string — Accessible name of the section's landmark. - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ### AppShellBottomNav - `children`: React.ReactNode — Content pinned to the bottom edge of the shell. - `withBorder`: boolean - `zIndex`: number - `accessibilityLabel`: string — Accessible name of the navigation landmark. - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ### AppShellMain - `children` (required): React.ReactNode - `id`: string — Element id (DOM `id` on web, `nativeID` on native). - `role`: Role = 'main' — Landmark role. @default 'main' - `maw`: number | string — Maximum width of the content column inside the main area (the area itself still fills the space between the chrome), to prevent stretching on wide screens. - `centerContent`: boolean — Center content when `maw` is set - `tableOfContents`: React.ReactNode — Table of contents content to show at the end side of the content - `hideTocOnMobile`: boolean — Hide table of contents on mobile - `tocWidth`: number | string — Width of the table of contents sidebar - `tocWithBorder`: boolean — Add border to table of contents - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ### AppShellSection - `children` (required): React.ReactNode - `grow`: boolean — Take the remaining space of the navbar/aside. - `withScrollArea`: boolean — Scroll the section's content when it overflows. - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ### BottomAppBar - `items`: BottomAppBarItem[] — Structured items definition for standard navigation bar - `activeKey`: string — Currently active item key - `onItemPress`: (key: string) => void — Callback when an item is pressed (fires after per-item onPress) - `showLabels`: boolean — Show labels under icons (default true). Items are still named for screen readers when hidden. - `variant`: 'solid' | 'surface' | 'elevated' | 'translucent' — Visual variant - `elevation`: number = 4 — Shadow strength for the `elevated` variant (Android-style elevation, 0–24). @default 4 - `fab`: React.ReactNode — Optional floating action button rendered centered & elevated - `children`: React.ReactNode — Content pinned to the bottom edge of the shell. - `withBorder`: boolean - `zIndex`: number - `accessibilityLabel`: string — Accessible name of the navigation landmark. - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ### StatusBarManager - `children`: React.ReactNode - `style`: 'auto' | 'light' | 'dark' - `backgroundColor`: string - `translucent`: boolean - `hidden`: boolean ### AppLayoutProvider - `blueprint` (required): AppLayoutBlueprint - `value`: AppLayoutRuntimeOverrides - `children` (required): React.ReactNode ### AppLayoutRenderer - `children` (required): React.ReactNode ### MobileMenu - `opened`: boolean — Whether the menu is open. - `onClose` (required): () => void — Called when the menu asks to close (backdrop press, Escape, Android back). - `children`: React.ReactNode - `config`: MobileMenuConfig - `accessibilityLabel`: string = 'Menu' — Accessible name of the menu dialog. @default 'Menu' - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ## Related hooks - `useAppShell(): AppShellContextValue` — Returns the full state of the enclosing `AppShell` — resolved section sizes, navbar open/collapsed/rail flags, the current `breakpoint` and `isMobile`, and the `openNavbar` / `closeNavbar` / `toggleNavbar` controls — and throws when called outside an `AppShell`. - `useAppShellApi(): AppShellApi` — Returns just the enclosing `AppShell`'s navbar controls (`openNavbar`, `closeNavbar`, `toggleNavbar`) — for menu buttons and links that drive the navbar without re-rendering on layout changes — and throws when called outside an `AppShell`. - `useAppShellLayout(): AppShellLayoutValue` — Returns just the enclosing `AppShell`'s resolved section sizes (`headerHeight`, `navbarWidth`, `asideWidth`, `footerHeight`, `bottomNavHeight`) — for content that has to position itself around the shell chrome — and throws when called outside an `AppShell`. - `useBreakpoint(): Breakpoint` — Returns the current breakpoint name, from the theme's breakpoint table (`theme.breakpoints`, or a `BreakpointProvider` override): `base` below `xs` (480), then `xs`, `sm` (576), `md` (768), `lg` (992), `xl` (1200). - `useNavbarHover(): boolean` — Returns `true` while the pointer is over the `AppShell` navbar rail (web, with `navbar.expandOnHover` on) and `false` otherwise, including outside an `AppShell` — for navbar content that should show its labels only while the collapsed rail is hover-expanded. - `useAppLayoutContext(): AppLayoutProviderValue` — Returns the enclosing `AppLayoutProvider`'s value — the `defineAppLayout` `blueprint` plus the resolved `runtime` (`query`, `pathname`, `navigation`, `platform`, `meta`) — for custom renderers that read the layout the way `AppLayoutRenderer` does, and throws when called outside an `AppLayoutProvider`. ## Types ```ts export interface HeaderConfig { height: ResponsiveSize; /** Hide the header: `AppShell.Header` renders nothing and reserves no height. @default false */ collapsed?: boolean; /** * Whether the main content starts below the header. `false` lets content run * underneath it (e.g. a translucent header over a hero image). @default true */ offset?: boolean; /** Stacking order of `AppShell.Header`. @default theme.zIndices.header */ zIndex?: number; } export interface NavbarConfig { width: ResponsiveSize; /** * Narrowest viewport at which the navbar is an inline rail (web). Below it the * navbar is an overlay drawer. Native apps always use the drawer. @default 'md' */ breakpoint?: Breakpoint; /** * Initial state. `mobile: false` starts the drawer open; `desktop: true` * starts the desktop navbar collapsed to its rail (`startCollapsedDesktop` * takes precedence). The state resets when the viewport crosses `breakpoint`. */ collapsed?: { mobile?: boolean; desktop?: boolean; }; /** * Stacking order of the inline rail. @default theme.zIndices.sticky * (The drawer uses `theme.zIndices.overlay`, above the header; set * `AppShell.Navbar zIndex` to override both.) */ zIndex?: number; collapsedWidth?: number; expandOnHover?: boolean; /** * When paired with `expandOnHover`, hovering the collapsed rail pushes the * main content aside (flexing the page) instead of overlaying it. * Defaults to `false` (overlay) to preserve existing behavior. */ expandOnHoverPush?: boolean; /** * Auto-expand the navbar (start open, not a collapsed rail) once the viewport * reaches this breakpoint or wider, e.g. `'xl'`. Overrides * `startCollapsedDesktop` at/above the breakpoint; smaller desktops keep the * collapsed-with-hover behavior. */ autoExpandBreakpoint?: Breakpoint; startCollapsedDesktop?: boolean; } export interface AsideConfig { width: ResponsiveSize; /** Narrowest viewport at which `collapsed.desktop` applies; below it `collapsed.mobile` does. @default 'md' */ breakpoint?: Breakpoint; collapsed?: { /** @default true */ mobile?: boolean; /** @default false */ desktop?: boolean; }; /** Stacking order of `AppShell.Aside`. @default theme.zIndices.sticky */ zIndex?: number; } export interface FooterConfig { height: ResponsiveSize; /** Hide the footer: `AppShell.Footer` renders nothing and reserves no height. @default false */ collapsed?: boolean; /** Whether the main content ends above the footer. `false` lets content run underneath it. @default true */ offset?: boolean; /** Stacking order of `AppShell.Footer`. @default theme.zIndices.sticky */ zIndex?: number; } export interface BottomNavConfig { height: ResponsiveSize; /** Only show the bottom navigation at mobile widths (and on native). @default true */ showOnlyMobile?: boolean; /** Hide the bottom navigation. @default false */ collapsed?: boolean; /** Stacking order of `AppShell.BottomNav`. @default theme.zIndices.sticky */ zIndex?: number; } export interface LayoutVisibilityConfig { header?: boolean; navbar?: boolean; aside?: boolean; footer?: boolean; bottomNav?: boolean; } export interface BottomAppBarItem { key: string; label: string; icon: React.ReactNode; activeIcon?: React.ReactNode; badgeCount?: number; onPress?: () => void; // per-item override } export interface BottomAppBarProps extends AppShellBottomNavProps { /** Structured items definition for standard navigation bar */ items?: BottomAppBarItem[]; /** Currently active item key */ activeKey?: string; /** Callback when an item is pressed (fires after per-item onPress) */ onItemPress?: (key: string) => void; /** Show labels under icons (default true). Items are still named for screen readers when hidden. */ showLabels?: boolean; /** Visual variant */ variant?: 'solid' | 'surface' | 'elevated' | 'translucent'; /** Shadow strength for the `elevated` variant (Android-style elevation, 0–24). @default 4 */ elevation?: number; /** Optional floating action button rendered centered & elevated */ fab?: React.ReactNode; } ``` ## Examples ### Basics Arrange a header and page content inside a responsive app frame. ```tsx import { AppShell, Block, Text } from '@plocks/ui'; export function Demo() { return ( App header} autoLayout withSafeArea={false} > Page content ); } ``` ### Enhanced ```tsx import { AppShell, Block, Text } from '@plocks/ui'; const sampleTOC = ( Contents Introduction Getting Started Installation NPM Package Yarn Setup Configuration Components AppShell Layout System Examples ); export function Demo() { 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 ); } ``` -------------------------------------------------------------------------------- # AudioPlayer AudioPlayer combines audio playback controls with a seekable waveform. ## Metadata - Import: `import { AudioPlayer } from '@plocks/media';` - Install: `npm install @plocks/media` — a separate package from `@plocks/ui` - Status: beta - Tags: audio, player, waveform, media, playback - Docs: https://plocks.dev/components/AudioPlayer - Source: https://github.com/platform-blocks/plocks/tree/main/packages/media/src/components/AudioPlayer ## Props - `source`: string | number | { uri: string } — Audio source - can be URL, local file, or asset - `peaks`: number[] — Pre-computed waveform peaks (optional - placeholder peaks are generated if not provided) - `w`: number = 300 — Waveform width in px — not the player's, which fills its parent. @default 300 - `h`: number = 60 — Waveform height in px. @default 60 - `autoPlay`: boolean — Whether to auto-play when loaded - `loop`: boolean — Whether to loop the audio - `volume`: number — Initial volume (0-1) - `rate`: number — Playback rate (0.5-2.0) - `showControls`: boolean — Whether to show player controls - `controls`: AudioPlayerControls — Which controls to display; merged over the defaults. - `controlsPosition`: 'top' | 'bottom' | 'overlay' | 'none' — Where the controls sit: above or below the waveform, laid over it, or hidden. Metadata renders above the waveform for `overlay` and `none`. - `onLoad`: (data: AudioLoadData) => void — Called when audio is loaded and ready - `onPlaybackStateChange`: (state: PlaybackState) => void — Called when playback state changes - `onProgress`: (data: ProgressData) => void — Called during playback with current time - `onEnd`: () => void — Called when playback finishes - `onError`: (error: AudioError) => void — Called on playback error - `generateWaveform`: boolean — Draw placeholder peaks when `peaks` is omitted. expo-audio can't analyze a file up front, so pass measured `peaks` when the waveform's shape matters. - `waveformOptions`: { samples?: number; } — Placeholder waveform options - `showTime`: boolean — Show time labels - `timeFormat`: 'mm:ss' | 'hh:mm:ss' | 'relative' — Time format: `mm:ss`, `hh:mm:ss`, or `relative` (elapsed / `-remaining`). - `showMetadata`: boolean — Show audio metadata - `metadata`: AudioMetadata — Audio metadata - `enableKeyboardShortcuts`: boolean = true — Keyboard shortcuts while the waveform (seek slider) has focus (web): Space play/pause, J / L skip back / forward, M mute. Arrow keys, Page Up/Down and Home/End seek. @default true - `keyboardShortcuts`: KeyboardShortcuts — Override the shortcut keys (`KeyboardEvent.key` values, case-insensitive). - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Plus the `Waveform` props (`color` `size` `barWidth` `barGap` `strokeWidth` `gradientColors` `progressColor` `interactive` `onSeek` `onDragStart` `onDrag` `onDragEnd` `accessibilityLabel` `accessibilityHint` `minBarHeight` `normalize` `fullWidth` `maxVisibleBars` `showProgressLine` `progressLineStyle` `showTimeStamps` `timeStampInterval` `loading` `error` `loadingProgress` `selection` `onSelectionChange` `showRMS` `rmsData` `markers` `enablePerformanceMonitoring` `onPerformanceMetrics`): https://plocks.dev/llms/components/Waveform.md Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ## Types ```ts export interface AudioPlayerControls { /** Play / pause button. @default true */ playPause?: boolean; /** Skip back / forward 10 seconds buttons. @default true */ skip?: boolean; /** Mute / unmute button. @default true */ volume?: boolean; /** Playback speed button (cycles 0.5x–2x). @default false */ speed?: boolean; /** The seekable waveform. @default true */ waveform?: boolean; } export interface AudioLoadData { duration: number; sampleRate: number; channels: number; bitrate?: number; format?: string; peaks?: number[]; } export interface PlaybackState { isPlaying: boolean; isLoading: boolean; isBuffering: boolean; currentTime: number; duration: number; volume: number; rate: number; loop: boolean; } export interface ProgressData { currentTime: number; duration: number; progress: number; // 0-1 position: number; // 0-1 for waveform /** Always 0: expo-audio doesn't report buffered ranges. */ buffered: number; } export interface AudioError { code: string; message: string; details?: unknown; } export interface AudioMetadata { title?: string; artist?: string; album?: string; artwork?: string; duration?: number; genre?: string; year?: number; } export interface KeyboardShortcuts { /** @default ' ' (Space) */ playPause?: string; /** @default 'l' */ skipForward?: string; /** @default 'j' */ skipBackward?: string; volumeUp?: string; volumeDown?: string; /** @default 'm' */ mute?: string; } ``` ## Examples ### Basics Point `source` at a URL or a bundled clip and pass its `peaks` to draw the real waveform; the player handles loading, play/pause, seeking and progress. Playback requires `expo-audio`; without it the controls render but report a missing-module error. ```tsx import { AudioPlayer } from '@plocks/media'; import { Block } from '@plocks/ui'; const MELODY_PEAKS = [ 0.765, 0.683, 0.598, 0.528, 0.464, 0.407, 0.36, 0.316, 0.277, 0.243, 0.949, 0.876, 0.776, 0.698, 0.592, 0.533, 0.477, 0.407, 0.365, 0.323, 0.528, 0.922, 0.851, 0.738, 0.643, 0.569, 0.489, 0.438, 0.383, 0.34, 0.297, 0.954, 0.885, 0.798, 0.693, 0.606, 0.525, 0.468, 0.41, 0.354, 0.308, 0.925, 0.919, 0.828, 0.727, 0.64, 0.557, 0.474, 0.415, 0.376, 0.338, 0.297, 0.997, 0.869, 0.775, 0.667, 0.595, 0.51, 0.444, 0.408, 0.356, 0.316, 1, 0.872, 0.786, 0.691, 0.619, 0.555, 0.483, 0.4, 0.359, 0.322, 0.288, 0.941, 0.835, 0.748, 0.661, 0.588, 0.506, 0.439, 0.39, 0.342, 0.306, 0.272, 0.238, 0.203, 0.148, 0.13, 0.114, 0.101, 0.088, 0.078, 0.068, 0.06, 0.053, 0.047, ]; export function Demo() { return ( ); } ``` -------------------------------------------------------------------------------- # AutoComplete Provide search functionality with suggestions, supporting single/multi-select, async data loading, and rich content display. ## Metadata - Import: `import { AutoComplete } from '@plocks/ui';` - Tags: input, search, typeahead, autocomplete, suggestions - Docs: https://plocks.dev/components/AutoComplete - Source: https://github.com/platform-blocks/plocks/tree/main/packages/ui/src/components/AutoComplete ## Props - `testID`: string — Test identifier, forwarded to the underlying text input (the field's focusable element); the root gets `${testID}-root`. - `data`: AutoCompleteOption[] = EMPTY_OPTIONS — Data source for suggestions - `onSearch`: (query: string) => Promise — Async data fetcher - `minSearchLength`: number = 2 — Minimum characters to trigger search - `searchDelay`: number = 300 — Debounce delay for `onSearch`, in ms (local `data` filters immediately). - `renderItem`: ( item: AutoCompleteOption, index: number, options: { query: string; onSelect: (item: AutoCompleteOption) => void; isHighlighted?: boolean; isSelected?: boolean; } ) => React.ReactNode — Custom item renderer (the row stays an accessible option). - `onSelect`: (item: AutoCompleteOption) => void — Selection handler - `renderValue`: ( item: AutoCompleteOption, context: { focused: boolean; clear: () => void; } ) => React.ReactNode — Custom renderer for the selected option shown inside the single-select input. When provided and an option is selected, the returned node is overlaid on the text field while it is not focused (focusing the field reveals the editable text so the query can be changed). Ignored in multiSelect mode — use `renderSelectedValue` for chips there. - `maxSuggestions`: number = 10 — Maximum number of suggestions to display (0 = no limit). - `showSuggestionsOnFocus`: boolean = true — Whether to show suggestions on focus (default: true) - `renderEmptyState`: () => React.ReactNode — Custom empty state component - `renderLoadingState`: () => React.ReactNode — Custom loading state component - `filter`: (item: AutoCompleteOption, query: string) => boolean = defaultFilter — Filter function for local data - `highlightMatches`: boolean = true — Whether to highlight matching text - `highlightColor`: string — Text color for the matched substring when `highlightMatches` is on. Defaults to the theme's link (accent text) color. - `highlightBackgroundColor`: string = 'transparent' — Background color painted behind the matched substring (default: transparent — the match is distinguished by color and weight). - `suggestionsStyle`: StyleProp — Styles for the suggestions surface. - `suggestionItemStyle`: StyleProp — Styles for each suggestion row. - `groupLabelProps`: Omit — Override props for each group header `` (style, weight, size, color, uppercase…). Headers use the theme's `sectionLabel` text role by default. - `multiSelect`: boolean = false — Enable multi-select mode - `selectedValues`: AutoCompleteOption[] = EMPTY_OPTIONS — Selected values for multi-select mode - `renderSelectedValue`: ( item: AutoCompleteOption, index: number, context: { onRemove: () => void; disabled: boolean; isFocused: boolean; inputValue: string; source: 'input' | 'modal'; } ) => React.ReactNode — Custom renderer for each selected value chip in multi-select mode - `selectedValuesContainerStyle`: StyleProp — Optional style override for the selected values container - `selectedValueChipProps`: Partial — Additional props applied to the default Chip renderer for selected values - `refocusAfterSelect`: boolean — Controls whether the input regains focus after selecting an option - `freeSolo`: boolean = false — Whether to allow free-form input (Enter commits the typed text as an option) - `displayProperty`: 'label' | 'value' = 'label' — Which field of the selected option is written into the input: its human-readable `label`, or its `value`. - `useModal`: boolean — Present suggestions in a modal sheet (true) or an anchored dropdown (false). Default: sheet on native and small screens, dropdown on desktop web. - `textInputProps`: Omit — Additional TextInput props - `placement`: 'top' | 'bottom' | 'left' | 'right' | 'auto' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | 'right-start' | 'right-end' = 'bottom-start' — Placement preference for the suggestions dropdown (default: 'bottom-start') - `fallbackPlacements`: PlacementType[] = DEFAULT_FALLBACK_PLACEMENTS — Placements to try when the preferred one doesn't fit. - `offset`: number = 4 — Gap between the field and the dropdown, px (default: 4) - `flip`: boolean = true — Enable flipping to opposite side when dropdown would go off-screen (default: true) - `shift`: boolean = false — Enable shifting within bounds when dropdown would go off-screen (default: false) - `boundary`: number = 12 — Distance from viewport edges in pixels (default: 12) - `autoReposition`: boolean = true — Enable automatic repositioning on scroll/resize (default: true) - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — field (`label` `description` `error` `helperText` `required` `withAsterisk` `disabled` `readOnly` `size` `radius` `variant` `name` `accessibilityLabel` `accessibilityHint` `keyboardFocusId` `labelProps` `descriptionProps` `onFocus` `onBlur`), text field (`value` `defaultValue` `onChangeText` `placeholder` `placeholderTextColor` `clearable` `clearButtonLabel` `onClear` `onEnter` `startSection` `endSection` `startSectionProps` `endSectionProps`), base (`style`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), sizing (`fullWidth`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`), disclaimer (`disclaimer` `disclaimerProps`): https://plocks.dev/llms/guides/shared-props.md ## Types ```ts export interface AutoCompleteOption { label: string; value: string; group?: string; disabled?: boolean; /** Additional data for the option (free-form; read it back in `renderItem` / `onSelect`). */ data?: unknown; } ``` ## Examples ### Basics Simple auto-complete. Start typing to filter the list. Selecting an option fills the input. ```tsx import { AutoComplete, Block } from '@plocks/ui'; const sports = [ { 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' }, ]; export function Demo() { return ( ); } ``` ### Multi-select tags Tap an item to add or remove it. Selected genres render as removable chips. ```tsx import { useState } from 'react'; import { AutoComplete, Block } from '@plocks/ui'; import type { AutoCompleteOption } from '@plocks/ui'; const genres = [ { 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' }, ]; export function Demo() { 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 ( ); } ``` ### Variants Compare the default, filled, outline, and unstyled field shells on AutoComplete. ```tsx import { Column, AutoComplete } from '@plocks/ui'; const options = ['Apple', 'Banana', 'Cherry'].map(value => ({ label: value, value: value.toLowerCase() })); const variants = ['default', 'filled', 'outline', 'unstyled'] as const; export function Demo() { return ( {variants.map(variant => ( ))} ); } ``` ### Select-Like Behavior Behaves like a Select: the field is non-editable (`editable={false}`), so it can't be typed into or filtered. Tapping opens the full option list (`filter={() => true}`) and the value is chosen from it. ```tsx import { AutoComplete, Block } from '@plocks/ui'; const countries = [ { label: 'United States', value: 'us' }, { label: 'Canada', value: 'ca' }, { label: 'United Kingdom', value: 'uk' }, { label: 'Germany', value: 'de' }, { label: 'France', value: 'fr' }, { label: 'Italy', value: 'it' }, { label: 'Spain', value: 'es' }, { label: 'Netherlands', value: 'nl' }, { label: 'Australia', value: 'au' }, { label: 'Japan', value: 'jp' }, { label: 'South Korea', value: 'kr' }, { label: 'Brazil', value: 'br' }, { label: 'Mexico', value: 'mx' }, { label: 'India', value: 'in' }, { label: 'China', value: 'cn' }, ]; export function Demo() { return ( true} highlightMatches={false} fullWidth /> ); } ``` ### Async auto-complete Performs a debounced search against a simulated API before returning matches. ```tsx import { AutoComplete, Block } from '@plocks/ui'; const languages = [ { label: 'JavaScript', value: 'javascript' }, { label: 'TypeScript', value: 'typescript' }, { label: 'Python', value: 'python' }, { label: 'Java', value: 'java' }, { label: 'C++', value: 'cpp' }, { label: 'C#', value: 'csharp' }, { label: 'Go', value: 'go' }, { label: 'Rust', value: 'rust' }, { label: 'Swift', value: 'swift' }, { label: 'Kotlin', value: 'kotlin' }, ]; const searchLanguages = async (query: string) => { await new Promise((resolve) => setTimeout(resolve, 400)); const normalized = query.toLowerCase(); return languages.filter((language) => language.label.toLowerCase().includes(normalized)); }; export function Demo() { return ( ); } ``` ### Free Solo Suggests fruits while still accepting custom values. ```tsx import { AutoComplete, Block } from '@plocks/ui'; const fruits = [ { label: 'Apple', value: 'apple' }, { label: 'Banana', value: 'banana' }, { label: 'Orange', value: 'orange' }, { label: 'Grape', value: 'grape' }, { label: 'Mango', value: 'mango' }, { label: 'Pineapple', value: 'pineapple' }, ]; export function Demo() { return ( ); } ``` ### Free Solo (multi-select) Suggests fruits but lets you add any custom value as a tag — press Enter to add what you typed. ```tsx import { useState } from 'react'; import { AutoComplete, Block } from '@plocks/ui'; import type { AutoCompleteOption } from '@plocks/ui'; const fruits = [ { label: 'Apple', value: 'apple' }, { label: 'Banana', value: 'banana' }, { label: 'Orange', value: 'orange' }, { label: 'Grape', value: 'grape' }, { label: 'Mango', value: 'mango' }, { label: 'Pineapple', value: 'pineapple' }, ]; export function Demo() { const [selected, setSelected] = useState([]); const handleToggle = (option: AutoCompleteOption) => { const isSelected = selected.some((item) => item.value === option.value); setSelected((current) => isSelected ? current.filter((item) => item.value !== option.value) : [...current, option], ); }; return ( ); } ``` ### Grouped suggestions Countries are organized by region to make large lists easier to scan. ```tsx import { AutoComplete, Block } from '@plocks/ui'; const countries = [ { label: 'United States', value: 'us', group: 'North America' }, { label: 'Canada', value: 'ca', group: 'North America' }, { label: 'Mexico', value: 'mx', group: 'North America' }, { label: 'United Kingdom', value: 'uk', group: 'Europe' }, { label: 'Germany', value: 'de', group: 'Europe' }, { label: 'France', value: 'fr', group: 'Europe' }, { label: 'Japan', value: 'jp', group: 'Asia' }, { label: 'India', value: 'in', group: 'Asia' }, { label: 'Australia', value: 'au', group: 'Oceania' }, { label: 'Brazil', value: 'br', group: 'South America' }, ]; export function Demo() { return ( ); } ``` ### Rich Content Pass `renderItem` to lay out each suggestion and `renderValue` to draw the chosen option inside the field; `refocusAfterSelect={false}` blurs the field on select so that value shows right away. ```tsx import { AutoComplete, Block, Column, Icon, MenuItemButton, Row, Text } from '@plocks/ui'; interface RichSportOption { label: string; value: string; emoji: string; color: string; price: number; duration: string; } const sports: RichSportOption[] = [ { label: 'Soccer', value: 'soccer', emoji: '⚽', color: '#22c55e', price: 75.5, duration: '90 min' }, { label: 'Basketball', value: 'basketball', emoji: '🏀', color: '#f97316', price: 120.0, duration: '48 min' }, { label: 'Football', value: 'football', emoji: '🏈', color: '#92400e', price: 180.0, duration: '60 min' }, { label: 'Volleyball', value: 'volleyball', emoji: '🏐', color: '#fbbf24', price: 60.0, duration: 'Best of 5' }, { label: 'Baseball', value: 'baseball', emoji: '⚾', color: '#ef4444', price: 85.0, duration: '9 innings' }, { label: 'Golf', value: 'golf', emoji: '⛳', color: '#15803d', price: 110.0, duration: '4 hrs' }, ]; const tint = (hex: string, alpha: string) => `${hex}${alpha}`; const renderTile = (sport: RichSportOption, size: number) => ( = 40 ? 'xl' : 'md'}>{sport.emoji} ); export function Demo() { return ( { const sport = item as RichSportOption; return ( helpers.onSelect(sport)} style={{ alignItems: 'stretch', gap: 0 }} > {renderTile(sport, 40)} {sport.label} {sport.duration} ${sport.price.toFixed(2)} avg ticket {helpers.isSelected ? ( ) : ( )} ); }} renderValue={(item) => { const sport = item as RichSportOption; return ( {renderTile(sport, 24)} {sport.label} {sport.duration} ${sport.price.toFixed(2)} ); }} minSearchLength={1} fullWidth /> ); } ``` ### Highlight colours `highlightMatches` bolds and tints the part of each suggestion that matches what you typed. Pass `highlightColor` (a CSS color or a palette shade such as `'highlight.8'`) to change that tint, and optionally `highlightBackgroundColor` to fill behind it. ```tsx import { AutoComplete, Block } from '@plocks/ui'; const fruits = [ { label: 'Apple', value: 'apple' }, { label: 'Banana', value: 'banana' }, { label: 'Cherry', value: 'cherry' }, { label: 'Date', value: 'date' }, { label: 'Elderberry', value: 'elderberry' }, { label: 'Fig', value: 'fig' }, { label: 'Grape', value: 'grape' }, { label: 'Honeydew', value: 'honeydew' }, ]; export function Demo() { return ( ); } ``` -------------------------------------------------------------------------------- # Avatar Displays user profile images, initials, or icons. ## Metadata - Import: `import { Avatar } from '@plocks/ui';` - Tags: avatar, profile, user, image, initials - Docs: https://plocks.dev/components/Avatar - Source: https://github.com/platform-blocks/plocks/tree/main/packages/ui/src/components/Avatar ## Props - `size`: ComponentSizeValue = 'md' — Size of the avatar: a token or the diameter in px - `src`: string | ImageSourcePropType — Image for the avatar: a remote URL string or a bundled asset (`require('./avatar.png')`) - `fallback`: React.ReactNode — Fallback shown when no image is provided: initials string or a custom React node (e.g. an icon). - `bg`: ColorProp = theme.text.muted — Fill of the avatar circle — not the root, which also holds the label. Resolves like every `bg`: a palette name is its subtle tint, `'primary.5'` a shade. - `textColor`: ColorProp = a readable color on the background — Text color for the fallback initials. @default a readable color on the background - `online`: boolean — Whether to show online status indicator - `indicatorColor`: ColorProp — Color override for the status indicator - `accessibilityLabel`: string — Accessible name of the avatar ("Jane Doe"). The avatar is an image with this name; without it, it is decorative. - `label`: React.ReactNode — Primary label displayed beside the avatar (string or custom React node) - `description`: React.ReactNode — Secondary description/subtext under the label - `gap`: number = 8 — Spacing between avatar and text block (px) - `showText`: boolean = true — Force horizontal layout off (set false to hide label/description wrapper) - `fallbackProps`: Omit — Override props applied to the fallback initials `` (style, fw, ff, size, c). - `labelProps`: Omit — Override props applied to the adjacent label `` (only when `label` is a string). - `descriptionProps`: Omit — Override props applied to the secondary description `` (only when `description` is a string). - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ## Sub-components `import { AvatarGroup } from '@plocks/ui';` ### AvatarGroup - `children` (required): React.ReactNode - `limit`: number — Show at most this many avatars, then a `+N` surplus avatar. - `spacing`: number = -8 — Overlap between avatars (negative px). @default -8 - `size`: ComponentSizeValue - `bordered`: boolean — Whether to add borders around avatars for separation - `surplusTooltip`: string — When `limit` hides avatars, wrap the `+N` surplus indicator in a Tooltip with this label. - `surplusLabel`: string = `${N} more` — Accessible name of the surplus avatar. @default `${N} more` - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ## Examples ### Basics Illustrates loading an avatar image with a reliable initials fallback for offline scenarios. ```tsx import { Avatar } from '@plocks/ui'; export function Demo() { return ( ); } ``` ### Sizes Choose a `size` token (`xs` through `3xl`) to scale the avatar. ```tsx import { Avatar, Row } from '@plocks/ui'; const SIZES = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const; export function Demo() { return ( {SIZES.map((size) => ( ))} ); } ``` ### Icon Render any `` inside an avatar by passing it to the `fallback` prop. ```tsx import { Avatar, Icon, Row } from '@plocks/ui'; export function Demo() { return ( } bg="#6366f1" /> } bg="#10b981" /> } bg="#f59e0b" /> } bg="#ef4444" /> ); } ``` ### Colors Preview semantic color tokens and custom hex backgrounds applied to avatar fallbacks. ```tsx import { Avatar, Row } from '@plocks/ui'; export function Demo() { return ( ); } ``` ### Groups Showcases how `AvatarGroup` overlaps avatars by default to conserve space. ```tsx import { Avatar, AvatarGroup } from '@plocks/ui'; const TEAM = [ { id: 1, initials: 'SJ', color: '#FF6B6B' }, { id: 2, initials: 'MC', color: '#4ECDC4' }, { id: 3, initials: 'ER', color: '#45B7D1' }, { id: 4, initials: 'DL', color: '#96CEB4' }, { id: 5, initials: 'KP', color: '#FFEAA7' }, { id: 6, initials: 'TW', color: '#DDA0DD' }, { id: 7, initials: 'AB', color: '#FFB6C1' } ]; export function Demo() { return ( {TEAM.map(({ id, initials, color }) => ( ))} ); } ``` ### Overflow Set `limit` to cap visible avatars and show the remaining count. Pass `surplusTooltip` to reveal who's hidden on hover. ```tsx import { Avatar, AvatarGroup } from '@plocks/ui'; const TEAM = [ { id: 1, name: 'Sarah Johnson', initials: 'SJ', color: '#FF6B6B' }, { id: 2, name: 'Marcus Chen', initials: 'MC', color: '#4ECDC4' }, { id: 3, name: 'Elena Ruiz', initials: 'ER', color: '#45B7D1' }, { id: 4, name: 'David Lee', initials: 'DL', color: '#96CEB4' }, { id: 5, name: 'Kira Patel', initials: 'KP', color: '#FFEAA7' }, { id: 6, name: 'Tom Ward', initials: 'TW', color: '#DDA0DD' }, { id: 7, name: 'Aisha Bello', initials: 'AB', color: '#FFB6C1' } ]; const LIMIT = 3; export function Demo() { const hidden = TEAM.slice(LIMIT).map((member) => member.name); return ( {TEAM.map(({ id, initials, color }) => ( ))} ); } ``` ### Status indicator Demonstrates the `online` presence indicator, including custom `indicatorColor` overrides for alternate states. ```tsx import { Avatar, Row } from '@plocks/ui'; export function Demo() { return ( ); } ``` -------------------------------------------------------------------------------- # BackgroundImage BackgroundImage places content over an image-filled container. ## Metadata - Import: `import { BackgroundImage } from '@plocks/ui';` - Status: beta - Docs: https://plocks.dev/components/BackgroundImage - Source: https://github.com/platform-blocks/plocks/tree/main/packages/ui/src/components/BackgroundImage ## Props - `w`: DimensionProp — Width - `h`: DimensionProp — Height - `miw`: DimensionProp — Minimum width - `maw`: DimensionProp — Maximum width - `mih`: DimensionProp — Minimum height - `mah`: DimensionProp — Maximum height - `bg`: ThemeColor — Background: a `theme.backgrounds` token (`'surface'`, `'subtle'`, `'elevated'`…), a palette name (its subtle tint), `'primary.5'` shade syntax, or any CSS color. - `opacity`: number — Opacity, `0`–`1` - `radius`: RadiusValue — Border radius: a `theme.radii` token, px number, `'none'` or `'full'`. - `borderWidth`: number — Border width - `borderColor`: string — Border color - `borderTopWidth`: number - `borderRightWidth`: number - `borderBottomWidth`: number - `borderLeftWidth`: number - `borderTopColor`: string - `borderRightColor`: string - `borderBottomColor`: string - `borderLeftColor`: string - `borderTopLeftRadius`: number - `borderTopRightRadius`: number - `borderStyle`: ViewStyle['borderStyle'] - `overflow`: ViewStyle['overflow'] - `aspectRatio`: number - `touchAction`: 'auto' | 'none' | 'pan-x' | 'pan-y' | 'manipulation' — Web touch gesture handling; useful for drag surfaces. - `translateY`: number — Small visual offset without affecting surrounding layout. - `rotate`: string — Rotation around the block center, such as `"45deg"`. - `shadow`: ShadowToken — Shadow: a `theme.shadows` token. - `fullWidth`: boolean — Whether to take full width (100%) - shorthand for w="full"; an explicit `w` wins - `fluid`: boolean — Makes block take full available height (flex: 1) - useful for scrollable containers - `grow`: boolean | number — Flex grow - `shrink`: boolean | number — Flex shrink - `basis`: number | string — Flex basis - `direction`: 'row' | 'column' | 'row-reverse' | 'column-reverse' — Flex direction - `align`: 'stretch' | 'flex-start' | 'flex-end' | 'center' | 'baseline' — Align items - `alignSelf`: ViewStyle['alignSelf'] — Alignment of this block within its parent. - `justify`: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly' — Justify content - `wrap`: boolean | 'nowrap' | 'wrap' | 'wrap-reverse' — Flex wrap - `gap`: number | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' — Gap between children (`theme.spacing` token or px). Defaults to `'sm'`; pass `0` to remove it. - `position`: 'relative' | 'absolute' — Position type - `top`: number | string — Top position - `right`: number | string — Right position (physical; use `end` to mirror in right-to-left layouts) - `bottom`: number | string — Bottom position - `left`: number | string — Left position (physical; use `start` to mirror in right-to-left layouts) - `inset`: number | string — Shorthand for all four physical insets. - `start`: number | string — Start inset (logical: left in LTR, right in RTL) - `end`: number | string — End inset (logical: right in LTR, left in RTL) - `zIndex`: number — Z-index - `flex`: boolean | number — Whether to render as a flex container Plus the `Block` props (`children` `className` `onPress` `onLongPress` `onPressIn` `onPressOut` `onMouseEnter` `onMouseLeave` `disabled`): https://plocks.dev/llms/components/Block.md Plus the `Image` props (`src` `source` `alt` `resizeMode`): https://plocks.dev/llms/components/Image.md Also accepts the shared props — base (`style` `testID`), spacing (`m` `mt` `mr` `mb` `ml` `mx` `my` `p` `pt` `pr` `pb` `pl` `px` `py`), visibility (`lightHidden` `darkHidden` `hiddenFrom` `visibleFrom`): https://plocks.dev/llms/guides/shared-props.md ## Examples ### Basics BackgroundImage places content over an image-filled container. ```tsx import { BackgroundImage, Text } from '@plocks/ui'; const image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAALklEQVR42mNITvsIR409P+CIAasokMuAVRQqgSkKksAqiiKB5goGrKJQCawuBgC2Wnfh+zNA9wAAAABJRU5ErkJggg=='; export function Demo() { return ( Content over an image ); } ``` -------------------------------------------------------------------------------- # Badge Badge displays a compact status or count on a parent element. ## Metadata - Import: `import { Badge } from '@plocks/ui';` - Tags: chip, tag, badge, label, removable - Docs: https://plocks.dev/components/Badge - Source: https://github.com/platform-blocks/plocks/tree/main/packages/ui/src/components/Badge ## Props - `children` (required): React.ReactNode - `size`: ComponentSizeValue — Size token (the badge renders well below a control of the same size), or height in px. - `variant`: 'filled' | 'outline' | 'light' | 'subtle' | 'gradient' = 'subtle' - `v`: 'filled' | 'outline' | 'light' | 'subtle' | 'gradient' — Shorthand alias for `variant`. `variant` wins when both are set. - `color`: ColorProp — Badge color. A palette token, `'primary.6'` shade syntax, or any CSS color. - `c`: ColorProp — Shorthand alias for `color`, resolved identically. `color` wins when both are set. - `onPress`: () => void — Makes the badge a button. - `startSection`: React.ReactNode — Content (usually an icon) before the label. - `endSection`: React.ReactNode — Content (usually an icon) after the label. - `onRemove`: () => void — Show a remove (×) button that calls this. - `removePosition`: 'left' | 'right' — Which side the remove button sits on (`left`/`right` follow the reading direction). - `removeButtonLabel`: string = `Remove