@ryanhelsing/ry-ui 1.0.8 → 1.0.10

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 (41) hide show
  1. package/.claude/skills/ry-ui-builder/SKILL.md +186 -0
  2. package/AGENT.md +3 -1
  3. package/README.md +16 -2
  4. package/dist/_redirects +1 -0
  5. package/dist/app.d.ts +2 -0
  6. package/dist/app.d.ts.map +1 -0
  7. package/dist/components/ry-testimonial.d.ts +19 -0
  8. package/dist/components/ry-testimonial.d.ts.map +1 -0
  9. package/dist/components/ry-theme-panel.d.ts +25 -0
  10. package/dist/components/ry-theme-panel.d.ts.map +1 -0
  11. package/dist/core/ry-icons.d.ts.map +1 -1
  12. package/dist/core/ry-transform.d.ts.map +1 -1
  13. package/dist/css/ry-structure.css +123 -3
  14. package/dist/css/ry-theme.css +141 -2
  15. package/dist/css/ry-tokens.css +4 -26
  16. package/dist/css/ry-ui.css +268 -31
  17. package/dist/pages/components.html +1827 -0
  18. package/dist/pages/landing.html +229 -0
  19. package/dist/ry-ui.d.ts +2 -0
  20. package/dist/ry-ui.d.ts.map +1 -1
  21. package/dist/ry-ui.js +382 -245
  22. package/dist/ry-ui.js.map +1 -1
  23. package/docs/components/forms.md +10 -3
  24. package/examples/starter-local.html +252 -0
  25. package/examples/starter-minimal.html +252 -0
  26. package/examples/themes/skeuomorphic/css-dark-neon-led-volume-dial/LICENSE.txt +21 -0
  27. package/examples/themes/skeuomorphic/css-dark-neon-led-volume-dial/README.md +7 -0
  28. package/examples/themes/skeuomorphic/css-dark-neon-led-volume-dial/dist/index.html +23 -0
  29. package/examples/themes/skeuomorphic/css-dark-neon-led-volume-dial/dist/style.css +126 -0
  30. package/examples/themes/skeuomorphic/css-dark-neon-led-volume-dial/src/index.html +5 -0
  31. package/examples/themes/skeuomorphic/css-dark-neon-led-volume-dial/src/style.scss +161 -0
  32. package/examples/themes/skeuomorphic/led-controls/LICENSE.txt +21 -0
  33. package/examples/themes/skeuomorphic/led-controls/README.md +7 -0
  34. package/examples/themes/skeuomorphic/led-controls/dist/index.html +17 -0
  35. package/examples/themes/skeuomorphic/led-controls/dist/script.js +27 -0
  36. package/examples/themes/skeuomorphic/led-controls/dist/style.css +135 -0
  37. package/examples/themes/skeuomorphic/led-controls/src/index.html +1 -0
  38. package/examples/themes/skeuomorphic/led-controls/src/script.ts +59 -0
  39. package/examples/themes/skeuomorphic/led-controls/src/style.scss +253 -0
  40. package/llms.txt +346 -0
  41. package/package.json +12 -4
@@ -0,0 +1,1827 @@
1
+ <ry-page>
2
+
3
+ <ry-header sticky>
4
+ <ry-cluster>
5
+ <strong>ry-ui</strong>
6
+ <span>Kitchen Sink</span>
7
+ </ry-cluster>
8
+
9
+ </ry-header>
10
+
11
+ <ry-theme-panel></ry-theme-panel>
12
+
13
+ <ry-main>
14
+
15
+ <!-- Sticky -->
16
+ <ry-section>
17
+ <h2>Sticky</h2>
18
+ <ry-example title="sticky" stacked>
19
+ <template>
20
+ <div style="height: 150px; overflow-y: auto; border: 1px solid var(--ry-color-border); border-radius: var(--ry-radius-lg);">
21
+ <header sticky style="background: var(--ry-color-bg); padding: var(--ry-space-3); border-bottom: 1px solid var(--ry-color-border);">
22
+ <strong>Sticky header</strong>
23
+ </header>
24
+ <stack style="padding: var(--ry-space-4);">
25
+ <p>Scroll down...</p>
26
+ <p>Keep scrolling...</p>
27
+ <p>The header sticks!</p>
28
+ <p>More content here...</p>
29
+ <p>Almost there...</p>
30
+ <p>Bottom reached.</p>
31
+ </stack>
32
+ </div>
33
+ </template>
34
+ </ry-example>
35
+ <table>
36
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
37
+ <tbody>
38
+ <tr><td><code>sticky</code></td><td>boolean</td><td>Sticks to top of scroll container</td></tr>
39
+ </tbody>
40
+ </table>
41
+ </ry-section>
42
+
43
+ <!-- Grid -->
44
+ <ry-section>
45
+ <h2>Grid</h2>
46
+ <ry-example title="grid">
47
+ <template>
48
+ <grid cols="3">
49
+ <card>One</card>
50
+ <card>Two</card>
51
+ <card>Three</card>
52
+ </grid>
53
+ </template>
54
+ </ry-example>
55
+ <ry-example title="grid auto-fit">
56
+ <template>
57
+ <grid cols="auto-fit" style="--ry-grid-min: 200px">
58
+ <card>Auto</card>
59
+ <card>Fit</card>
60
+ <card>Cards</card>
61
+ <card>Wrap</card>
62
+ <card>Fluidly</card>
63
+ </grid>
64
+ </template>
65
+ </ry-example>
66
+ <ry-example title="grid responsive breakpoints">
67
+ <template>
68
+ <grid cols="5" cols-md="3" cols-sm="1">
69
+ <card>1</card>
70
+ <card>2</card>
71
+ <card>3</card>
72
+ <card>4</card>
73
+ <card>5</card>
74
+ </grid>
75
+ </template>
76
+ </ry-example>
77
+ <table>
78
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
79
+ <tbody>
80
+ <tr><td><code>cols</code></td><td>1-6 | auto-fit | auto-fill</td><td>Number of columns or fluid mode</td></tr>
81
+ <tr><td><code>cols-sm</code></td><td>1-3</td><td>Columns at ≤640px</td></tr>
82
+ <tr><td><code>cols-md</code></td><td>1-4</td><td>Columns at 641-1024px</td></tr>
83
+ <tr><td><code>cols-lg</code></td><td>2-6</td><td>Columns at ≥1025px</td></tr>
84
+ <tr><td><code>--ry-grid-min</code></td><td>CSS length</td><td>Min column width for auto-fit/auto-fill (default 280px)</td></tr>
85
+ </tbody>
86
+ </table>
87
+ </ry-section>
88
+
89
+ <!-- Stack -->
90
+ <ry-section>
91
+ <h2>Stack</h2>
92
+ <ry-example title="stack">
93
+ <template>
94
+ <stack gap="sm">
95
+ <card>Stacked item 1</card>
96
+ <card>Stacked item 2</card>
97
+ <card>Stacked item 3</card>
98
+ </stack>
99
+ </template>
100
+ </ry-example>
101
+ <table>
102
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
103
+ <tbody>
104
+ <tr><td><code>gap</code></td><td>sm | md | lg</td><td>Vertical spacing</td></tr>
105
+ </tbody>
106
+ </table>
107
+ </ry-section>
108
+
109
+ <!-- Cluster -->
110
+ <ry-section>
111
+ <h2>Cluster</h2>
112
+ <ry-example title="cluster">
113
+ <template>
114
+ <cluster>
115
+ <badge>Tag 1</badge>
116
+ <badge>Tag 2</badge>
117
+ <badge>Tag 3</badge>
118
+ <badge>Tag 4</badge>
119
+ </cluster>
120
+ </template>
121
+ </ry-example>
122
+ <table>
123
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
124
+ <tbody>
125
+ <tr><td><code>gap</code></td><td>sm | md | lg</td><td>Horizontal spacing</td></tr>
126
+ </tbody>
127
+ </table>
128
+ </ry-section>
129
+
130
+ <!-- Card -->
131
+ <ry-section>
132
+ <h2>Card</h2>
133
+ <ry-example title="card">
134
+ <template>
135
+ <card>
136
+ <h3>Card Title</h3>
137
+ <p>Card content goes here.</p>
138
+ <actions>
139
+ <button>Action</button>
140
+ <button variant="ghost">Cancel</button>
141
+ </actions>
142
+ </card>
143
+ </template>
144
+ </ry-example>
145
+ <ry-example title="interactive cards" stacked>
146
+ <template>
147
+ <grid cols="3">
148
+ <card interactive>
149
+ <h3>Hover Me</h3>
150
+ <p>All cards lift on hover. Interactive cards get a stronger effect with primary border.</p>
151
+ </card>
152
+ <card interactive href="#card-demo">
153
+ <h3>With Link</h3>
154
+ <p>Interactive cards with <code>href</code> navigate on click or Enter key.</p>
155
+ </card>
156
+ <card interactive>
157
+ <h3>Keyboard Accessible</h3>
158
+ <p>Tab to focus, Enter or Space to activate. Emits <code>ry:click</code>.</p>
159
+ </card>
160
+ </grid>
161
+ </template>
162
+ </ry-example>
163
+ <table>
164
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
165
+ <tbody>
166
+ <tr><td><code>interactive</code></td><td>boolean</td><td>Clickable card with hover lift, focus ring, keyboard support</td></tr>
167
+ <tr><td><code>href</code></td><td>URL</td><td>Navigate on click (requires <code>interactive</code>)</td></tr>
168
+ </tbody>
169
+ </table>
170
+ </ry-section>
171
+
172
+ <!-- Button -->
173
+ <ry-section>
174
+ <h2>Button</h2>
175
+ <ry-example title="button">
176
+ <template>
177
+ <cluster>
178
+ <button>Primary</button>
179
+ <button variant="secondary">Secondary</button>
180
+ <button variant="outline">Outline</button>
181
+ <button variant="ghost">Ghost</button>
182
+ <button variant="danger">Danger</button>
183
+ <button variant="accent">Accent</button>
184
+ <button pressed>Pressed</button>
185
+ </cluster>
186
+ </template>
187
+ </ry-example>
188
+ <table>
189
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
190
+ <tbody>
191
+ <tr><td><code>variant</code></td><td>primary | secondary | outline | ghost | danger | accent</td><td>Button style</td></tr>
192
+ <tr><td><code>size</code></td><td>sm | md | lg</td><td>Button size</td></tr>
193
+ <tr><td><code>pressed</code></td><td>boolean</td><td>Pressed/active toggle state</td></tr>
194
+ <tr><td><code>modal</code></td><td>modal-id</td><td>Opens modal on click</td></tr>
195
+ <tr><td><code>drawer</code></td><td>drawer-id</td><td>Opens drawer on click</td></tr>
196
+ </tbody>
197
+ </table>
198
+ </ry-section>
199
+
200
+ <!-- Button Group -->
201
+ <ry-section>
202
+ <h2>Button Group</h2>
203
+ <ry-example title="button-group">
204
+ <template>
205
+ <stack gap="lg">
206
+ <button-group name="billing" value="monthly">
207
+ <button value="monthly">Monthly</button>
208
+ <button value="annually">Annually</button>
209
+ </button-group>
210
+
211
+ <button-group name="mode" value="terminal">
212
+ <button value="direct">Direct</button>
213
+ <button value="terminal">Terminal</button>
214
+ <button value="release">Release</button>
215
+ </button-group>
216
+ </stack>
217
+ </template>
218
+ <script slot="js" type="text/plain">
219
+ const group = document.querySelector('ry-button-group');
220
+
221
+ // Listen for selection changes
222
+ group.addEventListener('ry:change', (e) => {
223
+ console.log(e.detail.value); // "monthly" or "annually"
224
+ });
225
+
226
+ // Get/set programmatically
227
+ group.value; // "monthly"
228
+ group.value = 'annually'; // Switch selection
229
+ </script>
230
+ </ry-example>
231
+ <table>
232
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
233
+ <tbody>
234
+ <tr><td><code>name</code></td><td>string</td><td>Group name</td></tr>
235
+ <tr><td><code>value</code></td><td>string</td><td>Currently selected value</td></tr>
236
+ </tbody>
237
+ </table>
238
+ </ry-section>
239
+
240
+ <!-- Split -->
241
+ <ry-section>
242
+ <h2>Split</h2>
243
+ <ry-example title="split" stacked>
244
+ <template>
245
+ <split style="--ry-split-width: 200px; height: 120px;">
246
+ <card>Main content (flex: 1)</card>
247
+ <card>Sidebar (200px)</card>
248
+ </split>
249
+ </template>
250
+ </ry-example>
251
+ <ry-example title="split resizable" stacked>
252
+ <template>
253
+ <split resizable style="height: 150px;">
254
+ <card>Drag the handle to resize →</card>
255
+ <card>Resizable sidebar (default 300px)</card>
256
+ </split>
257
+ </template>
258
+ </ry-example>
259
+ <ry-example title="split resizable + persist" stacked>
260
+ <template>
261
+ <split resizable persist="demo-panel" style="height: 150px; --ry-split-width: 250px;">
262
+ <card>Width persists across page reloads. Double-click handle to reset.</card>
263
+ <card>Persisted sidebar</card>
264
+ </split>
265
+ </template>
266
+ <script slot="js" type="text/plain">
267
+ const split = document.querySelector('ry-split[resizable]');
268
+
269
+ // Listen for resize
270
+ split.addEventListener('ry:resize', (e) => {
271
+ console.log(e.detail.width); // new width in px
272
+ });
273
+
274
+ // Keyboard: Arrow keys (±10px), Shift+Arrow (±50px)
275
+ // Home = min width, End = max width
276
+ // Double-click handle = reset to default
277
+ </script>
278
+ </ry-example>
279
+ <table>
280
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
281
+ <tbody>
282
+ <tr><td><code>resizable</code></td><td>boolean</td><td>Enable drag-to-resize handle</td></tr>
283
+ <tr><td><code>persist</code></td><td>string</td><td>localStorage key — saves width across reloads</td></tr>
284
+ <tr><td><code>--ry-split-width</code></td><td>CSS length</td><td>Sidebar width (default 300px)</td></tr>
285
+ <tr><td><code>--ry-split-min-width</code></td><td>CSS length</td><td>Min width during resize (default 100px)</td></tr>
286
+ <tr><td><code>--ry-split-max-width</code></td><td>CSS length</td><td>Max width during resize (default 80% of container)</td></tr>
287
+ </tbody>
288
+ </table>
289
+ </ry-section>
290
+
291
+ <!-- Check List -->
292
+ <ry-section>
293
+ <h2>Check List</h2>
294
+ <ry-example title="check-list">
295
+ <template>
296
+ <ul class="ry-check-list">
297
+ <li>Unlimited projects</li>
298
+ <li>Priority support</li>
299
+ <li>Advanced analytics</li>
300
+ <li>Custom domains</li>
301
+ </ul>
302
+ </template>
303
+ </ry-example>
304
+ <table>
305
+ <thead><tr><th>Class</th><th>Description</th></tr></thead>
306
+ <tbody>
307
+ <tr><td><code>.ry-check-list</code></td><td>Green checkmark list (CSS-only, no JS)</td></tr>
308
+ </tbody>
309
+ </table>
310
+ </ry-section>
311
+
312
+ <!-- Accordion -->
313
+ <ry-section>
314
+ <h2>Accordion</h2>
315
+ <ry-example title="accordion">
316
+ <template>
317
+ <accordion>
318
+ <accordion-item title="What is ry-ui?" open>
319
+ A framework-agnostic, Light DOM component library.
320
+ </accordion-item>
321
+ <accordion-item title="Do I need a build step?">
322
+ Nope! Just link the CSS and JS files.
323
+ </accordion-item>
324
+ </accordion>
325
+ </template>
326
+ <script slot="js" type="text/plain">
327
+ const accordion = document.querySelector('ry-accordion');
328
+ const item = document.querySelector('ry-accordion-item');
329
+
330
+ // Listen for open/close
331
+ item.addEventListener('ry:toggle', (e) => {
332
+ console.log(e.detail.open); // true or false
333
+ });
334
+
335
+ // Open/close programmatically
336
+ item.open = true; // Expand
337
+ item.open = false; // Collapse
338
+
339
+ // Toggle
340
+ item.toggle();
341
+ </script>
342
+ </ry-example>
343
+ <table>
344
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
345
+ <tbody>
346
+ <tr><td><code>title</code></td><td>string</td><td>Header text (on accordion-item)</td></tr>
347
+ <tr><td><code>open</code></td><td>boolean</td><td>Initially expanded (on accordion-item)</td></tr>
348
+ </tbody>
349
+ </table>
350
+ </ry-section>
351
+
352
+ <!-- Tabs -->
353
+ <ry-section>
354
+ <h2>Tabs</h2>
355
+ <ry-example title="tabs">
356
+ <template>
357
+ <tabs>
358
+ <tab title="Overview" active>
359
+ <p>This is the overview tab content.</p>
360
+ </tab>
361
+ <tab title="Features">
362
+ <p>This is the features tab content.</p>
363
+ </tab>
364
+ </tabs>
365
+ </template>
366
+ <script slot="js" type="text/plain">
367
+ const tabs = document.querySelector('ry-tabs');
368
+
369
+ // Listen for tab changes
370
+ tabs.addEventListener('ry:change', (e) => {
371
+ console.log(e.detail.index); // 0, 1, 2...
372
+ console.log(e.detail.title); // "Overview"
373
+ });
374
+
375
+ // Switch tabs programmatically
376
+ tabs.activeIndex = 1; // Switch to second tab
377
+ </script>
378
+ </ry-example>
379
+ <table>
380
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
381
+ <tbody>
382
+ <tr><td><code>title</code></td><td>string</td><td>Tab label (on tab)</td></tr>
383
+ <tr><td><code>active</code></td><td>boolean</td><td>Initially selected (on tab)</td></tr>
384
+ </tbody>
385
+ </table>
386
+ </ry-section>
387
+
388
+ <!-- Modal -->
389
+ <ry-section>
390
+ <h2>Modal</h2>
391
+ <ry-example title="modal">
392
+ <template>
393
+ <button modal="example-modal">Open Modal</button>
394
+
395
+ <modal id="example-modal" title="Modal Title">
396
+ <p>Modal content with focus trapping.</p>
397
+ <actions slot="footer">
398
+ <button variant="ghost" close>Cancel</button>
399
+ <button>Confirm</button>
400
+ </actions>
401
+ </modal>
402
+ </template>
403
+ <script slot="js" type="text/plain">
404
+ const modal = document.querySelector('ry-modal');
405
+
406
+ // Listen for open/close
407
+ modal.addEventListener('ry:open', () => {
408
+ console.log('Modal opened');
409
+ });
410
+
411
+ modal.addEventListener('ry:close', () => {
412
+ console.log('Modal closed');
413
+ });
414
+
415
+ // Open/close programmatically
416
+ modal.open();
417
+ modal.close();
418
+
419
+ // Check state
420
+ if (modal.state === 'open') { ... }
421
+ </script>
422
+ </ry-example>
423
+ <table>
424
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
425
+ <tbody>
426
+ <tr><td><code>id</code></td><td>string</td><td>Modal identifier</td></tr>
427
+ <tr><td><code>title</code></td><td>string</td><td>Header title</td></tr>
428
+ <tr><td><code>close</code></td><td>boolean</td><td>Closes modal (on button)</td></tr>
429
+ </tbody>
430
+ </table>
431
+ </ry-section>
432
+
433
+ <!-- Dropdown -->
434
+ <ry-section>
435
+ <h2>Dropdown</h2>
436
+ <ry-example title="dropdown">
437
+ <template>
438
+ <dropdown>
439
+ <button slot="trigger">Options ▾</button>
440
+ <menu>
441
+ <menu-item>Edit</menu-item>
442
+ <menu-item>Duplicate</menu-item>
443
+ <divider></divider>
444
+ <menu-item>Delete</menu-item>
445
+ </menu>
446
+ </dropdown>
447
+ </template>
448
+ <script slot="js" type="text/plain">
449
+ const dropdown = document.querySelector('ry-dropdown');
450
+
451
+ // Listen for menu item selection
452
+ dropdown.addEventListener('ry:select', (e) => {
453
+ console.log(e.detail.value); // "Edit", "Delete", etc.
454
+ });
455
+
456
+ // Open/close programmatically
457
+ dropdown.open();
458
+ dropdown.close();
459
+ </script>
460
+ </ry-example>
461
+ <table>
462
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
463
+ <tbody>
464
+ <tr><td><code>slot="trigger"</code></td><td>—</td><td>Marks the trigger element</td></tr>
465
+ </tbody>
466
+ </table>
467
+ </ry-section>
468
+
469
+ <!-- Field -->
470
+ <ry-section>
471
+ <h2>Field</h2>
472
+ <ry-example title="field">
473
+ <template>
474
+ <stack>
475
+ <field label="Email" hint="We'll never share your email">
476
+ <input type="email" placeholder="you@example.com">
477
+ </field>
478
+ <field label="Password" error="Must be at least 8 characters">
479
+ <input type="password" placeholder="Enter password">
480
+ </field>
481
+ <field label="Message">
482
+ <textarea rows="3" placeholder="Your message..."></textarea>
483
+ </field>
484
+ </stack>
485
+ </template>
486
+ </ry-example>
487
+ <table>
488
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
489
+ <tbody>
490
+ <tr><td><code>label</code></td><td>string</td><td>Field label text</td></tr>
491
+ <tr><td><code>error</code></td><td>string</td><td>Error message (hides hint when set)</td></tr>
492
+ <tr><td><code>hint</code></td><td>string</td><td>Helper text below input</td></tr>
493
+ </tbody>
494
+ </table>
495
+ </ry-section>
496
+
497
+ <!-- Checkbox -->
498
+ <ry-section>
499
+ <h2>Checkbox</h2>
500
+ <ry-example title="checkbox">
501
+ <template>
502
+ <stack gap="sm">
503
+ <label>
504
+ <input type="checkbox" checked> Accept terms and conditions
505
+ </label>
506
+ <label>
507
+ <input type="checkbox"> Subscribe to newsletter
508
+ </label>
509
+ <label>
510
+ <input type="checkbox" disabled> Disabled option
511
+ </label>
512
+ </stack>
513
+ </template>
514
+ </ry-example>
515
+ <table>
516
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
517
+ <tbody>
518
+ <tr><td><code>checked</code></td><td>boolean</td><td>Initially checked</td></tr>
519
+ <tr><td><code>disabled</code></td><td>boolean</td><td>Disable interaction</td></tr>
520
+ </tbody>
521
+ </table>
522
+ </ry-section>
523
+
524
+ <!-- Radio -->
525
+ <ry-section>
526
+ <h2>Radio</h2>
527
+ <ry-example title="radio">
528
+ <template>
529
+ <stack gap="sm">
530
+ <label>
531
+ <input type="radio" name="plan" value="free" checked> Free plan
532
+ </label>
533
+ <label>
534
+ <input type="radio" name="plan" value="pro"> Pro plan
535
+ </label>
536
+ <label>
537
+ <input type="radio" name="plan" value="enterprise"> Enterprise
538
+ </label>
539
+ <label>
540
+ <input type="radio" name="plan" value="disabled" disabled> Disabled
541
+ </label>
542
+ </stack>
543
+ </template>
544
+ </ry-example>
545
+ <table>
546
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
547
+ <tbody>
548
+ <tr><td><code>name</code></td><td>string</td><td>Groups radios together</td></tr>
549
+ <tr><td><code>value</code></td><td>string</td><td>Value when selected</td></tr>
550
+ <tr><td><code>checked</code></td><td>boolean</td><td>Initially selected</td></tr>
551
+ <tr><td><code>disabled</code></td><td>boolean</td><td>Disable interaction</td></tr>
552
+ </tbody>
553
+ </table>
554
+ </ry-section>
555
+
556
+ <!-- Badge -->
557
+ <ry-section>
558
+ <h2>Badge</h2>
559
+ <ry-example title="badge">
560
+ <template>
561
+ <cluster>
562
+ <badge>Default</badge>
563
+ <badge variant="primary">Primary</badge>
564
+ <badge variant="success">Success</badge>
565
+ <badge variant="warning">Warning</badge>
566
+ <badge variant="danger">Danger</badge>
567
+ <badge variant="accent">Accent</badge>
568
+ <badge style="--ry-badge-color: oklch(0.6 0.2 150)">Custom Color</badge>
569
+ </cluster>
570
+ </template>
571
+ </ry-example>
572
+ <table>
573
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
574
+ <tbody>
575
+ <tr><td><code>variant</code></td><td>default | primary | success | warning | danger | accent</td><td>Badge style</td></tr>
576
+ <tr><td><code>--ry-badge-color</code></td><td>CSS color</td><td>Arbitrary background color via style</td></tr>
577
+ <tr><td><code>--ry-badge-text</code></td><td>CSS color</td><td>Arbitrary text color via style</td></tr>
578
+ </tbody>
579
+ </table>
580
+ </ry-section>
581
+
582
+ <!-- Alert -->
583
+ <ry-section>
584
+ <h2>Alert</h2>
585
+ <ry-example title="alert">
586
+ <template>
587
+ <stack>
588
+ <alert type="info" title="Info">
589
+ This is an informational message.
590
+ </alert>
591
+ <alert type="success" title="Success">
592
+ Operation completed successfully.
593
+ </alert>
594
+ <alert type="warning" title="Warning">
595
+ Please review before continuing.
596
+ </alert>
597
+ <alert type="danger" title="Error">
598
+ Something went wrong.
599
+ </alert>
600
+ </stack>
601
+ </template>
602
+ </ry-example>
603
+ <table>
604
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
605
+ <tbody>
606
+ <tr><td><code>type</code></td><td>info | success | warning | danger</td><td>Alert style</td></tr>
607
+ <tr><td><code>title</code></td><td>string</td><td>Alert heading</td></tr>
608
+ </tbody>
609
+ </table>
610
+ </ry-section>
611
+
612
+ <!-- Switch -->
613
+ <ry-section>
614
+ <h2>Switch</h2>
615
+ <ry-example title="switch">
616
+ <template>
617
+ <stack gap="sm">
618
+ <switch name="notifications" checked>Enable notifications</switch>
619
+ <switch name="darkMode">Dark mode</switch>
620
+ <switch disabled>Disabled switch</switch>
621
+ </stack>
622
+ </template>
623
+ <script slot="js" type="text/plain">
624
+ const toggle = document.querySelector('ry-switch');
625
+
626
+ // Listen for changes
627
+ toggle.addEventListener('ry:change', (e) => {
628
+ console.log(e.detail.checked); // true or false
629
+ console.log(e.detail.name); // "notifications"
630
+ });
631
+
632
+ // Get/set programmatically
633
+ toggle.checked; // true
634
+ toggle.checked = false; // Turn off
635
+
636
+ // Form usage with hidden input
637
+ const hidden = document.querySelector('input[name="notifications"]');
638
+ toggle.addEventListener('ry:change', (e) => {
639
+ hidden.value = e.detail.checked ? '1' : '0';
640
+ });
641
+ </script>
642
+ </ry-example>
643
+ <table>
644
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
645
+ <tbody>
646
+ <tr><td><code>name</code></td><td>string</td><td>Form field name</td></tr>
647
+ <tr><td><code>checked</code></td><td>boolean</td><td>Initially checked</td></tr>
648
+ <tr><td><code>disabled</code></td><td>boolean</td><td>Disable interaction</td></tr>
649
+ </tbody>
650
+ </table>
651
+ </ry-section>
652
+
653
+ <!-- Slider -->
654
+ <ry-section>
655
+ <h2>Slider</h2>
656
+ <ry-example title="slider" stacked>
657
+ <template>
658
+ <stack gap="lg">
659
+ <slider min="0" max="100" value="50"></slider>
660
+
661
+ <slider min="0" max="100" start="25" end="75" range labeled></slider>
662
+
663
+ <slider min="0" max="10" step="1" value="5" ticked labeled tooltip></slider>
664
+
665
+ <stack gap="sm">
666
+ <slider min="0" max="100" value="60" color="success"></slider>
667
+ <slider min="0" max="100" value="60" color="warning"></slider>
668
+ <slider min="0" max="100" value="60" color="danger"></slider>
669
+ <slider min="0" max="100" value="60" color="info"></slider>
670
+ </stack>
671
+
672
+ <slider min="0" max="100" value="50" disabled></slider>
673
+ </stack>
674
+ </template>
675
+ <script slot="js" type="text/plain">
676
+ const slider = document.querySelector('ry-slider');
677
+
678
+ // Listen for changes (fires during drag)
679
+ slider.addEventListener('ry:input', (e) => {
680
+ console.log(e.detail.value); // 50 (single slider)
681
+ // For range slider:
682
+ // e.detail.start, e.detail.end
683
+ });
684
+
685
+ // Listen for final value (fires on mouseup)
686
+ slider.addEventListener('ry:change', (e) => {
687
+ saveVolume(e.detail.value);
688
+ });
689
+
690
+ // Get/set programmatically
691
+ slider.value; // 50
692
+ slider.value = 75; // Update value
693
+
694
+ // Range slider
695
+ rangeSlider.start; // 25
696
+ rangeSlider.end; // 75
697
+ rangeSlider.start = 10;
698
+ </script>
699
+ </ry-example>
700
+ <table>
701
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
702
+ <tbody>
703
+ <tr><td><code>min</code></td><td>number</td><td>Minimum value (default: 0)</td></tr>
704
+ <tr><td><code>max</code></td><td>number</td><td>Maximum value (default: 100)</td></tr>
705
+ <tr><td><code>step</code></td><td>number</td><td>Step increment, 0 for smooth (default: 1)</td></tr>
706
+ <tr><td><code>value</code></td><td>number</td><td>Current value (single slider)</td></tr>
707
+ <tr><td><code>start</code> / <code>end</code></td><td>number</td><td>Range values (with range attribute)</td></tr>
708
+ <tr><td><code>range</code></td><td>boolean</td><td>Enable dual-handle range mode</td></tr>
709
+ <tr><td><code>labeled</code></td><td>boolean</td><td>Show value labels</td></tr>
710
+ <tr><td><code>ticked</code></td><td>boolean</td><td>Show tick marks</td></tr>
711
+ <tr><td><code>tooltip</code></td><td>boolean</td><td>Show value on thumb hover</td></tr>
712
+ <tr><td><code>vertical</code></td><td>boolean</td><td>Vertical orientation</td></tr>
713
+ <tr><td><code>reversed</code></td><td>boolean</td><td>Reverse direction</td></tr>
714
+ <tr><td><code>color</code></td><td>primary | secondary | success | warning | danger | info</td><td>Track color</td></tr>
715
+ <tr><td><code>size</code></td><td>sm | lg</td><td>Size variant</td></tr>
716
+ <tr><td><code>disabled</code></td><td>boolean</td><td>Disable interaction</td></tr>
717
+ </tbody>
718
+ </table>
719
+ </ry-section>
720
+
721
+ <!-- Knob -->
722
+ <ry-section>
723
+ <h2>Knob</h2>
724
+ <ry-example title="knob" stacked>
725
+ <template>
726
+ <stack gap="lg">
727
+ <cluster>
728
+ <knob min="0" max="100" value="50" label="Volume" description="Master output volume"></knob>
729
+ <knob min="0" max="100" value="75" label="Pan" description="Left/right stereo position"></knob>
730
+ <knob min="0" max="100" value="25" label="Reverb" description="Room reverb amount"></knob>
731
+ </cluster>
732
+
733
+ <cluster>
734
+ <knob min="0" max="3" step="1" value="1" labels="Off,Low,Med,High" label="Mode"></knob>
735
+ <knob min="0" max="4" step="1" value="2" labels="∿,△,▢,▲,◇" label="Wave"></knob>
736
+ </cluster>
737
+
738
+ <cluster>
739
+ <knob value="60" color="success" label="Success"></knob>
740
+ <knob value="60" color="warning" label="Warning"></knob>
741
+ <knob value="60" color="danger" label="Danger"></knob>
742
+ <knob value="60" color="secondary" label="Secondary"></knob>
743
+ </cluster>
744
+
745
+ <cluster>
746
+ <knob size="sm" value="25" label="Small"></knob>
747
+ <knob value="50" label="Default"></knob>
748
+ <knob size="lg" value="75" label="Large"></knob>
749
+ </cluster>
750
+
751
+ <knob min="0" max="100" value="50" label="Disabled" disabled></knob>
752
+ </stack>
753
+ </template>
754
+ <script slot="js" type="text/plain">
755
+ const knob = document.querySelector('ry-knob');
756
+
757
+ // Listen for changes (fires during drag)
758
+ knob.addEventListener('ry:input', (e) => {
759
+ console.log(e.detail.value); // 0-100
760
+ updateVolume(e.detail.value);
761
+ });
762
+
763
+ // Listen for final value (fires on release)
764
+ knob.addEventListener('ry:change', (e) => {
765
+ saveSettings({ volume: e.detail.value });
766
+ });
767
+
768
+ // Get/set programmatically
769
+ knob.value; // 50
770
+ knob.value = 75; // Update value
771
+
772
+ // For discrete knobs with labels
773
+ modeKnob.value; // 1 (index)
774
+ // Labels: "Off,Low,Med,High" → value 1 = "Low"
775
+ </script>
776
+ </ry-example>
777
+ <table>
778
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
779
+ <tbody>
780
+ <tr><td><code>min</code></td><td>number</td><td>Minimum value (default: 0)</td></tr>
781
+ <tr><td><code>max</code></td><td>number</td><td>Maximum value (default: 100)</td></tr>
782
+ <tr><td><code>step</code></td><td>number</td><td>Step increment, 0 for smooth (default: 0)</td></tr>
783
+ <tr><td><code>value</code></td><td>number</td><td>Current value</td></tr>
784
+ <tr><td><code>label</code></td><td>string</td><td>Label text below knob</td></tr>
785
+ <tr><td><code>labels</code></td><td>string</td><td>Comma-separated display labels for discrete values</td></tr>
786
+ <tr><td><code>description</code></td><td>string</td><td>Tooltip text shown on hover</td></tr>
787
+ <tr><td><code>color</code></td><td>primary | secondary | success | warning | danger</td><td>Track color</td></tr>
788
+ <tr><td><code>size</code></td><td>sm | lg</td><td>Size variant</td></tr>
789
+ <tr><td><code>disabled</code></td><td>boolean</td><td>Disable interaction</td></tr>
790
+ </tbody>
791
+ </table>
792
+ </ry-section>
793
+
794
+ <!-- Number Select -->
795
+ <ry-section>
796
+ <h2>Number Select</h2>
797
+ <ry-example title="number-select" stacked>
798
+ <template>
799
+ <ry-stack gap="xl">
800
+
801
+ <p><strong>Arrow placement</strong></p>
802
+ <ry-cluster gap="lg">
803
+ <ry-number-select min="0" max="100" value="50"></ry-number-select>
804
+ <ry-number-select min="0" max="100" value="50" arrows="start"></ry-number-select>
805
+ <ry-number-select min="0" max="100" value="50" arrows="end"></ry-number-select>
806
+ <ry-number-select min="0" max="100" value="50" arrows="none"></ry-number-select>
807
+ <ry-number-select min="0" max="100" value="50" arrows="stacked"></ry-number-select>
808
+ <ry-number-select min="0" max="100" value="50" arrows="stacked-end" icons="chevron"></ry-number-select>
809
+ <ry-number-select min="0" max="100" value="50" arrows="stacked-start" icons="chevron"></ry-number-select>
810
+ </ry-cluster>
811
+
812
+ <p><strong>Icon styles</strong></p>
813
+ <ry-cluster gap="lg">
814
+ <ry-number-select min="0" max="100" value="50"></ry-number-select>
815
+ <ry-number-select min="0" max="100" value="50" icons="chevron"></ry-number-select>
816
+ <ry-number-select min="0" max="100" value="50" icons="arrow"></ry-number-select>
817
+ <ry-number-select min="0" max="100" value="50" icons="chevron" arrows="stacked"></ry-number-select>
818
+ <ry-number-select min="0" max="100" value="50" icons="arrow" arrows="stacked"></ry-number-select>
819
+ </ry-cluster>
820
+
821
+ <p><strong>Drag direction</strong> — stacked defaults to vertical</p>
822
+ <ry-cluster gap="lg">
823
+ <ry-number-select min="0" max="100" value="50"></ry-number-select>
824
+ <ry-number-select min="0" max="100" value="50" drag="y"></ry-number-select>
825
+ <ry-number-select min="0" max="100" value="50" drag="none"></ry-number-select>
826
+ </ry-cluster>
827
+
828
+ <p><strong>Prefix &amp; suffix</strong></p>
829
+ <ry-cluster gap="lg">
830
+ <ry-number-select min="0" max="1000" value="50" prefix="$"></ry-number-select>
831
+ <ry-number-select min="0" max="360" value="90" suffix="°" icons="chevron" arrows="stacked"></ry-number-select>
832
+ <ry-number-select min="0" max="100" value="75" suffix="%"></ry-number-select>
833
+ <ry-number-select min="0" max="100" value="50" prefix="$" suffix="USD" editable></ry-number-select>
834
+ </ry-cluster>
835
+
836
+ <p><strong>Step intervals</strong></p>
837
+ <ry-cluster gap="lg">
838
+ <ry-number-select min="0" max="100" value="50" step="1"></ry-number-select>
839
+ <ry-number-select min="0" max="100" value="50" step="5"></ry-number-select>
840
+ <ry-number-select min="0" max="10" value="5" step="0.1"></ry-number-select>
841
+ <ry-number-select min="0" max="1" value="0.5" step="0.01"></ry-number-select>
842
+ </ry-cluster>
843
+
844
+ <p><strong>Behaviors</strong></p>
845
+ <ry-cluster gap="lg">
846
+ <ry-number-select min="0" max="100" value="50" editable></ry-number-select>
847
+ <ry-number-select min="0" max="7" value="3" wrap></ry-number-select>
848
+ <ry-number-select min="0" max="100" value="50" disabled></ry-number-select>
849
+ </ry-cluster>
850
+
851
+ <p><strong>Sizes</strong></p>
852
+ <ry-cluster gap="lg" style="align-items: center">
853
+ <ry-number-select min="0" max="100" value="50" size="xs"></ry-number-select>
854
+ <ry-number-select min="0" max="100" value="50" size="sm"></ry-number-select>
855
+ <ry-number-select min="0" max="100" value="50"></ry-number-select>
856
+ <ry-number-select min="0" max="100" value="50" size="lg"></ry-number-select>
857
+ </ry-cluster>
858
+
859
+ </ry-stack>
860
+ </template>
861
+ <script slot="js" type="text/plain">
862
+ const ns = document.querySelector('ry-number-select');
863
+
864
+ // Events — ry:input fires during drag, ry:change on commit
865
+ ns.addEventListener('ry:input', (e) => {
866
+ console.log('input:', e.detail.value);
867
+ });
868
+ ns.addEventListener('ry:change', (e) => {
869
+ console.log('change:', e.detail.value);
870
+ });
871
+
872
+ // Programmatic API
873
+ ns.value; // 50
874
+ ns.value = 75;
875
+ ns.min = 10;
876
+ ns.step = 5;
877
+ ns.drag = 'y'; // switch drag direction
878
+ ns.disabled = true;
879
+ </script>
880
+ </ry-example>
881
+ <table>
882
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
883
+ <tbody>
884
+ <tr><td><code>min</code></td><td>number</td><td>Minimum value (default: 0)</td></tr>
885
+ <tr><td><code>max</code></td><td>number</td><td>Maximum value (default: 100)</td></tr>
886
+ <tr><td><code>step</code></td><td>number</td><td>Step increment (default: 1)</td></tr>
887
+ <tr><td><code>value</code></td><td>number</td><td>Current value (default: 0)</td></tr>
888
+ <tr><td><code>arrows</code></td><td>both | start | end | stacked | none</td><td>Button placement (default: both)</td></tr>
889
+ <tr><td><code>icons</code></td><td>plus-minus | chevron | arrow</td><td>Button icon style (default: plus-minus)</td></tr>
890
+ <tr><td><code>drag</code></td><td>x | y | none</td><td>Drag direction (default: x, stacked defaults to y)</td></tr>
891
+ <tr><td><code>prefix</code></td><td>string</td><td>Text before value (e.g. "$")</td></tr>
892
+ <tr><td><code>suffix</code></td><td>string</td><td>Text after value (e.g. "°", "%")</td></tr>
893
+ <tr><td><code>editable</code></td><td>boolean</td><td>Allow direct typing on click</td></tr>
894
+ <tr><td><code>wrap</code></td><td>boolean</td><td>Wrap around min/max boundaries</td></tr>
895
+ <tr><td><code>size</code></td><td>xs | sm | lg</td><td>Size variant</td></tr>
896
+ <tr><td><code>disabled</code></td><td>boolean</td><td>Disable interaction</td></tr>
897
+ </tbody>
898
+ </table>
899
+ </ry-section>
900
+
901
+ <!-- Color Picker -->
902
+ <ry-section>
903
+ <h2>Color Picker</h2>
904
+ <ry-example title="color-picker" stacked>
905
+ <template>
906
+ <stack gap="lg">
907
+ <p><strong>Dropdown mode (default)</strong> - click to open:</p>
908
+ <cluster>
909
+ <color-picker value="#3b82f6"></color-picker>
910
+ <color-picker value="#22c55e"></color-picker>
911
+ <color-picker value="#8b5cf6"></color-picker>
912
+ <color-picker value="#ef4444" opacity></color-picker>
913
+ </cluster>
914
+
915
+ <p><strong>With swatches:</strong></p>
916
+ <color-picker
917
+ value="#3b82f6"
918
+ swatches="#ef4444;#f59e0b;#22c55e;#3b82f6;#8b5cf6;#ec4899;#1e293b;#ffffff"
919
+ ></color-picker>
920
+
921
+ <p><strong>Inline mode</strong> - always visible:</p>
922
+ <color-picker value="#3b82f6" inline></color-picker>
923
+
924
+ <p><strong>With opacity + swatches (inline):</strong></p>
925
+ <color-picker
926
+ value="rgba(59, 130, 246, 0.7)"
927
+ format="rgb"
928
+ opacity
929
+ inline
930
+ swatches="#ef4444;#f59e0b;#22c55e;#3b82f6;#8b5cf6;#ec4899"
931
+ ></color-picker>
932
+
933
+ <cluster>
934
+ <color-picker value="#64748b" disabled></color-picker>
935
+ <span style="color: var(--ry-color-text-muted)">Disabled</span>
936
+ </cluster>
937
+ </stack>
938
+ </template>
939
+ <script slot="js" type="text/plain">
940
+ // Get the element
941
+ const picker = document.querySelector('ry-color-picker');
942
+
943
+ // Listen for changes (fires during drag)
944
+ picker.addEventListener('ry:input', (e) => {
945
+ console.log(e.detail.value); // "#3b82f6"
946
+ console.log(e.detail.rgb); // { r: 59, g: 130, b: 246 }
947
+ console.log(e.detail.hsv); // { h: 217, s: 76, v: 96 }
948
+ });
949
+
950
+ // Listen for final value (fires on mouseup/blur)
951
+ picker.addEventListener('ry:change', (e) => {
952
+ saveColor(e.detail.value);
953
+ });
954
+
955
+ // Get/set programmatically
956
+ picker.value; // "#3b82f6"
957
+ picker.value = '#ff0000'; // Set new color
958
+ picker.setColor('hsl(200, 100%, 50%)'); // Accepts any format
959
+
960
+ // Access color in different formats
961
+ picker.rgb; // { r: 59, g: 130, b: 246 }
962
+ picker.hsl; // { h: 217, s: 89, l: 60 }
963
+ picker.hsv; // { h: 217, s: 76, v: 96 }
964
+
965
+ // Form submission
966
+ form.addEventListener('submit', (e) => {
967
+ const color = picker.value;
968
+ fetch('/api/save', { body: JSON.stringify({ color }) });
969
+ });
970
+ </script>
971
+ </ry-example>
972
+ <table>
973
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
974
+ <tbody>
975
+ <tr><td><code>value</code></td><td>color string</td><td>Initial color (hex, rgb, hsl)</td></tr>
976
+ <tr><td><code>format</code></td><td>hex | rgb | hsl</td><td>Output format (default: hex)</td></tr>
977
+ <tr><td><code>inline</code></td><td>boolean</td><td>Always show picker (no dropdown)</td></tr>
978
+ <tr><td><code>opacity</code></td><td>boolean</td><td>Enable alpha channel slider</td></tr>
979
+ <tr><td><code>swatches</code></td><td>string</td><td>Preset colors separated by semicolons</td></tr>
980
+ <tr><td><code>disabled</code></td><td>boolean</td><td>Disable interaction</td></tr>
981
+ </tbody>
982
+ </table>
983
+ </ry-section>
984
+
985
+ <!-- Color Input -->
986
+ <ry-section>
987
+ <h2>Color Input</h2>
988
+ <ry-example title="color-input" stacked>
989
+ <template>
990
+ <stack gap="lg">
991
+ <p>Input-style color picker with editable hex value:</p>
992
+ <cluster>
993
+ <color-input value="#3b82f6"></color-input>
994
+ <color-input value="#22c55e"></color-input>
995
+ <color-input value="#ef4444"></color-input>
996
+ </cluster>
997
+
998
+ <p>With opacity support:</p>
999
+ <color-input value="rgba(139, 92, 246, 0.8)" opacity></color-input>
1000
+
1001
+ <cluster>
1002
+ <color-input value="#64748b" disabled></color-input>
1003
+ <span style="color: var(--ry-color-text-muted)">Disabled</span>
1004
+ </cluster>
1005
+ </stack>
1006
+ </template>
1007
+ <script slot="js" type="text/plain">
1008
+ const input = document.querySelector('ry-color-input');
1009
+
1010
+ // Listen for changes
1011
+ input.addEventListener('ry:change', (e) => {
1012
+ console.log(e.detail.value); // "#3b82f6"
1013
+ });
1014
+
1015
+ // Get/set programmatically
1016
+ input.value; // "#3b82f6"
1017
+ input.value = '#ff0000';
1018
+
1019
+ // Works great in forms
1020
+ const hidden = document.querySelector('input[name="brandColor"]');
1021
+ input.addEventListener('ry:change', (e) => {
1022
+ hidden.value = e.detail.value;
1023
+ });
1024
+ </script>
1025
+ </ry-example>
1026
+ <table>
1027
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1028
+ <tbody>
1029
+ <tr><td><code>value</code></td><td>color string</td><td>Initial color (hex, rgb, hsl)</td></tr>
1030
+ <tr><td><code>format</code></td><td>hex | rgb | hsl</td><td>Output format (default: hex)</td></tr>
1031
+ <tr><td><code>opacity</code></td><td>boolean</td><td>Enable alpha channel slider</td></tr>
1032
+ <tr><td><code>placeholder</code></td><td>string</td><td>Placeholder text for input</td></tr>
1033
+ <tr><td><code>disabled</code></td><td>boolean</td><td>Disable interaction</td></tr>
1034
+ </tbody>
1035
+ </table>
1036
+ </ry-section>
1037
+
1038
+ <!-- Gradient Picker -->
1039
+ <ry-section>
1040
+ <h2>Gradient Picker</h2>
1041
+ <ry-example title="gradient-picker" stacked>
1042
+ <template>
1043
+ <stack gap="lg">
1044
+ <p><strong>Linear gradient</strong> (default):</p>
1045
+ <gradient-picker value="linear-gradient(90deg, #3b82f6 0%, #8b5cf6 100%)"></gradient-picker>
1046
+
1047
+ <p><strong>Multi-stop rainbow with CSS output:</strong></p>
1048
+ <gradient-picker value="linear-gradient(90deg, #ef4444 0%, #f59e0b 25%, #22c55e 50%, #3b82f6 75%, #8b5cf6 100%)" output></gradient-picker>
1049
+
1050
+ <p><strong>Radial gradient:</strong></p>
1051
+ <gradient-picker value="radial-gradient(circle, #fbbf24 0%, #f97316 50%, #dc2626 100%)"></gradient-picker>
1052
+ </stack>
1053
+ </template>
1054
+ <script slot="js" type="text/plain">
1055
+ const picker = document.querySelector('ry-gradient-picker');
1056
+
1057
+ // Listen for changes
1058
+ picker.addEventListener('ry:input', (e) => {
1059
+ console.log(e.detail.value); // CSS gradient string
1060
+ console.log(e.detail.stops); // [{ id, color, position }, ...]
1061
+ console.log(e.detail.type); // "linear" | "radial"
1062
+ console.log(e.detail.angle); // 90 (degrees, linear only)
1063
+ });
1064
+
1065
+ picker.addEventListener('ry:change', (e) => {
1066
+ applyGradient(e.detail.value);
1067
+ });
1068
+
1069
+ // Get/set programmatically
1070
+ picker.value; // "linear-gradient(90deg, ...)"
1071
+ picker.value = 'radial-gradient(circle, #ff0000 0%, #0000ff 100%)';
1072
+ picker.type; // "linear" | "radial"
1073
+ picker.angle; // 0-360
1074
+ picker.stops; // [{ id, color, position }, ...]
1075
+
1076
+ // Add/remove stops
1077
+ picker.addStop('#ff00ff', 50);
1078
+ picker.removeStop('stop-2');
1079
+ </script>
1080
+ </ry-example>
1081
+ <table>
1082
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1083
+ <tbody>
1084
+ <tr><td><code>value</code></td><td>CSS gradient string</td><td>Initial gradient value</td></tr>
1085
+ <tr><td><code>disabled</code></td><td>boolean</td><td>Disable interaction</td></tr>
1086
+ </tbody>
1087
+ </table>
1088
+ </ry-section>
1089
+
1090
+ <!-- Toggle Button -->
1091
+ <ry-section>
1092
+ <h2>Toggle Button</h2>
1093
+ <ry-example title="toggle-button" stacked>
1094
+ <template>
1095
+ <stack gap="lg">
1096
+ <grid cols="3">
1097
+ <toggle-button name="plan" value="bronze" block>
1098
+ <strong>Bronze</strong><br>
1099
+ <small>Standard Network</small><br>
1100
+ <big><b>$85</b>/month</big><br>
1101
+ <small>Deductible: $6,000 · Coverage: 60%</small><br>
1102
+ <small>✓ Preventive care ✓ Generic Rx ✓ Telehealth</small>
1103
+ </toggle-button>
1104
+ <toggle-button name="plan" value="silver" block>
1105
+ <strong>Silver</strong><br>
1106
+ <small>Enhanced Network</small><br>
1107
+ <big><b>$145</b>/month</big><br>
1108
+ <small>Deductible: $3,000 · Coverage: 80%</small><br>
1109
+ <small>✓ Lower deductible ✓ Specialists ✓ Mental health</small>
1110
+ </toggle-button>
1111
+ <toggle-button name="plan" value="gold" block>
1112
+ <strong>Gold</strong><br>
1113
+ <small>Premium Network</small><br>
1114
+ <big><b>$220</b>/month</big><br>
1115
+ <small>Deductible: $1,000 · Coverage: 90%</small><br>
1116
+ <small>✓ Lowest out-of-pocket ✓ Full Rx ✓ No referrals</small>
1117
+ </toggle-button>
1118
+ </grid>
1119
+
1120
+ <cluster>
1121
+ <toggle-button name="coverage" value="medical" pressed>🏥 Medical</toggle-button>
1122
+ <toggle-button name="coverage" value="dental">🦷 Dental</toggle-button>
1123
+ <toggle-button name="coverage" value="vision">👁 Vision</toggle-button>
1124
+ </cluster>
1125
+
1126
+ <stack>
1127
+ <grid cols="2">
1128
+ <toggle-button name="selection" value="ppo200" block>
1129
+ <strong>Blue PPO $200</strong><br>
1130
+ <small>Employee Only · $200 Deductible</small><br>
1131
+ <b>$171.28</b>/month
1132
+ </toggle-button>
1133
+ <toggle-button name="selection" value="ppo500" block>
1134
+ <strong>Blue PPO $500</strong><br>
1135
+ <small>Employee Only · $500 Deductible</small><br>
1136
+ <b>$142.53</b>/month
1137
+ </toggle-button>
1138
+ </grid>
1139
+ <toggle-button name="selection" value="waive" pressed>Waive $0/month</toggle-button>
1140
+ </stack>
1141
+ </stack>
1142
+ </template>
1143
+ <script slot="js" type="text/plain">
1144
+ const buttons = document.querySelectorAll('ry-toggle-button[name="plan"]');
1145
+
1146
+ // Listen for changes on each button
1147
+ buttons.forEach(btn => {
1148
+ btn.addEventListener('ry:change', (e) => {
1149
+ console.log(e.detail.pressed); // true or false
1150
+ console.log(e.detail.value); // "bronze", "silver", etc.
1151
+ });
1152
+ });
1153
+
1154
+ // Get selected value from a group
1155
+ function getSelectedPlan() {
1156
+ const pressed = document.querySelector(
1157
+ 'ry-toggle-button[name="plan"][pressed]'
1158
+ );
1159
+ return pressed?.value; // "gold"
1160
+ }
1161
+
1162
+ // Set programmatically
1163
+ button.pressed = true;
1164
+
1165
+ // Form submission
1166
+ form.addEventListener('submit', (e) => {
1167
+ const plan = getSelectedPlan();
1168
+ fetch('/api/enroll', { body: JSON.stringify({ plan }) });
1169
+ });
1170
+ </script>
1171
+ </ry-example>
1172
+ <table>
1173
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1174
+ <tbody>
1175
+ <tr><td><code>name</code></td><td>string</td><td>Groups buttons (same name = only one pressed)</td></tr>
1176
+ <tr><td><code>value</code></td><td>string</td><td>Value when selected</td></tr>
1177
+ <tr><td><code>pressed</code></td><td>boolean</td><td>Currently pressed</td></tr>
1178
+ <tr><td><code>disabled</code></td><td>boolean</td><td>Disable interaction</td></tr>
1179
+ <tr><td><code>size</code></td><td>sm | md | lg</td><td>Button size</td></tr>
1180
+ </tbody>
1181
+ </table>
1182
+ </ry-section>
1183
+
1184
+ <!-- Tooltip -->
1185
+ <ry-section>
1186
+ <h2>Tooltip</h2>
1187
+ <ry-example title="tooltip">
1188
+ <template>
1189
+ <cluster>
1190
+ <tooltip content="Top tooltip" position="top">
1191
+ <button>Top</button>
1192
+ </tooltip>
1193
+ <tooltip content="Bottom tooltip" position="bottom">
1194
+ <button>Bottom</button>
1195
+ </tooltip>
1196
+ <tooltip content="Left tooltip" position="left">
1197
+ <button>Left</button>
1198
+ </tooltip>
1199
+ <tooltip content="Right tooltip" position="right">
1200
+ <button>Right</button>
1201
+ </tooltip>
1202
+ </cluster>
1203
+ </template>
1204
+ </ry-example>
1205
+ <table>
1206
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1207
+ <tbody>
1208
+ <tr><td><code>content</code></td><td>string</td><td>Tooltip text</td></tr>
1209
+ <tr><td><code>position</code></td><td>top | bottom | left | right</td><td>Tooltip position</td></tr>
1210
+ </tbody>
1211
+ </table>
1212
+ </ry-section>
1213
+
1214
+ <!-- Drawer -->
1215
+ <ry-section>
1216
+ <h2>Drawer</h2>
1217
+ <ry-example title="drawer">
1218
+ <template>
1219
+ <button drawer="example-drawer">Open Drawer</button>
1220
+
1221
+ <drawer id="example-drawer" side="right">
1222
+ <h3>Drawer Title</h3>
1223
+ <p>Drawer content here.</p>
1224
+ </drawer>
1225
+ </template>
1226
+ <script slot="js" type="text/plain">
1227
+ const drawer = document.querySelector('ry-drawer');
1228
+
1229
+ // Listen for open/close
1230
+ drawer.addEventListener('ry:open', () => {
1231
+ console.log('Drawer opened');
1232
+ });
1233
+
1234
+ drawer.addEventListener('ry:close', () => {
1235
+ console.log('Drawer closed');
1236
+ });
1237
+
1238
+ // Open/close programmatically
1239
+ drawer.open();
1240
+ drawer.close();
1241
+
1242
+ // Check state
1243
+ if (drawer.state === 'open') { ... }
1244
+ </script>
1245
+ </ry-example>
1246
+ <table>
1247
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1248
+ <tbody>
1249
+ <tr><td><code>id</code></td><td>string</td><td>Drawer identifier</td></tr>
1250
+ <tr><td><code>side</code></td><td>left | right | bottom</td><td>Slide direction</td></tr>
1251
+ </tbody>
1252
+ </table>
1253
+ </ry-section>
1254
+
1255
+ <!-- Toast -->
1256
+ <ry-section>
1257
+ <h2>Toast</h2>
1258
+ <ry-example title="toast">
1259
+ <template>
1260
+ <cluster>
1261
+ <button onclick="RyToast.success('Saved!')">Success</button>
1262
+ <button onclick="RyToast.error('Failed!')">Error</button>
1263
+ <button onclick="RyToast.warning('Warning!')">Warning</button>
1264
+ <button onclick="RyToast.info('Info!')">Info</button>
1265
+ </cluster>
1266
+ </template>
1267
+ <script slot="js" type="text/plain">
1268
+ // Show toasts (global API)
1269
+ RyToast.success('Changes saved!');
1270
+ RyToast.error('Something went wrong');
1271
+ RyToast.warning('Please review');
1272
+ RyToast.info('New update available');
1273
+
1274
+ // With options
1275
+ RyToast.success('Saved!', {
1276
+ duration: 5000, // ms (default: 3000)
1277
+ });
1278
+
1279
+ // Common patterns
1280
+ async function saveData() {
1281
+ try {
1282
+ await api.save(data);
1283
+ RyToast.success('Saved!');
1284
+ } catch (err) {
1285
+ RyToast.error(err.message);
1286
+ }
1287
+ }
1288
+ </script>
1289
+ </ry-example>
1290
+ <table>
1291
+ <thead><tr><th>Method</th><th>Description</th></tr></thead>
1292
+ <tbody>
1293
+ <tr><td><code>RyToast.success(msg)</code></td><td>Show success toast</td></tr>
1294
+ <tr><td><code>RyToast.error(msg)</code></td><td>Show error toast</td></tr>
1295
+ <tr><td><code>RyToast.warning(msg)</code></td><td>Show warning toast</td></tr>
1296
+ <tr><td><code>RyToast.info(msg)</code></td><td>Show info toast</td></tr>
1297
+ </tbody>
1298
+ </table>
1299
+ </ry-section>
1300
+
1301
+ <!-- Select -->
1302
+ <ry-section>
1303
+ <h2>Select</h2>
1304
+ <ry-example title="select">
1305
+ <template>
1306
+ <select placeholder="Choose a country" name="country">
1307
+ <option value="us">United States</option>
1308
+ <option value="uk">United Kingdom</option>
1309
+ <option value="ca">Canada</option>
1310
+ <option value="au">Australia</option>
1311
+ </select>
1312
+ </template>
1313
+ <script slot="js" type="text/plain">
1314
+ const select = document.querySelector('ry-select');
1315
+
1316
+ // Listen for selection changes
1317
+ select.addEventListener('ry:change', (e) => {
1318
+ console.log(e.detail.value); // "us"
1319
+ console.log(e.detail.label); // "United States"
1320
+ });
1321
+
1322
+ // Get/set programmatically
1323
+ select.value; // "us"
1324
+ select.value = 'uk'; // Select United Kingdom
1325
+
1326
+ // Open/close the dropdown
1327
+ select.open();
1328
+ select.close();
1329
+
1330
+ // Form integration
1331
+ const formData = new FormData(form);
1332
+ formData.get('country'); // "us"
1333
+ </script>
1334
+ </ry-example>
1335
+ <table>
1336
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1337
+ <tbody>
1338
+ <tr><td><code>placeholder</code></td><td>string</td><td>Placeholder text</td></tr>
1339
+ <tr><td><code>name</code></td><td>string</td><td>Form field name</td></tr>
1340
+ <tr><td><code>disabled</code></td><td>boolean</td><td>Disable select (on option)</td></tr>
1341
+ <tr><td><code>multiple</code></td><td>boolean</td><td>Enable multi-select with tags</td></tr>
1342
+ <tr><td><code>clearable</code></td><td>boolean</td><td>Show clear button (multi)</td></tr>
1343
+ <tr><td><code>max-selections</code></td><td>number</td><td>Max selections (multi)</td></tr>
1344
+ </tbody>
1345
+ </table>
1346
+
1347
+ <h3 style="margin-top: var(--ry-space-6);">Multi-Select</h3>
1348
+ <ry-example title="select-multi" stacked>
1349
+ <template>
1350
+ <select multiple clearable placeholder="Choose countries...">
1351
+ <option value="us">United States</option>
1352
+ <option value="uk">United Kingdom</option>
1353
+ <option value="ca">Canada</option>
1354
+ <option value="au">Australia</option>
1355
+ <option value="de">Germany</option>
1356
+ <option value="fr">France</option>
1357
+ </select>
1358
+ </template>
1359
+ </ry-example>
1360
+ </ry-section>
1361
+
1362
+ <!-- Combobox -->
1363
+ <ry-section>
1364
+ <h2>Combobox</h2>
1365
+ <ry-example title="combobox">
1366
+ <template>
1367
+ <combobox placeholder="Search countries..." name="country">
1368
+ <option value="us">United States</option>
1369
+ <option value="uk">United Kingdom</option>
1370
+ <option value="ca">Canada</option>
1371
+ <option value="au">Australia</option>
1372
+ <option value="de">Germany</option>
1373
+ <option value="fr">France</option>
1374
+ <option value="jp">Japan</option>
1375
+ <option value="br">Brazil</option>
1376
+ </combobox>
1377
+ </template>
1378
+ <script slot="js" type="text/plain">
1379
+ const combobox = document.querySelector('ry-combobox');
1380
+
1381
+ // Listen for selection
1382
+ combobox.addEventListener('ry:change', (e) => {
1383
+ console.log(e.detail.value); // "us"
1384
+ console.log(e.detail.label); // "United States"
1385
+ });
1386
+
1387
+ // Get/set programmatically
1388
+ combobox.value; // "us"
1389
+ combobox.value = 'uk'; // Select United Kingdom
1390
+
1391
+ // Form integration
1392
+ const formData = new FormData(form);
1393
+ formData.get('country'); // "us"
1394
+ </script>
1395
+ </ry-example>
1396
+ <table>
1397
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1398
+ <tbody>
1399
+ <tr><td><code>placeholder</code></td><td>string</td><td>Placeholder text for input</td></tr>
1400
+ <tr><td><code>name</code></td><td>string</td><td>Form field name</td></tr>
1401
+ <tr><td><code>value</code></td><td>string</td><td>Currently selected value</td></tr>
1402
+ <tr><td><code>disabled</code></td><td>boolean</td><td>Disable combobox</td></tr>
1403
+ </tbody>
1404
+ </table>
1405
+ </ry-section>
1406
+
1407
+ <!-- Code -->
1408
+ <ry-section>
1409
+ <h2>Code</h2>
1410
+ <ry-example title="ry-code">
1411
+ <template>
1412
+ <ry-code language="js" title="app.js">
1413
+ const greeting = "Hello!";
1414
+ console.log(greeting);
1415
+ </ry-code>
1416
+ </template>
1417
+ </ry-example>
1418
+ <table>
1419
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1420
+ <tbody>
1421
+ <tr><td><code>language</code></td><td>js | css | html | json</td><td>Syntax highlighting</td></tr>
1422
+ <tr><td><code>title</code></td><td>string</td><td>Header title</td></tr>
1423
+ <tr><td><code>line-numbers</code></td><td>boolean</td><td>Show line numbers</td></tr>
1424
+ </tbody>
1425
+ </table>
1426
+ <p><em>Note: Use <code>&lt;ry-code&gt;</code> with prefix to avoid conflict with native <code>&lt;code&gt;</code> tag.</em></p>
1427
+ </ry-section>
1428
+
1429
+ <!-- Table -->
1430
+ <ry-section>
1431
+ <h2>Table</h2>
1432
+ <ry-example title="table">
1433
+ <template>
1434
+ <table>
1435
+ <thead>
1436
+ <tr>
1437
+ <th>Name</th>
1438
+ <th>Role</th>
1439
+ <th>Status</th>
1440
+ </tr>
1441
+ </thead>
1442
+ <tbody>
1443
+ <tr>
1444
+ <td>Alice Johnson</td>
1445
+ <td>Engineer</td>
1446
+ <td>Active</td>
1447
+ </tr>
1448
+ <tr>
1449
+ <td>Bob Smith</td>
1450
+ <td>Designer</td>
1451
+ <td>Active</td>
1452
+ </tr>
1453
+ <tr>
1454
+ <td>Carol White</td>
1455
+ <td>Manager</td>
1456
+ <td>Away</td>
1457
+ </tr>
1458
+ </tbody>
1459
+ </table>
1460
+ </template>
1461
+ </ry-example>
1462
+ <table>
1463
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1464
+ <tbody>
1465
+ <tr><td><code>data-bordered</code></td><td>boolean</td><td>Add borders to all cells</td></tr>
1466
+ <tr><td><code>data-striped</code></td><td>boolean</td><td>Alternate row backgrounds</td></tr>
1467
+ </tbody>
1468
+ </table>
1469
+ </ry-section>
1470
+
1471
+ <!-- Tree -->
1472
+ <ry-section>
1473
+ <h2>Tree</h2>
1474
+ <ry-example title="tree" stacked>
1475
+ <template>
1476
+ <tree sortable>
1477
+ <tree-item label="src" open>
1478
+ <tree-item label="app" open>
1479
+ <tree-item label="layout.tsx"></tree-item>
1480
+ <tree-item label="page.tsx"></tree-item>
1481
+ </tree-item>
1482
+ <tree-item label="components" open>
1483
+ <tree-item label="ui" open>
1484
+ <tree-item label="button.tsx" selected></tree-item>
1485
+ </tree-item>
1486
+ <tree-item label="header.tsx"></tree-item>
1487
+ <tree-item label="footer.tsx"></tree-item>
1488
+ </tree-item>
1489
+ <tree-item label="lib" open>
1490
+ <tree-item label="utils.ts"></tree-item>
1491
+ </tree-item>
1492
+ </tree-item>
1493
+ </tree>
1494
+ </template>
1495
+ <script slot="js" type="text/plain">
1496
+ const tree = document.querySelector('ry-tree');
1497
+
1498
+ // Listen for file selection
1499
+ tree.addEventListener('ry:select', (e) => {
1500
+ console.log(e.detail.label); // "button.tsx"
1501
+ });
1502
+
1503
+ // Listen for folder toggle
1504
+ tree.addEventListener('ry:toggle', (e) => {
1505
+ console.log(e.detail.label, e.detail.open);
1506
+ });
1507
+
1508
+ // Listen for drag-and-drop moves
1509
+ tree.addEventListener('ry:move', (e) => {
1510
+ console.log(`Moved "${e.detail.item}" ${e.detail.position} "${e.detail.target}"`);
1511
+
1512
+ // Get the full new structure as JSON
1513
+ const state = tree.toJSON();
1514
+ console.log(JSON.stringify(state, null, 2));
1515
+ });
1516
+
1517
+ // Serialize current state
1518
+ tree.toJSON(); // [{ label: "src", open: true, children: [...] }]
1519
+ tree.value; // Same thing
1520
+
1521
+ // Create tree from JSON data
1522
+ const copy = RyTree.from(tree.toJSON());
1523
+ document.body.appendChild(copy);
1524
+ </script>
1525
+ </ry-example>
1526
+ <table>
1527
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1528
+ <tbody>
1529
+ <tr><td><code>label</code></td><td>string</td><td>Item label (on tree-item)</td></tr>
1530
+ <tr><td><code>open</code></td><td>boolean</td><td>Initially expanded folder (on tree-item with children)</td></tr>
1531
+ <tr><td><code>selected</code></td><td>boolean</td><td>Initially selected file (on leaf tree-item)</td></tr>
1532
+ <tr><td><code>sortable</code></td><td>boolean</td><td>Enable drag-and-drop rearranging (on tree)</td></tr>
1533
+ <tr><td><code>no-animate</code></td><td>boolean</td><td>Disable expand/collapse animation (on tree)</td></tr>
1534
+ <tr><td><code>--ry-tree-duration</code></td><td>CSS time</td><td>Animation duration (default: 150ms)</td></tr>
1535
+ </tbody>
1536
+ </table>
1537
+ </ry-section>
1538
+
1539
+ <!-- Icon -->
1540
+ <ry-section>
1541
+ <h2>Icon</h2>
1542
+ <ry-example title="icon">
1543
+ <template>
1544
+ <cluster>
1545
+ <icon name="heart"></icon>
1546
+ <icon name="star"></icon>
1547
+ <icon name="search"></icon>
1548
+ <icon name="settings"></icon>
1549
+ <icon name="user"></icon>
1550
+ <icon name="check"></icon>
1551
+ </cluster>
1552
+ </template>
1553
+ </ry-example>
1554
+ <table>
1555
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1556
+ <tbody>
1557
+ <tr><td><code>name</code></td><td>close | check | chevron-* | copy | sun | moon | info | warning | error | success | search | menu | plus | minus | settings | user | heart | star | trash | edit | external-link | download | upload</td><td>Icon name</td></tr>
1558
+ <tr><td><code>size</code></td><td>number</td><td>Size in pixels (default: 24)</td></tr>
1559
+ <tr><td><code>label</code></td><td>string</td><td>Accessible label</td></tr>
1560
+ </tbody>
1561
+ </table>
1562
+ </ry-section>
1563
+
1564
+ <!-- Tag -->
1565
+ <ry-section>
1566
+ <h2>Tag</h2>
1567
+ <ry-example title="tag" stacked>
1568
+ <template>
1569
+ <cluster>
1570
+ <tag>Default</tag>
1571
+ <tag variant="primary">Primary</tag>
1572
+ <tag variant="success">Success</tag>
1573
+ <tag variant="warning">Warning</tag>
1574
+ <tag variant="danger">Danger</tag>
1575
+ <tag removable>Removable</tag>
1576
+ <tag variant="primary" removable>Remove me</tag>
1577
+ <tag size="sm">Small</tag>
1578
+ <tag size="lg">Large</tag>
1579
+ </cluster>
1580
+ </template>
1581
+ </ry-example>
1582
+ <table>
1583
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1584
+ <tbody>
1585
+ <tr><td><code>variant</code></td><td>default | primary | success | warning | danger</td><td>Color variant</td></tr>
1586
+ <tr><td><code>size</code></td><td>sm | md | lg</td><td>Size</td></tr>
1587
+ <tr><td><code>removable</code></td><td>boolean</td><td>Show close button</td></tr>
1588
+ <tr><td><code>data-value</code></td><td>string</td><td>Value included in ry:remove event</td></tr>
1589
+ </tbody>
1590
+ </table>
1591
+ </ry-section>
1592
+
1593
+ <!-- Tag Input -->
1594
+ <ry-section>
1595
+ <h2>Tag Input</h2>
1596
+ <ry-example title="tag-input" stacked>
1597
+ <template>
1598
+ <tag-input placeholder="Add tags (comma separated)..." value="JavaScript,TypeScript,CSS"></tag-input>
1599
+ </template>
1600
+ </ry-example>
1601
+ <table>
1602
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1603
+ <tbody>
1604
+ <tr><td><code>placeholder</code></td><td>string</td><td>Input placeholder</td></tr>
1605
+ <tr><td><code>delimiter</code></td><td>string</td><td>Delimiter character (default ",")</td></tr>
1606
+ <tr><td><code>max-tags</code></td><td>number</td><td>Max number of tags</td></tr>
1607
+ <tr><td><code>value</code></td><td>string</td><td>Initial comma-separated values</td></tr>
1608
+ <tr><td><code>name</code></td><td>string</td><td>Form field name</td></tr>
1609
+ </tbody>
1610
+ </table>
1611
+ </ry-section>
1612
+
1613
+ <!-- Hero -->
1614
+ <ry-section>
1615
+ <h2>Hero</h2>
1616
+ <ry-example title="hero" stacked>
1617
+ <template>
1618
+ <hero align="center">
1619
+ <h1>Build apps faster</h1>
1620
+ <p>Framework-agnostic, Light DOM web components. CSS is the source of truth.</p>
1621
+ <cluster>
1622
+ <button>Get Started</button>
1623
+ <button variant="outline">Learn More</button>
1624
+ </cluster>
1625
+ </hero>
1626
+ </template>
1627
+ </ry-example>
1628
+ <table>
1629
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1630
+ <tbody>
1631
+ <tr><td><code>align</code></td><td>center | left</td><td>Text alignment</td></tr>
1632
+ <tr><td><code>size</code></td><td>sm | md | lg</td><td>Padding size</td></tr>
1633
+ <tr><td><code>full-bleed</code></td><td>boolean</td><td>Remove max-width</td></tr>
1634
+ </tbody>
1635
+ </table>
1636
+ </ry-section>
1637
+
1638
+ <!-- Stat -->
1639
+ <ry-section>
1640
+ <h2>Stat</h2>
1641
+ <ry-example title="stat">
1642
+ <template>
1643
+ <grid cols="3">
1644
+ <stat trend="up">
1645
+ <span slot="value">2,847</span>
1646
+ <span slot="label">Active Users</span>
1647
+ </stat>
1648
+ <stat trend="down">
1649
+ <span slot="value">1.2%</span>
1650
+ <span slot="label">Bounce Rate</span>
1651
+ </stat>
1652
+ <stat>
1653
+ <span slot="value">$12,340</span>
1654
+ <span slot="label">Revenue</span>
1655
+ </stat>
1656
+ </grid>
1657
+ </template>
1658
+ </ry-example>
1659
+ <table>
1660
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1661
+ <tbody>
1662
+ <tr><td><code>trend</code></td><td>up | down</td><td>Trend arrow direction</td></tr>
1663
+ <tr><td><code>size</code></td><td>sm | md | lg</td><td>Value text size</td></tr>
1664
+ <tr><td><code>align</code></td><td>left | center</td><td>Alignment</td></tr>
1665
+ </tbody>
1666
+ </table>
1667
+ </ry-section>
1668
+
1669
+ <!-- Feature Grid -->
1670
+ <ry-section>
1671
+ <h2>Feature Grid</h2>
1672
+ <ry-example title="feature-grid" stacked>
1673
+ <template>
1674
+ <feature-grid cols="3">
1675
+ <feature icon="settings" align="center">
1676
+ <h3>Easy Setup</h3>
1677
+ <p>One line of code to get started. No build step required.</p>
1678
+ </feature>
1679
+ <feature icon="heart" align="center">
1680
+ <h3>Developer Friendly</h3>
1681
+ <p>Clean API, great docs, and TypeScript support.</p>
1682
+ </feature>
1683
+ <feature icon="star" align="center">
1684
+ <h3>Lightweight</h3>
1685
+ <p>Under 10KB gzipped. No dependencies.</p>
1686
+ </feature>
1687
+ </feature-grid>
1688
+ </template>
1689
+ </ry-example>
1690
+ <table>
1691
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1692
+ <tbody>
1693
+ <tr><td><code>cols</code> (grid)</td><td>2 | 3 | 4</td><td>Number of columns</td></tr>
1694
+ <tr><td><code>icon</code> (feature)</td><td>icon name</td><td>Icon from registry</td></tr>
1695
+ <tr><td><code>align</code> (feature)</td><td>left | center</td><td>Content alignment</td></tr>
1696
+ </tbody>
1697
+ </table>
1698
+ </ry-section>
1699
+
1700
+ <!-- Pricing -->
1701
+ <ry-section>
1702
+ <h2>Pricing</h2>
1703
+ <ry-example title="pricing" stacked>
1704
+ <template>
1705
+ <pricing>
1706
+ <pricing-card>
1707
+ <h3>Free</h3>
1708
+ <div slot="price">$0<span>/mo</span></div>
1709
+ <ul class="ry-check-list">
1710
+ <li>5 projects</li>
1711
+ <li>Community support</li>
1712
+ <li>Basic analytics</li>
1713
+ </ul>
1714
+ <button variant="outline">Get Started</button>
1715
+ </pricing-card>
1716
+ <pricing-card featured>
1717
+ <h3>Pro</h3>
1718
+ <div slot="price">$29<span>/mo</span></div>
1719
+ <ul class="ry-check-list">
1720
+ <li>Unlimited projects</li>
1721
+ <li>Priority support</li>
1722
+ <li>Advanced analytics</li>
1723
+ </ul>
1724
+ <button>Choose Pro</button>
1725
+ </pricing-card>
1726
+ <pricing-card>
1727
+ <h3>Enterprise</h3>
1728
+ <div slot="price">$99<span>/mo</span></div>
1729
+ <ul class="ry-check-list">
1730
+ <li>Everything in Pro</li>
1731
+ <li>SLA guarantee</li>
1732
+ <li>Custom integrations</li>
1733
+ </ul>
1734
+ <button variant="outline">Contact Us</button>
1735
+ </pricing-card>
1736
+ </pricing>
1737
+ </template>
1738
+ </ry-example>
1739
+ <table>
1740
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1741
+ <tbody>
1742
+ <tr><td><code>featured</code></td><td>boolean</td><td>Highlight this card</td></tr>
1743
+ </tbody>
1744
+ </table>
1745
+ </ry-section>
1746
+
1747
+ <!-- Testimonial -->
1748
+ <ry-section>
1749
+ <h2>Testimonial</h2>
1750
+ <ry-example title="testimonial">
1751
+ <template>
1752
+ <ry>
1753
+ <grid cols="3">
1754
+ <testimonial stars="5">
1755
+ <img slot="avatar" src="https://i.pravatar.cc/128?img=1" alt="Sarah Chen">
1756
+ <blockquote>This library changed how we build UIs. Everything just works.</blockquote>
1757
+ <span slot="name">Sarah Chen</span>
1758
+ <span slot="role">CTO, Acme Corp</span>
1759
+ </testimonial>
1760
+ <testimonial stars="5">
1761
+ <img slot="avatar" src="https://i.pravatar.cc/128?img=3" alt="Marcus Johnson">
1762
+ <blockquote>Clean, fast, and framework-agnostic. Exactly what we needed.</blockquote>
1763
+ <span slot="name">Marcus Johnson</span>
1764
+ <span slot="role">Lead Engineer, StartupCo</span>
1765
+ </testimonial>
1766
+ <testimonial stars="4">
1767
+ <img slot="avatar" src="https://i.pravatar.cc/128?img=5" alt="Emily Park">
1768
+ <blockquote>We shipped our entire dashboard in half the time.</blockquote>
1769
+ <span slot="name">Emily Park</span>
1770
+ <span slot="role">Product Manager, BigCo</span>
1771
+ </testimonial>
1772
+ </grid>
1773
+ </ry>
1774
+ </template>
1775
+ </ry-example>
1776
+ <table>
1777
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1778
+ <tbody>
1779
+ <tr><td><code>stars</code></td><td>1–5</td><td>Star rating display</td></tr>
1780
+ <tr><td><code>slot="avatar"</code></td><td>img element</td><td>Avatar photo</td></tr>
1781
+ <tr><td><code>slot="name"</code></td><td>text</td><td>Person's name</td></tr>
1782
+ <tr><td><code>slot="role"</code></td><td>text</td><td>Title / company</td></tr>
1783
+ </tbody>
1784
+ </table>
1785
+ </ry-section>
1786
+
1787
+ <!-- Carousel -->
1788
+ <ry-section>
1789
+ <h2>Carousel</h2>
1790
+ <ry-example title="carousel" stacked>
1791
+ <template>
1792
+ <carousel arrows dots loop>
1793
+ <div style="padding: var(--ry-space-8); text-align: center; background: var(--ry-color-bg-subtle); border-radius: var(--ry-radius-lg);">
1794
+ <h3>Slide 1</h3>
1795
+ <p>First slide content</p>
1796
+ </div>
1797
+ <div style="padding: var(--ry-space-8); text-align: center; background: var(--ry-color-bg-muted); border-radius: var(--ry-radius-lg);">
1798
+ <h3>Slide 2</h3>
1799
+ <p>Second slide content</p>
1800
+ </div>
1801
+ <div style="padding: var(--ry-space-8); text-align: center; background: var(--ry-color-bg-subtle); border-radius: var(--ry-radius-lg);">
1802
+ <h3>Slide 3</h3>
1803
+ <p>Third slide content</p>
1804
+ </div>
1805
+ </carousel>
1806
+ </template>
1807
+ </ry-example>
1808
+ <table>
1809
+ <thead><tr><th>Attribute</th><th>Values</th><th>Description</th></tr></thead>
1810
+ <tbody>
1811
+ <tr><td><code>arrows</code></td><td>boolean</td><td>Show prev/next arrows</td></tr>
1812
+ <tr><td><code>dots</code></td><td>boolean</td><td>Show dot indicators</td></tr>
1813
+ <tr><td><code>loop</code></td><td>boolean</td><td>Loop back to start</td></tr>
1814
+ <tr><td><code>autoplay</code></td><td>boolean</td><td>Auto-advance slides</td></tr>
1815
+ <tr><td><code>interval</code></td><td>number</td><td>Autoplay interval in ms (default 5000)</td></tr>
1816
+ <tr><td><code>pause-on-hover</code></td><td>boolean</td><td>Pause autoplay on hover</td></tr>
1817
+ </tbody>
1818
+ </table>
1819
+ </ry-section>
1820
+
1821
+ </ry-main>
1822
+
1823
+ <ry-footer>
1824
+ <p>ry-ui &bull; Framework-agnostic Light DOM components</p>
1825
+ </ry-footer>
1826
+
1827
+ </ry-page>