@ryanhelsing/ry-ui 1.0.2 → 1.0.4

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 (59) hide show
  1. package/AGENT.md +460 -0
  2. package/AGENTS.md +57 -0
  3. package/README.md +45 -1
  4. package/dist/components/ry-button-group.d.ts +32 -0
  5. package/dist/components/ry-button-group.d.ts.map +1 -0
  6. package/dist/components/ry-carousel.d.ts +21 -0
  7. package/dist/components/ry-carousel.d.ts.map +1 -0
  8. package/dist/components/ry-feature.d.ts +21 -0
  9. package/dist/components/ry-feature.d.ts.map +1 -0
  10. package/dist/components/ry-field.d.ts +7 -1
  11. package/dist/components/ry-field.d.ts.map +1 -1
  12. package/dist/components/ry-hero.d.ts +16 -0
  13. package/dist/components/ry-hero.d.ts.map +1 -0
  14. package/dist/components/ry-number-select.d.ts.map +1 -1
  15. package/dist/components/ry-pricing.d.ts +21 -0
  16. package/dist/components/ry-pricing.d.ts.map +1 -0
  17. package/dist/components/ry-select.d.ts +8 -1
  18. package/dist/components/ry-select.d.ts.map +1 -1
  19. package/dist/components/ry-split.d.ts +28 -0
  20. package/dist/components/ry-split.d.ts.map +1 -0
  21. package/dist/components/ry-stat.d.ts +17 -0
  22. package/dist/components/ry-stat.d.ts.map +1 -0
  23. package/dist/components/ry-tag-input.d.ts +18 -0
  24. package/dist/components/ry-tag-input.d.ts.map +1 -0
  25. package/dist/components/ry-tag.d.ts +19 -0
  26. package/dist/components/ry-tag.d.ts.map +1 -0
  27. package/dist/core/ry-transform.d.ts.map +1 -1
  28. package/dist/css/ry-structure.css +739 -149
  29. package/dist/css/ry-theme.css +581 -180
  30. package/dist/css/ry-tokens.css +120 -24
  31. package/dist/css/ry-ui.css +4965 -1065
  32. package/dist/ry-ui.d.ts +9 -0
  33. package/dist/ry-ui.d.ts.map +1 -1
  34. package/dist/ry-ui.js +1309 -778
  35. package/dist/ry-ui.js.map +1 -1
  36. package/dist/themes/dark.css +7 -90
  37. package/dist/themes/light.css +6 -35
  38. package/dist/themes/ocean.css +22 -26
  39. package/docs/components/accordion.md +31 -0
  40. package/docs/components/button-group.md +36 -0
  41. package/docs/components/button.md +65 -0
  42. package/docs/components/color.md +84 -0
  43. package/docs/components/display.md +69 -0
  44. package/docs/components/drawer.md +36 -0
  45. package/docs/components/dropdown.md +33 -0
  46. package/docs/components/forms.md +90 -0
  47. package/docs/components/knob.md +42 -0
  48. package/docs/components/layout.md +217 -0
  49. package/docs/components/modal.md +38 -0
  50. package/docs/components/number-select.md +42 -0
  51. package/docs/components/slider.md +48 -0
  52. package/docs/components/tabs.md +30 -0
  53. package/docs/components/theme-toggle.md +36 -0
  54. package/docs/components/toast.md +27 -0
  55. package/docs/components/tooltip.md +14 -0
  56. package/docs/components/tree.md +46 -0
  57. package/docs/theming.md +182 -0
  58. package/package.json +8 -4
  59. package/USING_CDN.md +0 -591
@@ -1,92 +1,9 @@
1
1
  /**
2
- * ry-ui Dark Theme
2
+ * ry-ui Dark Theme — DEPRECATED
3
+ *
4
+ * Dark mode is now built into ry-tokens.css via light-dark().
5
+ * To force dark mode: <html data-ry-theme="dark">
6
+ * To respect OS preference: omit data-ry-theme (default behavior).
7
+ *
8
+ * This file is kept for backward compatibility but has no effect.
3
9
  */
4
-
5
- [data-ry-theme="dark"] {
6
- /* Colors */
7
- --ry-color-primary: #60a5fa;
8
- --ry-color-primary-hover: #3b82f6;
9
- --ry-color-primary-active: #2563eb;
10
-
11
- --ry-color-secondary: #94a3b8;
12
- --ry-color-secondary-hover: #cbd5e1;
13
- --ry-color-secondary-active: #e2e8f0;
14
-
15
- /* Text */
16
- --ry-color-text: #f1f5f9;
17
- --ry-color-text-muted: #94a3b8;
18
- --ry-color-text-inverse: #0f172a;
19
-
20
- /* Background */
21
- --ry-color-bg: #0f172a;
22
- --ry-color-bg-subtle: #1e293b;
23
- --ry-color-bg-muted: #334155;
24
-
25
- /* Border */
26
- --ry-color-border: #334155;
27
- --ry-color-border-strong: #475569;
28
-
29
- /* Overlay */
30
- --ry-color-overlay: rgba(0, 0, 0, 0.7);
31
-
32
- /* Shadows (more subtle in dark mode) */
33
- --ry-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
34
- --ry-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
35
- --ry-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4);
36
- --ry-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.5);
37
-
38
- /* Focus */
39
- --ry-focus-ring: 0 0 0 3px rgba(96, 165, 250, 0.5);
40
-
41
- /* Code block - Vibrant (using theme colors) */
42
- --ry-code-bg: #0f172a;
43
- --ry-code-header-bg: #1e293b;
44
- --ry-code-text-color: #f1f5f9;
45
- --ry-code-title-color: #94a3b8;
46
- --ry-code-icon-color: #64748b;
47
- --ry-code-icon-hover-bg: #334155;
48
- --ry-code-icon-hover-color: #f1f5f9;
49
- --ry-code-line-number-color: rgba(71, 85, 105, 0.6);
50
- --ry-code-line-border-color: #334155;
51
- --ry-code-color-preview-border: rgba(241, 245, 249, 0.3);
52
- /* Syntax - theme button colors */
53
- --ry-code-keyword: #ef4444;
54
- --ry-code-property: #60a5fa;
55
- --ry-code-value: #f59e0b;
56
- --ry-code-string: #22c55e;
57
- --ry-code-number: #f59e0b;
58
- --ry-code-comment: #94a3b8;
59
- --ry-code-selector: #06b6d4;
60
- --ry-code-punctuation: #94a3b8;
61
- --ry-code-tag: #06b6d4;
62
- --ry-code-attribute: #60a5fa;
63
- }
64
-
65
- /* Alert overrides for dark mode */
66
- [data-ry-theme="dark"] .ry-alert--info,
67
- [data-ry-theme="dark"] ry-alert[type="info"] {
68
- background-color: #164e63;
69
- border-color: #06b6d4;
70
- color: #a5f3fc;
71
- }
72
-
73
- [data-ry-theme="dark"] .ry-alert--success,
74
- [data-ry-theme="dark"] ry-alert[type="success"] {
75
- background-color: #14532d;
76
- border-color: #22c55e;
77
- color: #bbf7d0;
78
- }
79
-
80
- [data-ry-theme="dark"] .ry-alert--warning,
81
- [data-ry-theme="dark"] ry-alert[type="warning"] {
82
- background-color: #78350f;
83
- border-color: #f59e0b;
84
- color: #fef3c7;
85
- }
86
-
87
- [data-ry-theme="dark"] .ry-alert--danger,
88
- [data-ry-theme="dark"] ry-alert[type="danger"] {
89
- background-color: #7f1d1d;
90
- border-color: #ef4444;
91
- color: #fecaca;
92
- }
@@ -1,38 +1,9 @@
1
1
  /**
2
- * ry-ui Light Theme
2
+ * ry-ui Light Theme — DEPRECATED
3
3
  *
4
- * This is the default theme.
5
- * Most values inherit from ry-tokens.css.
4
+ * Light mode is the default in ry-tokens.css via light-dark().
5
+ * To force light mode: <html data-ry-theme="light">
6
+ * To respect OS preference: omit data-ry-theme (default behavior).
7
+ *
8
+ * This file is kept for backward compatibility but has no effect.
6
9
  */
7
-
8
- [data-ry-theme="light"] {
9
- /* Light theme is the default, so minimal overrides needed */
10
- --ry-color-bg: #ffffff;
11
- --ry-color-bg-subtle: #f8fafc;
12
- --ry-color-bg-muted: #f1f5f9;
13
- --ry-color-text: #1e293b;
14
- --ry-color-text-muted: #64748b;
15
-
16
- /* Code block - Cool/Minimal (GitHub style) */
17
- --ry-code-bg: #f6f8fa;
18
- --ry-code-header-bg: #eaeef2;
19
- --ry-code-text-color: #24292f;
20
- --ry-code-title-color: #57606a;
21
- --ry-code-icon-color: #8c959f;
22
- --ry-code-icon-hover-bg: #d0d7de;
23
- --ry-code-icon-hover-color: #24292f;
24
- --ry-code-line-number-color: rgba(140, 149, 159, 0.6);
25
- --ry-code-line-border-color: #d0d7de;
26
- --ry-code-color-preview-border: rgba(36, 41, 47, 0.3);
27
- /* Syntax */
28
- --ry-code-keyword: #cf222e;
29
- --ry-code-property: #0550ae;
30
- --ry-code-value: #0a3069;
31
- --ry-code-string: #0a3069;
32
- --ry-code-number: #0550ae;
33
- --ry-code-comment: #6e7781;
34
- --ry-code-selector: #116329;
35
- --ry-code-punctuation: #24292f;
36
- --ry-code-tag: #116329;
37
- --ry-code-attribute: #0550ae;
38
- }
@@ -6,43 +6,39 @@
6
6
 
7
7
  [data-ry-theme="ocean"] {
8
8
  /* Primary - ocean blues */
9
- --ry-color-primary: #0ea5e9;
10
- --ry-color-primary-hover: #0284c7;
11
- --ry-color-primary-active: #0369a1;
9
+ --ry-color-primary: oklch(0.685 0.148 237.3);
10
+ --ry-color-primary-hover: oklch(0.588 0.139 242);
11
+ --ry-color-primary-active: oklch(0.5 0.119 242.8);
12
12
 
13
13
  /* Secondary - teal */
14
- --ry-color-secondary: #06b6d4;
15
- --ry-color-secondary-hover: #0891b2;
16
- --ry-color-secondary-active: #0e7490;
14
+ --ry-color-secondary: oklch(0.715 0.126 215.2);
15
+ --ry-color-secondary-hover: oklch(0.609 0.111 221.7);
16
+ --ry-color-secondary-active: oklch(0.52 0.094 223.1);
17
17
 
18
18
  /* Text */
19
- --ry-color-text: #0c4a6e;
20
- --ry-color-text-muted: #64748b;
21
- --ry-color-text-inverse: #ffffff;
19
+ --ry-color-text: oklch(0.391 0.085 240.9);
20
+ --ry-color-text-muted: oklch(0.554 0.041 257.4);
21
+ --ry-color-text-inverse: oklch(1 0 0);
22
22
 
23
23
  /* Background - light blues */
24
- --ry-color-bg: #f0f9ff;
25
- --ry-color-bg-subtle: #e0f2fe;
26
- --ry-color-bg-muted: #bae6fd;
24
+ --ry-color-bg: oklch(0.977 0.013 236.8);
25
+ --ry-color-bg-subtle: oklch(0.951 0.025 236.9);
26
+ --ry-color-bg-muted: oklch(0.901 0.056 230.9);
27
27
 
28
28
  /* Border */
29
- --ry-color-border: #7dd3fc;
30
- --ry-color-border-strong: #38bdf8;
29
+ --ry-color-border: oklch(0.828 0.101 230.3);
30
+ --ry-color-border-strong: oklch(0.753 0.139 232.7);
31
31
 
32
32
  /* Shadows with blue tint */
33
- --ry-shadow-sm: 0 1px 2px 0 rgba(14, 165, 233, 0.1);
34
- --ry-shadow-md: 0 4px 6px -1px rgba(14, 165, 233, 0.15);
35
- --ry-shadow-lg: 0 10px 15px -3px rgba(14, 165, 233, 0.15);
36
- --ry-shadow-xl: 0 20px 25px -5px rgba(14, 165, 233, 0.2);
33
+ --ry-shadow-sm: 0 1px 2px 0 oklch(0.685 0.148 237.3 / 0.1);
34
+ --ry-shadow-md: 0 4px 6px -1px oklch(0.685 0.148 237.3 / 0.15);
35
+ --ry-shadow-lg: 0 10px 15px -3px oklch(0.685 0.148 237.3 / 0.15);
36
+ --ry-shadow-xl: 0 20px 25px -5px oklch(0.685 0.148 237.3 / 0.2);
37
37
 
38
38
  /* Focus */
39
- --ry-focus-ring: 0 0 0 3px rgba(14, 165, 233, 0.5);
40
- }
39
+ --ry-focus-ring: 0 0 0 3px oklch(0.685 0.148 237.3 / 0.5);
41
40
 
42
- /* Alert overrides */
43
- [data-ry-theme="ocean"] .ry-alert--info,
44
- [data-ry-theme="ocean"] ry-alert[type="info"] {
45
- background-color: #e0f2fe;
46
- border-color: #0ea5e9;
47
- color: #0369a1;
41
+ /* Alerts */
42
+ --ry-color-info-bg: oklch(0.951 0.025 236.9);
43
+ --ry-color-info-text: oklch(0.5 0.119 242.8);
48
44
  }
@@ -0,0 +1,31 @@
1
+ # Accordion
2
+
3
+ ## `<ry-accordion>`
4
+
5
+ | Attribute (on accordion-item) | Values | Description |
6
+ |-------------------------------|--------|-------------|
7
+ | `title` | string | Header text |
8
+ | `open` | boolean | Initially expanded |
9
+
10
+ Events: `ry:toggle` on item — `e.detail.open` (boolean)
11
+
12
+ ```html
13
+ <ry-accordion>
14
+ <ry-accordion-item title="Section 1" open>Content here.</ry-accordion-item>
15
+ <ry-accordion-item title="Section 2">More content.</ry-accordion-item>
16
+ </ry-accordion>
17
+ ```
18
+
19
+ JS:
20
+ ```js
21
+ const item = document.querySelector('ry-accordion-item');
22
+
23
+ item.addEventListener('ry:toggle', (e) => {
24
+ console.log(e.detail.open); // true or false
25
+ });
26
+
27
+ // Programmatic
28
+ item.open = true;
29
+ item.open = false;
30
+ item.toggle();
31
+ ```
@@ -0,0 +1,36 @@
1
+ # Button Group
2
+
3
+ ## `<ry-button-group>`
4
+
5
+ Segmented control with radio-group behavior. Only one child button can be selected at a time.
6
+
7
+ | Attribute | Values | Description |
8
+ |-----------|--------|-------------|
9
+ | `name` | string | Optional group name |
10
+ | `value` | string | Currently selected button value |
11
+
12
+ Events: `ry:change` — `e.detail.value`
13
+
14
+ Child buttons must have a `value` attribute to participate. Clicking a button sets `pressed` + `aria-pressed="true"` on it and clears the others.
15
+
16
+ ```html
17
+ <ry-button-group name="billing" value="monthly">
18
+ <ry-button value="monthly">Monthly</ry-button>
19
+ <ry-button value="annually">Annually</ry-button>
20
+ </ry-button-group>
21
+
22
+ <ry-button-group name="mode" value="terminal">
23
+ <ry-button value="direct">Direct</ry-button>
24
+ <ry-button value="terminal">Terminal</ry-button>
25
+ <ry-button value="release">Release</ry-button>
26
+ </ry-button-group>
27
+ ```
28
+
29
+ JS:
30
+ ```js
31
+ const group = document.querySelector('ry-button-group');
32
+ group.addEventListener('ry:change', (e) => {
33
+ console.log(e.detail.value); // "monthly" | "annually"
34
+ });
35
+ group.value = 'annually'; // programmatic selection
36
+ ```
@@ -0,0 +1,65 @@
1
+ # Button
2
+
3
+ ## `<ry-button>`
4
+
5
+ | Attribute | Values | Description |
6
+ |-----------|--------|-------------|
7
+ | `variant` | primary \| secondary \| outline \| ghost \| danger | Style (default: primary) |
8
+ | `size` | sm \| md \| lg | Size |
9
+ | `modal` | string | Opens modal with this ID |
10
+ | `drawer` | string | Opens drawer with this ID |
11
+ | `disabled` | boolean | Disable |
12
+
13
+ ```html
14
+ <ry-button>Primary</ry-button>
15
+ <ry-button variant="outline">Outline</ry-button>
16
+ <ry-button variant="danger" size="sm">Delete</ry-button>
17
+ <ry-button modal="confirm">Open Modal</ry-button>
18
+ <ry-button drawer="nav">Open Drawer</ry-button>
19
+ ```
20
+
21
+ ## `<ry-toggle-button>`
22
+
23
+ Selectable button. Same-name buttons form a radio group (only one pressed).
24
+
25
+ | Attribute | Values | Description |
26
+ |-----------|--------|-------------|
27
+ | `name` | string | Group name (same name = single selection) |
28
+ | `value` | string | Value when selected |
29
+ | `pressed` | boolean | Currently pressed |
30
+ | `block` | boolean | Full-width |
31
+ | `size` | sm \| md \| lg | Size |
32
+ | `disabled` | boolean | Disable |
33
+
34
+ Events: `ry:change` — `e.detail.pressed`, `e.detail.value`
35
+
36
+ ```html
37
+ <ry-cluster>
38
+ <ry-toggle-button name="size" value="s">S</ry-toggle-button>
39
+ <ry-toggle-button name="size" value="m" pressed>M</ry-toggle-button>
40
+ <ry-toggle-button name="size" value="l">L</ry-toggle-button>
41
+ </ry-cluster>
42
+ ```
43
+
44
+ Full-width card-style selection:
45
+
46
+ ```html
47
+ <ry-grid cols="3">
48
+ <ry-toggle-button name="plan" value="free" block>
49
+ <strong>Free</strong><br><b>$0</b>/month
50
+ </ry-toggle-button>
51
+ <ry-toggle-button name="plan" value="pro" block pressed>
52
+ <strong>Pro</strong><br><b>$10</b>/month
53
+ </ry-toggle-button>
54
+ </ry-grid>
55
+ ```
56
+
57
+ JS:
58
+ ```js
59
+ // Get selected value from a group
60
+ const selected = document.querySelector('ry-toggle-button[name="plan"][pressed]');
61
+ selected?.value; // "pro"
62
+
63
+ // Set programmatically
64
+ button.pressed = true;
65
+ ```
@@ -0,0 +1,84 @@
1
+ # Color Components
2
+
3
+ ## `<ry-color-picker>`
4
+
5
+ Full color picker with saturation/hue panel.
6
+
7
+ | Attribute | Values | Description |
8
+ |-----------|--------|-------------|
9
+ | `value` | color string | Initial color (hex, rgb, hsl) |
10
+ | `format` | hex \| rgb \| hsl | Output format (default: hex) |
11
+ | `inline` | boolean | Always visible (no dropdown) |
12
+ | `opacity` | boolean | Enable alpha channel |
13
+ | `swatches` | string | Preset colors separated by `;` |
14
+ | `disabled` | boolean | Disable |
15
+
16
+ Events: `ry:input` (during drag), `ry:change` (on release)
17
+ API: `.value`, `.rgb`, `.hsl`, `.hsv`, `.setColor(str)`
18
+
19
+ ```html
20
+ <ry-color-picker value="#3b82f6"></ry-color-picker>
21
+ <ry-color-picker value="#3b82f6" opacity inline swatches="#ef4444;#22c55e;#3b82f6"></ry-color-picker>
22
+ ```
23
+
24
+ JS:
25
+ ```js
26
+ const picker = document.querySelector('ry-color-picker');
27
+
28
+ picker.addEventListener('ry:input', (e) => {
29
+ console.log(e.detail.value); // "#3b82f6"
30
+ console.log(e.detail.rgb); // { r: 59, g: 130, b: 246 }
31
+ console.log(e.detail.hsv); // { h: 217, s: 76, v: 96 }
32
+ });
33
+
34
+ picker.value = '#ff0000';
35
+ picker.setColor('hsl(200, 100%, 50%)');
36
+ picker.rgb; // { r, g, b }
37
+ picker.hsl; // { h, s, l }
38
+ ```
39
+
40
+ ## `<ry-color-input>`
41
+
42
+ Compact swatch + editable hex input.
43
+
44
+ | Attribute | Values | Description |
45
+ |-----------|--------|-------------|
46
+ | `value` | color string | Initial color |
47
+ | `format` | hex \| rgb \| hsl | Output format |
48
+ | `opacity` | boolean | Enable alpha |
49
+ | `disabled` | boolean | Disable |
50
+
51
+ Events: `ry:change` — `e.detail.value`
52
+
53
+ ```html
54
+ <ry-color-input value="#3b82f6"></ry-color-input>
55
+ <ry-color-input value="rgba(139, 92, 246, 0.8)" opacity></ry-color-input>
56
+ ```
57
+
58
+ ## `<ry-gradient-picker>`
59
+
60
+ CSS gradient editor with draggable color stops.
61
+
62
+ | Attribute | Values | Description |
63
+ |-----------|--------|-------------|
64
+ | `value` | CSS gradient string | Initial gradient |
65
+ | `output` | boolean | Show CSS output |
66
+ | `disabled` | boolean | Disable |
67
+
68
+ Events: `ry:input`, `ry:change` — `e.detail.value`, `e.detail.stops`, `e.detail.type`, `e.detail.angle`
69
+ API: `.value`, `.type`, `.angle`, `.stops`, `.addStop(color, pos)`, `.removeStop(id)`
70
+
71
+ ```html
72
+ <ry-gradient-picker value="linear-gradient(90deg, #3b82f6 0%, #8b5cf6 100%)"></ry-gradient-picker>
73
+ <ry-gradient-picker value="radial-gradient(circle, #fbbf24 0%, #dc2626 100%)"></ry-gradient-picker>
74
+ ```
75
+
76
+ JS:
77
+ ```js
78
+ const picker = document.querySelector('ry-gradient-picker');
79
+ picker.value; // "linear-gradient(90deg, ...)"
80
+ picker.type; // "linear" | "radial"
81
+ picker.angle; // 0-360
82
+ picker.stops; // [{ id, color, position }, ...]
83
+ picker.addStop('#ff00ff', 50);
84
+ ```
@@ -0,0 +1,69 @@
1
+ # Display Components
2
+
3
+ ## `<ry-badge>`
4
+
5
+ | Attribute | Values | Description |
6
+ |-----------|--------|-------------|
7
+ | `variant` | default \| primary \| success \| warning \| danger | Color |
8
+
9
+ ```html
10
+ <ry-badge>Default</ry-badge>
11
+ <ry-badge variant="success">Active</ry-badge>
12
+ <ry-badge variant="danger">Offline</ry-badge>
13
+ ```
14
+
15
+ ## `<ry-alert>`
16
+
17
+ | Attribute | Values | Description |
18
+ |-----------|--------|-------------|
19
+ | `type` | info \| success \| warning \| danger | Style |
20
+ | `title` | string | Heading |
21
+
22
+ ```html
23
+ <ry-alert type="info" title="Info">Informational message.</ry-alert>
24
+ <ry-alert type="danger" title="Error">Something went wrong.</ry-alert>
25
+ ```
26
+
27
+ ## `<ry-icon>`
28
+
29
+ | Attribute | Values | Description |
30
+ |-----------|--------|-------------|
31
+ | `name` | close, check, chevron-down, chevron-up, chevron-left, chevron-right, copy, sun, moon, info, warning, error, success, search, menu, plus, minus, settings, user, heart, star, trash, edit, external-link, download, upload | Icon |
32
+ | `size` | number | Pixels (default: 24) |
33
+ | `label` | string | Accessible label |
34
+
35
+ ```html
36
+ <ry-icon name="heart" size="16"></ry-icon>
37
+ <ry-icon name="settings"></ry-icon>
38
+ ```
39
+
40
+ ## `<ry-code>`
41
+
42
+ Use `<ry-code>` with prefix even inside `<ry>` to avoid conflict with native `<code>`.
43
+
44
+ | Attribute | Values | Description |
45
+ |-----------|--------|-------------|
46
+ | `language` | js \| css \| html \| json | Highlighting |
47
+ | `title` | string | Header |
48
+ | `line-numbers` | boolean | Show line numbers |
49
+
50
+ ```html
51
+ <ry-code language="js" title="app.js">
52
+ const x = 1;
53
+ console.log(x);
54
+ </ry-code>
55
+ ```
56
+
57
+ ## Table (native HTML)
58
+
59
+ Standard `<table>` elements are styled automatically by the ry-ui theme.
60
+
61
+ ```html
62
+ <table>
63
+ <thead><tr><th>Name</th><th>Role</th></tr></thead>
64
+ <tbody>
65
+ <tr><td>Alice</td><td>Engineer</td></tr>
66
+ <tr><td>Bob</td><td>Designer</td></tr>
67
+ </tbody>
68
+ </table>
69
+ ```
@@ -0,0 +1,36 @@
1
+ # Drawer
2
+
3
+ ## `<ry-drawer>`
4
+
5
+ | Attribute | Values | Description |
6
+ |-----------|--------|-------------|
7
+ | `id` | string | Identifier (match button's `drawer` attr) |
8
+ | `side` | left \| right \| bottom | Slide direction |
9
+
10
+ Events: `ry:open`, `ry:close`
11
+ API: `.open()`, `.close()`, `.state`
12
+
13
+ ```html
14
+ <ry-button drawer="nav">Menu</ry-button>
15
+
16
+ <ry-drawer id="nav" side="left">
17
+ <h3>Navigation</h3>
18
+ <ry-stack>
19
+ <a href="/">Home</a>
20
+ <a href="/about">About</a>
21
+ </ry-stack>
22
+ </ry-drawer>
23
+ ```
24
+
25
+ JS:
26
+ ```js
27
+ const drawer = document.querySelector('ry-drawer');
28
+
29
+ drawer.addEventListener('ry:open', () => console.log('opened'));
30
+ drawer.addEventListener('ry:close', () => console.log('closed'));
31
+
32
+ // Programmatic
33
+ drawer.open();
34
+ drawer.close();
35
+ drawer.state; // "open" or "closed"
36
+ ```
@@ -0,0 +1,33 @@
1
+ # Dropdown
2
+
3
+ ## `<ry-dropdown>`
4
+
5
+ Use `slot="trigger"` on the trigger element. Menu items go inside `<ry-menu>`.
6
+
7
+ Events: `ry:select` — `e.detail.value`
8
+ API: `.open()`, `.close()`
9
+
10
+ ```html
11
+ <ry-dropdown>
12
+ <ry-button slot="trigger">Options</ry-button>
13
+ <ry-menu>
14
+ <ry-menu-item>Edit</ry-menu-item>
15
+ <ry-menu-item>Duplicate</ry-menu-item>
16
+ <ry-divider></ry-divider>
17
+ <ry-menu-item>Delete</ry-menu-item>
18
+ </ry-menu>
19
+ </ry-dropdown>
20
+ ```
21
+
22
+ JS:
23
+ ```js
24
+ const dropdown = document.querySelector('ry-dropdown');
25
+
26
+ dropdown.addEventListener('ry:select', (e) => {
27
+ console.log(e.detail.value); // "Edit", "Delete", etc.
28
+ });
29
+
30
+ // Programmatic
31
+ dropdown.open();
32
+ dropdown.close();
33
+ ```
@@ -0,0 +1,90 @@
1
+ # Form Components
2
+
3
+ ## `<ry-field>`
4
+
5
+ Wraps native `<input>` / `<textarea>` / `<select>` with auto-generated label, error, and hint. Handles accessibility linking (aria-describedby, aria-invalid) automatically.
6
+
7
+ | Attribute | Values | Description |
8
+ |-----------|--------|-------------|
9
+ | `label` | string | Label text |
10
+ | `error` | string | Error message (hides hint when set) |
11
+ | `hint` | string | Helper text shown below input |
12
+
13
+ ```html
14
+ <ry-field label="Email" hint="We'll never share your email">
15
+ <input type="email" placeholder="you@example.com">
16
+ </ry-field>
17
+
18
+ <ry-field label="Password" error="Must be at least 8 characters">
19
+ <input type="password">
20
+ </ry-field>
21
+
22
+ <!-- Setting error="" clears the error and shows hint again -->
23
+ ```
24
+
25
+ ## `<ry-select>`
26
+
27
+ | Attribute | Values | Description |
28
+ |-----------|--------|-------------|
29
+ | `placeholder` | string | Placeholder text |
30
+ | `name` | string | Form field name |
31
+
32
+ Events: `ry:change` — `e.detail.value`, `e.detail.label`
33
+ API: `.value`, `.open()`, `.close()`
34
+
35
+ ```html
36
+ <ry-select placeholder="Country" name="country">
37
+ <ry-option value="us">United States</ry-option>
38
+ <ry-option value="uk">United Kingdom</ry-option>
39
+ </ry-select>
40
+ ```
41
+
42
+ JS:
43
+ ```js
44
+ const select = document.querySelector('ry-select');
45
+ select.addEventListener('ry:change', (e) => {
46
+ console.log(e.detail.value); // "us"
47
+ console.log(e.detail.label); // "United States"
48
+ });
49
+ select.value = 'uk';
50
+ ```
51
+
52
+ ## `<ry-switch>`
53
+
54
+ | Attribute | Values | Description |
55
+ |-----------|--------|-------------|
56
+ | `name` | string | Form field name |
57
+ | `checked` | boolean | Initially on |
58
+ | `disabled` | boolean | Disable |
59
+
60
+ Events: `ry:change` — `e.detail.checked`, `e.detail.name`
61
+ API: `.checked`
62
+
63
+ ```html
64
+ <ry-switch name="notifications" checked>Email notifications</ry-switch>
65
+ ```
66
+
67
+ JS:
68
+ ```js
69
+ const sw = document.querySelector('ry-switch');
70
+ sw.addEventListener('ry:change', (e) => {
71
+ console.log(e.detail.checked); // true or false
72
+ });
73
+ sw.checked = false;
74
+ ```
75
+
76
+ ## Checkbox & Radio
77
+
78
+ Standard HTML, auto-styled by ry-ui theme. No custom elements needed.
79
+
80
+ ```html
81
+ <ry-stack gap="sm">
82
+ <label><input type="checkbox" checked> Accept terms</label>
83
+ <label><input type="checkbox"> Newsletter</label>
84
+ </ry-stack>
85
+
86
+ <ry-stack gap="sm">
87
+ <label><input type="radio" name="plan" value="free" checked> Free</label>
88
+ <label><input type="radio" name="plan" value="pro"> Pro</label>
89
+ </ry-stack>
90
+ ```