@krizaka/ui 2.0.0-beta.3 → 2.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -150,7 +150,7 @@ Colours are roles of the preset (`bg-surface-2`, `text-fg-secondary`, `border-bo
150
150
  | `@krizaka/ui/confirm-button` | `ConfirmButton` (two presses: `confirmLabel`, `onConfirm`, `timeoutMs` 4000, `label` for an icon, `armedContent`; the `Button` variants), `confirmArmed` | client |
151
151
  | `@krizaka/ui/progress` | `Progress` (`variant` bar·ring, `size` sm·md·lg, `value`/`max`, indeterminate without a value, `label`, `valueText`, a ring's centre as children) | server |
152
152
  | `@krizaka/ui` | the marks, `MotionObserver`, `RotatingWord`, `cx` (unchanged) | client |
153
- | `@krizaka/ui/native` | the marks for React Native (unchanged) | native |
153
+ | `@krizaka/ui/native` | React Native: `ThemeProvider`/`useTheme`, `Txt`, `Button`, `IconButton`, `Card.*`, `Badge`, `Chip` (+ `Group`), `Avatar` (+ `Group`), `Skeleton`, `EmptyState`, `Spinner`, `Countdown`, `Segmented`, `Progress`, `Toaster`/`toast`, the marks — see [React Native](#react-native) | native |
154
154
  | `@krizaka/ui/motion.css`, `@krizaka/ui/tailwind.css` | the motion signature; the `@source` of the primitives' classes | CSS |
155
155
  | `@krizaka/ui/registry/<name>`, `@krizaka/ui/registry/demos/<name>` | the registry (JSON) and the demos — see [Registry](#registry) | data · client |
156
156
 
@@ -198,17 +198,79 @@ A site that renders the demos adds them to Tailwind's sources: `@source "../node
198
198
 
199
199
  ## React Native
200
200
 
201
- The marks have one geometry; `@krizaka/ui/native` draws it with `react-native-svg` for the apps
202
- ([orochia-mobile](https://github.com/krizaka/orochia-mobile)). The orbit turns and the flame breathes on the native
203
- driver, and stay still when the system asks to reduce motion. `react-dom`, `react-native` and `react-native-svg` are
201
+ `@krizaka/ui/native` is the platform for the React Native apps
202
+ ([orochia-mobile](https://github.com/krizaka/orochia-mobile)): the same primitives as the web, **the same prop names
203
+ where the concept is the same**, the same tokens — `StyleSheet` only, no NativeWind, no dependency beyond
204
+ `react-native` (≥ 0.76) and `react-native-svg` (≥ 15), which the apps already have. The `@krizaka/tokens/native` values
205
+ are inlined in the bundle: the app installs nothing else. `react-dom`, `react-native` and `react-native-svg` are
204
206
  optional peers: a web app never installs the native ones, a native app never installs `react-dom`.
205
207
 
208
+ ```bash
209
+ npm install @krizaka/ui # react, react-native and react-native-svg come from the app
210
+ ```
211
+
212
+ ### Theme
213
+
214
+ Once, at the root. `mode` is `dark` · `light` · `system` (followed through `useColorScheme`); `overrides` are a
215
+ product's roles over the platform's, per theme — e.g. `nativeTheme` from `@krizaka/orochia-design-system/tokens`.
216
+ Without a provider, `useTheme()` follows the system with the platform's roles. No storage dependency: the app
217
+ persists the choice in `onModeChange`.
218
+
206
219
  ```tsx
207
- import { OrochiaMark } from "@krizaka/ui/native";
220
+ import { ThemeProvider, Toaster, useTheme } from "@krizaka/ui/native";
221
+ import { nativeTheme } from "@krizaka/orochia-design-system/tokens";
222
+
223
+ <ThemeProvider mode={mode} onModeChange={saveMode} overrides={nativeTheme} fonts={{ display: "Outfit-Bold" }}>
224
+ <App />
225
+ <Toaster closeLabel={t("common.close")} offset={insets.top + 8} />
226
+ </ThemeProvider>;
208
227
 
209
- <OrochiaMark size={72} title="Orochia" neutral={theme.border} />
228
+ const { theme, scheme, mode, setMode, radius } = useTheme(); // theme.surface1, theme.textPrimary, theme.accent…
210
229
  ```
211
230
 
231
+ ### Components
232
+
233
+ | Native | Props (same names as the web unless noted) | Differences with the web |
234
+ | :-- | :-- | :-- |
235
+ | `Txt` | `variant` display·title·body·caption·label·mono, `tone` text·secondary·muted·accent·success·warning·danger·onAccent·onMedia | Native only (the web uses classes). `title`/`display` are headings. |
236
+ | `Button`, `IconButton` | `variant` primary·secondary·outline·ghost·danger, `size` sm·md·lg, `shape` rounded·pill, `loading`, `disabled`, `onPress`; `IconButton`: `label` required, `icon` | The text is `label` (a string), not children; `icon` before it; no `asChild`. Haptics stay in the app's `onPress`. |
237
+ | `Card.Root/Media/Image/Overlay/Body/Title/Description/Footer` | `tone` default·elevated, `radius`, `Media aspect`, `Image src`/`fallback`, `Overlay corner`, `Body padding` | `Root onPress` makes the card a button (the web uses `asChild` + a link). No `Stat`, no `reveal`. |
238
+ | `Badge` | `tone` neutral·accent·success·warning·danger·scrim, `size` sm·md, `dot`, `pulse` | — |
239
+ | `Chip`, `Chip.Group` | `selected`/`defaultSelected`/`onSelectedChange`, `value`, `size`, `removable` + `removeLabel` + `onRemove`; Group: `type` single·multiple, `value`/`defaultValue`/`onValueChange` | A toggle chip is a `checkbox` (web: a pressed button). `Group scrollable`: one horizontal row. |
240
+ | `Avatar`, `Avatar.Group` | `src`, `alt`, `fallback`, `size` xs·sm·md·lg·xl; Group `max` | `size` also takes points; the default fallback is the initial of `alt`. |
241
+ | `Skeleton` | `shape` text·circle·rect | `width`, `height` as props (no classes). |
242
+ | `EmptyState` | `icon`, `title`, `description`, `action` | `title`/`description` are strings. |
243
+ | `Spinner` | `label`, `size` sm·md·lg | The platform's `ActivityIndicator`. |
244
+ | `Countdown` | `target`, `skewMs`, `units`, `urgentBelowMs`, `size`, `label` | Same clock (`countdown/core`): one interval for every countdown on screen. |
245
+ | `Segmented` | `options` (`value`, `label`, `disabled`), `value`, `onValueChange`, `size` | The web `Tabs variant="segmented"` (a tab list). To filter, `Chip.Group`. |
246
+ | `Progress` | `variant` bar·ring, `size`, `value`/`max`, `label`, `valueText`, a ring's centre as children | The fill is the `accent` → `accent2` gradient (react-native-svg); it animates to each new value. |
247
+ | `Toaster`, `toast` | `toast(title, { description, tone, icon, action, onPress, id, duration, onDismiss })`, `toast.success/warning/error/info`, `toast.dismiss(id?)`; `Toaster closeLabel position offset duration max` | No sonner: a light queue of its own. `offset` takes the safe-area inset (no safe-area dependency). |
248
+ | `OrochiaMark` | `size`, `animated`, `title`, `neutral` | The mark, drawn with react-native-svg. |
249
+
250
+ Every animation (skeleton, badge dot, urgent countdown, progress, toast entrance, the marks) stops when the system
251
+ asks to reduce motion (`useReducedMotion()`). Accessibility goes through `role` and `aria-*` (RN ≥ 0.71): headings,
252
+ `button`, `checkbox`/`radio`/`radiogroup`, `tab`/`tablist`, `progressbar` with its value, `timer`, a polite live
253
+ region for the toasts.
254
+
255
+ ```tsx
256
+ import { Button, Card, Countdown, OrochiaMark } from "@krizaka/ui/native";
257
+
258
+ <Card.Root onPress={open}>
259
+ <Card.Body>
260
+ <Card.Title>{item.title}</Card.Title>
261
+ <Countdown target={item.endsAt} skewMs={skew} units={units} label={t("item.endsIn")} size="sm" />
262
+ <Button variant="primary" label={t("item.join")} onPress={join} />
263
+ </Card.Body>
264
+ </Card.Root>
265
+
266
+ <OrochiaMark size={72} title="Orochia" neutral={theme.borderDefault} />
267
+ ```
268
+
269
+ The native stories (`Native/*` in the catalogue) render through react-native-web in the web Storybook: audited by axe
270
+ and screenshot-compared in dark and light like the web ones. Tests run on Jest with React Native's preset and
271
+ Testing Library (`src/native/*.test.tsx`); `scripts/native-dist.mjs` checks that `dist/native.js` carries no
272
+ `"use client"`, no DOM and no import beyond React, `react-native` and `react-native-svg`.
273
+
212
274
  ## The marks
213
275
 
214
276
  ```tsx
package/dist/native.d.ts CHANGED
@@ -1,4 +1,277 @@
1
- import React from 'react';
1
+ import * as React from 'react';
2
+ import React__default from 'react';
3
+ import { ViewProps, PressableProps, StyleProp, ViewStyle, TextProps, ImageProps, DimensionValue, ActivityIndicatorProps } from 'react-native';
4
+
5
+ /** Milliseconds left until `target` (never negative), ticking every second. `skewMs` = server clock − this clock. */
6
+ declare function useCountdown(target: Date | string | number, skewMs?: number): number;
7
+ /** Splits a duration into days, hours, minutes and seconds. */
8
+ declare function splitDuration(ms: number): {
9
+ d: number;
10
+ h: number;
11
+ m: number;
12
+ s: number;
13
+ };
14
+ /** The short unit labels, passed translated (e.g. `{ d: "d", h: "h", m: "m", s: "s" }`). */
15
+ type CountdownUnits = {
16
+ d: string;
17
+ h: string;
18
+ m: string;
19
+ s: string;
20
+ };
21
+
22
+ type AvatarSize = "xs" | "sm" | "md" | "lg" | "xl";
23
+ type AvatarProps = Omit<ViewProps, "children"> & {
24
+ src?: string | null;
25
+ /** The person's name: the image's accessible name and the source of the default initial. */
26
+ alt?: string;
27
+ /** Shown while there is no image or when it fails: default, the first letter of `alt`. */
28
+ fallback?: React.ReactNode;
29
+ /** A size of the scale, or points. */
30
+ size?: AvatarSize | number;
31
+ };
32
+ /** A round picture; the initial (or `fallback`) when there is none or it fails to load. */
33
+ declare function AvatarRoot({ src, alt, fallback, size, style, ...props }: AvatarProps): React.JSX.Element;
34
+ type AvatarGroupProps = ViewProps & {
35
+ /** How many avatars are shown; the rest becomes a `+n` avatar. */
36
+ max?: number;
37
+ /** The size of the avatars (and of the `+n` one). */
38
+ size?: AvatarSize | number;
39
+ };
40
+ /** Overlapping avatars; past `max`, a `+n` avatar counts the rest. */
41
+ declare function AvatarGroup({ max, size, style, children, ...props }: AvatarGroupProps): React.JSX.Element;
42
+ /** `Avatar` (an image with a fallback) and `Avatar.Group`. */
43
+ declare const Avatar: typeof AvatarRoot & {
44
+ Group: typeof AvatarGroup;
45
+ };
46
+
47
+ type BadgeTone = "neutral" | "accent" | "success" | "warning" | "danger" | "scrim";
48
+ type BadgeProps = ViewProps & {
49
+ tone?: BadgeTone;
50
+ size?: "sm" | "md";
51
+ /** A small dot before the text, in the tone's colour. */
52
+ dot?: boolean;
53
+ /** The dot breathes (still when the system reduces motion). */
54
+ pulse?: boolean;
55
+ children?: React.ReactNode;
56
+ };
57
+ /** A short status in capitals: a tone, a size, an optional (pulsing) dot. */
58
+ declare function Badge({ tone, size, dot, pulse, style, children, ...props }: BadgeProps): React.JSX.Element;
59
+
60
+ type ButtonVariant = "primary" | "secondary" | "outline" | "ghost" | "danger";
61
+ type ButtonSize = "sm" | "md" | "lg";
62
+ type BaseProps = Omit<PressableProps, "children" | "style"> & {
63
+ variant?: ButtonVariant;
64
+ size?: ButtonSize;
65
+ /** `rounded` (default) or `pill`. */
66
+ shape?: "rounded" | "pill";
67
+ /** Disables, shows a spinner in place of the icon and says busy. */
68
+ loading?: boolean;
69
+ style?: StyleProp<ViewStyle>;
70
+ };
71
+ type ButtonProps = BaseProps & {
72
+ /** The visible text — passed translated. It is also the accessible name. */
73
+ label: string;
74
+ /** Before the label (an icon component from the app's icon set). */
75
+ icon?: React.ReactNode;
76
+ };
77
+ /** The action. `variant` × `size` × `shape`, `loading`, `icon`; the haptics, if any, belong to the app's `onPress`. */
78
+ declare function Button({ label, icon, variant, size, shape, loading, disabled, style, ...props }: ButtonProps): React.JSX.Element;
79
+ type IconButtonProps = BaseProps & {
80
+ /** The accessible name — required, passed translated. */
81
+ label: string;
82
+ icon: React.ReactNode;
83
+ };
84
+ /** A square button holding only an icon: `label` is required, it is its accessible name. */
85
+ declare function IconButton({ label, icon, variant, size, shape, loading, disabled, style, ...props }: IconButtonProps): React.JSX.Element;
86
+
87
+ type TxtVariant = "display" | "title" | "body" | "caption" | "label" | "mono";
88
+ type TxtTone = "text" | "secondary" | "muted" | "accent" | "success" | "warning" | "danger" | "onAccent" | "onMedia";
89
+ type TxtProps = TextProps & {
90
+ /** The type scale: display · title · body (default) · caption · label · mono (tabular figures). */
91
+ variant?: TxtVariant;
92
+ /** A text role of the theme (default `text`). */
93
+ tone?: TxtTone;
94
+ };
95
+ /** Text in the platform's scale, coloured by a role of the current theme. `title` and `display` are headers for screen readers. */
96
+ declare function Txt({ variant, tone, style, ...props }: TxtProps): React.JSX.Element;
97
+ declare const textStyles: {
98
+ display: {
99
+ fontSize: number;
100
+ lineHeight: number;
101
+ fontWeight: "900";
102
+ letterSpacing: number;
103
+ };
104
+ title: {
105
+ fontSize: number;
106
+ lineHeight: number;
107
+ fontWeight: "800";
108
+ };
109
+ body: {
110
+ fontSize: number;
111
+ lineHeight: number;
112
+ };
113
+ caption: {
114
+ fontSize: number;
115
+ lineHeight: number;
116
+ };
117
+ label: {
118
+ fontSize: number;
119
+ lineHeight: number;
120
+ fontWeight: "700";
121
+ };
122
+ mono: {
123
+ fontSize: number;
124
+ lineHeight: number;
125
+ fontWeight: "700";
126
+ fontVariant: "tabular-nums"[];
127
+ };
128
+ };
129
+
130
+ type CardRootProps = ViewProps & {
131
+ /** `default` (the raised surface) or `elevated` (one step higher, with a shadow). */
132
+ tone?: "default" | "elevated";
133
+ radius?: "md" | "lg" | "xl";
134
+ /**
135
+ * Makes the whole card pressable: a button for screen readers, named by its content (an `aria-label`, if any, must
136
+ * contain the visible title).
137
+ */
138
+ onPress?: PressableProps["onPress"];
139
+ };
140
+ /** The frame: the theme's surface, a hairline border, rounded corners; `onPress` makes it a pressable card. */
141
+ declare function CardRoot({ tone, radius, onPress, style, ...props }: CardRootProps): React.JSX.Element;
142
+ declare const ASPECT: {
143
+ readonly video: number;
144
+ readonly square: 1;
145
+ readonly portrait: number;
146
+ };
147
+ type CardMediaProps = ViewProps & {
148
+ aspect?: keyof typeof ASPECT | "auto";
149
+ };
150
+ /** The media area at the top of a card: a fixed ratio, the `media` role behind (what shows while an image loads). */
151
+ declare function CardMedia({ aspect, style, ...props }: CardMediaProps): React.JSX.Element;
152
+ type CardImageProps = Omit<ImageProps, "source" | "src"> & {
153
+ src?: string | null;
154
+ /** Shown when there is no image: an icon, an illustration (decorative). */
155
+ fallback?: React.ReactNode;
156
+ };
157
+ /** The image of a media (it fills it), or its `fallback` when there is none. Decorative unless `alt` is given. */
158
+ declare function CardImage({ src, fallback, alt, style, ...props }: CardImageProps): React.JSX.Element;
159
+ declare const CORNER: Record<"top-left" | "top-right" | "bottom-left" | "bottom-right", ViewStyle>;
160
+ type CardOverlayProps = ViewProps & {
161
+ corner?: keyof typeof CORNER;
162
+ };
163
+ /** What sits on the media (badges, a caption), in a corner. */
164
+ declare function CardOverlay({ corner, style, ...props }: CardOverlayProps): React.JSX.Element;
165
+ declare const PADDING: {
166
+ readonly none: 0;
167
+ readonly sm: 12;
168
+ readonly md: 16;
169
+ readonly lg: 24;
170
+ };
171
+ type CardBodyProps = ViewProps & {
172
+ padding?: keyof typeof PADDING;
173
+ };
174
+ declare function CardBody({ padding, style, ...props }: CardBodyProps): React.JSX.Element;
175
+ /** The card's name: one line, a header for screen readers. */
176
+ declare function CardTitle({ numberOfLines, ...props }: Omit<TxtProps, "variant">): React.JSX.Element;
177
+ declare function CardDescription({ numberOfLines, ...props }: Omit<TxtProps, "variant">): React.JSX.Element;
178
+ declare function CardFooter({ style, ...props }: ViewProps): React.JSX.Element;
179
+ /** `Card.Root/Media/Image/Overlay/Body/Title/Description/Footer` — the web parts, on View, Image and Text. */
180
+ declare const Card: {
181
+ Root: typeof CardRoot;
182
+ Media: typeof CardMedia;
183
+ Image: typeof CardImage;
184
+ Overlay: typeof CardOverlay;
185
+ Body: typeof CardBody;
186
+ Title: typeof CardTitle;
187
+ Description: typeof CardDescription;
188
+ Footer: typeof CardFooter;
189
+ };
190
+
191
+ type ChipSize = "sm" | "md";
192
+ type CommonProps = Omit<PressableProps, "children" | "style" | "onPress"> & {
193
+ size?: ChipSize;
194
+ style?: StyleProp<ViewStyle>;
195
+ /** The text (passed translated) or a node (an icon and a text). */
196
+ children: React.ReactNode;
197
+ };
198
+ type ToggleChipProps = CommonProps & {
199
+ /** Inside `Chip.Group`: the value this chip stands for (required there, ignored alone). */
200
+ value?: string;
201
+ /** Alone: pressed or not (`defaultSelected` uncontrolled), `onSelectedChange` on every press. */
202
+ selected?: boolean;
203
+ defaultSelected?: boolean;
204
+ onSelectedChange?: (selected: boolean) => void;
205
+ removable?: false;
206
+ };
207
+ type RemovableChipProps = CommonProps & {
208
+ /** A static chip (a chosen tag, an applied filter) with a remove button. */
209
+ removable: true;
210
+ /** The accessible name of the remove button — passed translated, e.g. "Remove #night". */
211
+ removeLabel: string;
212
+ onRemove: () => void;
213
+ };
214
+ type ChipProps = ToggleChipProps | RemovableChipProps;
215
+ /** A selectable pill (`selected` / `onSelectedChange`), an item of `Chip.Group` (`value`), or `removable`. */
216
+ declare function ChipBase(props: ChipProps): React.JSX.Element;
217
+ type GroupBase = {
218
+ size?: ChipSize;
219
+ disabled?: boolean;
220
+ /** One horizontal scrolling row (a filter bar) instead of wrapping lines. */
221
+ scrollable?: boolean;
222
+ /** The accessible name of the group — passed translated. */
223
+ "aria-label"?: string;
224
+ style?: StyleProp<ViewStyle>;
225
+ children: React.ReactNode;
226
+ };
227
+ type ChipGroupProps = (GroupBase & {
228
+ type: "single";
229
+ value?: string;
230
+ defaultValue?: string;
231
+ onValueChange?: (value: string) => void;
232
+ }) | (GroupBase & {
233
+ type: "multiple";
234
+ value?: string[];
235
+ defaultValue?: string[];
236
+ onValueChange?: (value: string[]) => void;
237
+ });
238
+ /** A set of chips: `single` (one value, a radio group; pressing the chosen chip keeps it) or `multiple` (toggles). */
239
+ declare function ChipGroup(props: ChipGroupProps): React.JSX.Element;
240
+ /** `Chip` and `Chip.Group`. */
241
+ declare const Chip: typeof ChipBase & {
242
+ Group: typeof ChipGroup;
243
+ };
244
+
245
+ type CountdownProps = {
246
+ target: Date | string | number;
247
+ /** Server clock − this clock, in milliseconds. */
248
+ skewMs?: number;
249
+ /** The short unit labels, passed translated. */
250
+ units: CountdownUnits;
251
+ /** Below this many milliseconds it turns urgent (the danger role, the last segment breathes). Default 60 s. */
252
+ urgentBelowMs?: number;
253
+ size?: "sm" | "md" | "lg";
254
+ /** Accessible name, e.g. "Ends in". */
255
+ label: string;
256
+ style?: StyleProp<ViewStyle>;
257
+ };
258
+ /**
259
+ * Time left until a moment, as segments (2d 04h 13m — days only when there are some) in tabular figures, on the same
260
+ * clock as the web `Countdown` (one interval for every countdown on screen). Under `urgentBelowMs` it turns to the
261
+ * danger role and its last segment breathes (still when the system reduces motion).
262
+ */
263
+ declare function Countdown({ target, skewMs, units, urgentBelowMs, size, label, style }: CountdownProps): React.JSX.Element;
264
+
265
+ type EmptyStateProps = Omit<ViewProps, "children"> & {
266
+ /** A decorative icon (hidden from screen readers). */
267
+ icon?: React.ReactNode;
268
+ title: string;
269
+ description?: string;
270
+ /** What to do next: a Button. */
271
+ action?: React.ReactNode;
272
+ };
273
+ /** What a list, a search or a screen says when it has nothing to show — every word is a prop. */
274
+ declare function EmptyState({ icon, title, description, action, style, ...props }: EmptyStateProps): React.JSX.Element;
2
275
 
3
276
  interface NativeMarkProps {
4
277
  /** Width and height in points. */
@@ -15,6 +288,201 @@ interface NativeMarkProps {
15
288
  * react-native-svg. Three layers so that the motion runs on the native driver: the orbit turns, the eye and the flame
16
289
  * breathe, the serpent stays still.
17
290
  */
18
- declare function OrochiaMark({ size, animated, title, neutral }: NativeMarkProps): React.JSX.Element;
291
+ declare function OrochiaMark({ size, animated, title, neutral }: NativeMarkProps): React__default.JSX.Element;
292
+
293
+ type ProgressProps = {
294
+ /** `bar` (default) or `ring`. */
295
+ variant?: "bar" | "ring";
296
+ size?: "sm" | "md" | "lg";
297
+ /** How far, between 0 and `max`. `null` or absent: indeterminate (a wait of unknown length). */
298
+ value?: number | null;
299
+ max?: number;
300
+ /** The accessible name — passed translated ("Upload", "Raised towards the goal"). */
301
+ label: string;
302
+ /** The value in words for screen readers ("$420 of $1,000") — default: the percentage. */
303
+ valueText?: string;
304
+ /** A ring's centre: an amount, a percentage, an icon. */
305
+ children?: React.ReactNode;
306
+ style?: StyleProp<ViewStyle>;
307
+ };
308
+ /**
309
+ * A progress bar or ring in the accent gradient (`accent` → `accent2`): it moves to `value` / `max` when it changes,
310
+ * indeterminate without a value. Still under reduced motion. A `progressbar` with its value for screen readers.
311
+ */
312
+ declare function Progress({ variant, size, value, max, label, valueText, children, style }: ProgressProps): React.JSX.Element;
313
+
314
+ type SegmentedOption<T extends string> = {
315
+ value: T;
316
+ label: string;
317
+ disabled?: boolean;
318
+ };
319
+ type SegmentedProps<T extends string> = {
320
+ /** The views, in order; `label` passed translated. */
321
+ options: readonly SegmentedOption<T>[];
322
+ value: T;
323
+ onValueChange: (value: T) => void;
324
+ size?: "sm" | "md";
325
+ /** The accessible name of the tab list — passed translated. */
326
+ "aria-label"?: string;
327
+ style?: StyleProp<ViewStyle>;
328
+ };
329
+ /**
330
+ * Switches between views of one screen: equal segments on a track, the chosen one raised — the web
331
+ * `Tabs variant="segmented"`. A tab list for screen readers. To filter a list, use `Chip.Group`.
332
+ */
333
+ declare function Segmented<T extends string>({ options, value, onValueChange, size, "aria-label": label, style }: SegmentedProps<T>): React.JSX.Element;
334
+
335
+ type SkeletonProps = {
336
+ /** `text` (a line), `circle` (an avatar), `rect` (a media, a card). */
337
+ shape?: "text" | "circle" | "rect";
338
+ width?: DimensionValue;
339
+ height?: DimensionValue;
340
+ style?: StyleProp<ViewStyle>;
341
+ testID?: string;
342
+ };
343
+ /** A pulsing placeholder the size of what is loading — hidden from screen readers; still when the system reduces motion. */
344
+ declare function Skeleton({ shape, width, height, style, testID }: SkeletonProps): React.JSX.Element;
345
+
346
+ type SpinnerProps = Omit<ActivityIndicatorProps, "size" | "color"> & {
347
+ /** The accessible name of the wait, e.g. "Loading" — passed translated. */
348
+ label: string;
349
+ size?: "sm" | "md" | "lg";
350
+ };
351
+ /** An indeterminate wait: the platform's activity indicator in the accent, named by `label`. */
352
+ declare function Spinner({ label, size, ...props }: SpinnerProps): React.JSX.Element;
353
+
354
+ type ThemeName = "dark" | "light";
355
+ /** The colour roles of one theme (the @krizaka/tokens roles). */
356
+ type Theme = {
357
+ readonly surface0: string;
358
+ readonly surface1: string;
359
+ readonly surface2: string;
360
+ readonly surface3: string;
361
+ readonly media: string;
362
+ readonly borderSubtle: string;
363
+ readonly borderDefault: string;
364
+ readonly borderStrong: string;
365
+ readonly textPrimary: string;
366
+ readonly textSecondary: string;
367
+ readonly textMuted: string;
368
+ readonly textOnMedia: string;
369
+ readonly accent: string;
370
+ readonly accentHover: string;
371
+ readonly accentSoft: string;
372
+ readonly accent2: string;
373
+ readonly onAccent: string;
374
+ readonly ring: string;
375
+ readonly success: string;
376
+ readonly warning: string;
377
+ readonly danger: string;
378
+ readonly info: string;
379
+ readonly scrim: string;
380
+ readonly scrimStrong: string;
381
+ readonly overlay: string;
382
+ };
383
+ /** Corner radii, in points. */
384
+ type Radius = {
385
+ readonly sm: number;
386
+ readonly md: number;
387
+ readonly lg: number;
388
+ readonly xl: number;
389
+ readonly full: number;
390
+ };
391
+ /** Motion: the cubic-bezier control points of the platform's ease (`Easing.bezier(...motion.ease)`). */
392
+ type Motion = {
393
+ readonly ease: readonly [number, number, number, number];
394
+ };
395
+ type Mode = "dark" | "light" | "system";
396
+ /** A product's colour roles per theme (e.g. `nativeTheme` of a product design system): only the roles it changes. */
397
+ type ThemeOverrides = {
398
+ readonly dark?: Partial<Theme>;
399
+ readonly light?: Partial<Theme>;
400
+ };
401
+ /** Font families, when the app has loaded them (`expo-font`…). Absent: the system font. */
402
+ type ThemeFonts = {
403
+ readonly sans?: string;
404
+ readonly display?: string;
405
+ readonly mono?: string;
406
+ };
407
+ type ThemeContextValue = {
408
+ /** The chosen mode: dark, light or follow the system. */
409
+ mode: Mode;
410
+ setMode: (mode: Mode) => void;
411
+ /** The resolved scheme: what `mode` gives on this device right now. */
412
+ scheme: ThemeName;
413
+ /** The colour roles of the resolved scheme, overrides applied. */
414
+ theme: Theme;
415
+ radius: Radius;
416
+ motion: Motion;
417
+ fonts: ThemeFonts;
418
+ };
419
+ type ThemeProviderProps = {
420
+ children: React.ReactNode;
421
+ /** Controlled mode (with `onModeChange`). */
422
+ mode?: Mode;
423
+ /** Uncontrolled: the mode before the user picks one. Default: follow the system. */
424
+ defaultMode?: Mode;
425
+ /** Every change of mode — the app persists it (no storage dependency here). */
426
+ onModeChange?: (mode: Mode) => void;
427
+ /** The product's roles over the platform's, per theme. */
428
+ overrides?: ThemeOverrides;
429
+ fonts?: ThemeFonts;
430
+ };
431
+ /** Once, at the root of the app. Without it, `useTheme()` follows the system with the platform's roles. */
432
+ declare function ThemeProvider({ children, mode: controlled, defaultMode, onModeChange, overrides, fonts }: ThemeProviderProps): React.JSX.Element;
433
+ /** The current theme: `{ mode, setMode, scheme, theme, radius, motion, fonts }`. */
434
+ declare function useTheme(): ThemeContextValue;
435
+ /** True while the system asks to reduce motion: every loop and transition of /native stops. */
436
+ declare function useReducedMotion(): boolean;
437
+ /** A role at a lower opacity (`#rrggbb` → `rgba()`): the soft tints of the status tones. Other forms pass through. */
438
+ declare function alpha(color: string, opacity: number): string;
439
+
440
+ type ToastTone = "default" | "success" | "warning" | "error" | "info";
441
+ type ToastOptions = {
442
+ /** Replaces a toast with the same id (a notification shown once). */
443
+ id?: string;
444
+ description?: string;
445
+ tone?: ToastTone;
446
+ /** A decorative icon before the text. */
447
+ icon?: React.ReactNode;
448
+ /** Milliseconds on screen; `Infinity` keeps it until dismissed. Default: the Toaster's `duration`. */
449
+ duration?: number;
450
+ /** Pressing the toast (opening what it is about); it is then dismissed. */
451
+ onPress?: () => void;
452
+ /** A button inside the toast; it is then dismissed. */
453
+ action?: {
454
+ label: string;
455
+ onPress: () => void;
456
+ };
457
+ /** Called once the toast leaves, whatever the reason. */
458
+ onDismiss?: () => void;
459
+ };
460
+ type ToastItem = ToastOptions & {
461
+ id: string;
462
+ title: string;
463
+ };
464
+ declare function show(title: string, options?: ToastOptions): string;
465
+ declare function dismiss(id?: string): void;
466
+ /** `toast(title, options)`, `toast.success/warning/error/info(…)`, `toast.dismiss(id?)` — the web `toast`'s names. */
467
+ declare const toast: typeof show & {
468
+ success: (title: string, options?: Omit<ToastOptions, "tone">) => string;
469
+ warning: (title: string, options?: Omit<ToastOptions, "tone">) => string;
470
+ error: (title: string, options?: Omit<ToastOptions, "tone">) => string;
471
+ info: (title: string, options?: Omit<ToastOptions, "tone">) => string;
472
+ dismiss: typeof dismiss;
473
+ };
474
+ type ToasterProps = {
475
+ /** The accessible name of each toast's close button — passed translated. */
476
+ closeLabel: string;
477
+ position?: "top" | "bottom";
478
+ /** Distance from the edge, e.g. the safe-area inset + a margin (no safe-area dependency here). */
479
+ offset?: number;
480
+ /** Default milliseconds on screen. */
481
+ duration?: number;
482
+ /** At most this many on screen; the oldest leave first. */
483
+ max?: number;
484
+ };
485
+ /** Mount once, at the root (inside `ThemeProvider`). Then call `toast()` from anywhere. */
486
+ declare function Toaster({ closeLabel, position, offset, duration, max }: ToasterProps): React.JSX.Element | null;
19
487
 
20
- export { type NativeMarkProps, OrochiaMark };
488
+ export { Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, AvatarRoot, type AvatarSize, Badge, type BadgeProps, type BadgeTone, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Card, CardBody, type CardBodyProps, CardDescription, CardFooter, CardImage, type CardImageProps, CardMedia, type CardMediaProps, CardOverlay, type CardOverlayProps, CardRoot, type CardRootProps, CardTitle, Chip, type ChipGroupProps, type ChipProps, type ChipSize, Countdown, type CountdownProps, type CountdownUnits, EmptyState, type EmptyStateProps, IconButton, type IconButtonProps, type Mode, type Motion, type NativeMarkProps, OrochiaMark, Progress, type ProgressProps, type Radius, Segmented, type SegmentedOption, type SegmentedProps, Skeleton, type SkeletonProps, Spinner, type SpinnerProps, type Theme, type ThemeContextValue, type ThemeFonts, type ThemeName, type ThemeOverrides, ThemeProvider, type ThemeProviderProps, type ToastItem, type ToastOptions, type ToastTone, Toaster, type ToasterProps, Txt, type TxtProps, type TxtTone, type TxtVariant, alpha, splitDuration, textStyles, toast, useCountdown, useReducedMotion, useTheme };