sonahang-ui 1.0.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.
Files changed (117) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -0
  3. package/dist/components/Accordion/Accordion.d.ts +36 -0
  4. package/dist/components/Accordion/AccordionItem.d.ts +15 -0
  5. package/dist/components/Accordion/accordion.context.d.ts +18 -0
  6. package/dist/components/Accordion/index.d.ts +5 -0
  7. package/dist/components/Alert/Alert.d.ts +24 -0
  8. package/dist/components/Alert/alert.tokens.d.ts +2 -0
  9. package/dist/components/Alert/index.d.ts +4 -0
  10. package/dist/components/Avatar/Avatar.d.ts +17 -0
  11. package/dist/components/Avatar/AvatarGroup.d.ts +12 -0
  12. package/dist/components/Avatar/avatar.context.d.ts +7 -0
  13. package/dist/components/Avatar/avatar.tokens.d.ts +2 -0
  14. package/dist/components/Avatar/getInitials.d.ts +6 -0
  15. package/dist/components/Avatar/index.d.ts +6 -0
  16. package/dist/components/Breadcrumb/Breadcrumb.d.ts +19 -0
  17. package/dist/components/Breadcrumb/BreadcrumbItem.d.ts +25 -0
  18. package/dist/components/Breadcrumb/breadcrumb.context.d.ts +10 -0
  19. package/dist/components/Breadcrumb/index.d.ts +4 -0
  20. package/dist/components/Button/Button.d.ts +20 -0
  21. package/dist/components/Button/button.tokens.d.ts +4 -0
  22. package/dist/components/Button/buttonClassNames.d.ts +17 -0
  23. package/dist/components/Button/index.d.ts +6 -0
  24. package/dist/components/Checkbox/Checkbox.d.ts +24 -0
  25. package/dist/components/Checkbox/CheckboxGroup.d.ts +28 -0
  26. package/dist/components/Checkbox/checkbox.context.d.ts +17 -0
  27. package/dist/components/Checkbox/index.d.ts +4 -0
  28. package/dist/components/Chip/Chip.d.ts +21 -0
  29. package/dist/components/Chip/chip.tokens.d.ts +4 -0
  30. package/dist/components/Chip/index.d.ts +4 -0
  31. package/dist/components/CodeBlock/CodeBlock.d.ts +41 -0
  32. package/dist/components/CodeBlock/dedentCode.d.ts +12 -0
  33. package/dist/components/CodeBlock/index.d.ts +3 -0
  34. package/dist/components/Dialog/Dialog.d.ts +70 -0
  35. package/dist/components/Dialog/dialog.tokens.d.ts +8 -0
  36. package/dist/components/Dialog/index.d.ts +4 -0
  37. package/dist/components/Drawer/Drawer.d.ts +72 -0
  38. package/dist/components/Drawer/drawer.tokens.d.ts +10 -0
  39. package/dist/components/Drawer/index.d.ts +4 -0
  40. package/dist/components/Dropdown/Dropdown.d.ts +24 -0
  41. package/dist/components/Dropdown/DropdownItem.d.ts +16 -0
  42. package/dist/components/Dropdown/DropdownMenu.d.ts +13 -0
  43. package/dist/components/Dropdown/DropdownSeparator.d.ts +10 -0
  44. package/dist/components/Dropdown/DropdownTrigger.d.ts +8 -0
  45. package/dist/components/Dropdown/dropdown.context.d.ts +24 -0
  46. package/dist/components/Dropdown/index.d.ts +11 -0
  47. package/dist/components/EmptyState/EmptyState.d.ts +47 -0
  48. package/dist/components/EmptyState/index.d.ts +2 -0
  49. package/dist/components/FileDrop/FileDrop.d.ts +41 -0
  50. package/dist/components/FileDrop/index.d.ts +3 -0
  51. package/dist/components/FileDrop/isFileAccepted.d.ts +11 -0
  52. package/dist/components/Input/Input.d.ts +20 -0
  53. package/dist/components/Input/index.d.ts +2 -0
  54. package/dist/components/InputOtp/InputOtp.d.ts +39 -0
  55. package/dist/components/InputOtp/index.d.ts +4 -0
  56. package/dist/components/InputOtp/sanitizeOtpValue.d.ts +20 -0
  57. package/dist/components/MinMaxSlider/MinMaxSlider.d.ts +41 -0
  58. package/dist/components/MinMaxSlider/index.d.ts +2 -0
  59. package/dist/components/Pagination/Pagination.d.ts +41 -0
  60. package/dist/components/Pagination/getPaginationRange.d.ts +25 -0
  61. package/dist/components/Pagination/index.d.ts +4 -0
  62. package/dist/components/ProgressBar/ProgressBar.d.ts +34 -0
  63. package/dist/components/ProgressBar/index.d.ts +4 -0
  64. package/dist/components/ProgressBar/progressBar.tokens.d.ts +2 -0
  65. package/dist/components/Radio/Radio.d.ts +18 -0
  66. package/dist/components/Radio/RadioGroup.d.ts +28 -0
  67. package/dist/components/Radio/index.d.ts +4 -0
  68. package/dist/components/Radio/radio.context.d.ts +17 -0
  69. package/dist/components/RangeSlider/RangeSlider.d.ts +36 -0
  70. package/dist/components/RangeSlider/index.d.ts +2 -0
  71. package/dist/components/SegmentedControl/SegmentedControl.d.ts +39 -0
  72. package/dist/components/SegmentedControl/index.d.ts +2 -0
  73. package/dist/components/Select/Select.d.ts +39 -0
  74. package/dist/components/Select/index.d.ts +2 -0
  75. package/dist/components/Skeleton/Skeleton.d.ts +24 -0
  76. package/dist/components/Skeleton/index.d.ts +4 -0
  77. package/dist/components/Skeleton/skeleton.tokens.d.ts +4 -0
  78. package/dist/components/Spinner/Spinner.d.ts +22 -0
  79. package/dist/components/Spinner/index.d.ts +4 -0
  80. package/dist/components/Spinner/spinner.tokens.d.ts +2 -0
  81. package/dist/components/Stepper/Step.d.ts +20 -0
  82. package/dist/components/Stepper/Stepper.d.ts +25 -0
  83. package/dist/components/Stepper/index.d.ts +5 -0
  84. package/dist/components/Stepper/stepper.context.d.ts +14 -0
  85. package/dist/components/Switch/Switch.d.ts +22 -0
  86. package/dist/components/Switch/SwitchGroup.d.ts +30 -0
  87. package/dist/components/Switch/index.d.ts +4 -0
  88. package/dist/components/Switch/switch.context.d.ts +19 -0
  89. package/dist/components/Tabs/Tab.d.ts +10 -0
  90. package/dist/components/Tabs/TabList.d.ts +20 -0
  91. package/dist/components/Tabs/TabPanel.d.ts +16 -0
  92. package/dist/components/Tabs/Tabs.d.ts +22 -0
  93. package/dist/components/Tabs/index.d.ts +9 -0
  94. package/dist/components/Tabs/tabs.context.d.ts +24 -0
  95. package/dist/components/Tag/Tag.d.ts +10 -0
  96. package/dist/components/Tag/index.d.ts +4 -0
  97. package/dist/components/Tag/tag.tokens.d.ts +2 -0
  98. package/dist/components/Text/Text.d.ts +19 -0
  99. package/dist/components/Text/index.d.ts +4 -0
  100. package/dist/components/Text/text.tokens.d.ts +8 -0
  101. package/dist/components/Textarea/Textarea.d.ts +19 -0
  102. package/dist/components/Textarea/index.d.ts +2 -0
  103. package/dist/components/Tooltip/Tooltip.d.ts +44 -0
  104. package/dist/components/Tooltip/index.d.ts +4 -0
  105. package/dist/components/Tooltip/resolveTooltipPlacement.d.ts +39 -0
  106. package/dist/index.d.ts +82 -0
  107. package/dist/internal/bodyScrollLock.d.ts +2 -0
  108. package/dist/internal/portalInto.d.ts +7 -0
  109. package/dist/internal/useModalSurface.d.ts +27 -0
  110. package/dist/sonahang-ui.cjs +7 -0
  111. package/dist/sonahang-ui.cjs.map +1 -0
  112. package/dist/sonahang-ui.css +2 -0
  113. package/dist/sonahang-ui.js +2578 -0
  114. package/dist/sonahang-ui.js.map +1 -0
  115. package/dist/tokens/colors.d.ts +167 -0
  116. package/package.json +101 -0
  117. package/style.css.d.ts +17 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sonahang Rai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # sonahang-ui
2
+
3
+ A React component library and design system — 31 accessible, themeable components with zero runtime dependencies, documented in Storybook.
4
+
5
+ - **No runtime dependencies.** React is a peer dependency; nothing else ships.
6
+ - **No provider.** Theming is CSS variables on the root element, so there's nothing to wrap your app in and nothing re-renders when the theme changes.
7
+ - **Light and dark out of the box.** Dark mode follows the OS, or you pin it with `data-theme`.
8
+ - **Typed.** Written in TypeScript, with declarations bundled.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install sonahang-ui
14
+ ```
15
+
16
+ React 19 is a peer dependency:
17
+
18
+ ```bash
19
+ npm install react@^19 react-dom@^19
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ Import the stylesheet once, at the entry point of your app:
25
+
26
+ ```tsx
27
+ import 'sonahang-ui/style.css';
28
+ ```
29
+
30
+ Then use the components anywhere:
31
+
32
+ ```tsx
33
+ import { Button, Input, Alert } from 'sonahang-ui';
34
+
35
+ export function SignIn() {
36
+ return (
37
+ <form>
38
+ <Input label="Email" type="email" placeholder="you@example.com" />
39
+ <Alert variant="info">We'll email you a one-time code.</Alert>
40
+ <Button type="submit" fullWidth>
41
+ Send code
42
+ </Button>
43
+ </form>
44
+ );
45
+ }
46
+ ```
47
+
48
+ The stylesheet is a single file covering every component — it is not split per
49
+ component, so importing it once is enough regardless of what you use. The JS is
50
+ tree-shakeable, so unused components are dropped by your bundler.
51
+
52
+ > The stylesheet loads Inter from Google Fonts via `@import`. If you'd rather
53
+ > self-host the font or use your own, override `--font-family-sans`.
54
+
55
+ ## Components
56
+
57
+ Layout and content: `Text`, `Logo`, `CodeBlock`, `EmptyState`, `Skeleton`
58
+
59
+ Actions: `Button`, `Dropdown`, `SegmentedControl`
60
+
61
+ Forms: `Input`, `InputOtp`, `Textarea`, `Select`, `Checkbox`, `Radio`, `Switch`, `RangeSlider`, `MinMaxSlider`, `FileDrop`
62
+
63
+ Feedback: `Alert`, `Spinner`, `ProgressBar`, `Tooltip`, `Dialog`, `Drawer`
64
+
65
+ Navigation: `Tabs`, `Breadcrumb`, `Pagination`, `Stepper`, `Accordion`
66
+
67
+ Data display: `Avatar`, `Tag`, `Chip`
68
+
69
+ Every component, every prop, and every state is documented with live controls
70
+ in [Storybook](https://github.com/sonangrai/sonahang-ui) — that's the real
71
+ reference, this file is the summary.
72
+
73
+ ## Theming
74
+
75
+ Components never reference a raw color. They read semantic CSS variables, so a
76
+ theme is a handful of overrides:
77
+
78
+ ```css
79
+ :root {
80
+ --color-accent: #0f766e;
81
+ --color-accent-hover: #115e59;
82
+ --color-accent-subtle-bg: #f0fdfa;
83
+ }
84
+ ```
85
+
86
+ Dark mode applies automatically from `prefers-color-scheme`. To pin it, set
87
+ `data-theme` on the root element — that always wins over the OS preference:
88
+
89
+ ```html
90
+ <html data-theme="dark">
91
+ ```
92
+
93
+ The same values are exported for JS, for charts and canvases where a CSS
94
+ variable won't reach:
95
+
96
+ ```ts
97
+ import { palette, semanticColors, colorVar } from 'sonahang-ui';
98
+
99
+ colorVar('bg-surface'); // 'var(--color-bg-surface)' — stays theme-aware
100
+ palette.accent[600]; // '#7429e0' — a fixed snapshot
101
+ ```
102
+
103
+ ## TypeScript
104
+
105
+ Declarations are bundled, so there's no `@types` package to install. Every
106
+ component exports its props type alongside it:
107
+
108
+ ```ts
109
+ import type { ButtonProps, ButtonVariant } from 'sonahang-ui';
110
+ ```
111
+
112
+ ## Development
113
+
114
+ ```bash
115
+ pnpm install
116
+ pnpm dev # landing page at :5173
117
+ pnpm storybook # docs at :6006
118
+ pnpm dev:all # both at once
119
+ pnpm test # vitest
120
+ pnpm build:lib # the published package
121
+ ```
122
+
123
+ ## License
124
+
125
+ MIT © Sonahang Rai
@@ -0,0 +1,36 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ import type { AccordionHeadingLevel, AccordionType } from "./accordion.context";
3
+ import "./Accordion.css";
4
+ export type AccordionProps = {
5
+ children?: ReactNode;
6
+ /** Whether more than one item can be open at a time. Defaults to `single`. */
7
+ type?: AccordionType;
8
+ /**
9
+ * Values of the open items — makes the component controlled.
10
+ *
11
+ * Always an array, even for `type="single"`, so the prop's type doesn't
12
+ * change with another prop's value. A single accordion simply never holds
13
+ * more than one entry.
14
+ */
15
+ value?: string[];
16
+ /** Initially open items when uncontrolled. */
17
+ defaultValue?: string[];
18
+ /** Fired with the full list of open items. */
19
+ onChange?: (value: string[]) => void;
20
+ /**
21
+ * For `type="single"`, whether clicking the open item closes it. Defaults
22
+ * to true; set false when one item must always stay open.
23
+ */
24
+ collapsible?: boolean;
25
+ /** Heading rank wrapped around each trigger. Defaults to `3`. */
26
+ headingLevel?: AccordionHeadingLevel;
27
+ className?: string;
28
+ } & Omit<HTMLAttributes<HTMLDivElement>, "className" | "onChange">;
29
+ /**
30
+ * Stack of collapsible sections.
31
+ *
32
+ * Each trigger is a button inside a real heading, so the sections show up in
33
+ * the document outline and screen-reader heading navigation — an accordion
34
+ * built from bare buttons is invisible to both.
35
+ */
36
+ export declare function Accordion({ children, type, value, defaultValue, onChange, collapsible, headingLevel, className, ...props }: AccordionProps): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ import "./Accordion.css";
3
+ export type AccordionItemProps = {
4
+ /** Identifies this section in the accordion's open list. */
5
+ value: string;
6
+ /** Content of the trigger. */
7
+ title: ReactNode;
8
+ /** Body revealed when open. */
9
+ children?: ReactNode;
10
+ /** Prevents the section being opened or closed. */
11
+ disabled?: boolean;
12
+ className?: string;
13
+ } & Omit<HTMLAttributes<HTMLDivElement>, "className" | "title">;
14
+ /** One collapsible section of an `Accordion`. */
15
+ export declare function AccordionItem({ value, title, children, disabled, className, ...props }: AccordionItemProps): import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ /** `single` closes the open item when another opens; `multiple` doesn't. */
2
+ export type AccordionType = "single" | "multiple";
3
+ /** Heading rank the triggers are wrapped in, to fit the page outline. */
4
+ export type AccordionHeadingLevel = 2 | 3 | 4 | 5 | 6;
5
+ export type AccordionContextValue = {
6
+ /** Values of the currently open items. */
7
+ value: string[];
8
+ /** Opens or closes one item, applying the single/multiple rule. */
9
+ toggle: (value: string) => void;
10
+ baseId: string;
11
+ headingLevel: AccordionHeadingLevel;
12
+ };
13
+ export declare const AccordionContext: import("react").Context<AccordionContextValue | undefined>;
14
+ /** Reads the surrounding `Accordion`, failing loudly rather than silently. */
15
+ export declare function useAccordionContext(component: string): AccordionContextValue;
16
+ /** Ids are derived rather than registered, so trigger and panel always agree. */
17
+ export declare const triggerId: (baseId: string, value: string) => string;
18
+ export declare const panelId: (baseId: string, value: string) => string;
@@ -0,0 +1,5 @@
1
+ export { Accordion } from "./Accordion";
2
+ export type { AccordionProps } from "./Accordion";
3
+ export { AccordionItem } from "./AccordionItem";
4
+ export type { AccordionItemProps } from "./AccordionItem";
5
+ export type { AccordionType, AccordionHeadingLevel } from "./accordion.context";
@@ -0,0 +1,24 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ import type { AlertVariant } from "./alert.tokens";
3
+ import "./Alert.css";
4
+ export type AlertProps = {
5
+ /** Status this alert conveys. Defaults to `info`. */
6
+ variant?: AlertVariant;
7
+ /** Bolded heading above the body. */
8
+ title?: ReactNode;
9
+ /** Body content. */
10
+ children?: ReactNode;
11
+ /** Replaces the variant's icon, or hides it entirely with `false`. */
12
+ icon?: ReactNode | false;
13
+ /** Renders a dismiss button and is called when it's activated. */
14
+ onDismiss?: () => void;
15
+ /** Accessible name for the dismiss button. Defaults to "Dismiss". */
16
+ dismissLabel?: string;
17
+ /**
18
+ * Overrides how assistive tech announces this. Defaults to `alert` for
19
+ * warning/error and `status` for success/info.
20
+ */
21
+ role?: "alert" | "status" | "none";
22
+ } & Omit<HTMLAttributes<HTMLDivElement>, "title" | "role">;
23
+ /** Coloured banner conveying the outcome or status of something. */
24
+ export declare function Alert({ variant, title, children, icon, onDismiss, dismissLabel, role, className, ...props }: AlertProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ export declare const alertVariants: readonly ["success", "info", "warning", "error"];
2
+ export type AlertVariant = (typeof alertVariants)[number];
@@ -0,0 +1,4 @@
1
+ export { Alert } from "./Alert";
2
+ export type { AlertProps } from "./Alert";
3
+ export { alertVariants } from "./alert.tokens";
4
+ export type { AlertVariant } from "./alert.tokens";
@@ -0,0 +1,17 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ import type { AvatarSize } from "./avatar.tokens";
3
+ import "./Avatar.css";
4
+ export type AvatarProps = {
5
+ /** Image URL. Falls back to initials (then an icon) if it fails to load. */
6
+ src?: string;
7
+ /** Person's name — used for the accessible label and to derive initials. */
8
+ name?: string;
9
+ /** Overrides the image alt text. Defaults to `name`. */
10
+ alt?: string;
11
+ /** Size of the avatar. Defaults to `md`, or the size set by `AvatarGroup`. */
12
+ size?: AvatarSize;
13
+ /** Custom fallback, rendered instead of initials when there's no image. */
14
+ children?: ReactNode;
15
+ } & Omit<HTMLAttributes<HTMLSpanElement>, "children">;
16
+ /** User avatar with an image → initials → icon fallback chain. */
17
+ export declare function Avatar({ src, name, alt, size, className, children, ...props }: AvatarProps): import("react").JSX.Element;
@@ -0,0 +1,12 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ import type { AvatarSize } from "./avatar.tokens";
3
+ import "./Avatar.css";
4
+ export type AvatarGroupProps = {
5
+ /** Maximum avatars to show before collapsing the rest into a "+N" counter. */
6
+ max?: number;
7
+ /** Size applied to every avatar in the group. Individual `size` props win. */
8
+ size?: AvatarSize;
9
+ children?: ReactNode;
10
+ } & Omit<HTMLAttributes<HTMLDivElement>, "children">;
11
+ /** Overlapping stack of `Avatar`s, with optional overflow counter. */
12
+ export declare function AvatarGroup({ max, size, className, children, ...props }: AvatarGroupProps): import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Lets `AvatarGroup` set the size for every avatar it contains without
3
+ * cloning children. An explicit `size` on an individual `Avatar` still wins.
4
+ * Kept in its own module so the component files only export components
5
+ * (see the react-refresh rule in eslint.config.js).
6
+ */
7
+ export declare const AvatarSizeContext: import("react").Context<"sm" | "md" | "lg" | undefined>;
@@ -0,0 +1,2 @@
1
+ export declare const avatarSizes: readonly ["sm", "md", "lg"];
2
+ export type AvatarSize = (typeof avatarSizes)[number];
@@ -0,0 +1,6 @@
1
+ /**
2
+ * "Ada Lovelace" -> "AL", "Ada" -> "A", "Ada B. Lovelace" -> "AL".
3
+ * Returns "" for names with no word characters, so callers can fall
4
+ * through to the placeholder icon.
5
+ */
6
+ export declare function getInitials(name: string, max?: number): string;
@@ -0,0 +1,6 @@
1
+ export { Avatar } from "./Avatar";
2
+ export type { AvatarProps } from "./Avatar";
3
+ export { AvatarGroup } from "./AvatarGroup";
4
+ export type { AvatarGroupProps } from "./AvatarGroup";
5
+ export { avatarSizes } from "./avatar.tokens";
6
+ export type { AvatarSize } from "./avatar.tokens";
@@ -0,0 +1,19 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ import "./Breadcrumb.css";
3
+ export type BreadcrumbProps = {
4
+ children?: ReactNode;
5
+ /** Rendered between crumbs. Defaults to a chevron. */
6
+ separator?: ReactNode;
7
+ /** Accessible name for the landmark. Defaults to "Breadcrumb". */
8
+ "aria-label"?: string;
9
+ className?: string;
10
+ } & Omit<HTMLAttributes<HTMLElement>, "className">;
11
+ /**
12
+ * Trail of links showing where the current page sits in the hierarchy.
13
+ *
14
+ * A `nav` landmark wrapping an ordered list, since the order is meaningful.
15
+ * The trailing crumb is treated as the current page automatically — the
16
+ * position in the list is what makes it current, so callers shouldn't have to
17
+ * repeat that.
18
+ */
19
+ export declare function Breadcrumb({ children, separator, "aria-label": ariaLabel, className, ...props }: BreadcrumbProps): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import type { AnchorHTMLAttributes, ReactNode } from "react";
2
+ import "./Breadcrumb.css";
3
+ export type BreadcrumbItemProps = {
4
+ children?: ReactNode;
5
+ /** Destination. Omit to render plain text, or to supply your own link. */
6
+ href?: string;
7
+ /**
8
+ * Marks this crumb as the current page. Defaults to true for the trailing
9
+ * crumb; set it explicitly to override that.
10
+ */
11
+ current?: boolean;
12
+ className?: string;
13
+ } & Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href" | "className">;
14
+ /**
15
+ * One crumb in a `Breadcrumb`.
16
+ *
17
+ * Renders an anchor when given an `href`, plain text otherwise. The current
18
+ * crumb is never a link — linking to the page you're already on is noise —
19
+ * and carries `aria-current="page"`.
20
+ *
21
+ * For router links, omit `href` and pass your own element as children; it's
22
+ * rendered as-is. The current crumb shouldn't be a link anyway, so that only
23
+ * applies to the earlier ones.
24
+ */
25
+ export declare function BreadcrumbItem({ children, href, current, className, ...props }: BreadcrumbItemProps): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from "react";
2
+ export type BreadcrumbContextValue = {
3
+ /** True for the trailing crumb, which is the current page. */
4
+ isLast: boolean;
5
+ /** Rendered between crumbs. Decorative — always `aria-hidden`. */
6
+ separator: ReactNode;
7
+ };
8
+ export declare const BreadcrumbContext: import("react").Context<BreadcrumbContextValue | undefined>;
9
+ /** Reads the surrounding `Breadcrumb`, failing loudly rather than silently. */
10
+ export declare function useBreadcrumbContext(component: string): BreadcrumbContextValue;
@@ -0,0 +1,4 @@
1
+ export { Breadcrumb } from "./Breadcrumb";
2
+ export type { BreadcrumbProps } from "./Breadcrumb";
3
+ export { BreadcrumbItem } from "./BreadcrumbItem";
4
+ export type { BreadcrumbItemProps } from "./BreadcrumbItem";
@@ -0,0 +1,20 @@
1
+ import type { ButtonHTMLAttributes, ReactNode } from "react";
2
+ import type { ButtonSize, ButtonVariant } from "./button.tokens";
3
+ import "./Button.css";
4
+ export type ButtonProps = {
5
+ /** Visual style of the button. Defaults to `primary`. Use `icon` for an icon-only, square button. */
6
+ variant?: ButtonVariant;
7
+ /** Size of the button. Defaults to `md`. */
8
+ size?: ButtonSize;
9
+ /** Stretches the button to fill its container's width. */
10
+ fullWidth?: boolean;
11
+ /** Icon to render alongside `children`, or on its own when `variant="icon"`. Note: icon-only buttons need an `aria-label`. */
12
+ icon?: ReactNode;
13
+ /** Where `icon` renders relative to `children`. Ignored when `variant="icon"`. Defaults to `left`. */
14
+ iconPosition?: "left" | "right";
15
+ /** Shows a spinner on the left and disables the button while an async action is pending. Content stays visible, so `children` can be swapped for a loading message. */
16
+ loading?: boolean;
17
+ children?: ReactNode;
18
+ } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children">;
19
+ /** Interactive button primitive for the design system. */
20
+ export declare function Button({ variant, size, fullWidth, icon, iconPosition, loading, disabled, className, children, ...props }: ButtonProps): import("react").JSX.Element;
@@ -0,0 +1,4 @@
1
+ export declare const buttonVariants: readonly ["primary", "secondary", "outline", "ghost", "icon"];
2
+ export type ButtonVariant = (typeof buttonVariants)[number];
3
+ export declare const buttonSizes: readonly ["sm", "md", "lg"];
4
+ export type ButtonSize = (typeof buttonSizes)[number];
@@ -0,0 +1,17 @@
1
+ import type { ButtonSize, ButtonVariant } from "./button.tokens";
2
+ export type ButtonClassNameOptions = {
3
+ variant?: ButtonVariant;
4
+ size?: ButtonSize;
5
+ fullWidth?: boolean;
6
+ loading?: boolean;
7
+ className?: string;
8
+ };
9
+ /**
10
+ * The class list `Button` puts on itself.
11
+ *
12
+ * Exported because `<button>` can't be a link: anything that needs to look
13
+ * like a button while navigating — an `EmptyState` call to action, a "read the
14
+ * docs" link — has to be an `<a>`, and should get its appearance from here
15
+ * rather than from a second copy of the same rules.
16
+ */
17
+ export declare function buttonClassNames({ variant, size, fullWidth, loading, className, }?: ButtonClassNameOptions): string;
@@ -0,0 +1,6 @@
1
+ export { Button } from './Button';
2
+ export type { ButtonProps } from './Button';
3
+ export { buttonVariants, buttonSizes } from './button.tokens';
4
+ export { buttonClassNames } from './buttonClassNames';
5
+ export type { ButtonClassNameOptions } from './buttonClassNames';
6
+ export type { ButtonVariant, ButtonSize } from './button.tokens';
@@ -0,0 +1,24 @@
1
+ import type { InputHTMLAttributes, ReactNode, Ref } from "react";
2
+ import "./Checkbox.css";
3
+ export type CheckboxProps = {
4
+ /** Value this option represents. Required when inside a `CheckboxGroup`. */
5
+ value?: string;
6
+ /** Visible label sitting beside the control. */
7
+ label?: ReactNode;
8
+ /**
9
+ * Renders the mixed state (a dash). Purely visual until toggled — a
10
+ * checkbox is never submitted as indeterminate.
11
+ */
12
+ indeterminate?: boolean;
13
+ /** Class for the wrapping `<label>`. Use `inputClassName` for the control. */
14
+ className?: string;
15
+ /** Class for the underlying `<input type="checkbox">`. */
16
+ inputClassName?: string;
17
+ ref?: Ref<HTMLInputElement>;
18
+ } & Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "className" | "ref">;
19
+ /**
20
+ * A single checkbox. Usually rendered inside a `CheckboxGroup`, which supplies
21
+ * the shared `name` and selection; works standalone too, in which case
22
+ * `checked`/`onChange` behave like a plain `<input type="checkbox">`.
23
+ */
24
+ export declare function Checkbox({ value, label, indeterminate, className, inputClassName, name, checked, disabled, onChange, ref, ...props }: CheckboxProps): import("react").JSX.Element;
@@ -0,0 +1,28 @@
1
+ import type { ReactNode } from "react";
2
+ import "./Checkbox.css";
3
+ export type CheckboxGroupProps = {
4
+ children?: ReactNode;
5
+ /** Selected values — makes the group controlled. */
6
+ value?: string[];
7
+ /** Initial selection when uncontrolled. */
8
+ defaultValue?: string[];
9
+ /** Fired with the full list of selected values. */
10
+ onChange?: (value: string[]) => void;
11
+ /** Visible label for the group. Provide this or `aria-label`. */
12
+ label?: ReactNode;
13
+ /** Hint shown below the options. Replaced by `error` when that's set. */
14
+ helperText?: ReactNode;
15
+ /** Validation message. Its presence also marks the group invalid. */
16
+ error?: ReactNode;
17
+ /** Shared field name for the options. Auto-generated when omitted. */
18
+ name?: string;
19
+ /** Layout direction for the options. Defaults to `vertical`. */
20
+ orientation?: "vertical" | "horizontal";
21
+ /** Disables every option in the group. */
22
+ disabled?: boolean;
23
+ /** Accessible name when there's no visible `label`. */
24
+ "aria-label"?: string;
25
+ className?: string;
26
+ };
27
+ /** Groups `Checkbox` options, coordinating their shared name and selection. */
28
+ export declare function CheckboxGroup({ children, value, defaultValue, onChange, label, helperText, error, name, orientation, disabled, "aria-label": ariaLabel, className, }: CheckboxGroupProps): import("react").JSX.Element;
@@ -0,0 +1,17 @@
1
+ export type CheckboxGroupContextValue = {
2
+ /** Shared `name`, so the options submit together as one field. */
3
+ name: string;
4
+ /** Currently selected values. Checkboxes are multi-select, unlike radios. */
5
+ value: string[];
6
+ /** Called with the option's value when it's toggled on or off. */
7
+ onToggle: (value: string, checked: boolean) => void;
8
+ /** Set when the whole group is disabled. */
9
+ disabled?: boolean;
10
+ };
11
+ /**
12
+ * Lets `CheckboxGroup` coordinate its `Checkbox` children without cloning
13
+ * them. A `Checkbox` used outside a group falls back to its own props.
14
+ * Kept in its own module so the component files only export components
15
+ * (see the react-refresh rule in eslint.config.js).
16
+ */
17
+ export declare const CheckboxGroupContext: import("react").Context<CheckboxGroupContextValue | undefined>;
@@ -0,0 +1,4 @@
1
+ export { Checkbox } from "./Checkbox";
2
+ export type { CheckboxProps } from "./Checkbox";
3
+ export { CheckboxGroup } from "./CheckboxGroup";
4
+ export type { CheckboxGroupProps } from "./CheckboxGroup";
@@ -0,0 +1,21 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ import type { ChipAction, ChipVariant } from "./chip.tokens";
3
+ import "./Chip.css";
4
+ export type ChipProps = {
5
+ /** Visual style of the chip. Defaults to `primary`. */
6
+ variant?: ChipVariant;
7
+ /** Trailing icon button to render. Omit for a chip with no action. */
8
+ action?: ChipAction;
9
+ /** Fired when the trailing icon button is activated. */
10
+ onAction?: () => void;
11
+ /**
12
+ * Accessible name for the trailing button. Defaults to the action verb
13
+ * plus the label when `children` is a string — e.g. "Remove Vite".
14
+ */
15
+ actionLabel?: string;
16
+ /** Disables the trailing button and mutes the chip. */
17
+ disabled?: boolean;
18
+ children?: ReactNode;
19
+ } & Omit<HTMLAttributes<HTMLSpanElement>, "children">;
20
+ /** Compact, actionable label — a `Tag` with a trailing add or remove button. */
21
+ export declare function Chip({ variant, action, onAction, actionLabel, disabled, className, children, ...props }: ChipProps): import("react").JSX.Element;
@@ -0,0 +1,4 @@
1
+ export declare const chipVariants: readonly ["primary", "secondary", "outline"];
2
+ export type ChipVariant = (typeof chipVariants)[number];
3
+ export declare const chipActions: readonly ["add", "remove"];
4
+ export type ChipAction = (typeof chipActions)[number];
@@ -0,0 +1,4 @@
1
+ export { Chip } from "./Chip";
2
+ export type { ChipProps } from "./Chip";
3
+ export { chipVariants, chipActions } from "./chip.tokens";
4
+ export type { ChipVariant, ChipAction } from "./chip.tokens";
@@ -0,0 +1,41 @@
1
+ import type { HTMLAttributes } from "react";
2
+ import "./CodeBlock.css";
3
+ export type CodeBlockProps = {
4
+ /**
5
+ * The code.
6
+ *
7
+ * A string rather than nodes, because it has to be copied to the clipboard
8
+ * and counted into lines — neither of which is possible with arbitrary
9
+ * children. Shared indentation is stripped, so a template literal can be
10
+ * indented to match the code around it.
11
+ */
12
+ children: string;
13
+ /** Shown on the left of the header. */
14
+ filename?: string;
15
+ /**
16
+ * Puts `language-<value>` on the `<code>`, which is the hook Prism, Shiki
17
+ * and highlight.js look for. Nothing here highlights anything — see the
18
+ * component docs. Also labels the header when there's no filename.
19
+ */
20
+ language?: string;
21
+ /** Numbers down the left of the code. Defaults to false. */
22
+ showLineNumbers?: boolean;
23
+ /** Soft-wraps long lines instead of scrolling sideways. Defaults to false. */
24
+ wrap?: boolean;
25
+ /** Whether to render the copy button. Defaults to true. */
26
+ copyable?: boolean;
27
+ /** Accessible name for the copy button. Defaults to "Copy code". */
28
+ copyLabel?: string;
29
+ /** Caps the height of the code area, which scrolls past it. */
30
+ maxHeight?: number | string;
31
+ className?: string;
32
+ } & Omit<HTMLAttributes<HTMLDivElement>, "className" | "children">;
33
+ /**
34
+ * Code with a filename and a copy button.
35
+ *
36
+ * **It does not highlight anything.** A syntax highlighter is a large
37
+ * dependency with opinions about themes and grammars, and this library ships
38
+ * none. `language` puts the conventional `language-*` class on the `<code>`
39
+ * so whichever highlighter the consumer already has can pick the block up.
40
+ */
41
+ export declare function CodeBlock({ children, filename, language, showLineNumbers, wrap, copyable, copyLabel, maxHeight, className, ...props }: CodeBlockProps): import("react").JSX.Element;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Removes the indentation a template literal picks up from the code around it.
3
+ *
4
+ * Blank first and last lines go first — a template literal almost always opens
5
+ * with the newline after the backtick and closes with the indentation before
6
+ * it — then the smallest indent shared by every remaining non-blank line is
7
+ * stripped from all of them. Relative indentation inside the block survives.
8
+ *
9
+ * Indentation is counted in characters, so a block mixing tabs and spaces at
10
+ * the same depth won't line up. Pick one.
11
+ */
12
+ export declare function dedentCode(source: string): string;
@@ -0,0 +1,3 @@
1
+ export { CodeBlock } from "./CodeBlock";
2
+ export type { CodeBlockProps } from "./CodeBlock";
3
+ export { dedentCode } from "./dedentCode";