@inkorange/space-ui 1.0.3 → 1.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.
package/README.md CHANGED
@@ -1,204 +1,197 @@
1
- # SpaceUI
1
+ # SpaceUI: Dark interfaces with their own light source
2
2
 
3
- **Components for dark, instrument-dense interfaces.**
3
+ <!-- badges:start -->
4
4
 
5
- 24 React components and 20 icons on a seven-role token system, with zero
6
- runtime dependencies.
5
+ [![npm](https://img.shields.io/npm/v/@inkorange/space-ui)](https://www.npmjs.com/package/@inkorange/space-ui)
6
+ ![coverage](https://img.shields.io/badge/coverage-82%25-green)
7
+ ![minzip](https://img.shields.io/badge/minzip-21.7%20kB-blue)
8
+ ![dependencies](https://img.shields.io/badge/dependencies-0-blue)
9
+ [![docs](https://img.shields.io/badge/docs-live%20gallery-0b7dd6)](https://space-components.vercel.app)
7
10
 
8
- <!-- Absolute URLs on purpose: npm renders this file too, and relative paths
9
- resolve against the registry rather than the repository. -->
10
- <img alt="A lit-glass button, select, badges, slider, progress bar and folder tabs on a dark ground"
11
- src="https://raw.githubusercontent.com/inkorange/space-ui/main/.github/assets/components.svg"
12
- width="100%">
13
-
14
- 📖 **[Browse the gallery →](https://space-components.vercel.app)** — every
15
- component, its props, its tokens, and the source for each example.
11
+ <!-- badges:end -->
16
12
 
17
- <a href="https://space-components.vercel.app">
18
- <img alt="The SpaceUI documentation gallery: components, their props, tokens and source"
19
- src="https://raw.githubusercontent.com/inkorange/space-ui/main/.github/assets/doc-screen.jpg"
20
- width="100%">
21
- </a>
13
+ Lit glass. An orbiting rim. Faint starlight across a surface. Not a theme you
14
+ configure — the way these components look out of the box, with nothing to
15
+ compose and nothing to opt into.
22
16
 
23
- ## Install
17
+ <!-- Absolute URLs on purpose: npm renders this file too, and relative paths
18
+ resolve against the registry rather than the repository. This sheet is a
19
+ screenshot of the real components, shot from the gallery — see
20
+ `pnpm assets`. -->
21
+ <img alt="Stickersheet: Button, Badge, Select, TextField, Slider, Progress, IconToggle, RadioGroup, Loader, Tabs, icons, Text, TextArea, Card and ScrollArea on a dark ground"
22
+ src="https://raw.githubusercontent.com/inkorange/space-ui/main/.github/assets/components.jpg"
23
+ width="100%">
24
24
 
25
25
  ```bash
26
26
  pnpm add @inkorange/space-ui
27
27
  ```
28
28
 
29
29
  ```tsx
30
- // once, at your app entry — one import, defaults included
31
30
  import "@inkorange/space-ui/styles.css";
32
-
33
- import { Button, Card, Heading } from "@inkorange/space-ui";
31
+ import { Card, Heading, Text, Badge, Button } from "@inkorange/space-ui";
34
32
 
35
33
  export function Planet() {
36
34
  return (
37
35
  <Card>
38
- <Heading size="4">Kepler-442b</Heading>
36
+ <Heading size="5">Kepler-442b</Heading>
37
+ <Badge color="success">Temperate</Badge>
38
+ <Text size="2" color="muted">
39
+ A super-earth in the habitable zone, 1,206 light years out.
40
+ </Text>
39
41
  <Button>Build planet</Button>
40
42
  </Card>
41
43
  );
42
44
  }
43
45
  ```
44
46
 
45
- `styles.css` is the precompiled component CSS **with the default token values
46
- baked in**, so that single import gives you a working library. Plain CSS — no
47
- Sass, CSS modules, or framework tooling required downstream. All components are
48
- client components (`"use client"` is baked into the bundle).
47
+ That is the whole setup. Every component arrives already wearing the design.
49
48
 
50
- ### Theming is a one-file swap
49
+ ## The design language is the component
51
50
 
52
- Every component reads role-named tokens and nothing else, so a whole theme —
53
- light mode, a brand palette — is one `:root` block loaded *after* the library:
51
+ Most systems hand you primitives and a theme to paint them with. This one does
52
+ not separate the two. A `Button` is a lit-glass capsule whose rim catches light
53
+ like a planet's atmosphere; a `Slider` is a glass tube with a limb-lit moon you
54
+ drag along it. There is no plain mode, because a plain mode would be a
55
+ different library.
54
56
 
55
- ```css
56
- /* theme.css, imported after styles.css */
57
- :root {
58
- --sp-gray-panel: #ffffff;
59
- --sp-gray-text: #14161c;
60
- --sp-primary-solid: #7c5cff;
61
- --sp-font-family: "Inter", system-ui, sans-serif;
62
- }
63
- ```
57
+ What you *can* control is movement. Ambient motion suits a hero button and
58
+ distracts in a dense form, so one prop stills it — the look untouched, only the
59
+ loops stopped:
64
60
 
65
- Same specificity, later source order, so your values win. You only override
66
- what you want to change — anything you leave out keeps the shipped default.
67
- `@inkorange/space-ui/tokens.css` is exported separately if you want to read or
68
- extend the full default set.
61
+ ```tsx
62
+ <Button>Build planet</Button>
63
+ {/* rim orbits, stars twinkle */}
64
+ <Button animated={false}>Build planet</Button>
65
+ {/* same glass, held still */}
66
+ ```
69
67
 
70
- **Requirements:** React 19 as a peer dependency, and a dark surface —
71
- components are built for the space-dark ground (`#111113`) and assume it.
72
- There is no light theme.
68
+ Readers who ask for reduced motion get the stilled treatment automatically,
69
+ whatever the prop says.
73
70
 
74
- ## Four rules, enforced
71
+ ## Built for dense, dark UI
75
72
 
76
- Each of these is a constraint the build checks, not a preference the docs
77
- describe. That is the difference between a design system and a folder of
78
- components.
73
+ Forms, overlays and controls that hold up when there are a lot of them on
74
+ screen at once — the kind of interface where someone is adjusting values and
75
+ watching something respond.
79
76
 
80
- | Rule | What it means | Enforced by |
81
- | --- | --- | --- |
82
- | **Eight-point spacing** | Every gap, inset, and offset lands on the grid. Off-grid values round **up**, so density never creeps in by accident. | `12px → 16px` |
83
- | **Seven gray roles** | Grays are named for the job they do, not their step on a ramp. Fifteen Radix variants collapsed into seven roles. | `--sp-gray-text-dim` |
84
- | **No framework in the surface** | No Radix, no runtime dependencies, no framework-specific props. | `radixImports.test.ts` |
85
- | **CSS arrives precompiled** | Consumers import one stylesheet. Authored in Sass; nothing downstream needs a Sass toolchain. | `styles.css` |
77
+ ```tsx
78
+ <Select value={star} onValueChange={setStar}>
79
+ {types.map((t) => (
80
+ <Select.Item key={t} value={t}>{t}-type star</Select.Item>
81
+ ))}
82
+ </Select>
83
+ ```
86
84
 
87
- ## Design tokens
85
+ The small things are handled so you never have to notice them. That Select
86
+ sizes itself to its widest option, so it does not change width as you choose.
87
+ Dialogs ride the browser's own top layer, so focus trapping and Escape are the
88
+ platform's job rather than a library's approximation. Tabs connect to the panel
89
+ beneath them.
88
90
 
89
- Radix gave us fifteen numbered grays. A numbered ramp makes you guess — is
90
- `gray-11` a label, a border, or a placeholder? SpaceUI ships seven, each named
91
- for the work it does, so the name tells you where it belongs and a reviewer can
92
- tell when it is wrong. Set a role's value once and every correct use follows.
91
+ ## Make it yours — it's all CSS variables
93
92
 
94
- Tokens are plain CSS custom properties on `:root`. Nothing about them is
95
- React-specific.
93
+ No component holds a value of its own. Every colour, size and duration in the
94
+ library resolves through a **CSS custom property** in the `--sp-` namespace,
95
+ and the defaults ship inside `styles.css` — so retheming is a block of
96
+ variables you write, never a fork, a config file, or a build step.
96
97
 
97
- ```css
98
- @import "@inkorange/space-ui/tokens.css";
98
+ There are three tiers, from broadest to narrowest.
99
99
 
100
- .planet-panel {
101
- background: var(--sp-gray-panel);
102
- border: 1px solid var(--sp-gray-border);
103
- padding: var(--spacing-md); /* 16px — on the grid */
104
- gap: var(--spacing-sm);
105
- }
100
+ **Palette roles.** Named for the job the colour does, never the hue. Change one
101
+ and every component playing that role moves with it:
106
102
 
107
- .planet-panel__label {
108
- color: var(--sp-gray-text-dim); /* secondary text */
109
- font-size: var(--sp-font-sm);
103
+ ```css
104
+ :root {
105
+ --sp-primary-solid: #7c5cff; /* solid accent fill: badges, progress */
106
+ --sp-primary-text: #b9a6ff; /* accent text and links */
107
+ --sp-gray-panel: #14161c; /* card and panel fills */
108
+ --sp-gray-border: #d6ebfd30; /* hairlines and control borders */
109
+ --sp-danger-text: #ff9592; /* destructive, not "red" */
110
110
  }
111
111
  ```
112
112
 
113
- Because every component reads the same roles, rebranding is one block of
114
- overrides rather than a pass through the component tree:
113
+ **Surface channels.** The lit glass is built from layered translucency, so
114
+ those variables carry colour *channels* and let each layer pick its own alpha:
115
115
 
116
116
  ```css
117
- /* theme.css — loaded after tokens.css */
118
117
  :root {
119
- --sp-gray-panel: #14161c;
120
- --sp-primary-solid: #7c5cff;
121
- --sp-font-family: "Inter", system-ui, sans-serif;
118
+ --sp-rim-rgb: 190 160 255; /* the lit atmosphere limb */
119
+ --sp-glow-rgb: 150 130 255; /* hover and focus bloom */
120
+ --sp-glass-rgb: 18 14 34; /* the glass fill itself */
122
121
  }
123
122
  ```
124
123
 
125
- Every token, its resolved value, and the Radix variant it replaced is listed
126
- under **Foundations** in the gallery, along with a reverse index of which
127
- components read which token.
128
-
129
- ### Fonts
124
+ Inside the components these read as `rgb(var(--sp-rim-rgb) / 0.4)`, so give
125
+ them space-separated channels — a `#hex` or a full `rgb(...)` leaves the alpha
126
+ with nothing to work on.
130
127
 
131
- Almost nothing sets a `font-family` — the library inherits whatever your app
132
- already loaded, and nothing renders through a portal, so inheritance reaches
133
- every component. Two components pin a stack (`Badge` and `Button`), and
134
- both read the same token:
128
+ **Component properties.** Where one component needs a dial of its own it
129
+ exposes one, always named `--sp-[component]-[modifier]-[type]`:
135
130
 
136
131
  ```css
137
- :root { --sp-font-family: "Inter", system-ui, sans-serif; }
132
+ --sp-progress-height: 12px;
133
+ --sp-select-trigger-max-width: 24rem;
134
+ --sp-loader-moon-size: 10px;
138
135
  ```
139
136
 
140
- There is deliberately no per-component font hook. Typeface is a system-level
141
- decision made in one place, and a test fails the build if a component
142
- introduces its own.
137
+ Every component page in the gallery lists its own in a **Custom properties**
138
+ table, saying what each one changes.
143
139
 
144
- ## The look is not optional
140
+ Because these are ordinary CSS variables, they cascade. Declare them on `:root`
141
+ to theme the application, or on any element to retheme just that subtree —
142
+ a warmer palette inside one panel, a tighter control in one toolbar:
145
143
 
146
- Lit glass, an orbiting rim, faint starfield specks — that is what a SpaceUI
147
- component *is*. There is no plain mode and no class to compose: install the
148
- package and you get the system.
149
-
150
- What you can turn off is the **motion**, because ambient loops that suit a hero
151
- button distract in a dense form or a long list. One prop, one meaning:
152
-
153
- ```tsx
154
- <Button>Build planet</Button> {/* ambient motion, the default */}
155
- <Button animated={false}>Build planet</Button> {/* same look, no loops */}
156
-
157
- <TextField.Root animated={false} />
158
- <Select.Trigger animated={false} />
144
+ ```css
145
+ .launch-panel {
146
+ --sp-glass-rgb: 30 16 14;
147
+ --sp-rim-rgb: 255 180 130;
148
+ --sp-control-height: 32px;
149
+ }
159
150
  ```
160
151
 
161
- `animated={false}` stills the orbiting rim, the glint sweep and the twinkling
162
- stars. Every gradient, rim and shadow stays exactly where it was. Readers who
163
- set `prefers-reduced-motion` get this without asking — the library honours it
164
- whatever the prop says.
165
-
166
- `Slider` takes no `animated` prop: its skin has no ambient loop, and a prop
167
- that does nothing is worse than no prop.
152
+ You override only what you name, and everything you leave alone keeps its
153
+ shipped value. Supply no variables at all and the library still renders exactly
154
+ as it does in the gallery.
168
155
 
169
- ## Extracted, not invented
156
+ ## The components
170
157
 
171
- These components ran in production before they became a package, so they
172
- arrived with their edge cases already found rather than imagined. The
173
- extraction kept the rendered output identical — a few deliberate exceptions to
174
- the spacing grid survive where parity with the shipped app beat grid purity,
175
- and they are commented where they occur.
158
+ Every one of these has a live page in the gallery — the component running, the
159
+ source behind it, its full prop table with defaults, and the custom properties
160
+ it exposes.
176
161
 
162
+ | | |
163
+ | --- | --- |
164
+ | **Typography** | [Text](https://space-components.vercel.app/?story=components--typography--text-sizes) · [Link](https://space-components.vercel.app/?story=components--typography--text-sizes) · [Heading](https://space-components.vercel.app/?story=components--typography--headings) · [Badge](https://space-components.vercel.app/?story=components--typography--badges) · [Separator](https://space-components.vercel.app/?story=components--typography--separator) |
165
+ | **Layout** | [Card](https://space-components.vercel.app/?story=components--layout--card) · [Flex](https://space-components.vercel.app/?story=components--layout--flex-and-grid) · [Grid](https://space-components.vercel.app/?story=components--layout--flex-and-grid) · [Box](https://space-components.vercel.app/?story=components--layout--flex-and-grid) · [ScrollArea](https://space-components.vercel.app/?story=components--layout--scroll-area) |
166
+ | **Forms** | [TextField](https://space-components.vercel.app/?story=components--forms--text-field) · [TextArea](https://space-components.vercel.app/?story=components--forms--text-area) · [Select](https://space-components.vercel.app/?story=components--forms--select) · [Slider](https://space-components.vercel.app/?story=components--forms--slider) · [RadioGroup](https://space-components.vercel.app/?story=components--forms--radio-group) |
167
+ | **Buttons** | [Button](https://space-components.vercel.app/?story=components--buttons--buttons) · [IconToggle](https://space-components.vercel.app/?story=components--buttons--icon-toggle) |
168
+ | **Feedback** | [Loader](https://space-components.vercel.app/?story=components--buttons--loader) · [Progress](https://space-components.vercel.app/?story=components--buttons--progress) |
169
+ | **Overlays** | [Dialog](https://space-components.vercel.app/?story=components--overlays--dialog) · [AlertDialog](https://space-components.vercel.app/?story=components--overlays--alert-dialog) · [DropdownMenu](https://space-components.vercel.app/?story=components--overlays--dropdown-menu) · [Tabs](https://space-components.vercel.app/?story=components--overlays--tabs) · [Tooltip](https://space-components.vercel.app/?story=components--overlays--tooltip) |
170
+ | **Icons** | [20 icons](https://space-components.vercel.app/?story=components--icons--all-icons) |
171
+ | **Foundations** | [Color](https://space-components.vercel.app/?story=foundations--color) · [Spacing](https://space-components.vercel.app/?story=foundations--spacing) · [Type scale](https://space-components.vercel.app/?story=foundations--type-scale) · [Motion](https://space-components.vercel.app/?story=foundations--motion) · [Token usage](https://space-components.vercel.app/?story=foundations--token-usage) |
177
172
 
178
- ## Develop
173
+ <a href="https://space-components.vercel.app">
174
+ <img alt="The SpaceUI documentation gallery: components, their props, tokens and source"
175
+ src="https://raw.githubusercontent.com/inkorange/space-ui/main/.github/assets/doc-screen.jpg"
176
+ width="100%">
177
+ </a>
179
178
 
180
- ```bash
181
- pnpm install
182
- pnpm docs # Ladle gallery at localhost:61000
183
- pnpm test # 49 tests, incl. the Radix-ban and token-grid guards
184
- pnpm build
185
- ```
179
+ Start at **[space-components.vercel.app](https://space-components.vercel.app)** — the
180
+ introduction covers the token system and how to theme it, and every component
181
+ page shows the code you would actually write.
186
182
 
187
- The gallery runs against the library **source**, not `dist` — component edits
188
- hot-reload with no build step in the loop. Prop tables and token usage on the
189
- docs site are generated from the source at build time, so they cannot drift
190
- from what ships.
183
+ ## What you get
191
184
 
192
- ## Releases
185
+ **Zero runtime dependencies**, and about 22 kB over the wire gzipped — a 16 kB
186
+ ESM bundle and a 7 kB stylesheet. React 19, precompiled CSS, no build tooling
187
+ required downstream. Both numbers are measured from the build on every CI run,
188
+ so the badges above cannot drift.
193
189
 
194
- [Changesets](https://github.com/changesets/changesets): every PR that changes
195
- `packages/space-ui/src` adds one (`pnpm changeset`). Merging to main
196
- opens/updates a "Version Packages" PR; merging that publishes to npm and tags
197
- the release.
190
+ These ran in production before they became a package, so they arrived with
191
+ their edge cases already found rather than imagined.
198
192
 
199
- - **patch** — visual fix, internal refactor, docs
200
- - **minor** — new component, new prop, new export
201
- - **major** — removed/renamed export or prop, changed rendering default
193
+ **Requires** React 19 and a dark surface. There is deliberately no light theme;
194
+ these components assume the dark they were drawn for.
202
195
 
203
196
  ## License
204
197
 
@@ -1,8 +1,17 @@
1
- import { Trigger as DialogTrigger, Close as DialogClose, Content as DialogContent, Title as DialogTitle, Description as DialogDescription } from "./Dialog";
2
- export declare const Root: ({ open, onOpenChange, children }: import("./Dialog").RootProps) => import("react").JSX.Element;
3
- export declare const Trigger: typeof DialogTrigger;
4
- export declare const Content: typeof DialogContent;
5
- export declare const Title: typeof DialogTitle;
6
- export declare const Description: typeof DialogDescription;
7
- export declare const Cancel: typeof DialogClose;
8
- export declare const Action: typeof DialogClose;
1
+ import { Trigger, Close, Content, Title, Description, type DialogProps } from "./Dialog";
2
+ export type { DialogProps as AlertDialogProps };
3
+ /**
4
+ * A modal that interrupts to confirm something consequential.
5
+ *
6
+ * Unlike a Dialog it has no backdrop dismissal, because dismissing an alert
7
+ * by missing it is not a decision. Put `Cancel` before `Action` so the safe
8
+ * choice takes focus when it opens.
9
+ */
10
+ export declare const AlertDialog: (({ open, onOpenChange, children }: DialogProps) => import("react").JSX.Element) & {
11
+ Trigger: typeof Trigger;
12
+ Content: typeof Content;
13
+ Title: typeof Title;
14
+ Description: typeof Description;
15
+ Cancel: typeof Close;
16
+ Action: typeof Close;
17
+ };
@@ -14,26 +14,19 @@ export interface DialogCtx {
14
14
  setHasDesc: (v: boolean) => void;
15
15
  }
16
16
  export declare const useDialog: () => DialogCtx;
17
- export interface RootProps {
17
+ export interface DialogProps {
18
18
  /** Controls the dialog. Omit both this and onOpenChange to let the dialog
19
19
  * manage its own state. */
20
20
  open?: boolean;
21
21
  /** Called whenever the dialog opens or closes, including via Escape or a
22
22
  * backdrop click. */
23
23
  onOpenChange?: (o: boolean) => void;
24
- /** A Trigger and a Content. */
24
+ /** A `Trigger` and a `Content`, in any arrangement — this renders
25
+ * nothing itself, so they can sit anywhere beneath it. */
25
26
  children?: ReactNode;
26
27
  }
27
- /** Shared by Dialog.Root and AlertDialog.Root (alert flag differs). */
28
- export declare function makeRoot(alert: boolean): ({ open, onOpenChange, children }: RootProps) => React.JSX.Element;
29
- /**
30
- * Owns a dialog's open state. Works controlled via `open`/`onOpenChange`, or
31
- * uncontrolled if you pass neither.
32
- *
33
- * Renders nothing itself — it only provides context, so Trigger and Content
34
- * can sit anywhere beneath it in the tree.
35
- */
36
- export declare const Root: ({ open, onOpenChange, children }: RootProps) => React.JSX.Element;
28
+ /** Shared by Dialog and AlertDialog; only the alert flag differs. */
29
+ export declare function makeRoot(alert: boolean): ({ open, onOpenChange, children }: DialogProps) => React.JSX.Element;
37
30
  /**
38
31
  * Opens the dialog. Chains onto its child's existing click handler rather
39
32
  * than replacing it, and renders no element of its own — so the child stays
@@ -79,3 +72,10 @@ export declare function Title({ className, ...rest }: HeadingProps): React.JSX.E
79
72
  * description. Optional, and only announced when present.
80
73
  */
81
74
  export declare function Description(props: TextProps): React.JSX.Element;
75
+ export declare const Dialog: (({ open, onOpenChange, children }: DialogProps) => React.JSX.Element) & {
76
+ Trigger: typeof Trigger;
77
+ Close: typeof Close;
78
+ Content: typeof Content;
79
+ Title: typeof Title;
80
+ Description: typeof Description;
81
+ };
@@ -1,42 +1,38 @@
1
1
  import type * as React from "react";
2
2
  import { type ReactNode } from "react";
3
- /**
4
- * Owns a menu's open state and closes it on an outside click or Escape.
5
- * Renders nothing of its own.
6
- */
7
- export declare function Root({ children, }: {
8
- /** A Trigger and a Content. */
9
- children?: ReactNode;
10
- }): React.JSX.Element;
11
- /**
12
- * The control that opens the menu, and the element the menu is positioned
13
- * against. Wraps its child rather than rendering a button of its own.
14
- */
15
- export declare function Trigger({ children, }: {
16
- /** The control that opens the menu, and what the menu anchors to. */
17
- children?: ReactNode;
18
- }): React.ReactElement<Record<string, unknown>, string | React.JSXElementConstructor<any>> | null;
19
- /**
20
- * The menu surface, anchored to the trigger and aligned to its start or end
21
- * edge. Owns the keyboard model: arrows move between items, Escape closes and
22
- * returns focus to the trigger.
23
- */
24
- export declare function Content({ align, children, }: {
3
+ import { type ButtonProps } from "./Button";
4
+ export interface DropdownMenuProps extends Omit<ButtonProps, "children"> {
5
+ /** The trigger's content. The trigger is always a Button, so this is what
6
+ * goes inside it — text, or an icon with `iconOnly`. */
7
+ label: ReactNode;
25
8
  /** Which edge of the trigger the menu lines up with. Use `end` when the
26
9
  * trigger sits near the right edge and the menu would overflow. */
27
10
  align?: "start" | "end";
28
- /** Items, Labels and Separators. */
11
+ /** `DropdownMenu.Item`, `.Separator` and `.Label`, in any order. */
29
12
  children?: ReactNode;
30
- }): React.JSX.Element;
13
+ }
14
+ /**
15
+ * A menu of actions hung off a button.
16
+ *
17
+ * The trigger is built in and is always a Button — a dropdown whose trigger
18
+ * looked like anything else was never a shape worth supporting, and making it
19
+ * arbitrary cost a `cloneElement` that had to graft aria wiring and a click
20
+ * handler onto someone else's element.
21
+ *
22
+ * The menu rides the platform's top layer via `popover`, so click-outside
23
+ * dismissal and stacking come from the browser rather than an approximation.
24
+ * Arrows move between items, Escape closes and returns focus to the trigger.
25
+ */
26
+ declare function DropdownMenuRoot({ label, align, children, ...buttonProps }: DropdownMenuProps): React.JSX.Element;
31
27
  /**
32
28
  * A non-interactive heading for a group of items. Skipped by arrow-key
33
29
  * navigation, since there is nothing to activate.
34
30
  */
35
- export declare function Label({ children, }: {
31
+ declare function Label({ children, }: {
36
32
  /** Heading text for the group beneath it. Not focusable. */
37
33
  children?: ReactNode;
38
34
  }): React.JSX.Element;
39
- export interface ItemProps {
35
+ export interface DropdownMenuItemProps {
40
36
  /** Render the child element instead of the default item — for a link that
41
37
  * should stay an anchor rather than becoming a button. */
42
38
  asChild?: boolean;
@@ -54,9 +50,15 @@ export interface ItemProps {
54
50
  * Pass `color="danger"` for a destructive action so it reads differently from
55
51
  * its neighbours before it is chosen, not after.
56
52
  */
57
- export declare function Item({ asChild, color, onSelect, children }: ItemProps): React.JSX.Element;
53
+ declare function Item({ asChild, color, onSelect, children }: DropdownMenuItemProps): React.JSX.Element;
58
54
  /**
59
55
  * A rule between groups of items — for separating a destructive action from
60
56
  * routine ones, so the two are not adjacent to a moving cursor.
61
57
  */
62
- export declare function Separator(): React.JSX.Element;
58
+ declare function Separator(): React.JSX.Element;
59
+ export declare const DropdownMenu: typeof DropdownMenuRoot & {
60
+ Item: typeof Item;
61
+ Separator: typeof Separator;
62
+ Label: typeof Label;
63
+ };
64
+ export {};
@@ -1,6 +1,6 @@
1
1
  import type * as React from "react";
2
2
  import { type ReactNode } from "react";
3
- export interface RootProps {
3
+ export interface RadioGroupProps {
4
4
  /** The selected item's value. Fully controlled. */
5
5
  value: string;
6
6
  /** Called with the newly selected value. */
@@ -9,15 +9,15 @@ export interface RootProps {
9
9
  disabled?: boolean;
10
10
  /** Merged onto the group wrapper. */
11
11
  className?: string;
12
- /** The Items. */
12
+ /** One or more `RadioGroup.Item`. */
13
13
  children?: ReactNode;
14
14
  }
15
15
  /**
16
16
  * A single-choice control for short lists, where seeing every option at once
17
17
  * matters more than saving space. Reach for Select when it does not.
18
18
  */
19
- export declare function Root({ value, onValueChange, disabled, className, children }: RootProps): React.JSX.Element;
20
- export interface ItemProps {
19
+ declare function RadioGroupRoot({ value, onValueChange, disabled, className, children }: RadioGroupProps): React.JSX.Element;
20
+ export interface RadioGroupItemProps {
21
21
  /** Reported to the group's onValueChange when chosen. */
22
22
  value: string;
23
23
  /** Disables this option only. */
@@ -25,8 +25,16 @@ export interface ItemProps {
25
25
  /** The option's label. Part of the click target, not merely beside it. */
26
26
  children?: ReactNode;
27
27
  }
28
- /** With children: self-labeling (<label> wraps input+orb+children) — the
29
- * SaveShareDialog pattern. Without children: emits a bare span so an
30
- * OUTER label (ConfigurationPanel's <Text as="label">) owns association —
31
- * nesting labels would be invalid HTML. */
32
- export declare function Item({ value, disabled, children }: ItemProps): React.JSX.Element;
28
+ /**
29
+ * One option.
30
+ *
31
+ * With children it labels itself: a <label> wraps the input, the orb and the
32
+ * children, so the text is part of the click target. Without children it
33
+ * emits a bare span instead, so an OUTER label can own the association —
34
+ * nesting one label inside another is invalid HTML.
35
+ */
36
+ declare function Item({ value, disabled, children }: RadioGroupItemProps): React.JSX.Element;
37
+ export declare const RadioGroup: typeof RadioGroupRoot & {
38
+ Item: typeof Item;
39
+ };
40
+ export {};