achery-ui 0.1.0

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.
@@ -0,0 +1,1293 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode, SVGProps, HTMLAttributes, ElementType, ButtonHTMLAttributes, InputHTMLAttributes, SelectHTMLAttributes, TextareaHTMLAttributes } from 'react';
3
+ import { A as AccentColor } from './accents-DrqL1lLe.cjs';
4
+ import * as _vanilla_extract_recipes from '@vanilla-extract/recipes';
5
+ import { RecipeVariants } from '@vanilla-extract/recipes';
6
+
7
+ /** Light or dark colour mode. */
8
+ type ThemeMode = 'light' | 'dark';
9
+ /**
10
+ * Value provided by {@link ThemeContext} and returned by {@link useTheme}.
11
+ * Gives components read/write access to the active theme and accent colour.
12
+ */
13
+ interface ThemeContextValue {
14
+ /** Currently active colour mode. */
15
+ theme: ThemeMode;
16
+ /** Set the colour mode explicitly. */
17
+ setTheme: (theme: ThemeMode) => void;
18
+ /** Toggle between `'light'` and `'dark'`. */
19
+ toggleTheme: () => void;
20
+ /** Currently active accent colour. */
21
+ accent: AccentColor;
22
+ /** Set the accent colour. */
23
+ setAccent: (accent: AccentColor) => void;
24
+ }
25
+
26
+ /** Props for the {@link AcheryProvider} component. */
27
+ interface AcheryProviderProps {
28
+ children: ReactNode;
29
+ /**
30
+ * Initial colour theme.
31
+ * @default 'light'
32
+ */
33
+ defaultTheme?: ThemeMode;
34
+ /**
35
+ * Initial accent colour.
36
+ * @default 'terracotta'
37
+ */
38
+ defaultAccent?: AccentColor;
39
+ /** className applied to the root `[data-achery-root]` div. */
40
+ className?: string;
41
+ /** Inline styles applied to the root `[data-achery-root]` div. */
42
+ style?: React.CSSProperties;
43
+ }
44
+ /**
45
+ * Root provider for the Achery design system. Must wrap any part of the tree
46
+ * that uses Achery components.
47
+ *
48
+ * Responsibilities:
49
+ * - Injects theme CSS (light, dark, accents, global reset) as side-effect imports
50
+ * - Renders a `[data-achery-root][data-theme][data-accent]` div that scopes all
51
+ * CSS custom properties
52
+ * - Mirrors those attributes onto `<html>` so portaled content (Modal, Tooltip,
53
+ * Toast) inherits the same CSS vars outside the root div
54
+ * - Provides {@link ThemeContext} for {@link useTheme}
55
+ *
56
+ * @example
57
+ * ```tsx
58
+ * // app entry point
59
+ * import { AcheryProvider } from 'achery-ui'
60
+ *
61
+ * export default function App() {
62
+ * return (
63
+ * <AcheryProvider defaultTheme="light" defaultAccent="terracotta">
64
+ * <YourApp />
65
+ * </AcheryProvider>
66
+ * )
67
+ * }
68
+ * ```
69
+ */
70
+ declare function AcheryProvider({ children, defaultTheme, defaultAccent, className, style, }: AcheryProviderProps): react_jsx_runtime.JSX.Element;
71
+ /**
72
+ * Hook that returns the current theme state and setters from the nearest
73
+ * {@link AcheryProvider}.
74
+ *
75
+ * @throws If called outside an `<AcheryProvider>`.
76
+ *
77
+ * @example
78
+ * ```tsx
79
+ * function ThemeToggle() {
80
+ * const { theme, toggleTheme, accent, setAccent } = useTheme()
81
+ * return (
82
+ * <Button
83
+ * glyph={theme === 'dark' ? 'sun' : 'moon'}
84
+ * onClick={toggleTheme}
85
+ * aria-label="Toggle theme"
86
+ * />
87
+ * )
88
+ * }
89
+ * ```
90
+ */
91
+ declare function useTheme(): ThemeContextValue;
92
+
93
+ declare const vars: {
94
+ color: {
95
+ bg: `var(--${string})`;
96
+ bg2: `var(--${string})`;
97
+ bgSunken: `var(--${string})`;
98
+ surface: `var(--${string})`;
99
+ surface2: `var(--${string})`;
100
+ fg: `var(--${string})`;
101
+ fg2: `var(--${string})`;
102
+ fg3: `var(--${string})`;
103
+ fgMute: `var(--${string})`;
104
+ border: `var(--${string})`;
105
+ border2: `var(--${string})`;
106
+ borderMute: `var(--${string})`;
107
+ rule: `var(--${string})`;
108
+ accent: `var(--${string})`;
109
+ accentFg: `var(--${string})`;
110
+ accent2: `var(--${string})`;
111
+ accent3: `var(--${string})`;
112
+ success: `var(--${string})`;
113
+ warn: `var(--${string})`;
114
+ danger: `var(--${string})`;
115
+ info: `var(--${string})`;
116
+ selectionBg: `var(--${string})`;
117
+ selectionFg: `var(--${string})`;
118
+ };
119
+ font: {
120
+ display: `var(--${string})`;
121
+ body: `var(--${string})`;
122
+ mono: `var(--${string})`;
123
+ };
124
+ space: {
125
+ sp1: `var(--${string})`;
126
+ sp2: `var(--${string})`;
127
+ sp3: `var(--${string})`;
128
+ sp4: `var(--${string})`;
129
+ sp5: `var(--${string})`;
130
+ sp6: `var(--${string})`;
131
+ sp7: `var(--${string})`;
132
+ sp8: `var(--${string})`;
133
+ sp9: `var(--${string})`;
134
+ sp10: `var(--${string})`;
135
+ sp11: `var(--${string})`;
136
+ sp12: `var(--${string})`;
137
+ };
138
+ radius: {
139
+ none: `var(--${string})`;
140
+ hairline: `var(--${string})`;
141
+ sm: `var(--${string})`;
142
+ pill: `var(--${string})`;
143
+ };
144
+ shadow: {
145
+ stamp: `var(--${string})`;
146
+ stampLg: `var(--${string})`;
147
+ press: `var(--${string})`;
148
+ soft: `var(--${string})`;
149
+ };
150
+ duration: {
151
+ fast: `var(--${string})`;
152
+ base: `var(--${string})`;
153
+ slow: `var(--${string})`;
154
+ };
155
+ ease: {
156
+ out: `var(--${string})`;
157
+ snap: `var(--${string})`;
158
+ };
159
+ };
160
+
161
+ /** Standard three-stop size scale shared across components. */
162
+ type ComponentSize = 'sm' | 'md' | 'lg';
163
+ /**
164
+ * Visual variant for the {@link Button} component.
165
+ * - `primary` — high-emphasis, ink fill with stamp shadow
166
+ * - `secondary` — medium-emphasis, surface fill
167
+ * - `accent` — accent-colour fill; one per view
168
+ * - `ghost` — minimal border-only; for toolbar/icon buttons
169
+ * - `danger` — destructive actions
170
+ */
171
+ type ButtonVariant = 'primary' | 'secondary' | 'accent' | 'ghost' | 'danger';
172
+ /**
173
+ * Semantic colour tone for the {@link Badge} component.
174
+ * - `saved` — green; completed or published
175
+ * - `drafting` — amber; in-progress or pending
176
+ * - `stopped` — red; error, blocked, or inactive
177
+ * - `archived` — muted; historical or soft-deleted
178
+ * - `neutral` — no semantic weight; informational
179
+ */
180
+ type BadgeTone = 'saved' | 'drafting' | 'stopped' | 'archived' | 'neutral';
181
+ /**
182
+ * Union of all valid glyph names in the Achery icon set.
183
+ * Pass to the `name` prop of {@link Glyph}, or to any component that accepts
184
+ * a `glyph?: GlyphName` prop (Button, Card, Sidebar items, etc.).
185
+ */
186
+ type GlyphName = 'arrow-right' | 'arrow-up' | 'asterism' | 'book' | 'circle' | 'compass' | 'cross' | 'eye' | 'feather' | 'fern' | 'flask' | 'flourish' | 'hand' | 'hex' | 'key' | 'leaf' | 'mark' | 'mercury' | 'minus' | 'moon' | 'plus' | 'salt' | 'scroll' | 'sigil' | 'sprig' | 'square' | 'star' | 'sulfur' | 'sun' | 'tick' | 'triangle' | 'triangle-down' | 'wordmark';
187
+ /**
188
+ * Sort direction for the {@link Table} component.
189
+ * `null` means no active sort (natural/insertion order).
190
+ */
191
+ type SortDirection = 'asc' | 'desc' | null;
192
+
193
+ /** Props for the {@link Glyph} component. */
194
+ interface GlyphProps extends SVGProps<SVGSVGElement> {
195
+ /**
196
+ * Name of the glyph to render. One of the 33 icons in the Achery glyph set.
197
+ *
198
+ * **Geometric:** `circle`, `square`, `triangle`, `triangle-down`, `hex`, `minus`, `plus`, `cross`, `tick`, `arrow-right`, `arrow-up`
199
+ *
200
+ * **Botanical / alchemical:** `fern`, `sprig`, `leaf`, `feather`, `flourish`, `asterism`, `sigil`, `salt`, `sulfur`, `mercury`
201
+ *
202
+ * **Editorial / tools:** `book`, `scroll`, `feather`, `key`, `flask`, `compass`, `eye`, `hand`, `star`, `moon`, `sun`
203
+ *
204
+ * **Brand:** `mark`, `wordmark`
205
+ */
206
+ name: GlyphName;
207
+ /**
208
+ * Size in pixels — applied to both `width` and `height`.
209
+ * @default 16
210
+ */
211
+ size?: number;
212
+ /**
213
+ * Accessible label for the glyph. When provided, sets `aria-label` and
214
+ * removes `aria-hidden`. Omit for decorative use.
215
+ */
216
+ title?: string;
217
+ className?: string;
218
+ }
219
+ /**
220
+ * Renders a single SVG glyph from the Achery icon set. Each glyph is inlined
221
+ * as a React component — tree-shakeable, inherits `currentColor`.
222
+ *
223
+ * For decorative use, omit `title` (the glyph is `aria-hidden` by default).
224
+ * For semantic use (icon-only button labels etc.), provide a `title`.
225
+ *
226
+ * @example
227
+ * ```tsx
228
+ * // Decorative
229
+ * <Glyph name="fern" size={24} />
230
+ *
231
+ * // Semantic (in an icon-only button)
232
+ * <button aria-label="Close"><Glyph name="cross" size={16} /></button>
233
+ * ```
234
+ */
235
+ declare function Glyph({ name, size, title, className, style, ...props }: GlyphProps): react_jsx_runtime.JSX.Element;
236
+
237
+ type PolymorphicProps<E extends ElementType> = HTMLAttributes<HTMLElement> & {
238
+ as?: E;
239
+ children?: ReactNode;
240
+ className?: string;
241
+ };
242
+ /**
243
+ * Large decorative text set in the display (serif) typeface. Polymorphic — renders
244
+ * as any HTML element via the `as` prop; defaults to `<p>`.
245
+ *
246
+ * Use for hero headings, pull quotes, or editorial callouts where the display
247
+ * face should carry the visual weight.
248
+ *
249
+ * @example
250
+ * ```tsx
251
+ * <Display as="h1">The Alchemist's Field Guide</Display>
252
+ * <Display as="blockquote">Patience is a precipitate.</Display>
253
+ * ```
254
+ */
255
+ declare function Display({ as: Tag, className, children, ...props }: PolymorphicProps<ElementType>): react_jsx_runtime.JSX.Element;
256
+ /** Props for the {@link Heading} component. */
257
+ interface HeadingProps extends HTMLAttributes<HTMLHeadingElement> {
258
+ /**
259
+ * Heading level — renders the corresponding `<h1>`–`<h5>` element and
260
+ * applies the matching size/weight from the type scale.
261
+ * @default 1
262
+ */
263
+ level?: 1 | 2 | 3 | 4 | 5;
264
+ children?: ReactNode;
265
+ className?: string;
266
+ }
267
+ /**
268
+ * Section heading set in the body (sans-serif) typeface at appropriate scale.
269
+ * Uses the correct semantic HTML element (`h1`–`h5`) based on `level`.
270
+ *
271
+ * @example
272
+ * ```tsx
273
+ * <Heading level={1}>Recipes</Heading>
274
+ * <Heading level={3}>Mordants & Fixatives</Heading>
275
+ * ```
276
+ */
277
+ declare function Heading({ level, className, children, ...props }: HeadingProps): react_jsx_runtime.JSX.Element;
278
+ /** Props for the {@link Body} component. */
279
+ interface BodyProps extends HTMLAttributes<HTMLParagraphElement> {
280
+ /**
281
+ * Size variant.
282
+ * - `base` — 14px, standard reading text (default)
283
+ * - `lead` — 16px, introductory or summary paragraphs
284
+ * - `small` — 12px, captions, helper text, footnotes
285
+ * @default 'base'
286
+ */
287
+ variant?: 'base' | 'lead' | 'small';
288
+ children?: ReactNode;
289
+ className?: string;
290
+ }
291
+ /**
292
+ * Body text rendered as a `<p>` element. Three size variants map to the
293
+ * base reading scale.
294
+ *
295
+ * @example
296
+ * ```tsx
297
+ * <Body>Combine oak gall with iron sulphate in a ratio of 2:1.</Body>
298
+ * <Body variant="lead">An introduction to the chapter.</Body>
299
+ * <Body variant="small">Last updated 3 days ago.</Body>
300
+ * ```
301
+ */
302
+ declare function Body({ variant, className, children, ...props }: BodyProps): react_jsx_runtime.JSX.Element;
303
+ /** Props for the {@link Mono} component. */
304
+ interface MonoProps extends HTMLAttributes<HTMLElement> {
305
+ /**
306
+ * Size variant.
307
+ * - `base` — 13px monospace (default)
308
+ * - `small` — 11px monospace; use for inline code in dense layouts
309
+ * @default 'base'
310
+ */
311
+ variant?: 'base' | 'small';
312
+ /** Element to render as. @default 'span' */
313
+ as?: ElementType;
314
+ children?: ReactNode;
315
+ className?: string;
316
+ }
317
+ /**
318
+ * Monospace text for code, measurements, IDs, and numeric values. Polymorphic
319
+ * via `as`; defaults to `<span>` for inline use.
320
+ *
321
+ * @example
322
+ * ```tsx
323
+ * <Mono>Fe₂(SO₄)₃</Mono>
324
+ * <Mono as="code" variant="small">recipe-042</Mono>
325
+ * ```
326
+ */
327
+ declare function Mono({ variant, as: Tag, className, children, ...props }: MonoProps): react_jsx_runtime.JSX.Element;
328
+
329
+ /** Props for the {@link Eyebrow} component. */
330
+ interface EyebrowProps extends HTMLAttributes<HTMLSpanElement> {
331
+ children: ReactNode;
332
+ /**
333
+ * Numeric count shown as a small badge after the label — useful for
334
+ * section totals, unread counts, etc.
335
+ */
336
+ count?: number;
337
+ /** Arbitrary content appended after the label and optional count. */
338
+ after?: ReactNode;
339
+ className?: string;
340
+ }
341
+ /**
342
+ * Compact uppercase label used to categorise sections, label groups of
343
+ * controls, or introduce content blocks. Rendered in small-caps with
344
+ * tracked letter-spacing.
345
+ *
346
+ * Pair with a numeric `count` to show totals inline, or use `after` for
347
+ * actions or secondary content alongside the label.
348
+ *
349
+ * @example
350
+ * ```tsx
351
+ * <Eyebrow>Ingredients</Eyebrow>
352
+ * <Eyebrow count={12}>Recipes</Eyebrow>
353
+ * <Eyebrow after={<Button size="sm" variant="ghost">Add</Button>}>Steps</Eyebrow>
354
+ * ```
355
+ */
356
+ declare function Eyebrow({ children, count, after, className, ...props }: EyebrowProps): react_jsx_runtime.JSX.Element;
357
+
358
+ declare const badge: _vanilla_extract_recipes.RuntimeFn<{
359
+ tone: {
360
+ saved: {};
361
+ drafting: {};
362
+ stopped: {};
363
+ archived: {};
364
+ neutral: {};
365
+ success: {};
366
+ warn: {};
367
+ danger: {};
368
+ info: {};
369
+ };
370
+ variant: {
371
+ outline: {
372
+ background: "transparent";
373
+ };
374
+ solid: {
375
+ color: `var(--${string})`;
376
+ };
377
+ pill: {
378
+ borderRadius: `var(--${string})`;
379
+ background: "transparent";
380
+ };
381
+ };
382
+ }>;
383
+
384
+ type BadgeVariants = NonNullable<RecipeVariants<typeof badge>>;
385
+ /** Props for the {@link Badge} component. */
386
+ interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
387
+ /**
388
+ * Semantic colour tone.
389
+ * - `saved` — green; completed or published state
390
+ * - `drafting` — amber; in-progress or pending state
391
+ * - `stopped` — red; error, blocked, or inactive state
392
+ * - `archived` — muted; historical or soft-deleted state
393
+ * - `neutral` — default; informational, no semantic weight
394
+ * @default 'neutral'
395
+ */
396
+ tone?: BadgeVariants['tone'];
397
+ /**
398
+ * Visual style.
399
+ * - `outline` — border + tinted background (default); lighter weight
400
+ * - `solid` — filled background; higher visual weight
401
+ * @default 'outline'
402
+ */
403
+ variant?: BadgeVariants['variant'];
404
+ /** Show a small filled dot before the label — useful for status indicators. */
405
+ dot?: boolean;
406
+ children: ReactNode;
407
+ className?: string;
408
+ }
409
+ /**
410
+ * Compact inline label for status, category, or metadata. Combines a semantic
411
+ * `tone` (colour) with an `outline` or `solid` style.
412
+ *
413
+ * @example
414
+ * ```tsx
415
+ * <Badge tone="saved">Published</Badge>
416
+ * <Badge tone="drafting" dot>In progress</Badge>
417
+ * <Badge tone="stopped" variant="solid">Blocked</Badge>
418
+ * ```
419
+ */
420
+ declare function Badge({ tone, variant, dot, children, className, ...props }: BadgeProps): react_jsx_runtime.JSX.Element;
421
+
422
+ declare const button: _vanilla_extract_recipes.RuntimeFn<{
423
+ variant: {
424
+ primary: {
425
+ background: `var(--${string})`;
426
+ color: `var(--${string})`;
427
+ boxShadow: `var(--${string})`;
428
+ selectors: {
429
+ '&:hover:not(:disabled)': {
430
+ transform: "translate(-1px, -1px)";
431
+ boxShadow: `3px 3px 0 0 var(--${string})`;
432
+ };
433
+ '&:active:not(:disabled)': {
434
+ transform: "translate(2px, 2px)";
435
+ boxShadow: "none";
436
+ };
437
+ };
438
+ };
439
+ secondary: {
440
+ background: `var(--${string})`;
441
+ color: `var(--${string})`;
442
+ selectors: {
443
+ '&:hover:not(:disabled)': {
444
+ background: `var(--${string})`;
445
+ borderWidth: "2px";
446
+ };
447
+ '&:active:not(:disabled)': {
448
+ boxShadow: `var(--${string})`;
449
+ };
450
+ };
451
+ };
452
+ accent: {
453
+ background: `var(--${string})`;
454
+ color: `var(--${string})`;
455
+ borderColor: `var(--${string})`;
456
+ boxShadow: `var(--${string})`;
457
+ selectors: {
458
+ '&:hover:not(:disabled)': {
459
+ transform: "translate(-1px, -1px)";
460
+ boxShadow: `3px 3px 0 0 var(--${string})`;
461
+ };
462
+ '&:active:not(:disabled)': {
463
+ transform: "translate(2px, 2px)";
464
+ boxShadow: "none";
465
+ };
466
+ };
467
+ };
468
+ ghost: {
469
+ background: "transparent";
470
+ color: `var(--${string})`;
471
+ borderColor: `var(--${string})`;
472
+ selectors: {
473
+ '&:hover:not(:disabled)': {
474
+ color: `var(--${string})`;
475
+ borderColor: `var(--${string})`;
476
+ };
477
+ '&:active:not(:disabled)': {
478
+ boxShadow: `var(--${string})`;
479
+ };
480
+ };
481
+ };
482
+ danger: {
483
+ background: `var(--${string})`;
484
+ color: `var(--${string})`;
485
+ borderColor: `var(--${string})`;
486
+ boxShadow: `var(--${string})`;
487
+ selectors: {
488
+ '&:hover:not(:disabled)': {
489
+ transform: "translate(-1px, -1px)";
490
+ boxShadow: `3px 3px 0 0 var(--${string})`;
491
+ };
492
+ '&:active:not(:disabled)': {
493
+ transform: "translate(2px, 2px)";
494
+ boxShadow: "none";
495
+ };
496
+ };
497
+ };
498
+ };
499
+ size: {
500
+ sm: {
501
+ fontSize: "11px";
502
+ padding: "5px 10px";
503
+ gap: `var(--${string})`;
504
+ };
505
+ md: {
506
+ fontSize: "12px";
507
+ padding: "7px 12px";
508
+ };
509
+ };
510
+ }>;
511
+
512
+ type ButtonVariants = NonNullable<RecipeVariants<typeof button>>;
513
+ /** Props for the {@link Button} component. */
514
+ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
515
+ /**
516
+ * Visual style of the button.
517
+ * - `primary` — high-emphasis, filled in foreground ink with stamp shadow
518
+ * - `secondary` — medium-emphasis, surface fill (default)
519
+ * - `accent` — accent-colour fill; use for the single primary CTA per view
520
+ * - `ghost` — minimal, border only; use for toolbar/icon actions
521
+ * - `danger` — destructive actions; uses danger semantic colour
522
+ * @default 'secondary'
523
+ */
524
+ variant?: ButtonVariants['variant'];
525
+ /**
526
+ * Size of the button.
527
+ * - `sm` — 11px text, compact padding; use in toolbars, table rows
528
+ * - `md` — 12px text, standard padding (default)
529
+ * - `lg` — 13px text, generous padding; use for primary CTAs
530
+ * @default 'md'
531
+ */
532
+ size?: ButtonVariants['size'];
533
+ /** Name of a {@link Glyph} to render alongside the label. */
534
+ glyph?: GlyphName;
535
+ /**
536
+ * Which side the glyph appears on.
537
+ * @default 'start'
538
+ */
539
+ glyphPosition?: 'start' | 'end';
540
+ /**
541
+ * Keyboard shortcut hint rendered as a subtle badge at the button's trailing edge.
542
+ * Display only — does not wire up any keyboard listener.
543
+ * @example kbd="⌘K"
544
+ */
545
+ kbd?: string;
546
+ children?: ReactNode;
547
+ className?: string;
548
+ }
549
+ /**
550
+ * The primary interactive element. Supports five variants, three sizes, optional
551
+ * glyphs, and keyboard shortcut hints.
552
+ *
553
+ * @example
554
+ * ```tsx
555
+ * <Button variant="accent" glyph="plus">New entry</Button>
556
+ * <Button variant="ghost" size="sm" glyph="cross" aria-label="Close" />
557
+ * <Button variant="primary" kbd="⌘S">Save</Button>
558
+ * ```
559
+ */
560
+ declare function Button({ variant, size, glyph, glyphPosition, kbd, children, className, ...props }: ButtonProps): react_jsx_runtime.JSX.Element;
561
+
562
+ /** Props for the {@link Toggle} component. */
563
+ interface ToggleProps {
564
+ /**
565
+ * Controlled pressed state. When provided, the component becomes controlled
566
+ * and `onPressedChange` must be handled externally.
567
+ */
568
+ pressed?: boolean;
569
+ /** Initial pressed state for uncontrolled usage. */
570
+ defaultPressed?: boolean;
571
+ /** Called when the pressed state changes. */
572
+ onPressedChange?: (pressed: boolean) => void;
573
+ disabled?: boolean;
574
+ /** Optional text label rendered beside the toggle track. */
575
+ label?: ReactNode;
576
+ className?: string;
577
+ /** Accessible label when no visible text label is present. */
578
+ 'aria-label'?: string;
579
+ }
580
+ /**
581
+ * Binary on/off switch built on Radix Toggle. Supports both controlled and
582
+ * uncontrolled usage via `pressed`/`defaultPressed`.
583
+ *
584
+ * Always provide either a visible `label` or an `aria-label` so the control
585
+ * is accessible to screen readers.
586
+ *
587
+ * @example
588
+ * ```tsx
589
+ * // Uncontrolled
590
+ * <Toggle defaultPressed label="Dark mode" />
591
+ *
592
+ * // Controlled
593
+ * <Toggle pressed={isDark} onPressedChange={setIsDark} label="Dark mode" />
594
+ *
595
+ * // Icon-only (needs aria-label)
596
+ * <Toggle aria-label="Enable notifications" />
597
+ * ```
598
+ */
599
+ declare function Toggle({ pressed, defaultPressed, onPressedChange, disabled, label, className, 'aria-label': ariaLabel, }: ToggleProps): react_jsx_runtime.JSX.Element;
600
+
601
+ /** Props for the {@link Marginalia} component. */
602
+ interface MarginaliaProps {
603
+ /**
604
+ * Glyph to display. Any of the 33 named glyphs from the Achery icon set.
605
+ * @default 'fern'
606
+ */
607
+ glyph?: GlyphName;
608
+ /**
609
+ * Size in pixels (applied to both width and height).
610
+ * @default 120
611
+ */
612
+ size?: number;
613
+ /**
614
+ * Opacity of the glyph, 0–1. Keep low (0.25–0.5) so the decorative
615
+ * element doesn't compete with content.
616
+ * @default 0.4
617
+ */
618
+ opacity?: number;
619
+ /** When true, renders the glyph in the current accent colour. */
620
+ accent?: boolean;
621
+ className?: string;
622
+ style?: React.CSSProperties;
623
+ }
624
+ /**
625
+ * Decorative botanical/alchemical glyph for use as corner or edge ornamentation.
626
+ * Renders `aria-hidden` — purely presentational.
627
+ *
628
+ * Position it absolutely within a relatively-positioned container (e.g. a {@link Card})
629
+ * to achieve the characteristic manuscript-margin effect.
630
+ *
631
+ * @example
632
+ * ```tsx
633
+ * // Inside a Card — Card handles positioning via its `marginalia` prop shorthand
634
+ * <Card marginalia="fern">…</Card>
635
+ *
636
+ * // Manual placement
637
+ * <div style={{ position: 'relative' }}>
638
+ * <Marginalia glyph="asterism" size={160} opacity={0.25} />
639
+ * <p>Content</p>
640
+ * </div>
641
+ * ```
642
+ */
643
+ declare function Marginalia({ glyph, size, opacity, className, style, }: MarginaliaProps): react_jsx_runtime.JSX.Element;
644
+
645
+ /** Props for the {@link Field} wrapper component. */
646
+ interface FieldProps {
647
+ /** Visible label rendered above the input. */
648
+ label?: string;
649
+ /** Helper text shown below the input when there is no error. */
650
+ hint?: string;
651
+ /**
652
+ * Error message. When present, replaces the hint and applies error styling
653
+ * to the field. Pass to {@link Input}, {@link Textarea}, or {@link Select}
654
+ * via their `error` boolean prop to also colour the input itself.
655
+ */
656
+ error?: string;
657
+ children: ReactNode;
658
+ className?: string;
659
+ }
660
+ /**
661
+ * Layout wrapper that pairs a label, input control, and hint/error text.
662
+ * Compose with {@link Input}, {@link Textarea}, or {@link Select} as `children`.
663
+ *
664
+ * @example
665
+ * ```tsx
666
+ * <Field label="Name" hint="As it appears on the specimen jar">
667
+ * <Input placeholder="Iron-gall ink" />
668
+ * </Field>
669
+ *
670
+ * <Field label="Notes" error="Required">
671
+ * <Textarea error rows={4} />
672
+ * </Field>
673
+ * ```
674
+ */
675
+ declare function Field({ label, hint, error, children, className }: FieldProps): react_jsx_runtime.JSX.Element;
676
+ /** Props for the {@link Input} component. */
677
+ interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
678
+ /** When true, applies error border colouring. Pair with {@link Field} `error` prop. */
679
+ error?: boolean;
680
+ className?: string;
681
+ }
682
+ /**
683
+ * Single-line text input. Forwards all native `<input>` attributes.
684
+ * Wrap in {@link Field} to add a label and hint/error text.
685
+ *
686
+ * @example
687
+ * ```tsx
688
+ * <Input placeholder="Search recipes…" type="search" />
689
+ * <Input error value={value} onChange={handleChange} />
690
+ * ```
691
+ */
692
+ declare function Input({ error, className, ...props }: InputProps): react_jsx_runtime.JSX.Element;
693
+ /** Props for the {@link Textarea} component. */
694
+ interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
695
+ /** When true, applies error border colouring. Pair with {@link Field} `error` prop. */
696
+ error?: boolean;
697
+ className?: string;
698
+ }
699
+ /**
700
+ * Multi-line text input. Forwards all native `<textarea>` attributes.
701
+ * Wrap in {@link Field} to add a label and hint/error text.
702
+ *
703
+ * @example
704
+ * ```tsx
705
+ * <Field label="Process notes">
706
+ * <Textarea rows={5} placeholder="Describe proportions and method…" />
707
+ * </Field>
708
+ * ```
709
+ */
710
+ declare function Textarea({ error, className, ...props }: TextareaProps): react_jsx_runtime.JSX.Element;
711
+ /** Props for the {@link Select} component. */
712
+ interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
713
+ /** When true, applies error border colouring. Pair with {@link Field} `error` prop. */
714
+ error?: boolean;
715
+ children: ReactNode;
716
+ className?: string;
717
+ }
718
+ /**
719
+ * Native `<select>` dropdown with Achery styling. For complex requirements
720
+ * (searchable, multi-select, grouped options) the Radix Select primitive is
721
+ * available via `TabsPrimitive` export — but for most form use-cases the
722
+ * native select is sufficient and more accessible.
723
+ *
724
+ * @example
725
+ * ```tsx
726
+ * <Field label="Chapter">
727
+ * <Select>
728
+ * <option value="">Select a chapter…</option>
729
+ * <option value="ink">Ink</option>
730
+ * <option value="pigment">Pigment</option>
731
+ * </Select>
732
+ * </Field>
733
+ * ```
734
+ */
735
+ declare function Select({ error, children, className, ...props }: SelectProps): react_jsx_runtime.JSX.Element;
736
+ /** Props for the {@link SearchInput} component. */
737
+ interface SearchInputProps extends InputHTMLAttributes<HTMLInputElement> {
738
+ className?: string;
739
+ }
740
+ /**
741
+ * Search input with a built-in compass glyph icon and `type="search"`.
742
+ * Use in headers, toolbars, or filter panels.
743
+ *
744
+ * @example
745
+ * ```tsx
746
+ * <SearchInput placeholder="Search recipes…" value={q} onChange={e => setQ(e.target.value)} />
747
+ * ```
748
+ */
749
+ declare function SearchInput({ className, ...props }: SearchInputProps): react_jsx_runtime.JSX.Element;
750
+
751
+ declare const card: _vanilla_extract_recipes.RuntimeFn<{
752
+ variant: {
753
+ flat: {};
754
+ stamp: {
755
+ boxShadow: `var(--${string})`;
756
+ };
757
+ stampLg: {
758
+ boxShadow: `var(--${string})`;
759
+ };
760
+ };
761
+ padding: {
762
+ none: {
763
+ padding: number;
764
+ };
765
+ sm: {
766
+ padding: `var(--${string})`;
767
+ };
768
+ md: {
769
+ padding: `var(--${string})`;
770
+ };
771
+ lg: {
772
+ padding: `var(--${string})`;
773
+ };
774
+ };
775
+ }>;
776
+
777
+ type CardVariants = NonNullable<RecipeVariants<typeof card>>;
778
+ /** Props for the {@link Card} component. */
779
+ interface CardProps extends HTMLAttributes<HTMLDivElement> {
780
+ /**
781
+ * Visual style.
782
+ * - `flat` — surface background, hairline border (default)
783
+ * - `stamp` — adds the characteristic hard-offset stamp shadow
784
+ * @default 'flat'
785
+ */
786
+ variant?: CardVariants['variant'];
787
+ /**
788
+ * Internal padding.
789
+ * - `none` — no padding; manage internally
790
+ * - `sm` — compact (12px)
791
+ * - `md` — standard (24px, default)
792
+ * - `lg` — generous (32px)
793
+ * @default 'md'
794
+ */
795
+ padding?: CardVariants['padding'];
796
+ /**
797
+ * Optional header slot — rendered above the body with a border-bottom rule.
798
+ * Use for {@link Eyebrow} labels, titles, or action rows.
799
+ */
800
+ header?: ReactNode;
801
+ /**
802
+ * Name of a {@link Glyph} to render as a decorative {@link Marginalia}
803
+ * element in the card's bottom-right corner.
804
+ */
805
+ marginalia?: GlyphName;
806
+ /**
807
+ * Size of the marginalia glyph in pixels.
808
+ * @default 80
809
+ */
810
+ marginaliaSize?: number;
811
+ children?: ReactNode;
812
+ className?: string;
813
+ }
814
+ /**
815
+ * Container surface for grouping related content. Two variants — `flat` for
816
+ * standard surfaces and `stamp` for the elevated hard-shadow look.
817
+ *
818
+ * Use the `header` slot for section labels and the `marginalia` prop for
819
+ * decorative botanical ornamentation in the corner.
820
+ *
821
+ * @example
822
+ * ```tsx
823
+ * <Card variant="stamp" header={<Eyebrow>Field notes</Eyebrow>} marginalia="fern">
824
+ * <Body>Mix oak gall and vitriol…</Body>
825
+ * </Card>
826
+ * ```
827
+ */
828
+ declare function Card({ variant, padding, header, marginalia, marginaliaSize, children, className, ...props }: CardProps): react_jsx_runtime.JSX.Element;
829
+
830
+ /** A single tab definition passed to {@link Tabs}. */
831
+ interface TabItem {
832
+ /** Unique string identifier — used as the controlled/uncontrolled value. */
833
+ value: string;
834
+ /** Content rendered in the tab trigger button. Usually a short text label. */
835
+ label: ReactNode;
836
+ /** Content rendered in the tab panel when this tab is active. */
837
+ content: ReactNode;
838
+ /** When true, the tab is visible but not selectable. */
839
+ disabled?: boolean;
840
+ }
841
+ /** Props for the {@link Tabs} component. */
842
+ interface TabsProps {
843
+ /** Ordered list of tab definitions. */
844
+ items: TabItem[];
845
+ /**
846
+ * Controlled active tab value. When provided alongside `onValueChange`,
847
+ * the component is fully controlled.
848
+ */
849
+ value?: string;
850
+ /**
851
+ * Initial active tab value for uncontrolled usage. Defaults to the
852
+ * `value` of the first item if not specified.
853
+ */
854
+ defaultValue?: string;
855
+ /** Called when the active tab changes. */
856
+ onValueChange?: (value: string) => void;
857
+ className?: string;
858
+ }
859
+ /**
860
+ * Accessible tab navigation built on Radix Tabs. Handles roving `tabindex`,
861
+ * arrow-key navigation, and ARIA roles automatically.
862
+ *
863
+ * Supports both controlled (`value` + `onValueChange`) and uncontrolled
864
+ * (`defaultValue`) usage. When neither is provided, the first tab is active
865
+ * by default.
866
+ *
867
+ * @example
868
+ * ```tsx
869
+ * <Tabs
870
+ * items={[
871
+ * { value: 'details', label: 'Details', content: <DetailsPanel /> },
872
+ * { value: 'history', label: 'History', content: <HistoryPanel /> },
873
+ * { value: 'notes', label: 'Notes', content: <NotesPanel />, disabled: true },
874
+ * ]}
875
+ * />
876
+ * ```
877
+ */
878
+ declare function Tabs({ items, value, defaultValue, onValueChange, className }: TabsProps): react_jsx_runtime.JSX.Element;
879
+
880
+ /** Props for the {@link Tooltip} component. */
881
+ interface TooltipProps {
882
+ /** Content rendered inside the tooltip bubble. */
883
+ content: ReactNode;
884
+ /**
885
+ * The element that triggers the tooltip on hover/focus. Must be a single
886
+ * focusable element — wrapped with Radix `asChild`.
887
+ */
888
+ children: ReactNode;
889
+ /**
890
+ * Preferred side of the trigger to display the tooltip.
891
+ * Flips automatically when there is insufficient space.
892
+ * @default 'top'
893
+ */
894
+ side?: 'top' | 'right' | 'bottom' | 'left';
895
+ /**
896
+ * Milliseconds before the tooltip opens after the pointer enters.
897
+ * Set to `0` for instant display.
898
+ * @default 400
899
+ */
900
+ delayDuration?: number;
901
+ /**
902
+ * Controlled open state. When provided alongside `onOpenChange`, the
903
+ * tooltip is fully controlled.
904
+ */
905
+ open?: boolean;
906
+ /** Initial open state for uncontrolled usage. */
907
+ defaultOpen?: boolean;
908
+ /** Called when the open state changes. */
909
+ onOpenChange?: (open: boolean) => void;
910
+ }
911
+ /**
912
+ * Contextual label that appears on hover or focus, built on Radix Tooltip.
913
+ * Renders into a portal so it is never clipped by overflow:hidden ancestors.
914
+ *
915
+ * Theme CSS vars are inherited via `data-achery-root` on `<html>`, ensuring
916
+ * the tooltip matches the active theme even though it is portaled.
917
+ *
918
+ * Always use for supplementary information — never to convey information
919
+ * required to complete a task (that belongs in visible UI).
920
+ *
921
+ * @example
922
+ * ```tsx
923
+ * <Tooltip content="Steeping time: three minutes" side="top">
924
+ * <Button variant="ghost" glyph="info" aria-label="More info" />
925
+ * </Tooltip>
926
+ *
927
+ * <Tooltip content="⌘K" delayDuration={0}>
928
+ * <Button variant="ghost">Search</Button>
929
+ * </Tooltip>
930
+ * ```
931
+ */
932
+ declare function Tooltip({ content: tooltipContent, children, side, delayDuration, open, defaultOpen, onOpenChange, }: TooltipProps): react_jsx_runtime.JSX.Element;
933
+
934
+ /** A single navigation item within a {@link NavGroupDef}. */
935
+ interface NavItemDef {
936
+ /** Unique identifier used for active state tracking and `onItemClick` callbacks. */
937
+ id: string;
938
+ /** Visible label. */
939
+ label: string;
940
+ /** Optional {@link Glyph} shown to the left of the label. */
941
+ glyph?: GlyphName;
942
+ /** Numeric badge shown at the trailing edge — useful for unread counts. */
943
+ count?: number;
944
+ /**
945
+ * When provided, the item renders as an `<a>` tag instead of a `<button>`.
946
+ * Use for top-level routes; omit for in-page actions.
947
+ */
948
+ href?: string;
949
+ }
950
+ /** A labelled group of {@link NavItemDef} items within a {@link Sidebar}. */
951
+ interface NavGroupDef {
952
+ /** Optional group heading rendered in eyebrow style above the items. */
953
+ label?: string;
954
+ items: NavItemDef[];
955
+ }
956
+ /** Props for the {@link Sidebar} component. */
957
+ interface SidebarProps {
958
+ /** Ordered list of navigation groups. Groups without a label are unlabelled. */
959
+ groups: NavGroupDef[];
960
+ /** `id` of the currently active navigation item — highlights it with accent colouring. */
961
+ activeId?: string;
962
+ /**
963
+ * Called when a navigation item (button variant) is clicked.
964
+ * Receives the `id` of the clicked item.
965
+ */
966
+ onItemClick?: (id: string) => void;
967
+ /**
968
+ * Content rendered at the bottom of the sidebar — typically user account
969
+ * info, a settings link, or version text.
970
+ */
971
+ footer?: ReactNode;
972
+ className?: string;
973
+ }
974
+ /**
975
+ * Vertical navigation sidebar. Renders one or more labelled groups of nav items,
976
+ * each of which can be a link (`href`) or a button (`onItemClick`).
977
+ *
978
+ * Active state is driven by `activeId` matching an item's `id`. The active item
979
+ * receives a filled accent-coloured background.
980
+ *
981
+ * @example
982
+ * ```tsx
983
+ * <Sidebar
984
+ * activeId={currentPage}
985
+ * onItemClick={navigate}
986
+ * groups={[
987
+ * {
988
+ * label: 'Workshop',
989
+ * items: [
990
+ * { id: 'recipes', label: 'Recipes', glyph: 'book', count: 12 },
991
+ * { id: 'ingredients', label: 'Ingredients', glyph: 'flask' },
992
+ * ],
993
+ * },
994
+ * {
995
+ * items: [{ id: 'settings', label: 'Settings', glyph: 'key' }],
996
+ * },
997
+ * ]}
998
+ * footer={<Body variant="small">v0.1.0</Body>}
999
+ * />
1000
+ * ```
1001
+ */
1002
+ declare function Sidebar({ groups, activeId, onItemClick, footer, className }: SidebarProps): react_jsx_runtime.JSX.Element;
1003
+
1004
+ /** Props for the {@link AppBar} component. */
1005
+ interface AppBarProps {
1006
+ /**
1007
+ * Primary brand name shown beside the hex mark.
1008
+ * @default 'Achery'
1009
+ */
1010
+ brandName?: string;
1011
+ /** Secondary brand descriptor shown after a divider — e.g. a workspace or project name. */
1012
+ brandSub?: string;
1013
+ /**
1014
+ * Show the central search field.
1015
+ * @default true
1016
+ */
1017
+ showSearch?: boolean;
1018
+ /** Placeholder text for the search field. @default 'Search…' */
1019
+ searchPlaceholder?: string;
1020
+ /** Keyboard shortcut hint displayed inside the search field (display only). */
1021
+ searchKbd?: string;
1022
+ /**
1023
+ * Arbitrary content inserted after the built-in controls (theme toggle, accent
1024
+ * picker, new button) and before the avatar. Use for custom action buttons.
1025
+ */
1026
+ actions?: ReactNode;
1027
+ /** Currently active accent colour — drives the accent picker selection indicator. */
1028
+ accent?: AccentColor;
1029
+ /**
1030
+ * Called when the user selects a new accent colour via the built-in picker.
1031
+ * When omitted, the accent picker is hidden.
1032
+ */
1033
+ onAccentChange?: (accent: AccentColor) => void;
1034
+ /**
1035
+ * Called when the theme toggle button is clicked.
1036
+ * When omitted, the theme toggle is hidden.
1037
+ */
1038
+ onToggleTheme?: () => void;
1039
+ /** Pass `true` when the dark theme is active to show the correct toggle icon. */
1040
+ isDark?: boolean;
1041
+ /** Up to two initials rendered in the avatar circle at the trailing edge. */
1042
+ avatarInitials?: string;
1043
+ /**
1044
+ * Called when the "New" button is clicked.
1045
+ * When omitted, the new button is hidden.
1046
+ */
1047
+ onNewClick?: () => void;
1048
+ className?: string;
1049
+ }
1050
+ /**
1051
+ * Top-of-page application bar. Contains brand identity, search, theme and
1052
+ * accent controls, and a slot for custom actions.
1053
+ *
1054
+ * Each built-in slot is opt-in — only provide the callback/prop to show it:
1055
+ * - `onAccentChange` → renders accent colour picker swatches
1056
+ * - `onToggleTheme` → renders sun/moon theme toggle button
1057
+ * - `onNewClick` → renders accent "New" button
1058
+ * - `avatarInitials` → renders user avatar circle
1059
+ *
1060
+ * @example
1061
+ * ```tsx
1062
+ * const { theme, toggleTheme, accent, setAccent } = useTheme()
1063
+ *
1064
+ * <AppBar
1065
+ * brandName="Achery"
1066
+ * brandSub="Field Guide"
1067
+ * isDark={theme === 'dark'}
1068
+ * onToggleTheme={toggleTheme}
1069
+ * accent={accent}
1070
+ * onAccentChange={setAccent}
1071
+ * onNewClick={() => setModalOpen(true)}
1072
+ * avatarInitials="FW"
1073
+ * />
1074
+ * ```
1075
+ */
1076
+ declare function AppBar({ brandName, brandSub, showSearch, searchPlaceholder, searchKbd, actions, accent, onAccentChange, onToggleTheme, isDark, avatarInitials, onNewClick, className, }: AppBarProps): react_jsx_runtime.JSX.Element;
1077
+
1078
+ /** Column definition for the {@link Table} component. */
1079
+ interface ColumnDef<T> {
1080
+ /** Field key on the row object — used for sorting and as the default cell value. */
1081
+ key: string;
1082
+ /** Column header label. */
1083
+ label: string;
1084
+ /** When true, the column header is clickable and cycles asc → desc → unsorted. */
1085
+ sortable?: boolean;
1086
+ /** Render cell content in monospace — useful for IDs, codes, and measurements. */
1087
+ mono?: boolean;
1088
+ /**
1089
+ * Custom cell renderer. Receives the full row object; return any React node.
1090
+ * When omitted, falls back to `String(row[key])`.
1091
+ */
1092
+ render?: (row: T) => ReactNode;
1093
+ /** CSS width value applied to the column (e.g. `'120px'`, `'20%'`). */
1094
+ width?: string;
1095
+ }
1096
+ /** Props for the {@link Table} component. */
1097
+ interface TableProps<T extends {
1098
+ [K in string]: unknown;
1099
+ }> {
1100
+ /** Column configuration array — order determines display order. */
1101
+ columns: ColumnDef<T>[];
1102
+ /** Array of row data objects. */
1103
+ data: T[];
1104
+ /** Returns a stable unique string key for a row — used as the React key and for selection. */
1105
+ rowKey: (row: T) => string;
1106
+ /**
1107
+ * Set of row keys that are currently selected. Selected rows receive a
1108
+ * left accent-coloured rule.
1109
+ */
1110
+ selectedKeys?: string[];
1111
+ /**
1112
+ * Called when a row is clicked. Receives the row's key and the full row object.
1113
+ * When provided, rows become clickable (pointer cursor).
1114
+ */
1115
+ onRowClick?: (key: string, row: T) => void;
1116
+ /**
1117
+ * Controlled sort column key. Provide alongside `sortDir` and `onSortChange`
1118
+ * for fully controlled sort state.
1119
+ */
1120
+ sortKey?: string;
1121
+ /** Controlled sort direction. Use with `sortKey`. */
1122
+ sortDir?: SortDirection;
1123
+ /** Initial sort column key for uncontrolled usage. */
1124
+ defaultSortKey?: string;
1125
+ /**
1126
+ * Initial sort direction for uncontrolled usage.
1127
+ * @default null
1128
+ */
1129
+ defaultSortDir?: SortDirection;
1130
+ /**
1131
+ * Called when the sort state changes. Receives the column key and new direction.
1132
+ * For controlled mode, apply these values to `sortKey`/`sortDir`.
1133
+ * For uncontrolled mode, use this for side-effects (analytics, persistence) only.
1134
+ */
1135
+ onSortChange?: (key: string, dir: SortDirection) => void;
1136
+ className?: string;
1137
+ }
1138
+ /**
1139
+ * Data table with sortable columns, row selection, and hybrid controlled/uncontrolled
1140
+ * sort state.
1141
+ *
1142
+ * **Sort modes:**
1143
+ * - Uncontrolled: provide `defaultSortKey`/`defaultSortDir`; table manages state internally.
1144
+ * - Controlled: provide `sortKey` + `sortDir` + `onSortChange`; caller owns state.
1145
+ *
1146
+ * Sorting cycles through asc → desc → unsorted on repeated clicks of the same column.
1147
+ * Sort is performed client-side via `String.localeCompare` with `numeric: true`.
1148
+ *
1149
+ * @example
1150
+ * ```tsx
1151
+ * <Table
1152
+ * columns={[
1153
+ * { key: 'name', label: 'Name', sortable: true },
1154
+ * { key: 'status', label: 'Status', render: r => <Badge tone={r.tone}>{r.status}</Badge> },
1155
+ * { key: 'id', label: 'ID', mono: true, width: '100px' },
1156
+ * ]}
1157
+ * data={recipes}
1158
+ * rowKey={r => r.id}
1159
+ * defaultSortKey="name"
1160
+ * onRowClick={(key, row) => navigate(`/recipes/${key}`)}
1161
+ * />
1162
+ * ```
1163
+ */
1164
+ declare function Table<T extends {
1165
+ [K in string]: unknown;
1166
+ }>({ columns, data, rowKey, selectedKeys, onRowClick, sortKey: controlledSortKey, sortDir: controlledSortDir, defaultSortKey, defaultSortDir, onSortChange, className, }: TableProps<T>): react_jsx_runtime.JSX.Element;
1167
+
1168
+ /** Props for the {@link Modal} component. */
1169
+ interface ModalProps {
1170
+ /**
1171
+ * Controlled open state. When provided alongside `onOpenChange`, the
1172
+ * component is fully controlled. Omit for uncontrolled usage with
1173
+ * `defaultOpen` and a `trigger`.
1174
+ */
1175
+ open?: boolean;
1176
+ /** Initial open state for uncontrolled usage. */
1177
+ defaultOpen?: boolean;
1178
+ /** Called when the open state changes — required for controlled usage. */
1179
+ onOpenChange?: (open: boolean) => void;
1180
+ /** Dialog title rendered in display typeface above the description. */
1181
+ title?: ReactNode;
1182
+ /** Secondary description line rendered below the title. */
1183
+ description?: ReactNode;
1184
+ /** Main body content of the dialog — typically a form or informational layout. */
1185
+ children?: ReactNode;
1186
+ /**
1187
+ * Footer slot rendered below the body with a top border. Typically holds
1188
+ * confirm/cancel {@link Button} elements aligned to the trailing edge.
1189
+ */
1190
+ footer?: ReactNode;
1191
+ /**
1192
+ * Element that opens the dialog when clicked. Rendered as a Radix
1193
+ * `asChild` trigger — pass any single React element (e.g. a {@link Button}).
1194
+ * Omit when controlling `open` externally.
1195
+ */
1196
+ trigger?: ReactNode;
1197
+ className?: string;
1198
+ }
1199
+ /**
1200
+ * Accessible dialog built on Radix Dialog. Renders into a portal at
1201
+ * `document.body`, so it appears above all other content regardless of
1202
+ * stacking context. Includes focus trapping, `Escape` to close, and
1203
+ * backdrop click to dismiss.
1204
+ *
1205
+ * Theme CSS vars are inherited via `data-achery-root` on `<html>`, so
1206
+ * the modal matches the active theme and accent even though it is portaled.
1207
+ *
1208
+ * @example
1209
+ * ```tsx
1210
+ * // Uncontrolled with trigger
1211
+ * <Modal
1212
+ * trigger={<Button variant="accent" glyph="plus">New recipe</Button>}
1213
+ * title="New recipe"
1214
+ * description="Add a recipe to the field guide."
1215
+ * footer={<><Button variant="ghost">Cancel</Button><Button variant="primary">Save</Button></>}
1216
+ * >
1217
+ * <Field label="Name"><Input autoFocus /></Field>
1218
+ * </Modal>
1219
+ *
1220
+ * // Controlled
1221
+ * <Modal open={isOpen} onOpenChange={setIsOpen} title="Confirm">…</Modal>
1222
+ * ```
1223
+ */
1224
+ declare function Modal({ open, defaultOpen, onOpenChange, title, description, children, footer, trigger, className, }: ModalProps): react_jsx_runtime.JSX.Element;
1225
+
1226
+ /** Shape of a single toast notification. */
1227
+ interface ToastData {
1228
+ /** Auto-generated unique ID — do not set manually; provided by {@link useToast}. */
1229
+ id: string;
1230
+ /** Primary message. Keep short — one clause. */
1231
+ title: string;
1232
+ /** Secondary detail line. Optional. */
1233
+ description?: string;
1234
+ /**
1235
+ * How long (ms) before the toast auto-dismisses.
1236
+ * Pass `0` to keep it on screen until manually dismissed.
1237
+ * @default 4000
1238
+ */
1239
+ duration?: number;
1240
+ /** Optional action element (e.g. an undo {@link Button}) rendered below the description. */
1241
+ action?: ReactNode;
1242
+ }
1243
+ interface ToastContextValue {
1244
+ toast: (data: Omit<ToastData, 'id'>) => void;
1245
+ }
1246
+ /**
1247
+ * Hook that returns the `toast()` imperative function for firing notifications.
1248
+ * Must be called within a component tree wrapped by {@link ToastProvider}.
1249
+ *
1250
+ * @throws If called outside a `<ToastProvider>`.
1251
+ *
1252
+ * @example
1253
+ * ```tsx
1254
+ * function SaveButton() {
1255
+ * const { toast } = useToast()
1256
+ * return (
1257
+ * <Button onClick={() => toast({ title: 'Saved.', description: 'Changes committed.' })}>
1258
+ * Save
1259
+ * </Button>
1260
+ * )
1261
+ * }
1262
+ * ```
1263
+ */
1264
+ declare function useToast(): ToastContextValue;
1265
+ /** Props for the {@link ToastProvider} component. */
1266
+ interface ToastProviderProps {
1267
+ children: ReactNode;
1268
+ }
1269
+ /**
1270
+ * Context provider that manages the toast queue and renders the notification
1271
+ * stack into a portal at `document.body`. Place once near the root of your app,
1272
+ * inside `<AcheryProvider>`.
1273
+ *
1274
+ * Use the {@link useToast} hook anywhere in the subtree to fire toasts
1275
+ * imperatively.
1276
+ *
1277
+ * @example
1278
+ * ```tsx
1279
+ * // app root
1280
+ * <AcheryProvider>
1281
+ * <ToastProvider>
1282
+ * <App />
1283
+ * </ToastProvider>
1284
+ * </AcheryProvider>
1285
+ *
1286
+ * // anywhere inside
1287
+ * const { toast } = useToast()
1288
+ * toast({ title: 'Entry deleted.', duration: 0, action: <Button size="sm">Undo</Button> })
1289
+ * ```
1290
+ */
1291
+ declare function ToastProvider({ children }: ToastProviderProps): react_jsx_runtime.JSX.Element;
1292
+
1293
+ export { AccentColor, AcheryProvider, type AcheryProviderProps, AppBar, type AppBarProps, Badge, type BadgeProps, type BadgeTone, Body, type BodyProps, Button, type ButtonProps, type ButtonVariant, Card, type CardProps, type ColumnDef, type ComponentSize, Display, Eyebrow, type EyebrowProps, Field, type FieldProps, Glyph, type GlyphName, type GlyphProps, Heading, type HeadingProps, Input, type InputProps, Marginalia, type MarginaliaProps, Modal, type ModalProps, Mono, type MonoProps, type NavGroupDef, type NavItemDef, SearchInput, type SearchInputProps, Select, type SelectProps, Sidebar, type SidebarProps, type SortDirection, type TabItem, Table, type TableProps, Tabs, type TabsProps, Textarea, type TextareaProps, type ThemeMode, type ToastData, ToastProvider, type ToastProviderProps, Toggle, type ToggleProps, Tooltip, type TooltipProps, useTheme, useToast, vars };