@gradeui/ui 0.10.0 → 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.
Files changed (86) hide show
  1. package/components/ui/accordion.md +1 -1
  2. package/components/ui/ai-chat-composer.md +37 -0
  3. package/components/ui/ai-chat.md +68 -22
  4. package/components/ui/alert.md +0 -21
  5. package/components/ui/app-shell.md +135 -18
  6. package/components/ui/avatar.md +12 -1
  7. package/components/ui/badge.md +2 -2
  8. package/components/ui/banner.md +146 -0
  9. package/components/ui/breadcrumb.md +49 -2
  10. package/components/ui/button.md +35 -3
  11. package/components/ui/calendar.md +1 -1
  12. package/components/ui/callout.md +45 -0
  13. package/components/ui/card.md +176 -6
  14. package/components/ui/carousel.md +56 -0
  15. package/components/ui/chart.md +1 -1
  16. package/components/ui/checkbox.md +1 -0
  17. package/components/ui/code.md +132 -0
  18. package/components/ui/collapsible.md +1 -1
  19. package/components/ui/command.md +1 -1
  20. package/components/ui/date-picker.md +1 -1
  21. package/components/ui/dialog.md +110 -6
  22. package/components/ui/dropdown-menu.md +97 -2
  23. package/components/ui/flex.md +1 -1
  24. package/components/ui/grid.md +1 -1
  25. package/components/ui/hover-card.md +98 -4
  26. package/components/ui/input.md +1 -1
  27. package/components/ui/label.md +1 -0
  28. package/components/ui/map.md +2 -2
  29. package/components/ui/media-surface.md +50 -7
  30. package/components/ui/multi-select.md +114 -0
  31. package/components/ui/popover.md +123 -4
  32. package/components/ui/progress.md +1 -0
  33. package/components/ui/radio-group.md +1 -1
  34. package/components/ui/resizable.md +1 -1
  35. package/components/ui/row.md +1 -1
  36. package/components/ui/scroll-area.md +1 -1
  37. package/components/ui/section-block.md +153 -0
  38. package/components/ui/select.md +1 -1
  39. package/components/ui/separator.md +1 -1
  40. package/components/ui/sheet.md +102 -4
  41. package/components/ui/side-menu.md +0 -40
  42. package/components/ui/sidebar.md +121 -0
  43. package/components/ui/simple-tabs.md +0 -27
  44. package/components/ui/skeleton.md +1 -1
  45. package/components/ui/slider.md +1 -1
  46. package/components/ui/sortable.md +101 -0
  47. package/components/ui/stack.md +19 -1
  48. package/components/ui/switch.md +1 -1
  49. package/components/ui/table.md +1 -0
  50. package/components/ui/tabs.md +19 -2
  51. package/components/ui/textarea.md +1 -1
  52. package/components/ui/toast.md +2 -2
  53. package/components/ui/toggle-group.md +12 -5
  54. package/components/ui/toolbar.md +167 -0
  55. package/components/ui/tooltip.md +1 -1
  56. package/components/ui/video-player.md +2 -2
  57. package/dist/contracts.d.mts +14 -0
  58. package/dist/contracts.d.ts +14 -0
  59. package/dist/contracts.js +63 -0
  60. package/dist/contracts.js.map +1 -0
  61. package/dist/contracts.mjs +63 -0
  62. package/dist/contracts.mjs.map +1 -0
  63. package/dist/index.d.mts +1651 -185
  64. package/dist/index.d.ts +1651 -185
  65. package/dist/index.js +123 -52
  66. package/dist/index.js.map +1 -1
  67. package/dist/index.mjs +123 -52
  68. package/dist/index.mjs.map +1 -1
  69. package/dist/map/google.js +1 -0
  70. package/dist/map/google.js.map +1 -1
  71. package/dist/map/google.mjs +1 -0
  72. package/dist/map/google.mjs.map +1 -1
  73. package/dist/map/mapbox.js +1 -0
  74. package/dist/map/mapbox.js.map +1 -1
  75. package/dist/map/mapbox.mjs +1 -0
  76. package/dist/map/mapbox.mjs.map +1 -1
  77. package/dist/map/maplibre.js +1 -0
  78. package/dist/map/maplibre.js.map +1 -1
  79. package/dist/map/maplibre.mjs +1 -0
  80. package/dist/map/maplibre.mjs.map +1 -1
  81. package/dist/styles.css +1 -1
  82. package/dist/tailwind-preset.js +1 -1
  83. package/dist/tailwind-preset.js.map +1 -1
  84. package/dist/tailwind-preset.mjs +1 -1
  85. package/dist/tailwind-preset.mjs.map +1 -1
  86. package/package.json +28 -9
@@ -1,16 +1,17 @@
1
1
  ---
2
2
  name: Button
3
3
  import: "@gradeui/ui"
4
- variants: [default, destructive, outline, secondary, ghost, link]
4
+ variants: [default, destructive, outline, secondary, ghost, link, raised]
5
5
  sizes: [sm, md, lg, icon]
6
6
  props:
7
- - variant? (default | destructive | outline | secondary | ghost | link)
7
+ - variant? (default | destructive | outline | secondary | ghost | link | raised)
8
8
  - size? (sm | md | lg | icon) — t-shirt scale aligned with Tabs/ToggleGroup heights (sm=h-7, md=h-8, lg=h-10). `default` still works as an alias for `md`.
9
9
  - asChild?: boolean — renders as the child element (use to wrap <a>/<Link>)
10
10
  - disabled?: boolean
11
11
  - All native button HTML attrs (onClick, type, etc.)
12
- when_to_use: Any clickable action. Use size="icon" for square icon-only buttons, variant="link" for inline links that should look like Button. A Button placed next to a TabsList of the same size lines up edge-to-edge without per-call overrides.
12
+ when_to_use: Any clickable action. Use size="icon" for square icon-only buttons, variant="link" for inline links that should look like Button, variant="raised" for high-commitment / weighty actions where the chrome can afford a tactile "physical key" treatment. A Button placed next to a TabsList of the same size lines up edge-to-edge without per-call overrides.
13
13
  composes_with: [Dialog, DropdownMenu, Tooltip, Card (in CardFooter), Row, Form controls]
14
+ aliases: [button, push button, plain button, bordered button, destructive button, capsule button, link button, action button, cta, raised button, pill button, key button]
14
15
  ---
15
16
 
16
17
  ```jsx
@@ -29,3 +30,34 @@ composes_with: [Dialog, DropdownMenu, Tooltip, Card (in CardFooter), Row, Form c
29
30
  <Button size="sm">New issue</Button>
30
31
  </Row>
31
32
  ```
33
+
34
+ ```jsx
35
+ // Raised variant — tactile bevel + drop shadow + ambient hover glow.
36
+ // Composed from the Presence elevation tokens (--elevation-3 rest,
37
+ // --elevation-hot hover, --elevation-pressed active). Tone is driven
38
+ // by --btn-glow, which defaults to --selected-glow (blue). Override
39
+ // per-button for "traffic light" semantics:
40
+ <Row gap="sm">
41
+ <Button variant="raised" style={{ "--btn-glow": "var(--warning)" }}>
42
+ Iterate
43
+ </Button>
44
+ <Button variant="raised" style={{ "--btn-glow": "var(--success)" }}>
45
+ Ship it
46
+ </Button>
47
+ </Row>
48
+ ```
49
+
50
+ ```jsx
51
+ // data-state="on" / aria-pressed="true" gives the held-down "key
52
+ // pressed" look — picks up the --selected blue stroke + heat-inner
53
+ // glow. Works as a Toggle/ToggleGroupItem child via asChild.
54
+ <Button variant="raised" data-state="on">Locked</Button>
55
+ ```
56
+
57
+ ```jsx
58
+ // Combine with Aura for AI-attention states. The three Aura styles
59
+ // (ring/gradient/shimmer) stack independently of the variant.
60
+ <Button variant="raised" className="gds-aura-ring">
61
+ Studio is reviewing this
62
+ </Button>
63
+ ```
@@ -15,7 +15,7 @@ props:
15
15
  - className?: string
16
16
  when_to_use: An inline date grid — date-of-birth pickers in profile forms, scheduling screens with a month view, range selection in reporting filters. For a compact trigger-and-popover input, use DatePicker / DateRangePicker (which wrap Calendar internally). For one-off relative dates ("yesterday", "last week") use a Select instead.
17
17
  composes_with: [Popover (DatePicker composes them), Card (inline scheduling card), Dialog (full-screen mobile date pick)]
18
- aliases: [calendar, date grid, month view, scheduler grid]
18
+ aliases: [calendar, date grid, month view, scheduler grid, calendar view, multidate picker, react native calendars]
19
19
  ---
20
20
 
21
21
  ```jsx
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: Callout
3
+ import: "@gradeui/ui"
4
+ subcomponents: [CalloutTitle, CalloutDescription]
5
+ variants: [default, destructive, success, warning, info]
6
+ props:
7
+ - variant? (default | destructive | success | warning | info) — semantic colouring; `default` is neutral
8
+ - All native div HTML attrs
9
+ when_to_use: Inline, ambient, non-blocking status/feedback that sits inside the layout flow. Form-level validation summaries, settings-page notices, page-level banners. NOT a toast (use Sonner for transient). NOT a modal (use Dialog when the user must respond). Put an icon as first child — it's auto-positioned; CalloutTitle + CalloutDescription follow.
10
+ composes_with: [lucide-react icons as first child, Button (inside CalloutDescription), Card (as a section callout)]
11
+ aliases: [callout, banner, notice, inline alert, in-app notification, status banner, info banner, info callout, warning callout, success callout]
12
+ ---
13
+
14
+ Renamed from `Alert` (May 2026). The old name implied modal/interruptive behaviour the component doesn't have — Apple HIG `Alert` is a modal, and `role="alert"` is assertive ARIA. Callout is honest about what it is: ambient, inline, non-blocking. For genuinely interruptive needs, reach for `<Dialog>`.
15
+
16
+ Variant tokens come from theme (`--destructive-soft`, `--success-deep`, etc.) so they restyle with the active Grade theme.
17
+
18
+ ```jsx
19
+ <Callout variant="warning">
20
+ <AlertTriangle />
21
+ <CalloutTitle>Low disk space</CalloutTitle>
22
+ <CalloutDescription>2GB remaining on /dev/sda1.</CalloutDescription>
23
+ </Callout>
24
+ ```
25
+
26
+ ```jsx
27
+ // Ambient success notice — uses role="status" (polite) so screen
28
+ // readers don't interrupt the user. Warning/destructive get
29
+ // role="alert" (assertive) instead.
30
+ <Callout variant="success">
31
+ <CheckCircle2 />
32
+ <CalloutTitle>Profile updated</CalloutTitle>
33
+ <CalloutDescription>Your changes are live.</CalloutDescription>
34
+ </Callout>
35
+ ```
36
+
37
+ ### Anti-patterns
38
+
39
+ DO NOT use `<Callout>` for interruptive or blocking messages. If the user must respond before continuing, use `<Dialog>` — the modal primitive that Apple HIG calls "Alert" and React Native exposes as `Alert.alert()`. Callout is ambient by design.
40
+
41
+ DO NOT pass `role="alert"` when the variant is `info` / `success` / `default` — the component already routes those to `role="status"` (polite), and overriding makes screen readers interrupt for non-urgent content.
42
+
43
+ DO NOT reach for `variant="warning"` to convey "this is just notable / FYI" — that's what `variant="info"` is for. Warning is for things that could go wrong if ignored; info is for ambient context.
44
+
45
+ The previous `variant="highlight"` (yellow) was dropped in the Alert → Callout rename — it overlapped `warning` semantically without offering a distinct intent. Use `warning` for amber attention and `info` for neutral attention.
@@ -3,13 +3,26 @@ name: Card
3
3
  import: "@gradeui/ui"
4
4
  subcomponents: [CardHeader, CardTitle, CardDescription, CardContent, CardFooter]
5
5
  props:
6
+ - surface? (solid | translucent | glass | glass-strong) — what the card surface is *made of*. `solid` is the default opaque `bg-card`. `translucent` is ~82% opacity for menu sheets. `glass` is ~58% opacity + 14px blur + edge highlight for floating panels. `glass-strong` is ~42% + 24px blur for full-page overlays. Composes with `shadow-elevation-*` (depth) and `gds-aura-*` (state signal).
6
7
  - Each subcomponent accepts native div HTML attrs (className, etc.)
7
- - No variantsCard is a flexible container surface
8
- when_to_use: Grouped content with a distinct surface — settings panels, dashboard tiles, list-of-cards layouts. Pair CardHeader (title + description) with CardContent and optional CardFooter (actions).
9
- composes_with: [Button (in CardFooter), Badge, Separator, Avatar, any form controls]
8
+ when_to_use: Grouped content with a distinct surface settings panels, dashboard tiles, list-of-cards layouts, marketing hero containers, AI suggestion overlays. Pair CardHeader (title + description) with CardContent and optional CardFooter (actions). Reach for `surface="glass"` whenever the card sits over a busy backdrop (gradient mesh, dot grid, generative art, image hero).
9
+ composes_with: [Button (in CardFooter), Badge, Separator, Avatar, Code, MediaSurface, any form controls]
10
+ aliases: [card, group box, groupbox, panel, tile, surface, glass card, frosted card, floating panel, hero card, ai suggestion card, dashboard tile, settings panel]
10
11
  ---
11
12
 
12
- Canonical structure do NOT skip CardHeader if the card has a title:
13
+ Card is the most common host for the **Presence** system (PRESENCE.md). Three independent axes layer on top of every card:
14
+
15
+ - **Surface** — what it's made of (`surface` prop: solid / translucent / glass / glass-strong)
16
+ - **Elevation** — how high it sits (`shadow-elevation-1..5` utility)
17
+ - **Aura** — what it's radiating (`gds-aura-ring`, `gds-aura-gradient`, `gds-aura-shimmer`)
18
+
19
+ The four scenarios below are the canonical recipes. Match the scenario to the screen you're building.
20
+
21
+ ---
22
+
23
+ ### Scenario 1 — Settings panel (default opaque)
24
+
25
+ You want a grouped content surface on a normal page: a settings panel, a list-of-cards tile, a dashboard widget. The page background is calm; the card just needs to sit cleanly on it.
13
26
 
14
27
  ```jsx
15
28
  <Card>
@@ -17,9 +30,166 @@ Canonical structure — do NOT skip CardHeader if the card has a title:
17
30
  <CardTitle>Billing</CardTitle>
18
31
  <CardDescription>Manage your subscription.</CardDescription>
19
32
  </CardHeader>
20
- <CardContent>…</CardContent>
33
+ <CardContent>
34
+ <Stack gap="md">
35
+ <Row justify="between">
36
+ <span className="text-sm">Plan</span>
37
+ <Badge>Pro</Badge>
38
+ </Row>
39
+ <Row justify="between">
40
+ <span className="text-sm">Renews</span>
41
+ <span className="text-sm text-muted-foreground">12 Jun 2026</span>
42
+ </Row>
43
+ </Stack>
44
+ </CardContent>
21
45
  <CardFooter>
22
- <Button>Save</Button>
46
+ <Button variant="outline">Cancel plan</Button>
47
+ <Button>Update payment</Button>
23
48
  </CardFooter>
24
49
  </Card>
25
50
  ```
51
+
52
+ No `surface` prop — the default `solid` is the right answer for almost every in-page card. Reach for glass only when there's something behind worth blurring.
53
+
54
+ ---
55
+
56
+ ### Scenario 2 — Glass card over a busy backdrop (marketing hero)
57
+
58
+ You're building a marketing hero. There's a gradient mesh, a dot grid, generative art, or a hero image behind the card. The card should read as **floating chrome** — translucent enough to let the backdrop breathe through, but with a defined edge so the content stays legible.
59
+
60
+ ```jsx
61
+ <SectionBlock background="gradient" padding="xl">
62
+ <Grid cols="2" gap="md">
63
+ <Card surface="glass" className="shadow-elevation-4">
64
+ <CardHeader>
65
+ <CardTitle>v0 — sidebar component</CardTitle>
66
+ <CardDescription>~300 lines</CardDescription>
67
+ </CardHeader>
68
+ <CardContent className="p-0">
69
+ <Code source={v0Code} language="tsx" bare className="p-4 text-xs max-h-72" />
70
+ </CardContent>
71
+ </Card>
72
+
73
+ <Card surface="glass" className="shadow-elevation-4 gds-aura-ring">
74
+ <CardHeader>
75
+ <CardTitle>GradeUI — sidebar component</CardTitle>
76
+ <CardDescription>6 lines</CardDescription>
77
+ </CardHeader>
78
+ <CardContent className="p-0">
79
+ <Code source={gradeCode} language="tsx" bare className="p-4 text-xs max-h-72" />
80
+ </CardContent>
81
+ </Card>
82
+ </Grid>
83
+ </SectionBlock>
84
+ ```
85
+
86
+ `surface="glass"` does five things at once: 58% opacity `bg-card`, 14px backdrop blur, an inner edge highlight (the "wet" rim that gives glass its boundary), a faint border, and it drops the base `bg-card` so the alpha actually shows. Layering `shadow-elevation-4` adds the floating-popover drop shadow; `gds-aura-ring` makes the second card pulse with a blue halo to signal "this is the recommended path".
87
+
88
+ ---
89
+
90
+ ### Scenario 3 — Translucent menu sheet (floating chrome with structure)
91
+
92
+ You want a floating panel — a command palette, a notification drawer, an AI suggestion overlay — that's visibly distinct from the canvas but doesn't need full glass blur. Translucent is for "I want presence without drama".
93
+
94
+ ```jsx
95
+ <Card surface="translucent" className="shadow-elevation-5 w-80">
96
+ <CardHeader>
97
+ <CardTitle>Suggested action</CardTitle>
98
+ <CardDescription>Studio noticed a layout opportunity.</CardDescription>
99
+ </CardHeader>
100
+ <CardContent>
101
+ <p className="text-sm">
102
+ Three buttons in your toolbar would line up edge-to-edge with the
103
+ tabs below if their size matched. Apply <code>size="sm"</code>?
104
+ </p>
105
+ </CardContent>
106
+ <CardFooter>
107
+ <Button variant="ghost" size="sm">Dismiss</Button>
108
+ <Button size="sm">Apply</Button>
109
+ </CardFooter>
110
+ </Card>
111
+ ```
112
+
113
+ 82% opacity is enough to feel layered but not enough to need backdrop blur — works equally well over a busy or a calm background. `shadow-elevation-5` (dialog tier) plus `translucent` is the "floating but not glass" signature.
114
+
115
+ ---
116
+
117
+ ### Scenario 4 — AI is generating (aura + surface composition)
118
+
119
+ You want to signal that Studio (or any AI agent) is actively working on this card. Aura is the right axis for state signals. It composes with any surface.
120
+
121
+ ```jsx
122
+ <Card
123
+ surface="glass"
124
+ className="shadow-elevation-4 gds-aura-ring gds-aura-shimmer"
125
+ style={{ "--aura-color": "var(--selected-glow)" }}
126
+ >
127
+ <CardHeader>
128
+ <CardTitle>Generating layout</CardTitle>
129
+ <CardDescription>About 4 seconds remaining.</CardDescription>
130
+ </CardHeader>
131
+ <CardContent>
132
+ <Stack gap="xs">
133
+ <Skeleton className="h-4 w-3/4" />
134
+ <Skeleton className="h-4 w-1/2" />
135
+ <Skeleton className="h-4 w-2/3" />
136
+ </Stack>
137
+ </CardContent>
138
+ </Card>
139
+ ```
140
+
141
+ Ring (pulsing halo) + shimmer (diagonal sweep) together = "actively generating". For "Studio is reviewing this", use ring alone. For "ready to ship", swap tone to `--success`. The skeletons inside are the content's own loading state — orthogonal to the card-level aura.
142
+
143
+ ---
144
+
145
+ ### Scenario 5 — Glass-strong for a full-page overlay backdrop
146
+
147
+ `surface="glass-strong"` is tuned for a different job than the other three: it's the **backdrop** behind a modal sheet, not the modal itself. Heavy blur (24px), 42% opacity. Use it to de-emphasise the page underneath while keeping it readable.
148
+
149
+ ```jsx
150
+ <Card surface="glass-strong" className="fixed inset-4 z-50">
151
+ <CardContent className="grid place-items-center h-full">
152
+ <Stack gap="md" align="center">
153
+ <Spinner />
154
+ <span className="text-lg">Saving your theme…</span>
155
+ </Stack>
156
+ </CardContent>
157
+ </Card>
158
+ ```
159
+
160
+ Almost always wrong for in-flow content — at 42% opacity the card reads as washed out. If you find yourself reaching for glass-strong for a regular card, you probably want `glass`.
161
+
162
+ ---
163
+
164
+ ### Anti-patterns
165
+
166
+ **DO NOT roll glass by hand with Tailwind utilities.** The wrong path:
167
+
168
+ ```jsx
169
+ {/* ❌ Tailwind soup — misses edge highlight, locks blur to a fixed step,
170
+ bypasses theme tuning, no Studio inspector knob. */}
171
+ <Card className="overflow-hidden border-border bg-card/40 backdrop-blur-md">
172
+ ```
173
+
174
+ The right path:
175
+
176
+ ```jsx
177
+ {/* ✅ Theme-aware bg, tuned blur, edge highlight, knob-discoverable. */}
178
+ <Card surface="glass">
179
+ ```
180
+
181
+ This is the single most common mistake. The model reaches for `bg-card/40 backdrop-blur-md` because every other DS leaves glass at the utility layer. Ours doesn't.
182
+
183
+ **DO NOT layer a solid `bg-card` className over `surface="glass"`.** The opaque fill defeats the blur. Card already drops `bg-card` when `surface` is set to anything other than `solid` — don't undo that by tacking `bg-card` back on via className. If you want a tinted glass, override `--card` on the element:
184
+
185
+ ```jsx
186
+ <Card surface="glass" style={{ "--card": "0.99 0.04 250" }}>
187
+ ...
188
+ </Card>
189
+ ```
190
+
191
+ **DO NOT use `surface="glass"` over a solid background.** Glass needs something behind it to blur. Over plain `bg-background` it reads as a slightly washed-out card and you pay for backdrop-filter for no gain. If the page is calm, use `solid`.
192
+
193
+ **DO NOT use `surface="glass-strong"` for in-flow content.** It's a full-page overlay material. At 42% opacity, regular cards read as washed out. Reach for `glass`.
194
+
195
+ **DO NOT skip CardHeader if the card has a title.** The header is the semantic anchor for the title + description pair. Inline `<h3>` inside CardContent breaks the visual rhythm and harms screen-reader navigation.
@@ -0,0 +1,56 @@
1
+ ---
2
+ name: Carousel
3
+ import: "@gradeui/ui"
4
+ subcomponents: [Carousel.Slide, Carousel.VideoSlide, Carousel.Dots, Carousel.Arrows]
5
+ props:
6
+ - Carousel: loop?: boolean — wrap last → first (default true)
7
+ - Carousel: align?: "start" | "center" | "end" — slide alignment (default start)
8
+ - Carousel: slidesPerView?: number — how many slides visible at once (default 1)
9
+ - Carousel: autoplay?: boolean | { delay?: number; pauseOnHover?: boolean; pauseWhenOffscreen?: boolean } — true for defaults (5s, hover/offscreen aware)
10
+ - Carousel: draggable?: boolean — drag-to-swipe (default true)
11
+ - Carousel: onSlideChange?: (index: number) => void
12
+ - Carousel.Slide: duration?: number — per-slide autoplay duration in ms; overrides the carousel default for this slide only
13
+ - Carousel.VideoSlide: src: string — video URL
14
+ - Carousel.VideoSlide: poster?: string — image shown until the slide is active
15
+ - Carousel.VideoSlide: alt?: string — accessible label for the video
16
+ - Carousel.VideoSlide: loop?: boolean — default true (the chosen video-default behaviour)
17
+ - Carousel.VideoSlide: controls?: boolean — default false (chosen default = no controls)
18
+ - Carousel.VideoSlide: fit?: "cover" | "contain" — object-fit (default cover)
19
+ - Carousel.VideoSlide: duration?: number — same as Carousel.Slide; overrides autoplay timing for THIS slide
20
+ - Carousel.Dots: position?: "below" | "overlay"
21
+ - Carousel.Arrows: position?: "overlay" | "outside"
22
+ when_to_use: Anywhere a horizontal stack of slides cycles automatically or on user input — marketing hero rotations, featured rails on a TV / streaming app, onboarding tours, image galleries, product carousels, testimonial cycles. Mixed video + still slides are a first-class case; the VideoSlide handles muted-autoplay + poster swap on activation.
23
+ composes_with: [MediaSurface, Card, Stack, Row]
24
+ aliases: [carousel, slideshow, slider, hero rotation, image gallery, featured row, swipe deck, paged view, page tabview, page view, swiper, react native swiper, page control]
25
+ ---
26
+
27
+ ```jsx
28
+ <Carousel autoplay={{ delay: 6000 }} loop>
29
+ <Carousel.Slide duration={15000}>
30
+ <MediaSurface aspect="wide" hint="poster" alt="Featured: Severance S2" />
31
+ </Carousel.Slide>
32
+
33
+ <Carousel.VideoSlide
34
+ src="/trailers/the-studio.mp4"
35
+ poster="/posters/the-studio.jpg"
36
+ alt="The Studio — official trailer"
37
+ />
38
+
39
+ <Carousel.Slide>
40
+ <MediaSurface aspect="wide" hint="poster" alt="Coming soon: Foundation S3" />
41
+ </Carousel.Slide>
42
+
43
+ <Carousel.Arrows />
44
+ <Carousel.Dots position="overlay" />
45
+ </Carousel>
46
+ ```
47
+
48
+ ### Anti-patterns
49
+
50
+ DO NOT confuse `<Carousel>` with `<Slider>`. `Slider` is the range input (a draggable thumb on a track) — the colloquial "slider" you'd put on a marketing page is a `Carousel`. When the user says "add a slider", check whether they want a range control or a slideshow before reaching for either.
51
+
52
+ DO NOT pass real `<img>` or `<video>` tags directly as `Carousel.Slide` children when the slide is meant to be a hero media tile. Use `<MediaSurface>` (still slots) or `<Carousel.VideoSlide>` (video slots) so themes, aspect ratios, and the future image-generation pipeline stay consistent. Raw `<img>` inside a slide is fine for fully-authored content (logo strips, certificates), but for "media that might get regenerated" the surface primitive is mandatory.
53
+
54
+ DO NOT set very short `duration` values (sub-2000ms) on still slides — the autoplay timer ignores the request implicitly when the carousel is paused (hover, offscreen) but very fast cycles read as broken to users. 5-15 seconds per slide is the natural range.
55
+
56
+ DO NOT mount the autoplay timer inside individual slides via `setInterval` and forget to clean up — use `<Carousel.Slide duration>` instead. The carousel root owns the single timer; per-slide overrides feed into it through a context-shared ref.
@@ -12,7 +12,7 @@ props:
12
12
  - ChartLegend / ChartLegendContent: pair the same way for the legend
13
13
  when_to_use: Reporting dashboards, single-purpose analytics cards (revenue, conversions, active users), or anywhere you'd otherwise hand-roll a Recharts setup. Bring the actual chart type from `recharts` — ChartContainer doesn't pick the chart shape for you, it themes whatever you nest. For sparkline-style decorative trends consider just rendering a small SVG line directly; ChartContainer is overkill for non-interactive ornament.
14
14
  composes_with: [Card (chart-in-a-card pattern), Tabs (multi-metric switcher), Recharts components (Bar, Line, Area, Pie, Radar from "recharts")]
15
- aliases: [chart, charts, graph, bar chart, line chart, area chart, recharts, analytics chart]
15
+ aliases: [chart, charts, graph, bar chart, line chart, area chart, recharts, analytics chart, swift chart, swiftui chart, victory chart, victory native]
16
16
  ---
17
17
 
18
18
  ```jsx
@@ -9,6 +9,7 @@ props:
9
9
  - id?: string — bind a Label's htmlFor to this
10
10
  when_to_use: Binary on/off tied to a list (select multiple, agree to terms). Single on/off that controls a setting is better with Switch.
11
11
  composes_with: [Label (via htmlFor), Card, Form rows, Table (for row selection)]
12
+ aliases: [checkbox, tickbox, tick box, check, multi-select item]
12
13
  ---
13
14
 
14
15
  ```jsx
@@ -0,0 +1,132 @@
1
+ ---
2
+ name: Code
3
+ import: "@gradeui/ui"
4
+ variants: []
5
+ props:
6
+ - source: string — the code to render
7
+ - language? (tsx | jsx | ts | js | html | css | json | bash | md | py | go | rust) — Prism language id; defaults to `tsx`
8
+ - highlight? — 1-indexed line number, array of numbers, or array of `[start, end]` ranges to emphasise
9
+ - diff? — `{ added?: number[]; removed?: number[] }` — 1-indexed lines for diff hero / changelog mode
10
+ - reveal? (none | lines | typewriter | diff) — entrance animation; defaults to `none`
11
+ - trigger? (mount | inView | manual) — what kicks the reveal off; defaults to `mount`
12
+ - play?: boolean — for `trigger="manual"`, set true to play
13
+ - speed? (slow | normal | fast) — animation feel preset. `normal` (default) maps to the canonical 50ms/22ms staggers + 180ms pre-delay. Pick a feel; don't tune individual numbers unless you have to.
14
+ - delay?: number — explicit delay before reveal starts (ms) — overrides the `speed` preset
15
+ - stagger?: number — explicit per-line stagger for `lines`/`diff`, per-token for `typewriter` (ms) — overrides the `speed` preset
16
+ - prompt?: string — string prepended to each line. Use for terminal emulation: `prompt="$ "` for bash, `prompt="> "` for PowerShell, `prompt=">>> "` for Python REPL. Prompt characters render in muted token colour, don't pick up the typewriter stagger, and are hidden from screen readers.
17
+ - showLineNumbers?: boolean
18
+ - filename?: string — optional label rendered in the header chrome
19
+ - wrap?: boolean — wrap long lines instead of horizontal scroll
20
+ - bare?: boolean — drop chrome (border, header, padding) — for inline use
21
+ - height? (auto | number | string) — container sizing. `auto` (default) grows with content. Number = pixels (`300` → `300px`). String passes through as CSS (`"20rem"`, `"50vh"`).
22
+ - maxLines?: number — cap the visible line count at exactly N line-heights. Wins over `height`. Use for terminal windows, code-tour cards, and surfaces that need a stable vertical rhythm regardless of snippet length.
23
+ when_to_use: Read-only code surface for marketing heroes, docs, changelog entries, AI-output displays. Use `diff` for the "diff hero" pattern (before/after side-by-side or stacked). Use `reveal="lines"` with `trigger="inView"` for scroll-driven marketing pages. Use `reveal="typewriter"` for AI-output / chat-style displays. Use `bare` for inline code inside prose. NOT a code editor — for editable code, reach for an external editor primitive (CodeMirror / Monaco).
24
+ composes_with: [SectionBlock, Card, Tabs (for multi-file examples), Carousel (slide-to-slide code progression)]
25
+ aliases: [code block, code, code snippet, code surface, syntax highlighted code, diff hero, diff view, diff block, changelog code, before after code, scroll-triggered code, typewriter code]
26
+ ---
27
+
28
+ Token palette is driven by `--gds-code-*` CSS variables in `styles/globals.css`. The component itself is presentation-agnostic: prism renders tokens, the variables colour them, motion handles the reveal. Override per-instance via inline `style` to retune one block without touching the theme.
29
+
30
+ The engine is `prism-react-renderer` (already used by Studio's CodeView). Sync, ~6kb, render-prop API — no async hydration flash, no bundle bloat from lang files.
31
+
32
+ ```jsx
33
+ // Plain block.
34
+ <Code language="tsx" source={`function greet(name) {
35
+ return \`Hello, \${name}\`;
36
+ }`} />
37
+ ```
38
+
39
+ ```jsx
40
+ // Line highlight — emphasis lines accept a number, an array, or
41
+ // [start, end] ranges. Composes cleanly with diff (diff colours win).
42
+ <Code
43
+ language="tsx"
44
+ highlight={[2, [4, 6]]}
45
+ source={`<Button>Save</Button>
46
+ <Button variant="raised">Ship it</Button>
47
+ <Button variant="raised" style={{ "--btn-glow": "var(--warning)" }}>
48
+ Iterate
49
+ </Button>`}
50
+ />
51
+ ```
52
+
53
+ ```jsx
54
+ // Diff hero — the marketing "before / after" pattern. Added lines get
55
+ // the success-tinted bg + `+` gutter; removed lines get destructive-
56
+ // tinted bg + `-` gutter. `showLineNumbers` is opt-in.
57
+ <Code
58
+ language="tsx"
59
+ filename="button.tsx"
60
+ diff={{ removed: [1], added: [2, 3, 4] }}
61
+ source={`<button className="px-4 py-2 rounded-md bg-blue-600 text-white shadow-md">
62
+ <Button variant="raised">
63
+ Ship it
64
+ </Button>`}
65
+ />
66
+ ```
67
+
68
+ ```jsx
69
+ // Scroll-triggered reveal — marketing hero. The block waits until the
70
+ // reader scrolls it into view, then staggers each line in. `once: true`
71
+ // is baked in: the reveal doesn't replay when the user scrolls away
72
+ // and back.
73
+ <Code
74
+ language="tsx"
75
+ reveal="lines"
76
+ trigger="inView"
77
+ stagger={50}
78
+ source={`<AppShell nav="three-pane">
79
+ <AppShellHeader>...</AppShellHeader>
80
+ <AppShellNav>...</AppShellNav>
81
+ <AppShellAside>...</AppShellAside>
82
+ <AppShellMain>...</AppShellMain>
83
+ </AppShell>`}
84
+ />
85
+ ```
86
+
87
+ ```jsx
88
+ // Typewriter — token-by-token reveal. Good for AI-output displays
89
+ // and "watch it generate" demos. Whitespace tokens are free (no
90
+ // delay) so leading indent doesn't feel like dead time. `speed`
91
+ // keeps it ergonomic — pick "slow" / "normal" / "fast" instead of
92
+ // tuning stagger by hand.
93
+ <Code
94
+ language="tsx"
95
+ reveal="typewriter"
96
+ trigger="inView"
97
+ speed="normal"
98
+ source={`const theme = await ai.generate({
99
+ brand: "Acme",
100
+ mood: "calm",
101
+ });`}
102
+ />
103
+ ```
104
+
105
+ ```jsx
106
+ // Terminal emulation — `prompt` prepends a static prompt string to
107
+ // each line. Combine with `reveal="typewriter"` for a scripted CLI
108
+ // session feel. Prompt chars are chrome (muted, aria-hidden, no
109
+ // animation), so the typewriter only stages the actual command.
110
+ <Code
111
+ language="bash"
112
+ prompt="$ "
113
+ reveal="typewriter"
114
+ trigger="inView"
115
+ speed="slow"
116
+ source={`pnpm add @gradeui/ui
117
+ pnpm gradeui init
118
+ pnpm dev`}
119
+ />
120
+ ```
121
+
122
+ ### Anti-patterns
123
+
124
+ DO NOT use `<Code>` as a code editor. It's read-only by design — the prism renderer doesn't take input. For editable code, compose your own surface around CodeMirror or Monaco.
125
+
126
+ DO NOT reach for a separate library to render code elsewhere in the app — Studio's CodeView, docs blocks, and marketing heroes should all share this primitive so the token palette stays single-source.
127
+
128
+ DO NOT pass `highlight` AND `diff` for the same line — diff wins, and the highlight emphasis is silently dropped. Use one signal per line.
129
+
130
+ DO NOT use `reveal="typewriter"` for long blocks (50+ lines). It works but feels laboured; use `reveal="lines"` instead.
131
+
132
+ DO NOT override the prism `theme` prop — the component intentionally hides it. Restyle via the `--gds-code-*` CSS variables so every Code block in the app shifts together with the active theme.
@@ -10,7 +10,7 @@ props:
10
10
  - CollapsibleContent: children: React.ReactNode — the content that animates in/out
11
11
  when_to_use: A single show/hide reveal — "Show advanced settings" rows, expandable inline help, "More details" sections inside cards. For multiple rows of expandable content where one-at-a-time matters, reach for Accordion. For a separate panel that floats above content, use Popover.
12
12
  composes_with: [Button (as the trigger, asChild), Card (expandable settings group), Row (header + chevron)]
13
- aliases: [collapsible, expand, show more, disclosure, advanced settings]
13
+ aliases: [collapsible, expand, show more, disclosure, advanced settings, disclosure group, expandable section, expandable view, show hide]
14
14
  ---
15
15
 
16
16
  ```jsx
@@ -16,7 +16,7 @@ props:
16
16
  - CommandDialog: open, onOpenChange — when you want the command palette mounted in a modal (cmd+k pattern)
17
17
  when_to_use: A searchable list of actions or destinations — global ⌘K palettes, "jump to" inputs, account switchers with filter. Wrap in CommandDialog when it should pop over the entire app on a hotkey. For straight forms with filter, prefer a Select with a search input. For free-text autocomplete tied to a single value, prefer Combobox built on Popover + Command.
18
18
  composes_with: [Dialog (CommandDialog wraps it), Popover (inline combobox), Tooltip]
19
- aliases: [command palette, command menu, cmd k, quick switcher, action menu]
19
+ aliases: [command palette, command menu, cmd k, quick switcher, action menu, spotlight, spotlight search, quick open, fuzzy finder]
20
20
  ---
21
21
 
22
22
  ```jsx
@@ -17,7 +17,7 @@ props:
17
17
  when_to_use: Any date or date-range entry. Use DatePicker for a single date (DOB, due date, booking). Use DateRangePicker for a span (report period, stay dates, filter window). Prefer these over <Input type="date"> — consistent theming, keyboard nav, a11y, and no browser-native UI drift.
18
18
  composes_with: [Label, Form, Card (in CardContent), Button (form submit)]
19
19
  subcomponents: [DateRangePicker]
20
- aliases: [datepicker, calendar input, date field, date range]
20
+ aliases: [datepicker, calendar input, date field, date range, datepickerios, react native date picker, calendar input field, date field control]
21
21
  ---
22
22
 
23
23
  ```jsx