@gradeui/ui 3.2.0 → 3.3.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/components/ui/button.md +6 -4
- package/components/ui/color-picker.md +34 -0
- package/components/ui/fill-picker.md +7 -3
- package/components/ui/gradient-editor.md +30 -0
- package/components/ui/section.md +52 -0
- package/components/ui/swatch.md +5 -2
- package/components/ui/toggle-group.md +21 -4
- package/dist/contracts.js +6 -6
- package/dist/contracts.js.map +1 -1
- package/dist/contracts.mjs +6 -6
- package/dist/contracts.mjs.map +1 -1
- package/dist/index.d.mts +195 -6
- package/dist/index.d.ts +195 -6
- package/dist/index.js +88 -88
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +88 -88
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/styles/globals.css +163 -15
package/components/ui/button.md
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
name: Button
|
|
3
3
|
import: "@gradeui/ui"
|
|
4
4
|
variants: [default, destructive, outline, secondary, ghost, link, raised]
|
|
5
|
-
sizes: [sm, md, lg
|
|
5
|
+
sizes: [2xs, xs, sm, md, lg]
|
|
6
6
|
props:
|
|
7
7
|
- variant? (default | destructive | outline | secondary | ghost | link | raised) — `raised` here is a back-compat alias (the raised TRAIT on a neutral key surface); prefer the `raised` prop
|
|
8
8
|
- raised?: boolean — presence TRAIT: tactile elevation (bevel + drop + hover glow + pressed sink) layered onto ANY variant — raised primary, raised outline, etc. Glow tone reads --btn-glow → --accent-glow → --selected-glow; override per-button via style={{ "--btn-glow": "var(--warning)" }}
|
|
9
|
-
- size? (sm | md | lg
|
|
9
|
+
- size? (2xs | xs | sm | md | lg) — t-shirt scale aligned with Tabs/ToggleGroup heights (2xs=h-5, xs=h-6, sm=h-7, md=h-8, lg=h-10). 2xs/xs are the dense tool-panel sizes (match Figma Button size=2xs/xs). `default` still works as an alias for `md`.
|
|
10
|
+
- iconOnly?: boolean — squares the button at the current `size` height (w = h, no horizontal padding) for icon-only buttons; the icon child is centered. This is THE way to make a square icon button at any density (sm→28², 2xs→20²).
|
|
10
11
|
- asChild?: boolean — renders as the child element (use to wrap <a>/<Link>)
|
|
11
12
|
- disabled?: boolean
|
|
12
13
|
- All native button HTML attrs (onClick, type, etc.)
|
|
13
|
-
when_to_use: Any clickable action. Use
|
|
14
|
+
when_to_use: Any clickable action. Use `iconOnly` for square icon-only buttons (at any size), variant="link" for inline links that should look like Button, the `raised` prop for high-commitment / weighty actions where the chrome can afford a tactile "physical key" treatment (composes with any variant; variant="raised" remains the neutral-key alias). A Button placed next to a TabsList of the same size lines up edge-to-edge without per-call overrides.
|
|
14
15
|
composes_with: [Dialog, DropdownMenu, Tooltip, Card (in CardFooter), Row, Form controls]
|
|
15
16
|
aliases: [button, push button, plain button, bordered button, destructive button, capsule button, link button, action button, cta, raised button, pill button, key button]
|
|
16
17
|
---
|
|
@@ -18,7 +19,8 @@ aliases: [button, push button, plain button, bordered button, destructive button
|
|
|
18
19
|
```jsx
|
|
19
20
|
<Button>Save</Button>
|
|
20
21
|
<Button variant="outline" size="sm">Cancel</Button>
|
|
21
|
-
<Button
|
|
22
|
+
<Button iconOnly variant="ghost"><Mail /></Button>
|
|
23
|
+
<Button size="sm" iconOnly variant="outline"><Plus /></Button>
|
|
22
24
|
```
|
|
23
25
|
|
|
24
26
|
```jsx
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ColorPicker
|
|
3
|
+
import: "@gradeui/ui"
|
|
4
|
+
props:
|
|
5
|
+
- value?: string | null — a Grade colour token NAME ("action/primary"), the literal "transparent", or null when nothing is picked
|
|
6
|
+
- onValueChange?: (value: string | null) => void — fired with the next value (token name, "transparent", or null)
|
|
7
|
+
- tokens?: { group, tokens }[] — token families offered in the list; defaults to the Grade semantic set (surface / action / status)
|
|
8
|
+
- searchable?: boolean — show the search input (default true)
|
|
9
|
+
- triggerVariant? (default | inline) — default = form-control surface (swatch + name); inline = just a clickable swatch for inspector / fill-row use
|
|
10
|
+
- placeholder?: string — trigger text when nothing is selected
|
|
11
|
+
- searchPlaceholder?: string — search-input placeholder
|
|
12
|
+
- emptyMessage?: string — shown when search returns no rows
|
|
13
|
+
- allowTransparent?: boolean — include a Transparent option at the top (default true)
|
|
14
|
+
- align? (start | center | end) — popover alignment (default start)
|
|
15
|
+
- disabled?: boolean — lock to a read-only display of the current value
|
|
16
|
+
when_to_use: The token-led single-select colour picker — the focused "pick one colour token" sibling of FillPicker's solid tab. Use it anywhere a value is ONE Grade colour token (a fill colour, a border colour, an accent override) rather than a full paint. Composes Popover + Command exactly like Combobox, but each row is a Swatch + the token's short name, grouped by family and searchable. triggerVariant="inline" reduces the trigger to a single clickable swatch — reach for that inside inspectors and the FillSection fill rows. For a full paint (gradient / image / shader) use FillPicker; for a list of fills use FillSection; for a multi-stop gradient use GradientEditor.
|
|
17
|
+
composes_with: [Popover, Command, Swatch, FillSection, GradientEditor, Field, PropertyList]
|
|
18
|
+
aliases: [color picker, colour picker, token picker, colour token picker, color token picker, swatch picker, paint colour, fill colour picker, accent picker, colour dropdown]
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
```jsx
|
|
22
|
+
// Token-led colour field.
|
|
23
|
+
<ColorPicker value={color} onValueChange={setColor} />
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```jsx
|
|
27
|
+
// Inline swatch trigger — the inspector / fill-row affordance.
|
|
28
|
+
<ColorPicker
|
|
29
|
+
triggerVariant="inline"
|
|
30
|
+
value={stopColor}
|
|
31
|
+
onValueChange={setStopColor}
|
|
32
|
+
aria-label="Stop colour"
|
|
33
|
+
/>
|
|
34
|
+
```
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: FillPicker
|
|
3
3
|
import: "@gradeui/ui"
|
|
4
|
+
subcomponents: [FillSection]
|
|
4
5
|
props:
|
|
5
6
|
- value: FillValue — current paint ({ type, color?, gradient?, src?, fit?, repeat?, tileSize?, preset?, palette?, postPreset?, opacity? }) (required)
|
|
6
7
|
- onChange: (value: FillValue) => void — called on any change (required)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
- FillSection: value — FillValue[] — the ordered list of fills to stack as rows
|
|
9
|
+
- FillSection: onChange — (value: FillValue[]) => void — fired with the next list on add / edit / remove / visibility toggle
|
|
10
|
+
- FillSection: title?: string — section heading (default "Fills")
|
|
11
|
+
when_to_use: Grade's paint picker — the control for choosing a frame's background fill, modelled on Figma's fill popover. A fill-type icon row (solid · gradient · image · pattern · video · shader) switches the panel below; a global opacity sits at the foot. Emits a FillValue that maps 1:1 onto BackgroundFill props. This is a Studio/inspector chrome control — pair it with BackgroundFill, which renders the chosen paint. Not for app content. Use the FillSection subcomponent to edit a LIST of fills (the Figma "Fill" inspector section): each row is a Solid/Gradient/Image toggle, the matching value control (ColorPicker / GradientEditor popover / image URL), an opacity %, a visibility eye, and a remove button, with an add button in the header.
|
|
12
|
+
composes_with: [BackgroundFill (renders the FillValue), Popover (host it in a popover), ColorPicker (the solid value), GradientEditor (the gradient value), ShaderPresetPicker (the shader tab), the inspector Fill section]
|
|
13
|
+
aliases: [fill picker, paint picker, background picker, fill chooser, fill popover, fill section, fill list, fills inspector, paint section]
|
|
10
14
|
notes: |
|
|
11
15
|
Grade is token-led, so the solid + gradient tabs lead with theme-token
|
|
12
16
|
swatches (`primary`, `accent`, `secondary`, `muted`, `card`,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: GradientEditor
|
|
3
|
+
import: "@gradeui/ui"
|
|
4
|
+
props:
|
|
5
|
+
- value: { type, angle?, stops } — the structured gradient (type linear/radial/angular, optional angle in deg, ordered stops). NOT a CSS string — render the string via gradientToCss(value).
|
|
6
|
+
- onChange: (value) => void — fired with the next structured gradient on any edit
|
|
7
|
+
when_to_use: Edit a multi-stop CSS gradient with token-led stops. A type Select (Linear / Radial / Angular) with reverse + rotate actions, a live full-width preview bar (a Swatch type="gradient"), then a Stops list where each stop is a position %, a colour (ColorPicker token or raw), an opacity %, and a remove button; an add button appends a stop. Token stops resolve to oklch(var(--<token>)) so the preview re-voices with the theme. Emits the structured GradientValue (kept editable + serialisable); the caller turns it into CSS with the exported gradientToCss(value). Use inside a Popover from a FillSection gradient row, or standalone in a theme builder. For a single solid colour use ColorPicker; for a full paint (solid / gradient / image / shader) use FillPicker.
|
|
8
|
+
composes_with: [Select, Button, Input, ColorPicker, Swatch, Popover, FillSection]
|
|
9
|
+
aliases: [gradient editor, gradient picker, gradient builder, css gradient editor, stop editor, gradient stops, linear gradient editor, conic gradient editor]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
```jsx
|
|
13
|
+
<GradientEditor
|
|
14
|
+
value={{
|
|
15
|
+
type: "linear",
|
|
16
|
+
angle: 90,
|
|
17
|
+
stops: [
|
|
18
|
+
{ id: "a", position: 0, token: "action/primary", opacity: 1 },
|
|
19
|
+
{ id: "b", position: 100, token: "action/accent", opacity: 1 },
|
|
20
|
+
],
|
|
21
|
+
}}
|
|
22
|
+
onChange={setGradient}
|
|
23
|
+
/>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```jsx
|
|
27
|
+
// Render the CSS string for a background.
|
|
28
|
+
import { gradientToCss } from "@gradeui/ui";
|
|
29
|
+
<div style={{ background: gradientToCss(gradient) }} />
|
|
30
|
+
```
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Section
|
|
3
|
+
import: "@gradeui/ui"
|
|
4
|
+
subcomponents: [Container, SectionEyebrow, SectionTitle, SectionSubtitle, SectionDescription, SectionActions, SectionMedia]
|
|
5
|
+
props:
|
|
6
|
+
- Section: scope? (default | inverse | brand | accent | muted | card) — colour SUBTHEME; applies the `scope-*` class so the whole band re-tones (bg/fg/card/muted/border) while action colours stay vivid. Unset = the page surface. See STUDIO-COLOR.md.
|
|
7
|
+
- Section: background?: ReactNode — visual band background slot: image / video / gradient / shader (drop a <BackgroundFill> here). Renders BEHIND the content; Section owns the relative/overflow/z plumbing. Works with `scope` (which re-tones the content tokens so text stays legible over the media).
|
|
8
|
+
- Section: pad? (none | sm | md | lg | xl) — vertical rhythm (responsive py); default lg. Section is ALWAYS full width — it never sets a max width.
|
|
9
|
+
- Section: as? (section | header | footer | div) — semantic element; default section.
|
|
10
|
+
- Container: maxW? (sm | md | lg | xl | prose | full) — centred max width + gutters; default lg. The MEASURE.
|
|
11
|
+
- Container: grid?: boolean — snap children to a 12-column grid (use `col-span-*` on children); default false.
|
|
12
|
+
- Container: as? (div | section) — semantic element; default div.
|
|
13
|
+
when_to_use: THE page scaffold. A page is an ordered stack of Sections — every distinct band (hero, logos, features, pricing, testimonial, CTA, footer) gets its OWN Section so each is independently themeable. `Section` is the full-width band (scope + vertical rhythm); drop a `Container` inside it for a measure, or omit the Container for a full-bleed band. Reach for Section/Container instead of hand-rolling `<section className="py-20"><div className="max-w-7xl mx-auto px-6">`. The content inside is free — use the parts (SectionEyebrow/Title/Subtitle/Description/Actions/Media) for the common heading+copy+CTA+media shape, or drop any JSX. SectionMedia is a slot for any media (MediaSurface image, Carousel, VideoPlayer, embed, or a whole app UI). Don't use Section for app chrome — that's AppShell.
|
|
14
|
+
composes_with: [Container, MediaSurface, Carousel, VideoPlayer, Button, Badge, Card, Grid, Stack]
|
|
15
|
+
aliases: [section, band, hero section, page section, content section, marketing section, landing section, full bleed, container, max width wrapper, page band, section block]
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
```jsx
|
|
19
|
+
// A page is a stack of Sections. Each band picks a scope; a Container
|
|
20
|
+
// holds the measure (omit it to let the band bleed full-width).
|
|
21
|
+
<Section scope="inverse" pad="xl">
|
|
22
|
+
<Container maxW="lg">
|
|
23
|
+
<SectionEyebrow>New</SectionEyebrow>
|
|
24
|
+
<SectionTitle>Use the agent you prefer.</SectionTitle>
|
|
25
|
+
<SectionSubtitle>Own the components. Ship on your subscription.</SectionSubtitle>
|
|
26
|
+
<SectionActions>
|
|
27
|
+
<Button size="lg">Open Studio</Button>
|
|
28
|
+
<Button size="lg" variant="outline">Docs</Button>
|
|
29
|
+
</SectionActions>
|
|
30
|
+
</Container>
|
|
31
|
+
</Section>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```jsx
|
|
35
|
+
// Full-bleed media band — no Container, so the media spans edge to edge.
|
|
36
|
+
// The scope re-tones the band; the media frames itself.
|
|
37
|
+
<Section scope="card" pad="lg">
|
|
38
|
+
<SectionMedia>
|
|
39
|
+
<MediaSurface hint="Studio canvas" alt="A generated screen" className="aspect-[21/9] w-full" />
|
|
40
|
+
</SectionMedia>
|
|
41
|
+
</Section>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```jsx
|
|
45
|
+
// Contained content on a grid — children snap to the 12-col Container grid.
|
|
46
|
+
<Section pad="lg">
|
|
47
|
+
<Container grid>
|
|
48
|
+
<div className="col-span-12 md:col-span-7">{/* lead */}</div>
|
|
49
|
+
<div className="col-span-12 md:col-span-5">{/* aside */}</div>
|
|
50
|
+
</Container>
|
|
51
|
+
</Section>
|
|
52
|
+
```
|
package/components/ui/swatch.md
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
name: Swatch
|
|
3
3
|
import: "@gradeui/ui"
|
|
4
4
|
subcomponents: [SwatchGroup]
|
|
5
|
-
sizes: [xs, sm, md, lg, xl]
|
|
5
|
+
sizes: [2xs, xs, sm, md, lg, xl]
|
|
6
6
|
props:
|
|
7
7
|
- color?: string — any raw CSS colour (`#1f6feb`, `oklch(...)`, `rgb(...)`, or `var(--x)`). Takes precedence over `token`. Use for one-off or external colours.
|
|
8
8
|
- token?: string — a Grade colour token NAME with no `--` and no `oklch()` wrap; resolved internally to `oklch(var(--<token>))`. THE design-system path — e.g. `token="brand-3"`, `token="primary"`, `token="chart-2"`. Re-voices live when the theme changes.
|
|
9
|
-
-
|
|
9
|
+
- type? (solid | gradient | image) — fill kind; default solid (or inferred from `image` / `gradient`). Determines what the chip renders in place.
|
|
10
|
+
- gradient?: string — CSS gradient for `type="gradient"`, e.g. `linear-gradient(135deg,#6366f1,#ec4899)`.
|
|
11
|
+
- image?: string — image URL for `type="image"`; rendered cover-fit behind the chip.
|
|
12
|
+
- size? (2xs | xs | sm | md | lg | xl) — t-shirt scale, 16px → 56px; default md (32px). 2xs (16px) suits dense colour lists. Prefer over h-*/w-* utilities.
|
|
10
13
|
- shape? (square | rounded | circle) — default rounded (rides `--radius`); circle for dot pickers; square for a hard tile.
|
|
11
14
|
- selected?: boolean — draws the shared selection ring (`--selected`). For palette / accent pickers.
|
|
12
15
|
- onSelect?: () => void — makes the swatch a pickable <button> (adds aria-pressed, focus ring, hover lift). Omit for a static display chip.
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
name: ToggleGroup
|
|
3
3
|
import: "@gradeui/ui"
|
|
4
4
|
subcomponents: [ToggleGroupItem]
|
|
5
|
-
variants: [default, outline]
|
|
6
|
-
sizes: [sm, md, lg]
|
|
5
|
+
variants: [default, outline, segmented]
|
|
6
|
+
sizes: [2xs, xs, sm, md, lg]
|
|
7
7
|
props:
|
|
8
8
|
- ToggleGroup: type: "single" | "multiple" — single picks one, multiple picks any number
|
|
9
9
|
- ToggleGroup: value?: string | string[] — controlled; matches `type` (string for single, string[] for multiple)
|
|
10
10
|
- ToggleGroup: defaultValue?: string | string[] — uncontrolled initial
|
|
11
11
|
- ToggleGroup: onValueChange?: (value: string | string[]) => void
|
|
12
|
-
- ToggleGroup: size? (sm | md | lg, default md) — cascades to every ToggleGroupItem via context, matches Tabs/Button heights
|
|
13
|
-
- ToggleGroup: variant? (default | outline)
|
|
12
|
+
- ToggleGroup: size? (2xs | xs | sm | md | lg, default md) — cascades to every ToggleGroupItem via context, matches Tabs/Button heights; 2xs/xs are the dense tool-panel sizes (2xs also drops text to text-2xs and icons to size-3 so labelled items read at panel density)
|
|
13
|
+
- ToggleGroup: variant? (default | outline | segmented) — segmented sits the items in a muted track with the active item as a soft raised pill, so it reads like a TabsList; reach for it in dense property panels (e.g. a Row/Stack direction toggle)
|
|
14
14
|
- ToggleGroupItem: value: string — what the group reports when this item is pressed
|
|
15
15
|
- ToggleGroupItem: tooltip?: ReactNode — when set, wraps the item in a Tooltip; required for icon-only items where the visible chrome doesn't carry a label
|
|
16
16
|
- ToggleGroupItem: tooltipSide? ("top" | "right" | "bottom" | "left", default "top") — side the tooltip renders on
|
|
@@ -41,3 +41,20 @@ aliases: [toggle group, segmented control, segmented buttons, button group, pill
|
|
|
41
41
|
<ToggleGroupItem value="underline" aria-label="Underline"><Underline /></ToggleGroupItem>
|
|
42
42
|
</ToggleGroup>
|
|
43
43
|
```
|
|
44
|
+
|
|
45
|
+
```jsx
|
|
46
|
+
// Segmented variant + 2xs — a dense property-panel toggle. Reads like a
|
|
47
|
+
// tab strip (muted track, active pill) but emits a value, so it's a form
|
|
48
|
+
// control, not panel-switching. This is the Studio Row/Stack control.
|
|
49
|
+
<ToggleGroup
|
|
50
|
+
type="single"
|
|
51
|
+
variant="segmented"
|
|
52
|
+
size="2xs"
|
|
53
|
+
value={direction}
|
|
54
|
+
onValueChange={(v) => v && setDirection(v)}
|
|
55
|
+
className="w-full"
|
|
56
|
+
>
|
|
57
|
+
<ToggleGroupItem value="row" className="flex-1"><Columns3 /> Row</ToggleGroupItem>
|
|
58
|
+
<ToggleGroupItem value="col" className="flex-1"><Rows3 /> Stack</ToggleGroupItem>
|
|
59
|
+
</ToggleGroup>
|
|
60
|
+
```
|