@ankhorage/zora
v1.0.10
Published
Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.
Downloads
7,759
Maintainers
Readme
ZORA
Opinionated React Native and React Native Web UI kit built on
@ankhorage/surface.
ZORA sits above Surface. Surface provides the foundation primitives, theme system, and low-level controls; ZORA adds product-facing components, app layouts, and ready-made patterns with stronger defaults.
Install
bun add @ankhorage/zoraPeer dependencies:
react >=18.2.0react-native >=0.72.0@expo/vector-icons >=14.0.0when using icon specsexpo-font >=14.0.4when using runtime font registration
Quick Start
Wrap your app in ZoraProvider, then import components from
@ankhorage/zora.
import React from 'react';
import {
AppShell,
Button,
Card,
Heading,
Page,
PageHeader,
Text,
Toolbar,
ToolbarAction,
ZoraProvider,
} from '@ankhorage/zora';
export function App() {
return (
<ZoraProvider>
<AppShell
header={
<Toolbar>
<ToolbarAction icon={{ name: 'menu-outline' }} label="Menu" />
</Toolbar>
}
>
<Page header={<PageHeader title="Dashboard" description="Ready to build." />}>
<Card
actions={<Button>Continue</Button>}
description="ZORA provides composed UI surfaces for apps."
title="Welcome"
>
<Heading level={3}>Next steps</Heading>
<Text tone="muted">Structured text comes from ZORA too.</Text>
</Card>
</Page>
</AppShell>
</ZoraProvider>
);
}Scoped themes
ZORA supports nested theme scopes. A component may set mode and, later,
themeId; everything inside inherits the nearest scope.
import React from 'react';
import { Button, Heading, Panel, Text, ZoraProvider, type ZoraTheme } from '@ankhorage/zora';
export function App({ appTheme }: { appTheme: ZoraTheme }) {
return (
<ZoraProvider theme={appTheme} initialMode="light">
<Panel mode="dark">
<Heading>Studio panel</Heading>
<Text>Text inherits dark mode.</Text>
<Button>Also scoped.</Button>
</Panel>
</ZoraProvider>
);
}ZORA themes use a single seed primaryColor. The selected primary color is preserved
identically for both light and dark mode. Color generation is handled by
@ankhorage/color-theory via Surface.
<ZoraProvider
theme={{
id: 'studio',
name: 'Studio',
appCategory: 'developer_tools',
primaryColor: '#0f766e',
harmony: 'analogous',
}}
>
<App />
</ZoraProvider>mode and themeId are available on public ZORA components through ZoraBaseProps.
Use component props for local component/subtree overrides.
Use ZoraThemeScope when the scope is conceptual and does not belong to one specific component:
import React from 'react';
import { SidebarLayout, Text, ZoraProvider, ZoraThemeScope, type ZoraTheme } from '@ankhorage/zora';
export function App({ appTheme }: { appTheme: ZoraTheme }) {
return (
<ZoraProvider theme={appTheme} initialMode="light">
<ZoraThemeScope mode="dark">
<SidebarLayout sidebar={<Text>Sidebar</Text>}>
<Text>Everything inside uses dark mode.</Text>
</SidebarLayout>
</ZoraThemeScope>
</ZoraProvider>
);
}themeId currently accepts the inherited theme id. Full theme registries arrive in a later phase.
Foundation primitives
ZORA re-exports selected Surface foundation primitives for app-facing layout code:
import { Box, Container, Grid, Heading, Stack, Text } from '@ankhorage/zora';Use ZORA Text and Heading for typography. Use Box, Stack, Grid, and
Container for layout. Surface remains the lower-level render foundation and
should not be required in normal app-facing UI code.
Navigation chrome (Expo Router)
ZORA provides product-facing navigation chrome that can be plugged into real Expo Router / React Navigation navigators. Expo Router owns navigation mechanics (state, linking, gestures); ZORA only renders the tab bar and drawer content.
routeMap is the primary source for icons, badges, disabled state, and explicit
labels. Navigator descriptor options are used as a label/title fallback only.
Tabs
import { Tabs } from 'expo-router';
import { ZoraTabBar, type ZoraNavigationRouteMap } from '@ankhorage/zora';
const routeMap: ZoraNavigationRouteMap = {
index: { label: 'Home', icon: { name: 'home-outline' } },
settings: { label: 'Settings', icon: { name: 'settings-outline' }, badge: '3' },
};
export default function Layout() {
return <Tabs tabBar={(props) => <ZoraTabBar {...props} routeMap={routeMap} />} />;
}Drawer
import { Drawer } from 'expo-router/drawer';
import { ZoraDrawerContent, type ZoraNavigationRouteMap } from '@ankhorage/zora';
const routeMap: ZoraNavigationRouteMap = {
home: { label: 'Home', icon: { name: 'home-outline' } },
account: { label: 'Account', icon: { name: 'person-outline' }, disabled: true },
};
export default function Layout() {
return <Drawer drawerContent={(props) => <ZoraDrawerContent {...props} routeMap={routeMap} />} />;
}Shared Types
These unions appear across the catalogue:
ZoraTonecomes from SurfaceButtonProps['tone'].ZoraEmphasiscomes from SurfaceButtonProps['variant'].ZoraBadgeEmphasiscomes from SurfaceBadgeProps['variant'].ZoraControlSizecomes from SurfaceButtonProps['size'].ZoraCardTone = 'default' | 'subtle' | 'outline'.ZoraContentWidth = 'narrow' | 'default' | 'wide'.
Width presets:
- Dialog widths:
narrow=420,default=520,wide=560. - Page widths:
narrow=760,default=1040,wide=1280.
Components
Heading
Structured titles with semantic levels, visual sizes, semantic tones, and
responsive props. Use Heading for titles and Text for body copy.
<Heading level={1} size={{ base: 'h2', md: 'h1' }}>
Build faster with ZORA
</Heading>
<Heading level={2} tone="primary">
Create consistent screens
</Heading>level expresses document hierarchy. size controls visual scale and can be
responsive for mobile and web layouts.
ZORA props:
| Prop | Type | Default | Notes |
| --------------- | ---------------------------------------- | ----------- | --------------------------------------------------------------- |
| children | React.ReactNode | - | Primary content. |
| text | string | - | Manifest-friendly content prop. |
| i18nKey | string | - | Runtime-resolved fallback key when no content prop is provided. |
| level | HeadingLevel | 2 | Semantic heading level from 1 through 6. |
| size | Responsive<HeadingSize> | level size | Visual scale: display, h1 through h6. |
| tone | Responsive<HeadingTone> | 'default' | Semantic text color. |
| align | Responsive<HeadingAlign> | - | Text alignment. |
| weight | Responsive<HeadingWeight> | recipe | Optional structured weight override. |
| italic | boolean | false | Italic style. |
| numberOfLines | number | - | Native/web truncation line count. |
| ellipsizeMode | 'head' \| 'middle' \| 'tail' \| 'clip' | - | Truncation behavior. |
| selectable | boolean | - | Allows text selection where supported. |
| testID | string | - | Test id. |
No inherited props. HeadingProps is declared directly by ZORA to keep heading
usage structured and template-safe.
Text
Structured body text with ZORA typography variants, semantic tones, and responsive props.
<Text variant="lead" tone="muted">
Build product screens with structured, theme-aware copy.
</Text>
<Text variant={{ base: 'bodySmall', md: 'body' }} align={{ base: 'center', md: 'left' }}>
Responsive text without raw styles.
</Text>ZORA props:
| Prop | Type | Default | Notes |
| --------------- | ---------------------------------------- | ----------- | --------------------------------------------------------------- |
| children | React.ReactNode | - | Primary content. |
| text | string | - | Manifest-friendly content prop. |
| i18nKey | string | - | Runtime-resolved fallback key when no content prop is provided. |
| variant | Responsive<TextVariant> | 'body' | Typography recipe. |
| tone | Responsive<TextTone> | 'default' | Semantic text color. |
| align | Responsive<TextAlign> | - | Text alignment. |
| weight | Responsive<TextWeight> | recipe | Optional structured weight override. |
| italic | boolean | false | Italic style. |
| numberOfLines | number | - | Native/web truncation line count. |
| ellipsizeMode | 'head' \| 'middle' \| 'tail' \| 'clip' | - | Truncation behavior. |
| selectable | boolean | - | Allows text selection where supported. |
| testID | string | - | Test id. |
No inherited props. TextProps is declared directly by ZORA to keep text
structured and template-safe.
Button
Action button with ZORA defaults for tone, emphasis, size, and icons.
<Button leadingIcon={{ name: 'checkmark-circle-outline' }}>Save</Button>ZORA props:
| Prop | Type | Default | Notes |
| -------------- | ----------------- | ----------- | ------------------------------------------ |
| children | React.ReactNode | - | Button label or content. |
| tone | ZoraTone | 'primary' | Passed to Surface as tone. |
| emphasis | ZoraEmphasis | 'solid' | Passed to Surface as variant. |
| size | ZoraControlSize | 'l' | Passed to Surface as size. |
| leadingIcon | ButtonIconSpec | - | Surface icon spec rendered before content. |
| trailingIcon | ButtonIconSpec | - | Surface icon spec rendered after content. |
Inherited props:
Inherits all Surface ButtonProps except children, size, tone, and
variant. This includes Surface button behavior such as loading,
fullWidth, pressability props, disabled state, accessibility props allowed by
Surface, and testID.
IconButton
Compact icon-only button for toolbars, rows, and actions.
<IconButton icon={{ name: 'trash-outline' }} label="Delete" tone="danger" />ZORA props:
| Prop | Type | Default | Notes |
| ---------- | ----------------- | ----------- | ---------------------------------- |
| icon | ButtonIconSpec | - | Required icon to render. |
| label | string | - | Required for accessibilityLabel. |
| tone | ZoraTone | 'neutral' | Button tone. |
| emphasis | ZoraEmphasis | 'ghost' | Button emphasis. |
| size | ZoraControlSize | 'm' | Button size. |
Inherited props:
Inherits behavior from Surface IconButton including disabled, loading,
onPress, and testID.
Avatar
User/profile image with name-based initials and optional icon fallback.
<Avatar name="Zora Kit" />
<Avatar size="l" tone="primary" name="Fabio Gartenmann" />ZORA props:
| Prop | Type | Default | Notes |
| -------------- | --------------------- | ----------- | ----------------------------------------------- |
| source | ImageSourcePropType | - | React Native Image source for the avatar. |
| name | string | - | Used to derive initials when initials absent. |
| initials | string | - | Explicit initials override. |
| iconFallback | ButtonIconSpec | - | Optional icon spec when no source/initials. |
| label | string | - | Accessibility label for the rendered content. |
| size | AvatarSize | 'm' | xs..xl size preset. |
| shape | AvatarShape | 'circle' | circle or rounded. |
| tone | ZoraTone | 'neutral' | Drives background and fallback content tone. |
| testID | string | - | Test id. |
Inherited props:
No inherited props. AvatarProps is declared directly by ZORA.
AvatarGroup
Overlapping avatar stack with optional overflow label.
<AvatarGroup
items={[
{ id: '1', name: 'Ada Lovelace' },
{ id: '2', name: 'Grace Hopper', tone: 'success' },
]}
/>ZORA props:
| Prop | Type | Default | Notes |
| --------------- | --------------------------- | ---------- | ------------------------------------ |
| items | AvatarGroupItem[] | - | Avatar sources and fallback fields. |
| max | number | 4 | Max visible avatars before overflow. |
| size | AvatarSize | 's' | Avatar size preset. |
| shape | AvatarShape | 'circle' | Avatar shape preset. |
| overflowLabel | (overflowCount) => string | +N | Overflow label formatter. |
| testID | string | - | Test id. |
AvatarGroupItem:
| Prop | Type | Notes |
| -------------- | --------------------- | ------------------------------- |
| id | string | Optional stable key. |
| source | ImageSourcePropType | Image source for the avatar. |
| name | string | Used to derive initials. |
| initials | string | Explicit initials override. |
| iconFallback | ButtonIconSpec | Optional icon fallback. |
| label | string | Accessibility label. |
| tone | ZoraTone | Overrides avatar tone per item. |
Inherited props:
No inherited props. AvatarGroupProps is declared directly by ZORA.
Badge
Small status label with ZORA tone, emphasis, and size defaults.
<Badge tone="success">Active</Badge>ZORA props:
| Prop | Type | Default | Notes |
| ---------- | ------------------- | ----------- | ------------------------------------ |
| children | React.ReactNode | - | Rendered as Surface badge content. |
| tone | ZoraTone | 'primary' | Passed to Surface as tone. |
| emphasis | ZoraBadgeEmphasis | 'soft' | Passed to Surface as variant. |
| size | ZoraControlSize | 'm' | Passed to Surface as size. |
Inherited props:
Inherits all Surface BadgeProps except content, size, tone, and
variant. The remaining inherited prop is testID.
Card
Composed content surface with optional header, actions, footer, and ZORA card tones.
<Card actions={<Button>Open</Button>} description="A reusable product surface." title="Project" />ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ----------------- | ----------- | ---------------------------------------------------------------------------------------- |
| children | React.ReactNode | - | Main card body. |
| title | React.ReactNode | - | Header title. |
| description | React.ReactNode | - | Header description. |
| eyebrow | React.ReactNode | - | Small muted text above the title. |
| actions | React.ReactNode | - | Header action area. |
| footer | React.ReactNode | - | Footer area below body content. |
| tone | ZoraCardTone | 'default' | Maps to Surface variants: default -> raised, subtle -> subtle, outline -> outline. |
| compact | boolean | false | Uses tighter padding and heading scale. |
Inherited props:
Inherits all Surface CardProps except children, p, radius, variant, and
style. ZORA owns spacing, radius, and variant selection for this wrapper.
MediaCard
Media-first card surface for listings, content previews, and catalog items.
<MediaCard
imageSource={require('./cover.png')}
imageLabel="Cover image"
title="MediaCard"
description="Composes an image slot, header content, badges, actions, and footer metadata."
badges={<Badge tone="primary">Featured</Badge>}
footer={<Rating value={4.5} />}
onPress={() => undefined}
/>ZORA props:
| Prop | Type | Default | Notes |
| ------------------ | --------------------- | ----------- | ---------------------------------------------------------- |
| imageSource | ImageSourcePropType | - | Image source (mutually exclusive with image). |
| imageLabel | string | - | Accessibility label for imageSource. |
| image | React.ReactNode | - | Custom image slot (mutually exclusive with imageSource). |
| imageAspectRatio | number | 16 / 9 | Aspect ratio used when rendering imageSource. |
| title | React.ReactNode | - | Required title. |
| description | React.ReactNode | - | Optional description under the title. |
| eyebrow | React.ReactNode | - | Optional caption above the title. |
| badges | React.ReactNode | - | Optional badge/tags region near the title. |
| actions | React.ReactNode | - | Optional trailing action area; disables onPress. |
| footer | React.ReactNode | - | Optional footer content under the body. |
| children | React.ReactNode | - | Optional body content. |
| tone | ZoraCardTone | 'default' | Passed to the underlying Card. |
| compact | boolean | false | Uses tighter spacing. |
| onPress | () => void | - | Makes the card pressable when no actions are present. |
| testID | string | - | Forwarded to the underlying Card. |
Inherited props:
No inherited props. MediaCardProps is declared directly by ZORA.
MetricCard
Compact metric surface for dashboards, stats, and summary cards.
<MetricCard
label="Monthly active users"
value="14.2k"
delta="+4.1%"
deltaTone="success"
description="Last 30 days"
/>ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ----------------- | ----------- | ------------------------------------------------------- |
| label | React.ReactNode | - | Required label above the value. |
| value | React.ReactNode | - | Required metric value. |
| description | React.ReactNode | - | Optional supporting copy. |
| icon | ButtonIconSpec | - | Optional icon shown next to the label. |
| delta | React.ReactNode | - | Optional delta badge content. |
| deltaTone | ZoraTone | 'neutral' | Tone used for the delta Badge. |
| actions | React.ReactNode | - | Optional trailing action area; disables onPress. |
| tone | ZoraCardTone | 'default' | Passed to the underlying Card. |
| compact | boolean | false | Uses tighter spacing. |
| onPress | () => void | - | Makes the card pressable when no actions are present. |
| testID | string | - | Forwarded to the underlying Card. |
Inherited props:
No inherited props. MetricCardProps is declared directly by ZORA.
Progress
Linear progress bar with semantic tone.
<Progress value={72} />
<Progress tone="success" value={38} />ZORA props:
| Prop | Type | Default | Notes |
| -------- | ----------------- | ----------- | ----------------------------- |
| value | number | - | Current progress value. |
| max | number | 100 | Maximum value for completion. |
| tone | ZoraTone | 'primary' | Fill tone. |
| size | ZoraControlSize | 'm' | Controls bar height. |
| testID | string | - | Test id. |
Inherited props:
No inherited props. ProgressProps is declared directly by ZORA.
Rating
Readonly rating display rendered as star icons.
<Rating value={4.5} />ZORA props:
| Prop | Type | Default | Notes |
| -------- | ----------------- | ----------- | ---------------------- |
| value | number | - | Current rating value. |
| max | number | 5 | Maximum rating value. |
| tone | ZoraTone | 'warning' | Tone for filled icons. |
| size | ZoraControlSize | 'm' | Icon sizing preset. |
| testID | string | - | Test id. |
Inherited props:
No inherited props. RatingProps is declared directly by ZORA.
Input
Text input wrapper with ZORA sizing and optional Surface icon specs.
<Input
autoCapitalize="none"
keyboardType="email-address"
leadingIcon={{ name: 'mail-outline' }}
placeholder="[email protected]"
/>ZORA props:
| Prop | Type | Default | Notes |
| ---------------- | --------------------- | ------- | ------------------------------------------------------------------------------ |
| size | ZoraControlSize | 'l' | Passed to Surface as size. |
| leadingIcon | ButtonIconSpec | - | Rendered as Surface leadingAccessory. |
| trailingIcon | ButtonIconSpec | - | Rendered as Surface trailingAccessory. |
| trailingAction | InputTrailingAction | - | Renders an icon-only trailing action (mutually exclusive with trailingIcon). |
Inherited props:
Inherits all Surface TextInputProps except leadingAccessory, size, and
trailingAccessory. Surface TextInputProps also inherit React Native
TextInputProps except defaultValue, editable, onChangeText,
placeholderTextColor, style, testID, and value; Surface re-exposes
value, defaultValue, onChangeText, placeholder, disabled, readOnly,
invalid, style, and testID.
SearchBar
Controlled search input with leading search icon and optional clear action.
<SearchBar value={query} onValueChange={setQuery} onSubmit={(value) => console.log(value)} />ZORA props:
| Prop | Type | Default | Notes |
| --------------- | ------------------------- | ---------- | --------------------------------------------- |
| value | string | - | Current search query. |
| onValueChange | (value: string) => void | - | Called when the query changes. |
| placeholder | string | 'Search' | Placeholder text. |
| onSubmit | (value: string) => void | - | Called on submit (returnKeyType="search"). |
| onClear | () => void | - | Called after clearing the query. |
| clearable | boolean | true | Shows clear action when value is non-empty. |
| size | ZoraControlSize | 'l' | Passed to the underlying Input. |
| disabled | boolean | - | Disables the underlying Input. |
| readOnly | boolean | - | Makes the underlying Input read-only. |
| testID | string | - | Test id. |
Inherited props:
No inherited props. SearchBarProps is declared directly by ZORA.
RadioGroup
Single-selection control built on top of Surface Radio, designed for use inside FormField.
<FormField label="Navigator type">
<RadioGroup
value="tabs"
onValueChange={(value) => console.log(value)}
options={[
{ value: 'tabs', label: 'Tabs' },
{ value: 'drawer', label: 'Drawer' },
]}
/>
</FormField>ZORA props:
| Prop | Type | Default | Notes |
| --------------- | ---------------------------- | ------------ | ------------------------------ |
| value | string | - | Currently selected value. |
| onValueChange | (value: string) => void | - | Called when selection changes. |
| options | RadioGroupOption[] | - | List of selectable options. |
| orientation | 'horizontal' \| 'vertical' | 'vertical' | Layout direction. |
| gap | 'xs' \| 's' \| 'm' \| 'l' | 's' | Spacing between items. |
Option shape:
type RadioGroupOption = {
value: string;
label: React.ReactNode;
description?: React.ReactNode;
disabled?: boolean;
};Inherited props:
Passes tone, size, invalid, readOnly, disabled, and testID
to underlying Surface Radio components.
CheckboxGroup
Multi-selection control built on top of Surface Checkbox, for selecting multiple values.
<FormField label="Features">
<CheckboxGroup
value={['a']}
onValueChange={(value) => console.log(value)}
options={[
{ value: 'a', label: 'Feature A' },
{ value: 'b', label: 'Feature B' },
]}
/>
</FormField>ZORA props:
| Prop | Type | Default | Notes |
| --------------- | ---------------------------- | ------------ | ------------------------------ |
| value | string[] | - | Array of selected values. |
| onValueChange | (value: string[]) => void | - | Called when selection changes. |
| options | CheckboxGroupOption[] | - | List of selectable options. |
| orientation | 'horizontal' \| 'vertical' | 'vertical' | Layout direction. |
| gap | 'xs' \| 's' \| 'm' \| 'l' | 's' | Spacing between items. |
Option shape:
type CheckboxGroupOption = {
value: string;
label: React.ReactNode;
description?: React.ReactNode;
disabled?: boolean;
};Inherited props:
Passes tone, size, invalid, readOnly, disabled, and testID
to underlying Surface Checkbox components.
Chip
Compact filter/action token with optional icon and selected state.
<Chip selected tone="primary" onPress={() => undefined}>
Selected
</Chip>ZORA props:
| Prop | Type | Default | Notes |
| ---------- | ----------------- | ----------- | ------------------------------------ |
| children | React.ReactNode | - | Chip label content. |
| icon | ButtonIconSpec | - | Optional leading icon spec. |
| selected | boolean | false | Selected styling state. |
| tone | ZoraTone | 'neutral' | Selected tone. |
| size | ZoraControlSize | 's' | Padding and icon sizing. |
| disabled | boolean | false | Disables interaction and mutes tone. |
| onPress | () => void | - | Optional press handler. |
| testID | string | - | Test id. |
Inherited props:
No inherited props. ChipProps is declared directly by ZORA.
ChipGroup
Controlled single- or multi-select chip set for filters and facets.
<ChipGroup
value="all"
onValueChange={setValue}
items={[
{ value: 'all', label: 'All' },
{ value: 'favorites', label: 'Favorites' },
]}
/>ZORA props:
| Prop | Type | Default | Notes |
| --------------- | -------------------- | ----------- | ------------------------------ |
| items | ChipGroupItem[] | - | Rendered chips. |
| value | string \| string[] | - | Selected value(s). |
| onValueChange | (value) => void | - | Selection change handler. |
| multiple | boolean | false | Enables multi-select mode. |
| tone | ZoraTone | 'neutral' | Tone for selected chips. |
| size | ZoraControlSize | 's' | Chip size. |
| wrap | boolean | true | Wrap chips on smaller screens. |
| disabled | boolean | - | Disables all chips. |
| testID | string | - | Test id. |
Inherited props:
No inherited props. ChipGroupProps is declared directly by ZORA.
Textarea
Multiline text input wrapper with ZORA sizing and optional Surface icon specs.
<Textarea leadingIcon={{ name: 'document-text-outline' }} rows={5} />ZORA props:
| Prop | Type | Default | Notes |
| -------------- | ----------------- | ------- | ---------------------------------------- |
| size | ZoraControlSize | 'l' | Passed to Surface as size. |
| leadingIcon | ButtonIconSpec | - | Rendered as Surface leadingAccessory. |
| trailingIcon | ButtonIconSpec | - | Rendered as Surface trailingAccessory. |
Inherited props:
Inherits all Surface TextareaProps except leadingAccessory, size, and
trailingAccessory. Surface TextareaProps extend Surface TextInputProps
except multiline, so React Native text input props are available through
Surface with the same Surface exclusions and re-exposed values listed for
Input.
Tabs
Generic controlled tabs for navigation and filtering.
<Tabs
items={[
{ value: 'all', label: 'All' },
{ value: 'active', label: 'Active' },
]}
onValueChange={setValue}
value={value}
/>ZORA props:
| Prop | Type | Default | Notes |
| --------------- | -------------------------------------- | ------------- | --------------------- |
| value | string | - | Active tab value. |
| items | TabItem[] | - | Array of tab objects. |
| onValueChange | (value: string) => void | - | Change handler. |
| variant | 'underline' \| 'pill' \| 'segmented' | 'underline' | Visual style. |
| size | ZoraControlSize | 'm' | Control size. |
Toolbar
Horizontal shell for actions and tools.
<Toolbar>
<ToolbarAction icon={{ name: 'add-outline' }} label="Add" />
<ToolbarAction icon={{ name: 'search-outline' }} label="Search" />
</Toolbar>Toolbar props:
| Prop | Type | Default | Notes |
| ---------- | ------------------------------- | ---------- | ----------------------------------------- |
| children | React.ReactNode | - | Toolbar content. |
| position | 'top' \| 'bottom' \| 'inline' | 'inline' | Layout position. |
| floating | boolean | false | Whether the toolbar floats with a shadow. |
| compact | boolean | true | Tighter padding. |
ToolbarAction props:
| Prop | Type | Default | Notes |
| --------- | ---------------- | ------- | -------------------- |
| icon | ButtonIconSpec | - | Required icon. |
| label | string | - | Accessibility label. |
| active | boolean | false | Highlighted state. |
| onPress | () => void | - | Click handler. |
Select
Standard dropdown selector wrapping @react-native-picker/picker.
<Select
onValueChange={setValue}
options={[
{ value: '1', label: 'Option 1' },
{ value: '2', label: 'Option 2' },
]}
value={value}
/>ZORA props:
| Prop | Type | Default | Notes |
| --------------- | ------------------------- | ------- | ------------------------ |
| value | string | - | Selected value. |
| options | SelectOption[] | - | Array of option objects. |
| onValueChange | (value: string) => void | - | Change handler. |
| invalid | boolean | false | Error state styling. |
| disabled | boolean | false | Interaction state. |
Modal
Centered overlay shell with optional header, body, footer, and width preset.
<Modal footer={<Button>Done</Button>} onDismiss={close} title="Details" visible={visible} />ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ------------------ | ----------- | ------------------------------------------ |
| children | React.ReactNode | - | Modal body. |
| title | React.ReactNode | - | Header title. |
| description | React.ReactNode | - | Header description. |
| footer | React.ReactNode | - | Footer area. |
| width | ZoraContentWidth | 'default' | Resolves to 420, 520, or 560 pixels. |
Inherited props:
Picks these Surface ModalProps: closeOnBackdrop, onDismiss, testID, and
visible.
Drawer
Side overlay shell with optional header, body, and footer.
<Drawer onDismiss={close} title="Filters" visible={visible}>
{content}
</Drawer>ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ----------------- | ------- | ------------------- |
| children | React.ReactNode | - | Drawer body. |
| title | React.ReactNode | - | Header title. |
| description | React.ReactNode | - | Header description. |
| footer | React.ReactNode | - | Footer area. |
Inherited props:
Picks these Surface DrawerProps: closeOnBackdrop, onDismiss, position,
testID, and visible.
Layouts
AppShell
Theme-aware application root shell providing the structural frame for an app.
It defines the top-level layout slots (header, body, footer, overlay)
and ensures a full-height, flexible container.
Use it as the outer layout inside ZoraProvider. Combine it with layout
primitives like SidebarLayout or Page to structure inner content.
<AppShell header={<Toolbar position="inline">{actions}</Toolbar>} footer={<BottomBar />}>
<Page header={<PageHeader title="Dashboard" />}>{content}</Page>
</AppShell>Example with overlay (e.g. mobile panel or drawer):
<AppShell footer={<BottomBar />} overlay={isOpen ? <MyDrawer onClose={close} /> : null}>
{content}
</AppShell>ZORA props:
| Prop | Type | Default | Notes |
| ----------- | ---------------------- | ------- | ------------------------------------------------------------------------------------ |
| children | React.ReactNode | - | Main application content. |
| header | React.ReactNode | - | Optional top section (e.g. toolbar or navigation). |
| footer | React.ReactNode | - | Optional bottom section (e.g. tab bar or actions). |
| overlay | React.ReactNode | - | Optional overlay layer rendered above content (e.g. drawer, modal, floating panels). |
| style | StyleProp<ViewStyle> | - | Style applied to the root container. |
| bodyStyle | StyleProp<ViewStyle> | - | Style applied to the main content container. |
| testID | string | - | Forwarded to the root Surface container. |
Inherited props:
No inherited props. AppShellProps is declared directly by ZORA.
Design notes
AppShellis a structural primitive, not a page layout.It does not manage sidebars or content splits — use
SidebarLayoutfor that.It does not provide theming — wrap with
ZoraProvider.overlayis rendered using absolute positioning and should be used for drawers, mobile panels, or floating UI.All inner content must support flexible layouts (
flex: 1,minHeight: 0) to behave correctly inside the shell.
Page
Constrained page container with optional header and footer slots.
<Page header={<PageHeader title="Projects" />} width="wide">
{content}
</Page>ZORA props:
| Prop | Type | Default | Notes |
| ---------- | ------------------ | ----------- | -------------------------------------------- |
| children | React.ReactNode | - | Page body. |
| header | React.ReactNode | - | Rendered above body content. |
| footer | React.ReactNode | - | Rendered below body content. |
| width | ZoraContentWidth | 'default' | Resolves to 760, 1040, or 1280 pixels. |
| testID | string | - | Forwarded to the root Surface container. |
Inherited props:
No inherited props. PageProps is declared directly by ZORA.
PageHeader
Top-level page heading with optional eyebrow, metadata, and actions.
<PageHeader actions={<Button>New</Button>} title="Projects" />ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ----------------- | ------- | ------------------------------------ |
| title | React.ReactNode | - | Required page title. |
| description | React.ReactNode | - | Supporting copy below title. |
| eyebrow | React.ReactNode | - | Small muted text above title. |
| actions | React.ReactNode | - | Action area opposite the heading. |
| meta | React.ReactNode | - | Extra content below description. |
| testID | string | - | Forwarded to the root Surface stack. |
Inherited props:
No inherited props. PageHeaderProps is declared directly by ZORA.
PageSection
Section wrapper that optionally renders a SectionHeader.
<PageSection actions={<Button>Refresh</Button>} title="Recent activity">
{content}
</PageSection>ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ----------------- | ------- | -------------------------------------------------- |
| title | React.ReactNode | - | Section title; when absent, no header is rendered. |
| description | React.ReactNode | - | Passed to the section header. |
| actions | React.ReactNode | - | Passed to the section header. |
| children | React.ReactNode | - | Section body. |
| testID | string | - | Forwarded to the root Surface stack. |
Inherited props:
No inherited props. PageSectionProps is declared directly by ZORA.
SidebarLayout
Responsive shell with required sidebar, main content, and optional aside.
<SidebarLayout sidebar={navigation} aside={details}>
{content}
</SidebarLayout>ZORA props:
| Prop | Type | Default | Notes |
| -------------- | ----------------- | ------- | ------------------------------------ |
| sidebar | React.ReactNode | - | Required left column content. |
| children | React.ReactNode | - | Main content. |
| aside | React.ReactNode | - | Optional right column content. |
| sidebarWidth | number | 280 | Desktop sidebar width. |
| asideWidth | number | 280 | Desktop aside width. |
| testID | string | - | Forwarded to the root Surface stack. |
Inherited props:
No inherited props. SidebarLayoutProps is declared directly by ZORA.
TopbarLayout
Top navigation shell with optional sidebar composition.
<TopbarLayout topbar={topbar} sidebar={sidebar}>
{content}
</TopbarLayout>ZORA props:
| Prop | Type | Default | Notes |
| ---------- | ----------------- | ------- | ---------------------------------------------------------------------------- |
| topbar | React.ReactNode | - | Required topbar content. |
| children | React.ReactNode | - | Main content. |
| sidebar | React.ReactNode | - | Optional sidebar; when present, content is rendered through SidebarLayout. |
| testID | string | - | Forwarded to the root Surface stack. |
Inherited props:
No inherited props. TopbarLayoutProps is declared directly by ZORA.
SettingsLayout
Reusable settings shell with page header, sidebar, and content region.
<SettingsLayout actions={<Button>Save</Button>} sidebar={nav} title="Settings">
{content}
</SettingsLayout>ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ----------------- | ------- | ------------------------------------------------------------- |
| title | React.ReactNode | - | Optional page title; when absent, no page header is rendered. |
| description | React.ReactNode | - | Header description. |
| sidebar | React.ReactNode | - | Required settings navigation or context sidebar. |
| children | React.ReactNode | - | Settings content. |
| actions | React.ReactNode | - | Header action area. |
| testID | string | - | Forwarded to Page. |
Inherited props:
No inherited props. SettingsLayoutProps is declared directly by ZORA.
AuthLayout
Centered authentication-style shell for sign-in, onboarding, and recovery screens.
<AuthLayout description="Sign in to continue" title="Welcome back">
{form}
</AuthLayout>ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ----------------- | ------- | ------------------------------------- |
| title | React.ReactNode | - | Card title. |
| description | React.ReactNode | - | Card description. |
| eyebrow | React.ReactNode | - | Card eyebrow. |
| children | React.ReactNode | - | Form or auth content. |
| footer | React.ReactNode | - | Card footer. |
| testID | string | - | Forwarded to the root Surface center. |
Inherited props:
No inherited props. AuthLayoutProps is declared directly by ZORA.
Patterns
FormField
Form field wrapper with rich label composition, description, helper text, and Surface field state.
<FormField helperText="We only use this for sign-in." label="Email">
<Input keyboardType="email-address" />
</FormField>ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ----------------- | ------- | ------------------------------------------ |
| label | React.ReactNode | - | Required field label. |
| description | React.ReactNode | - | Rendered under the label. |
| helperText | React.ReactNode | - | Passed to Surface Field as helperText. |
Inherited props:
Picks these Surface FieldProps: children, disabled, errorText,
invalid, readOnly, required, and testID.
Notice
Semantic notice surface with badge eyebrow, optional body, and actions.
<Notice actions={<Button>Review</Button>} title="Publish pipeline ready" tone="success" />ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ----------------- | ----------- | ----------------------------------- |
| title | React.ReactNode | - | Required notice title. |
| description | React.ReactNode | - | Notice description. |
| children | React.ReactNode | - | Optional body content. |
| actions | React.ReactNode | - | Optional action area. |
| tone | ZoraTone | 'primary' | Drives the badge eyebrow tone. |
| testID | string | - | Forwarded to the underlying Card. |
Inherited props:
No inherited props. NoticeProps is declared directly by ZORA.
Panel
Named composition surface that currently forwards to Card.
<Panel description="Release details" title="Release Candidate">
{content}
</Panel>ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ----------------- | ----------- | --------------------------------- |
| title | React.ReactNode | - | Header title. |
| description | React.ReactNode | - | Header description. |
| eyebrow | React.ReactNode | - | Small muted text above the title. |
| actions | React.ReactNode | - | Header action area. |
| footer | React.ReactNode | - | Footer area below body content. |
| children | React.ReactNode | - | Panel body. |
| tone | ZoraCardTone | 'default' | Same tone behavior as Card. |
| compact | boolean | false | Same compact behavior as Card. |
| testID | string | - | Forwarded through Card. |
Inherited props:
No inherited props. PanelProps is declared directly by ZORA.
SectionHeader
Reusable section heading with optional eyebrow, description, and actions.
<SectionHeader actions={<Badge>Live</Badge>} title="Activity" />ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ----------------- | ------- | ------------------------------------ |
| title | React.ReactNode | - | Required heading title. |
| description | React.ReactNode | - | Supporting copy. |
| eyebrow | React.ReactNode | - | Small muted text above the title. |
| actions | React.ReactNode | - | Action area opposite the heading. |
| testID | string | - | Forwarded to the root Surface stack. |
Inherited props:
No inherited props. SectionHeaderProps is declared directly by ZORA.
FilterBar
Composable row for search + chips + trailing actions.
<FilterBar leading={<SearchBar value={query} onValueChange={setQuery} />}>
<ChipGroup value="all" onValueChange={setFilter} items={[{ value: 'all', label: 'All' }]} />
</FilterBar>ZORA props:
| Prop | Type | Default | Notes |
| ---------- | ----------------- | ------- | ------------------------------ |
| leading | React.ReactNode | - | Optional leading content. |
| children | React.ReactNode | - | Main filter controls. |
| trailing | React.ReactNode | - | Optional trailing content. |
| wrap | boolean | true | Wraps content on small widths. |
| testID | string | - | Test id. |
Inherited props:
No inherited props. FilterBarProps is declared directly by ZORA.
List
List wrapper that renders ListRow items with dividers or spacing, or accepts custom children.
<List
items={[
{ title: 'Account', description: 'Profile and security', onPress: () => undefined },
{ title: 'Notifications', trailing: <Badge>On</Badge> },
]}
/>ZORA props:
| Prop | Type | Default | Notes |
| ------------ | ----------------- | ----------- | ----------------------------------------------------- |
| items | ListRowProps[] | - | Items mode. Mutually exclusive with children. |
| children | React.ReactNode | - | Custom children mode. |
| rowVariant | ListRowVariant | 'divider' | Default variant applied when an item omits variant. |
| compact | boolean | false | Default compact value applied to rows. |
| testID | string | - | Test id. |
Behavior notes:
- In items mode, ZORA inserts
Dividerbetweenvariant="divider"rows andSpacerbetweenvariant="card"rows.
Inherited props:
No inherited props. ListProps is declared directly by ZORA.
ListRow
Single list row with leading/trailing slots, optional metadata, and a structured action model.
<ListRow title="Profile" description="Update your details" onPress={() => undefined} />ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ----------------- | ----------- | ---------------------------------------------------------------- |
| title | React.ReactNode | - | Required row title. |
| description | React.ReactNode | - | Supporting description. |
| meta | React.ReactNode | - | Small secondary metadata below description. |
| leading | React.ReactNode | - | Leading slot (icon/avatar/media). |
| trailing | React.ReactNode | - | Trailing slot (badges/meta). |
| variant | ListRowVariant | 'divider' | divider or card. |
| compact | boolean | false | Uses tighter padding and typography spacing. |
| selected | boolean | false | Selected styling state. |
| disabled | boolean | false | Disables interaction and mutes presentation. |
| onPress | () => void | - | Makes the row pressable. Mutually exclusive with action. |
| action | React.ReactNode | - | Trailing interactive content. Mutually exclusive with onPress. |
| testID | string | - | Test id. |
Inherited props:
No inherited props. ListRowProps is declared directly by ZORA.
ListSection
Section wrapper for list content with an optional SectionHeader.
<ListSection title="Settings" items={[{ title: 'Account' }]} />ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ----------------- | ----------- | ----------------------------------------------------- |
| title | React.ReactNode | - | Optional section title (renders SectionHeader). |
| description | React.ReactNode | - | Optional section description. |
| eyebrow | React.ReactNode | - | Optional eyebrow above the title. |
| actions | React.ReactNode | - | Optional trailing header actions. |
| items | ListRowProps[] | - | Items mode list content. |
| children | React.ReactNode | - | Custom children mode list content. |
| rowVariant | ListRowVariant | 'divider' | Default variant applied when an item omits variant. |
| compact | boolean | false | Default compact value applied to rows. |
| testID | string | - | Test id. |
Inherited props:
No inherited props. ListSectionProps is declared directly by ZORA.
Timeline
Vertical-only timeline pattern for onboarding steps, order tracking, and activity sequences.
<Timeline
items={[
{ id: '1', title: 'Order placed', meta: '09:15', status: 'success' },
{ id: '2', title: 'In transit', meta: '11:42', status: 'primary' },
{ id: '3', title: 'Requires attention', meta: 'Today', status: 'warning' },
]}
/>ZORA props:
| Prop | Type | Default | Notes |
| --------- | ---------------- | ------- | --------------------------------------------- |
| items | TimelineItem[] | - | Ordered timeline items (vertical-only in v1). |
| compact | boolean | false | Uses tighter spacing between items. |
| testID | string | - | Test id. |
Item shape:
type TimelineItem = {
id: string;
title: React.ReactNode;
description?: React.ReactNode;
meta?: React.ReactNode;
status?: ZoraTone;
icon?: ButtonIconSpec;
};Inherited props:
No inherited props. TimelineProps is declared directly by ZORA.
SettingsRow
Compact settings row with optional metadata, control, and press handling.
<SettingsRow control={<Switch value={enabled} />} title="Notifications" />ZORA props:
| Prop | Type | Default | Notes |
| ------------- | ----------------- | ------- | ------------------------------------------- |
| title | React.ReactNode | - | Required row title. |
| description | React.ReactNode | - | Row description. |
| meta | React.ReactNode | - | Small muted metadata below the row content. |
| control | React.ReactNode | - | Trailing control or action content. |
| onPress | () => void | - | Makes the underlying card pressable. |
| disabled | boolean | false | Forwarded to the underlying card. |
| testID | string | - | Forwarded to the underlying card. |
Inherited props:
No inherited props. SettingsRowProps is declared directly by ZORA.
EmptyState
No-data surface with title, optional supporting text, actions, and footer.
<EmptyState
primaryAction={{ label: 'Create project', onPress: createProject }}
title="Nothing here yet"
/>ZORA props:
| Prop | Type | Default | Notes |
| ----------------- | ------------------ | ------- | ------------------------------------------------------------------------------------------------------- |
| title | React.ReactNode | - | Required empty-state title. |
| description | React.ReactNode | - | Supporting copy. |
| eyebrow | React.ReactNode | - | Card eyebrow. |
| primaryAction | EmptyStateAction | - | Primary action button. |
| secondaryAction | EmptyStateAction | - | Secondary action button; defaults to tone="neutral" and emphasis="soft" when omitted on the action. |
| footer | React.ReactNode | - | Footer content below actions. |
| testID | string | - | Forwarded to the underlying card. |
EmptyStateAction:
| Prop | Type | Default | Notes |
| ---------- | ----------------- | ------- | ---------------- |
| label | React.ReactNode | - | Button label. |
| onPress | () => void | - | Button handler. |
| tone | ZoraTone | - | Button tone. |
| emphasis | ZoraEmphasis | - | Button emphasis. |
Inherited props:
No inherited props. EmptyStateProps and EmptyStateAction are declared
directly by ZORA.
ConfirmDialog
Narrow confirmation modal for destructive or high-signal decisions.
<ConfirmDialog
confirmLabel="Archive"
confirmTone="danger"
onCancel={close}
onConfirm={archive}
title="Archive project?"
visible={visible}
/>ZORA props:
| Prop | Type | Default | Notes |
| ----------------- | ----------------- | ----------- | ------------------------------------------ |
| visible | boolean | - | Required modal visibility. |
| title | React.ReactNode | - | Required dialog title. |
| description | React.ReactNode | - | D
