shadcn-glass-ui 2.0.12 → 2.1.1
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/CHANGELOG.md +218 -5
- package/README.md +132 -43
- package/context7.json +2 -1
- package/dist/cli/index.cjs +1 -1
- package/dist/components.cjs +4 -4
- package/dist/components.d.ts +103 -29
- package/dist/components.js +1 -1
- package/dist/demo-screenshot-aurora.png +0 -0
- package/dist/demo-screenshot.png +0 -0
- package/dist/demo-screenshot.png.zip +0 -0
- package/dist/hooks.cjs +2 -2
- package/dist/index.cjs +5 -5
- package/dist/index.js +28 -28
- package/dist/index.js.map +1 -1
- package/dist/r/ai-card-glass.json +1 -1
- package/dist/r/avatar-glass.json +1 -1
- package/dist/r/badge-glass.json +1 -1
- package/dist/r/button-glass.json +1 -1
- package/dist/r/combobox-glass.json +1 -1
- package/dist/r/registry.json +2 -2
- package/dist/r/repository-card-glass.json +2 -1
- package/dist/r/slider-glass.json +4 -5
- package/dist/r/toggle-glass.json +2 -2
- package/dist/r/year-card-glass.json +1 -1
- package/dist/shadcn-glass-ui.css +1 -1
- package/dist/{theme-context-DNe_2vWJ.cjs → theme-context-BHXYJ4RE.cjs} +2 -2
- package/dist/{theme-context-DNe_2vWJ.cjs.map → theme-context-BHXYJ4RE.cjs.map} +1 -1
- package/dist/themes.cjs +1 -1
- package/dist/{trust-score-card-glass-Dgu46oWI.cjs → trust-score-card-glass-CGXmOIfq.cjs} +850 -150
- package/dist/trust-score-card-glass-CGXmOIfq.cjs.map +1 -0
- package/dist/{trust-score-card-glass-A7kas5OS.js → trust-score-card-glass-L9g0qamo.js} +1182 -482
- package/dist/trust-score-card-glass-L9g0qamo.js.map +1 -0
- package/dist/{use-focus-BRkQtQCj.cjs → use-focus-CeNHOiBa.cjs} +2 -2
- package/dist/{use-focus-BRkQtQCj.cjs.map → use-focus-CeNHOiBa.cjs.map} +1 -1
- package/dist/{use-wallpaper-tint-CfShPBo2.cjs → use-wallpaper-tint-Bt2G3g1v.cjs} +2 -2
- package/dist/{use-wallpaper-tint-CfShPBo2.cjs.map → use-wallpaper-tint-Bt2G3g1v.cjs.map} +1 -1
- package/dist/{utils-BXN7AcRu.cjs → utils-LYxxWvUn.cjs} +2 -2
- package/dist/{utils-BXN7AcRu.cjs.map → utils-LYxxWvUn.cjs.map} +1 -1
- package/dist/utils.cjs +1 -1
- package/docs/ADVANCED_PATTERNS.md +584 -0
- package/docs/AI_USAGE.md +135 -611
- package/docs/BEST_PRACTICES.md +2 -2
- package/docs/BREAKING_CHANGES.md +242 -0
- package/docs/COMPONENTS_CATALOG.md +8 -8
- package/docs/EXPORTS_STRUCTURE.md +3 -3
- package/docs/GETTING_STARTED.md +13 -8
- package/docs/PUBLISHING.md +1 -1
- package/docs/REGISTRY_SUMMARY.md +2 -2
- package/docs/REGISTRY_USAGE.md +1 -1
- package/docs/api/README.md +11 -11
- package/docs/api/interfaces/BadgeGlassProps.md +21 -14
- package/docs/api/interfaces/ButtonGlassProps.md +37 -30
- package/package.json +4 -3
- package/dist/trust-score-card-glass-A7kas5OS.js.map +0 -1
- package/dist/trust-score-card-glass-Dgu46oWI.cjs.map +0 -1
- package/dist/vite.svg +0 -1
- package/docs/migration/modal-glass-compound-api.md +0 -458
- package/docs/migration/select-to-combobox.md +0 -386
- package/docs/migration/tabs-glass-compound-api.md +0 -579
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
# Advanced Patterns
|
|
2
|
+
|
|
3
|
+
This guide covers advanced component patterns in shadcn-glass-ui: Compound Components, asChild
|
|
4
|
+
polymorphism, and composition techniques.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Table of Contents
|
|
9
|
+
|
|
10
|
+
1. [Compound Components](#compound-components)
|
|
11
|
+
- [ModalGlass](#modalglass)
|
|
12
|
+
- [TabsGlass](#tabsglass)
|
|
13
|
+
- [StepperGlass](#stepperglass)
|
|
14
|
+
2. [asChild Pattern](#aschild-pattern)
|
|
15
|
+
- [ButtonGlass with Next.js Link](#buttonglass-with-nextjs-link)
|
|
16
|
+
- [ButtonGlass with React Router](#buttonglass-with-react-router)
|
|
17
|
+
- [GlassCard with Custom Element](#glasscard-with-custom-element)
|
|
18
|
+
3. [Composition Patterns](#composition-patterns)
|
|
19
|
+
- [Form Validation](#form-validation)
|
|
20
|
+
- [Controlled vs Uncontrolled](#controlled-vs-uncontrolled)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Compound Components
|
|
25
|
+
|
|
26
|
+
Compound components provide flexible, composable APIs for complex UI patterns. Each sub-component
|
|
27
|
+
handles a specific concern.
|
|
28
|
+
|
|
29
|
+
### ModalGlass
|
|
30
|
+
|
|
31
|
+
**Sub-components:**
|
|
32
|
+
|
|
33
|
+
- `ModalGlass.Root` - Context provider with open/close state
|
|
34
|
+
- `ModalGlass.Overlay` - Backdrop with blur effect
|
|
35
|
+
- `ModalGlass.Content` - Main modal container
|
|
36
|
+
- `ModalGlass.Header` - Header layout
|
|
37
|
+
- `ModalGlass.Body` - Content area
|
|
38
|
+
- `ModalGlass.Footer` - Footer with actions
|
|
39
|
+
- `ModalGlass.Title` - Accessible title (ARIA)
|
|
40
|
+
- `ModalGlass.Description` - Accessible description (ARIA)
|
|
41
|
+
- `ModalGlass.Close` - Close button
|
|
42
|
+
|
|
43
|
+
#### Basic Usage
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { ModalGlass, ButtonGlass } from 'shadcn-glass-ui';
|
|
47
|
+
|
|
48
|
+
function MyModal() {
|
|
49
|
+
const [open, setOpen] = useState(false);
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<>
|
|
53
|
+
<ButtonGlass onClick={() => setOpen(true)}>Open Modal</ButtonGlass>
|
|
54
|
+
|
|
55
|
+
<ModalGlass.Root open={open} onOpenChange={setOpen}>
|
|
56
|
+
<ModalGlass.Overlay />
|
|
57
|
+
<ModalGlass.Content>
|
|
58
|
+
<ModalGlass.Header>
|
|
59
|
+
<ModalGlass.Title>Confirm Action</ModalGlass.Title>
|
|
60
|
+
<ModalGlass.Close />
|
|
61
|
+
</ModalGlass.Header>
|
|
62
|
+
|
|
63
|
+
<ModalGlass.Body>
|
|
64
|
+
<p>Are you sure you want to proceed?</p>
|
|
65
|
+
</ModalGlass.Body>
|
|
66
|
+
|
|
67
|
+
<ModalGlass.Footer>
|
|
68
|
+
<ButtonGlass variant="ghost" onClick={() => setOpen(false)}>
|
|
69
|
+
Cancel
|
|
70
|
+
</ButtonGlass>
|
|
71
|
+
<ButtonGlass variant="default">Confirm</ButtonGlass>
|
|
72
|
+
</ModalGlass.Footer>
|
|
73
|
+
</ModalGlass.Content>
|
|
74
|
+
</ModalGlass.Root>
|
|
75
|
+
</>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### With Form
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
<ModalGlass.Root open={open} onOpenChange={setOpen}>
|
|
84
|
+
<ModalGlass.Overlay />
|
|
85
|
+
<ModalGlass.Content size="lg">
|
|
86
|
+
<ModalGlass.Header>
|
|
87
|
+
<ModalGlass.Title>Create Account</ModalGlass.Title>
|
|
88
|
+
<ModalGlass.Description>Fill in your details to get started.</ModalGlass.Description>
|
|
89
|
+
<ModalGlass.Close />
|
|
90
|
+
</ModalGlass.Header>
|
|
91
|
+
|
|
92
|
+
<ModalGlass.Body>
|
|
93
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
94
|
+
<InputGlass label="Email" type="email" placeholder="you@example.com" required />
|
|
95
|
+
<InputGlass label="Password" type="password" placeholder="••••••••" required />
|
|
96
|
+
</form>
|
|
97
|
+
</ModalGlass.Body>
|
|
98
|
+
|
|
99
|
+
<ModalGlass.Footer>
|
|
100
|
+
<ButtonGlass variant="ghost" onClick={() => setOpen(false)}>
|
|
101
|
+
Cancel
|
|
102
|
+
</ButtonGlass>
|
|
103
|
+
<ButtonGlass variant="default" type="submit">
|
|
104
|
+
Create Account
|
|
105
|
+
</ButtonGlass>
|
|
106
|
+
</ModalGlass.Footer>
|
|
107
|
+
</ModalGlass.Content>
|
|
108
|
+
</ModalGlass.Root>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Size Variants
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
// Small modal (alerts, confirmations)
|
|
115
|
+
<ModalGlass.Content size="sm">
|
|
116
|
+
|
|
117
|
+
// Default size
|
|
118
|
+
<ModalGlass.Content size="default">
|
|
119
|
+
|
|
120
|
+
// Large modal (forms, complex content)
|
|
121
|
+
<ModalGlass.Content size="lg">
|
|
122
|
+
|
|
123
|
+
// Full-width modal
|
|
124
|
+
<ModalGlass.Content size="full">
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
### TabsGlass
|
|
130
|
+
|
|
131
|
+
**Sub-components:**
|
|
132
|
+
|
|
133
|
+
- `TabsGlass.Root` - Context provider with value state
|
|
134
|
+
- `TabsGlass.List` - Container for tab triggers
|
|
135
|
+
- `TabsGlass.Trigger` - Individual tab button
|
|
136
|
+
- `TabsGlass.Content` - Content panel for each tab
|
|
137
|
+
|
|
138
|
+
#### Basic Usage
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
import { TabsGlass } from 'shadcn-glass-ui';
|
|
142
|
+
|
|
143
|
+
function MyTabs() {
|
|
144
|
+
const [tab, setTab] = useState('overview');
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<TabsGlass.Root value={tab} onValueChange={setTab}>
|
|
148
|
+
<TabsGlass.List>
|
|
149
|
+
<TabsGlass.Trigger value="overview">Overview</TabsGlass.Trigger>
|
|
150
|
+
<TabsGlass.Trigger value="analytics">Analytics</TabsGlass.Trigger>
|
|
151
|
+
<TabsGlass.Trigger value="settings">Settings</TabsGlass.Trigger>
|
|
152
|
+
</TabsGlass.List>
|
|
153
|
+
|
|
154
|
+
<TabsGlass.Content value="overview">
|
|
155
|
+
<OverviewPanel />
|
|
156
|
+
</TabsGlass.Content>
|
|
157
|
+
|
|
158
|
+
<TabsGlass.Content value="analytics">
|
|
159
|
+
<AnalyticsPanel />
|
|
160
|
+
</TabsGlass.Content>
|
|
161
|
+
|
|
162
|
+
<TabsGlass.Content value="settings">
|
|
163
|
+
<SettingsPanel />
|
|
164
|
+
</TabsGlass.Content>
|
|
165
|
+
</TabsGlass.Root>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### With Icons and Badges
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
import { Home, BarChart, Settings, Inbox } from 'lucide-react';
|
|
174
|
+
import { TabsGlass, BadgeGlass } from 'shadcn-glass-ui';
|
|
175
|
+
|
|
176
|
+
<TabsGlass.Root value={tab} onValueChange={setTab}>
|
|
177
|
+
<TabsGlass.List>
|
|
178
|
+
<TabsGlass.Trigger value="home">
|
|
179
|
+
<Home className="w-4 h-4 mr-2" />
|
|
180
|
+
Home
|
|
181
|
+
</TabsGlass.Trigger>
|
|
182
|
+
|
|
183
|
+
<TabsGlass.Trigger value="inbox">
|
|
184
|
+
<Inbox className="w-4 h-4 mr-2" />
|
|
185
|
+
Inbox
|
|
186
|
+
<BadgeGlass variant="destructive" className="ml-2">
|
|
187
|
+
5
|
|
188
|
+
</BadgeGlass>
|
|
189
|
+
</TabsGlass.Trigger>
|
|
190
|
+
|
|
191
|
+
<TabsGlass.Trigger value="stats">
|
|
192
|
+
<BarChart className="w-4 h-4 mr-2" />
|
|
193
|
+
Statistics
|
|
194
|
+
</TabsGlass.Trigger>
|
|
195
|
+
</TabsGlass.List>
|
|
196
|
+
|
|
197
|
+
{/* Content panels */}
|
|
198
|
+
</TabsGlass.Root>;
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
#### Vertical Layout
|
|
202
|
+
|
|
203
|
+
```tsx
|
|
204
|
+
<TabsGlass.Root value={tab} onValueChange={setTab}>
|
|
205
|
+
<div className="flex gap-6">
|
|
206
|
+
{/* Vertical tab list */}
|
|
207
|
+
<TabsGlass.List className="flex-col w-48">
|
|
208
|
+
<TabsGlass.Trigger value="general">General</TabsGlass.Trigger>
|
|
209
|
+
<TabsGlass.Trigger value="security">Security</TabsGlass.Trigger>
|
|
210
|
+
<TabsGlass.Trigger value="privacy">Privacy</TabsGlass.Trigger>
|
|
211
|
+
</TabsGlass.List>
|
|
212
|
+
|
|
213
|
+
{/* Content area */}
|
|
214
|
+
<div className="flex-1">
|
|
215
|
+
<TabsGlass.Content value="general">
|
|
216
|
+
<GeneralSettings />
|
|
217
|
+
</TabsGlass.Content>
|
|
218
|
+
<TabsGlass.Content value="security">
|
|
219
|
+
<SecuritySettings />
|
|
220
|
+
</TabsGlass.Content>
|
|
221
|
+
<TabsGlass.Content value="privacy">
|
|
222
|
+
<PrivacySettings />
|
|
223
|
+
</TabsGlass.Content>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
</TabsGlass.Root>
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### Dynamic Tabs
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
const tabs = [
|
|
233
|
+
{ id: 'tab1', label: 'First', content: <FirstContent /> },
|
|
234
|
+
{ id: 'tab2', label: 'Second', content: <SecondContent /> },
|
|
235
|
+
{ id: 'tab3', label: 'Third', content: <ThirdContent /> },
|
|
236
|
+
];
|
|
237
|
+
|
|
238
|
+
<TabsGlass.Root value={activeTab} onValueChange={setActiveTab}>
|
|
239
|
+
<TabsGlass.List>
|
|
240
|
+
{tabs.map((tab) => (
|
|
241
|
+
<TabsGlass.Trigger key={tab.id} value={tab.id}>
|
|
242
|
+
{tab.label}
|
|
243
|
+
</TabsGlass.Trigger>
|
|
244
|
+
))}
|
|
245
|
+
</TabsGlass.List>
|
|
246
|
+
|
|
247
|
+
{tabs.map((tab) => (
|
|
248
|
+
<TabsGlass.Content key={tab.id} value={tab.id}>
|
|
249
|
+
{tab.content}
|
|
250
|
+
</TabsGlass.Content>
|
|
251
|
+
))}
|
|
252
|
+
</TabsGlass.Root>;
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
### StepperGlass
|
|
258
|
+
|
|
259
|
+
**Sub-components:**
|
|
260
|
+
|
|
261
|
+
- `StepperGlass.Root` - Context provider with step state
|
|
262
|
+
- `StepperGlass.Step` - Individual step container
|
|
263
|
+
- `StepperGlass.Indicator` - Step number/icon/dot
|
|
264
|
+
- `StepperGlass.Title` - Step title
|
|
265
|
+
- `StepperGlass.Description` - Step description
|
|
266
|
+
|
|
267
|
+
#### Basic Usage
|
|
268
|
+
|
|
269
|
+
```tsx
|
|
270
|
+
import { StepperGlass } from 'shadcn-glass-ui';
|
|
271
|
+
|
|
272
|
+
function CheckoutStepper() {
|
|
273
|
+
const [step, setStep] = useState(0);
|
|
274
|
+
|
|
275
|
+
return (
|
|
276
|
+
<StepperGlass.Root value={step} onValueChange={setStep}>
|
|
277
|
+
<StepperGlass.Step value={0}>
|
|
278
|
+
<StepperGlass.Indicator />
|
|
279
|
+
<StepperGlass.Title>Cart</StepperGlass.Title>
|
|
280
|
+
<StepperGlass.Description>Review items</StepperGlass.Description>
|
|
281
|
+
</StepperGlass.Step>
|
|
282
|
+
|
|
283
|
+
<StepperGlass.Step value={1}>
|
|
284
|
+
<StepperGlass.Indicator />
|
|
285
|
+
<StepperGlass.Title>Shipping</StepperGlass.Title>
|
|
286
|
+
<StepperGlass.Description>Enter address</StepperGlass.Description>
|
|
287
|
+
</StepperGlass.Step>
|
|
288
|
+
|
|
289
|
+
<StepperGlass.Step value={2}>
|
|
290
|
+
<StepperGlass.Indicator />
|
|
291
|
+
<StepperGlass.Title>Payment</StepperGlass.Title>
|
|
292
|
+
<StepperGlass.Description>Add payment method</StepperGlass.Description>
|
|
293
|
+
</StepperGlass.Step>
|
|
294
|
+
|
|
295
|
+
<StepperGlass.Step value={3}>
|
|
296
|
+
<StepperGlass.Indicator />
|
|
297
|
+
<StepperGlass.Title>Confirm</StepperGlass.Title>
|
|
298
|
+
<StepperGlass.Description>Place order</StepperGlass.Description>
|
|
299
|
+
</StepperGlass.Step>
|
|
300
|
+
</StepperGlass.Root>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
#### Variants
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
// Numbered steps (default)
|
|
309
|
+
<StepperGlass.Root variant="numbered">
|
|
310
|
+
|
|
311
|
+
// With icons
|
|
312
|
+
<StepperGlass.Root variant="icon">
|
|
313
|
+
<StepperGlass.Step value={0}>
|
|
314
|
+
<StepperGlass.Indicator icon={<ShoppingCart />} />
|
|
315
|
+
...
|
|
316
|
+
</StepperGlass.Step>
|
|
317
|
+
</StepperGlass.Root>
|
|
318
|
+
|
|
319
|
+
// Dot indicators
|
|
320
|
+
<StepperGlass.Root variant="dots">
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
#### Linear Mode (Wizard)
|
|
324
|
+
|
|
325
|
+
Locks future steps until previous ones are completed:
|
|
326
|
+
|
|
327
|
+
```tsx
|
|
328
|
+
<StepperGlass.Root
|
|
329
|
+
value={step}
|
|
330
|
+
onValueChange={setStep}
|
|
331
|
+
linear // Prevents clicking future steps
|
|
332
|
+
>
|
|
333
|
+
{/* Steps */}
|
|
334
|
+
</StepperGlass.Root>
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
#### Vertical Orientation
|
|
338
|
+
|
|
339
|
+
```tsx
|
|
340
|
+
<StepperGlass.Root orientation="vertical">{/* Steps render vertically */}</StepperGlass.Root>
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## asChild Pattern
|
|
346
|
+
|
|
347
|
+
The `asChild` pattern allows components to render as different elements while preserving styles and
|
|
348
|
+
behavior. Powered by Radix UI's Slot.
|
|
349
|
+
|
|
350
|
+
### Why asChild?
|
|
351
|
+
|
|
352
|
+
- **Semantic HTML** - Render as `<a>`, `<Link>`, or any element
|
|
353
|
+
- **No wrapper divs** - Clean DOM structure
|
|
354
|
+
- **Style preservation** - All glass effects maintained
|
|
355
|
+
- **Accessibility** - Proper element semantics
|
|
356
|
+
|
|
357
|
+
### ButtonGlass with Next.js Link
|
|
358
|
+
|
|
359
|
+
```tsx
|
|
360
|
+
import Link from 'next/link';
|
|
361
|
+
import { ButtonGlass } from 'shadcn-glass-ui';
|
|
362
|
+
|
|
363
|
+
// Renders as <a> with ButtonGlass styles
|
|
364
|
+
<ButtonGlass asChild variant="default">
|
|
365
|
+
<Link href="/dashboard">Go to Dashboard</Link>
|
|
366
|
+
</ButtonGlass>;
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**HTML output:**
|
|
370
|
+
|
|
371
|
+
```html
|
|
372
|
+
<a href="/dashboard" class="btn-glass btn-primary ..."> Go to Dashboard </a>
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### ButtonGlass with React Router
|
|
376
|
+
|
|
377
|
+
```tsx
|
|
378
|
+
import { Link } from 'react-router-dom';
|
|
379
|
+
import { ButtonGlass } from 'shadcn-glass-ui';
|
|
380
|
+
|
|
381
|
+
<ButtonGlass asChild variant="secondary">
|
|
382
|
+
<Link to="/settings">Settings</Link>
|
|
383
|
+
</ButtonGlass>;
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### ButtonGlass as External Link
|
|
387
|
+
|
|
388
|
+
```tsx
|
|
389
|
+
<ButtonGlass asChild variant="ghost">
|
|
390
|
+
<a href="https://github.com" target="_blank" rel="noopener noreferrer">
|
|
391
|
+
View on GitHub
|
|
392
|
+
</a>
|
|
393
|
+
</ButtonGlass>
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### GlassCard with Custom Element
|
|
397
|
+
|
|
398
|
+
```tsx
|
|
399
|
+
import { GlassCard } from 'shadcn-glass-ui';
|
|
400
|
+
|
|
401
|
+
// Render card as article
|
|
402
|
+
<GlassCard asChild>
|
|
403
|
+
<article>
|
|
404
|
+
<h2>Blog Post Title</h2>
|
|
405
|
+
<p>Content...</p>
|
|
406
|
+
</article>
|
|
407
|
+
</GlassCard>
|
|
408
|
+
|
|
409
|
+
// Render card as link
|
|
410
|
+
<GlassCard asChild>
|
|
411
|
+
<a href="/product/123">
|
|
412
|
+
<img src="product.jpg" alt="Product" />
|
|
413
|
+
<h3>Product Name</h3>
|
|
414
|
+
</a>
|
|
415
|
+
</GlassCard>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### With Icons
|
|
419
|
+
|
|
420
|
+
```tsx
|
|
421
|
+
import { ExternalLink } from 'lucide-react';
|
|
422
|
+
|
|
423
|
+
<ButtonGlass asChild variant="default">
|
|
424
|
+
<a href="https://docs.example.com" target="_blank">
|
|
425
|
+
Documentation
|
|
426
|
+
<ExternalLink className="w-4 h-4 ml-2" />
|
|
427
|
+
</a>
|
|
428
|
+
</ButtonGlass>;
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## Composition Patterns
|
|
434
|
+
|
|
435
|
+
### Form Validation
|
|
436
|
+
|
|
437
|
+
Use `FormFieldWrapper` with `InputGlass` for consistent validation UI:
|
|
438
|
+
|
|
439
|
+
```tsx
|
|
440
|
+
import { InputGlass, FormFieldWrapper } from 'shadcn-glass-ui';
|
|
441
|
+
|
|
442
|
+
function ValidatedInput({ label, error, success, ...props }) {
|
|
443
|
+
return (
|
|
444
|
+
<FormFieldWrapper label={label} error={error} success={success}>
|
|
445
|
+
<InputGlass error={error} success={success} {...props} />
|
|
446
|
+
</FormFieldWrapper>
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Usage
|
|
451
|
+
<ValidatedInput
|
|
452
|
+
label="Email"
|
|
453
|
+
error={errors.email?.message}
|
|
454
|
+
success={isEmailValid ? 'Email is available' : undefined}
|
|
455
|
+
placeholder="you@example.com"
|
|
456
|
+
/>;
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Controlled vs Uncontrolled
|
|
460
|
+
|
|
461
|
+
#### Controlled (recommended for forms)
|
|
462
|
+
|
|
463
|
+
```tsx
|
|
464
|
+
function ControlledTabs() {
|
|
465
|
+
const [value, setValue] = useState('tab1');
|
|
466
|
+
|
|
467
|
+
return (
|
|
468
|
+
<TabsGlass.Root value={value} onValueChange={setValue}>
|
|
469
|
+
{/* ... */}
|
|
470
|
+
</TabsGlass.Root>
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
#### Uncontrolled (simple cases)
|
|
476
|
+
|
|
477
|
+
```tsx
|
|
478
|
+
function UncontrolledTabs() {
|
|
479
|
+
return <TabsGlass.Root defaultValue="tab1">{/* ... */}</TabsGlass.Root>;
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Combining Patterns
|
|
484
|
+
|
|
485
|
+
Complex form with modal, tabs, and validation:
|
|
486
|
+
|
|
487
|
+
```tsx
|
|
488
|
+
function SettingsModal({ open, onOpenChange }) {
|
|
489
|
+
const [tab, setTab] = useState('profile');
|
|
490
|
+
|
|
491
|
+
return (
|
|
492
|
+
<ModalGlass.Root open={open} onOpenChange={onOpenChange}>
|
|
493
|
+
<ModalGlass.Overlay />
|
|
494
|
+
<ModalGlass.Content size="lg">
|
|
495
|
+
<ModalGlass.Header>
|
|
496
|
+
<ModalGlass.Title>Settings</ModalGlass.Title>
|
|
497
|
+
<ModalGlass.Close />
|
|
498
|
+
</ModalGlass.Header>
|
|
499
|
+
|
|
500
|
+
<ModalGlass.Body>
|
|
501
|
+
<TabsGlass.Root value={tab} onValueChange={setTab}>
|
|
502
|
+
<TabsGlass.List>
|
|
503
|
+
<TabsGlass.Trigger value="profile">Profile</TabsGlass.Trigger>
|
|
504
|
+
<TabsGlass.Trigger value="notifications">Notifications</TabsGlass.Trigger>
|
|
505
|
+
</TabsGlass.List>
|
|
506
|
+
|
|
507
|
+
<TabsGlass.Content value="profile">
|
|
508
|
+
<ProfileForm />
|
|
509
|
+
</TabsGlass.Content>
|
|
510
|
+
|
|
511
|
+
<TabsGlass.Content value="notifications">
|
|
512
|
+
<NotificationSettings />
|
|
513
|
+
</TabsGlass.Content>
|
|
514
|
+
</TabsGlass.Root>
|
|
515
|
+
</ModalGlass.Body>
|
|
516
|
+
|
|
517
|
+
<ModalGlass.Footer>
|
|
518
|
+
<ButtonGlass variant="ghost" onClick={() => onOpenChange(false)}>
|
|
519
|
+
Cancel
|
|
520
|
+
</ButtonGlass>
|
|
521
|
+
<ButtonGlass variant="default">Save Changes</ButtonGlass>
|
|
522
|
+
</ModalGlass.Footer>
|
|
523
|
+
</ModalGlass.Content>
|
|
524
|
+
</ModalGlass.Root>
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
## Common Pitfalls
|
|
532
|
+
|
|
533
|
+
### Mismatched Tab Values
|
|
534
|
+
|
|
535
|
+
```tsx
|
|
536
|
+
// Wrong - values don't match
|
|
537
|
+
<TabsGlass.Trigger value="profile">Profile</TabsGlass.Trigger>
|
|
538
|
+
<TabsGlass.Content value="user">...</TabsGlass.Content>
|
|
539
|
+
|
|
540
|
+
// Correct
|
|
541
|
+
<TabsGlass.Trigger value="profile">Profile</TabsGlass.Trigger>
|
|
542
|
+
<TabsGlass.Content value="profile">...</TabsGlass.Content>
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
### Missing List Wrapper
|
|
546
|
+
|
|
547
|
+
```tsx
|
|
548
|
+
// Wrong - triggers without List
|
|
549
|
+
<TabsGlass.Root>
|
|
550
|
+
<TabsGlass.Trigger value="tab1">Tab 1</TabsGlass.Trigger>
|
|
551
|
+
</TabsGlass.Root>
|
|
552
|
+
|
|
553
|
+
// Correct
|
|
554
|
+
<TabsGlass.Root>
|
|
555
|
+
<TabsGlass.List>
|
|
556
|
+
<TabsGlass.Trigger value="tab1">Tab 1</TabsGlass.Trigger>
|
|
557
|
+
</TabsGlass.List>
|
|
558
|
+
</TabsGlass.Root>
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### Forgetting onValueChange (Controlled)
|
|
562
|
+
|
|
563
|
+
```tsx
|
|
564
|
+
// Wrong - controlled without handler
|
|
565
|
+
<TabsGlass.Root value={tab}>
|
|
566
|
+
|
|
567
|
+
// Correct - controlled
|
|
568
|
+
<TabsGlass.Root value={tab} onValueChange={setTab}>
|
|
569
|
+
|
|
570
|
+
// Or - uncontrolled
|
|
571
|
+
<TabsGlass.Root defaultValue="overview">
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
## Related Documentation
|
|
577
|
+
|
|
578
|
+
- [Compound Components Guide](migration/compound-components-v2.md)
|
|
579
|
+
- [Component Catalog](COMPONENTS_CATALOG.md)
|
|
580
|
+
- [Breaking Changes](BREAKING_CHANGES.md)
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
**Last updated:** 2025-12-14
|