@ryanhelsing/ry-ui 1.0.9 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,280 +1,555 @@
1
1
  # ry-ui
2
2
 
3
- Framework-agnostic, Light DOM web components. CSS is the source of truth.
3
+ Framework-agnostic, Light DOM web components. Zero dependencies. CSS is the source of truth.
4
4
 
5
- ## North Star
5
+ ## Setup (2 lines)
6
6
 
7
- There are only so many types of things we do in any app. There should be one opinionated default way to do each of them. Invent all the wheels once, then never again.
8
-
9
- Every app is a composition of the same finite primitives: layout, navigation, data display, data entry, feedback, actions, auth, state. The patterns are solved. The industry just keeps re-solving them because engineers enjoy the puzzle.
10
-
11
- ry-ui normalizes this. No decisions to make. No architecture to debate. An LLM can leverage these primitives to build any app, anywhere, with a structure that is grokkable, dumb, and repeatable. Over-engineering becomes irrelevant when there's nothing left to over-engineer.
7
+ ```html
8
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ryanhelsing/ry-ui/dist/css/ry-ui.css">
9
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@ryanhelsing/ry-ui/dist/ry-ui.js"></script>
10
+ ```
12
11
 
13
- **The goal:** Write once in HTML/CSS/JS. Deploy to web, iOS, Android, desktop. The primitives are portable because they're universal.
12
+ ```bash
13
+ # or npm
14
+ npm install @ryanhelsing/ry-ui
15
+ ```
14
16
 
15
- ### FUNCTION / FORM / THEME
17
+ Set theme: `<html data-ry-theme="light">` `light` | `dark` | omit for OS preference
18
+ Set body: `<body style="background: var(--ry-color-bg); color: var(--ry-color-text);">`
16
19
 
17
- Every component is three independent layers:
20
+ ## DON'T / DO
18
21
 
19
- - **FUNCTION** (JS) Behavior. Extends `RyElement`. Manages state, events, keyboard, ARIA. Queries via `data-ry-target`.
20
- - **FORM** (Structure CSS) — Layout. `ry-structure.css`. Display, flex, grid, padding, overflow, transform/opacity transitions. No colors.
21
- - **THEME** (Visual CSS) — Appearance. `ry-theme.css`. Colors, shadows, borders, typography, focus rings. Entirely swappable.
22
+ DON'T write flexbox/grid CSS for page layout.
23
+ DO: `<ry-page><ry-header>H</ry-header><ry-main>M</ry-main><ry-footer>F</ry-footer></ry-page>`
22
24
 
23
- These layers are independently replaceable. Structure works with any theme or no theme. Function works regardless of CSS loaded.
25
+ DON'T write a custom modal with backdrop, focus trap, escape handling.
26
+ DO: `<ry-button modal="m">Open</ry-button><ry-modal id="m" title="T">Content</ry-modal>`
24
27
 
25
- ---
28
+ DON'T write a slide-out drawer with CSS transforms.
29
+ DO: `<ry-button drawer="d">Open</ry-button><ry-drawer id="d" side="left">Content</ry-drawer>`
26
30
 
27
- - [x] separate the css minimal structure from the theme
28
- - [x] separate the component behavior from the design (see `docs/arch/behavior-style-separation.md`)
31
+ DON'T write tab switching logic or CSS.
32
+ DO: `<ry-tabs><ry-tab title="A" active>A</ry-tab><ry-tab title="B">B</ry-tab></ry-tabs>`
29
33
 
30
- ### Next Up Missing Primitives That Block Real Apps
34
+ DON'T write a custom select dropdown with keyboard navigation.
35
+ DO: `<ry-select placeholder="Pick"><ry-option value="a">A</ry-option></ry-select>`
31
36
 
32
- - [ ] **Table / Data Grid** — sorting, column resize, virtual scrolling for large datasets. Highest-value missing component for any dashboard.
33
- - [ ] **Form** form orchestration around existing inputs. Validation, error states, submit handling, field grouping.
34
- - [ ] **Menu / Context Menu** — right-click menus, nested submenus. Required for any desktop-targeting app.
35
- - [ ] **Command Palette** — Cmd+K pattern. Table stakes for power-user apps and ry-os.
36
- - [ ] **Toast stacking** — ry-toast exists but needs positioning, stacking multiple toasts, auto-dismiss timers, queue behavior.
37
- - [ ] **Breadcrumb, Pagination, Stepper** — navigation patterns for any multi-page app.
37
+ DON'T write CSS variables for colors, spacing, shadows.
38
+ DO: Use `--ry-color-*`, `--ry-space-*`, `--ry-radius-*`, `--ry-shadow-*` tokens.
38
39
 
39
- ### Backlog
40
+ DON'T write button styles with hover/active/focus states.
41
+ DO: `<ry-button variant="primary">Click</ry-button>`
40
42
 
41
- - [ ] Placeholder, Step/Wizard, Loader/Progress, Comment/Feed, Statistic/Graph, Hero, Calendar, Rating, Search, Shape, Sticky, dialog/alert, carousel, qr-code, diff, format-bytes-number-currency, mutation-observer and resize observer
43
+ DON'T write toast/notification CSS and JS.
44
+ DO: `RyToast.success('Saved!')` / `RyToast.error('Failed')`
42
45
 
43
- - [ ] This is a theme on its own: https://codepen.io/oathanrex/pen/EayVMqZ
46
+ DON'T write accordion expand/collapse logic.
47
+ DO: `<ry-accordion><ry-accordion-item title="Q" open>A</ry-accordion-item></ry-accordion>`
44
48
 
45
- - [ ] bring in examples and extract and normalize - Flat Clean (accord/tailwind) / Flat Fun Waves (zevo) / Game (mario/astrobot/stardew/animal crossing) / Brute (square game) / Skeuomorphic / Glass ( dark and light, color sets 1,2,3 )
46
- - [ ] cross-platform transpiler - Swift/SwiftUI (see `docs/arch/cross-platform-transpiler.md`)
47
- - [ ] animation system with GSAP (see `docs/plans/animation-system.md`)
48
- - [ ] could use to write apps on ry-os lol.. rust based linux DE with html/js/css -> Dioxus -> rust apps / with an llm on device
49
- - [ ] https://github.com/GrapheneOS — hardened Android, good reference for security-first OS/DE design patterns
49
+ DON'T write a toggle switch from scratch.
50
+ DO: `<ry-switch name="notify" checked></ry-switch>`
50
51
 
51
- ## Quick Start
52
+ ## Full Page Template
52
53
 
53
54
  ```html
54
- <link rel="stylesheet" href="https://cdn.example.com/ry-ui.css">
55
- <script type="module" src="https://cdn.example.com/ry-ui.js"></script>
55
+ <!DOCTYPE html>
56
+ <html lang="en" data-ry-theme="light">
57
+ <head>
58
+ <meta charset="UTF-8">
59
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
60
+ <title>My App</title>
61
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ryanhelsing/ry-ui/dist/css/ry-ui.css">
62
+ </head>
63
+ <body style="background: var(--ry-color-bg); color: var(--ry-color-text);">
64
+ <ry-page>
65
+ <ry-header sticky>
66
+ <ry-cluster>
67
+ <strong>My App</strong>
68
+ <ry-nav>
69
+ <a href="/" aria-current="page">Home</a>
70
+ <a href="/about">About</a>
71
+ </ry-nav>
72
+ </ry-cluster>
73
+ <ry-actions>
74
+ <ry-theme-toggle themes="light,dark"></ry-theme-toggle>
75
+ </ry-actions>
76
+ </ry-header>
77
+
78
+ <ry-main>
79
+ <ry-section>
80
+ <h1>Hello World</h1>
81
+ <p>Your content here.</p>
82
+ </ry-section>
83
+ </ry-main>
84
+
85
+ <ry-footer>Built with ry-ui</ry-footer>
86
+ </ry-page>
87
+
88
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@ryanhelsing/ry-ui/dist/ry-ui.js"></script>
89
+ </body>
90
+ </html>
56
91
  ```
57
92
 
58
- ## Components
59
-
60
- ### Layout (CSS-only)
61
- - `<ry-page>` - Page container
62
- - `<ry-header>` / `<ry-footer>` - Page sections
63
- - `<ry-main>` / `<ry-section>` - Content areas
64
- - `<ry-grid cols="3">` - Responsive grid
65
- - `<ry-stack>` / `<ry-cluster>` - Flex layouts
66
-
67
- ### Layout
68
- - `<ry-split>` - Resizable two-column layout with optional `persist` for localStorage-backed width
69
-
70
- ### Interactive
71
- - `<ry-button>` - Buttons with variants
72
- - `<ry-button-group>` - Segmented control / radio button group
73
- - `<ry-modal>` - Modal dialogs
74
- - `<ry-drawer>` - Slide-out panels
75
- - `<ry-accordion>` - Collapsible sections
76
- - `<ry-tabs>` - Tabbed content
77
- - `<ry-dropdown>` - Dropdown menus
78
- - `<ry-select>` - Custom select
79
- - `<ry-combobox>` - Searchable dropdown
80
- - `<ry-switch>` - Toggle switch
81
- - `<ry-tooltip>` - Hover tooltips
82
- - `<ry-toast>` - Notifications
83
-
84
- ### Forms
85
- - `<ry-field>` - Form field wrapper with auto label, error, and hint
86
-
87
- ### Display
88
- - `<ry-card>` - Card containers
89
- - `<ry-badge>` - Status badges
90
- - `<ry-alert>` - Alert messages
91
-
92
93
  ## Clean Syntax
93
94
 
94
- Use `<ry>` wrapper to write unprefixed markup:
95
+ Wrap markup in `<ry>` to use unprefixed tags:
95
96
 
96
97
  ```html
97
98
  <ry>
98
99
  <accordion>
99
- <accordion-item title="FAQ" open>
100
- No ry- prefix needed inside the wrapper.
101
- </accordion-item>
100
+ <accordion-item title="FAQ" open>No ry- prefix needed.</accordion-item>
102
101
  </accordion>
103
102
  </ry>
104
103
  ```
105
104
 
106
- ## Examples
105
+ ---
106
+
107
+ ## Component Catalog
108
+
109
+ ### Layout (CSS-only, no JS)
110
+
111
+ | Component | Attributes | Description |
112
+ |-----------|-----------|-------------|
113
+ | `<ry-page>` | — | Root page container, flex column, min-height 100dvh |
114
+ | `<ry-header>` | `sticky` | Flex row, space-between. `sticky` pins to top |
115
+ | `<ry-main>` | — | Content area, max-width 1200px, centered |
116
+ | `<ry-footer>` | — | Footer with border-top |
117
+ | `<ry-section>` | — | Block section with bottom margin |
118
+ | `<ry-grid>` | `cols="1-6\|auto-fit\|auto-fill"`, `cols-sm`, `cols-md`, `cols-lg` | CSS grid. [Details](docs/components/layout.md) |
119
+ | `<ry-stack>` | `gap="sm\|md\|lg\|xl"` | Vertical flex column |
120
+ | `<ry-cluster>` | `gap="sm\|md\|lg"` | Horizontal flex row, wraps |
121
+ | `<ry-split>` | `resizable`, `persist="key"` | Two-column with drag resize. [Details](docs/components/layout.md) |
122
+ | `<ry-center>` | — | Flex center (both axes) |
123
+ | `<ry-nav>` | — | Horizontal nav links. Active: `a[aria-current="page"]` |
124
+ | `<ry-logo>` | — | Inline-flex, bold text |
125
+ | `<ry-actions>` | — | Flex row for action buttons |
126
+ | `<ry-divider>` | `vertical` | Horizontal line; `vertical` for inline separator |
127
+ | `<ry-aside>` | — | Sidebar content area |
128
+
129
+ ### Interactive Components
130
+
131
+ | Component | Key Attributes | Events |
132
+ |-----------|---------------|--------|
133
+ | `<ry-button>` | `variant="primary\|secondary\|outline\|ghost\|danger\|accent"`, `size="sm\|lg"`, `disabled`, `pressed`, `modal="id"`, `drawer="id"` | `ry:click` |
134
+ | `<ry-button-group>` | `name`, `value` | `ry:change` `{value}` |
135
+ | `<ry-toggle-button>` | `pressed`, `name`, `value`, `size`, `icon`, `disabled` | `ry:change` `{pressed, value}` |
136
+ | `<ry-modal>` | `id`, `title` | Trigger: `<ry-button modal="id">`. [Details](docs/components/modal.md) |
137
+ | `<ry-drawer>` | `id`, `position="left\|right"`, `size` | Trigger: `<ry-button drawer="id">`. [Details](docs/components/drawer.md) |
138
+ | `<ry-accordion>` | — | Container for accordion-items. [Details](docs/components/accordion.md) |
139
+ | `<ry-accordion-item>` | `title`, `open` | Collapsible section |
140
+ | `<ry-tabs>` | — | Children: `<ry-tab title="..." active>`. [Details](docs/components/tabs.md) |
141
+ | `<ry-dropdown>` | — | [Details](docs/components/dropdown.md) |
142
+ | `<ry-select>` | `placeholder`, `name`, `value`, `disabled` | `ry:change` `{value}`. Children: `<ry-option>` |
143
+ | `<ry-combobox>` | `placeholder`, `name`, `value`, `disabled` | `ry:change` `{value, label}`, `ry:input` — searchable dropdown |
144
+ | `<ry-switch>` | `checked`, `disabled`, `name` | `ry:change` `{value, label}` — value is `"true"`/`"false"` string |
145
+ | `<ry-tooltip>` | `content`, `position` | [Details](docs/components/tooltip.md) |
146
+ | `<ry-toast>` | — | `RyToast.success()`, `.error()`, `.warning()`, `.info()`. [Details](docs/components/toast.md) |
147
+ | `<ry-slider>` | `min`, `max`, `step`, `value`, `color`, `disabled` | `ry:change` `{value}`. [Details](docs/components/slider.md) |
148
+ | `<ry-knob>` | `min`, `max`, `step`, `value`, `color`, `size` | `ry:change` `{value}`. [Details](docs/components/knob.md) |
149
+ | `<ry-number-select>` | `min`, `max`, `step`, `value`, `arrows`, `prefix`, `suffix` | `ry:change` `{value}`. [Details](docs/components/number-select.md) |
150
+ | `<ry-color-picker>` | `value`, `format` | `ry:change` `{value}`. [Details](docs/components/color.md) |
151
+ | `<ry-color-input>` | `value`, `format` | `ry:change` `{value}` |
152
+ | `<ry-gradient-picker>` | `value` | `ry:change` `{value}` |
153
+ | `<ry-tree>` | `data` (JSON) | `ry:select`, `ry:move`. [Details](docs/components/tree.md) |
154
+ | `<ry-tag>` | `removable` | `ry:remove` |
155
+ | `<ry-tag-input>` | `name`, `value`, `placeholder` | `ry:change` `{tags}` |
156
+ | `<ry-carousel>` | `autoplay`, `interval` | `ry:change` `{index}` |
157
+ | `<ry-theme-toggle>` | `themes="light,dark"` | Cycles through themes |
158
+ | `<ry-theme-panel>` | `theme`, `mode` | Floating theme/mode selector. Persists to localStorage |
159
+ | `<ry-testimonial>` | `stars` | Quote card with avatar, name, role slots |
160
+
161
+ ### Display Components
162
+
163
+ | Component | Key Attributes | Description |
164
+ |-----------|---------------|-------------|
165
+ | `<ry-card>` | `interactive`, `href` | Card container. `interactive` adds click/keyboard. `href` navigates |
166
+ | `<ry-badge>` | `variant="primary\|success\|warning\|danger\|accent"` | Pill badge. Custom: `style="--ry-badge-color: #8B5CF6"` |
167
+ | `<ry-alert>` | `type="info\|success\|warning\|danger"` | Alert box with optional `[slot="title"]` |
168
+ | `<ry-field>` | `label`, `error`, `hint` | Form field wrapper. [Details](docs/components/forms.md) |
169
+ | `<ry-icon>` | `name` | SVG icon from registry |
170
+ | `<ry-code>` | `language`, `title` | Syntax-highlighted code block |
171
+ | `<ry-hero>` | `size="sm\|lg"`, `full-bleed`, `align="left"` | Marketing hero section |
172
+ | `<ry-stat>` | `size="sm\|lg"` | Stat card with `slot="value"`, `slot="label"` |
173
+ | `<ry-feature>` | `icon` | Feature card with icon |
174
+ | `<ry-feature-grid>` | `cols="2\|3\|4"` | Responsive grid for feature cards |
175
+ | `<ry-pricing>` | — | Container for pricing cards |
176
+ | `<ry-pricing-card>` | `featured` | Pricing tier. `featured` scales up with bold border |
177
+
178
+ ---
179
+
180
+ ## Patterns
181
+
182
+ ### Grid
183
+
184
+ ```html
185
+ <!-- Fixed columns (auto-responsive: 3-6 → 2 at ≤1024px → 1 at ≤640px) -->
186
+ <ry-grid cols="3">...</ry-grid>
187
+
188
+ <!-- Explicit per-breakpoint -->
189
+ <ry-grid cols="5" cols-md="3" cols-sm="1">...</ry-grid>
190
+
191
+ <!-- Fluid auto-fit -->
192
+ <ry-grid cols="auto-fit">...</ry-grid>
193
+ <ry-grid cols="auto-fit" style="--ry-grid-min: 240px">...</ry-grid>
194
+ ```
195
+
196
+ ### Split Layout
107
197
 
108
198
  ```html
109
- <!-- Modal -->
110
- <ry-button modal="demo">Open Modal</ry-button>
111
- <ry-modal id="demo" title="Hello">Content here</ry-modal>
112
-
113
- <!-- Drawer -->
114
- <ry-button drawer="menu">Open</ry-button>
115
- <ry-drawer id="menu" side="left">Menu content</ry-drawer>
116
-
117
- <!-- Select -->
118
- <ry-select placeholder="Country" name="country">
119
- <ry-option value="us">United States</ry-option>
120
- <ry-option value="uk">United Kingdom</ry-option>
121
- </ry-select>
122
-
123
- <!-- Split Panel (resizable, persistent) -->
124
- <ry-split resizable persist="sidebar">
125
- <main>Main content</main>
126
- <aside>Sidebar</aside>
199
+ <ry-split resizable persist="sidebar" style="--ry-split-width: 400px">
200
+ <div>Main content</div>
201
+ <div>Resizable sidebar — drag, arrow keys, double-click to reset</div>
127
202
  </ry-split>
203
+ ```
128
204
 
129
- <!-- Button Group -->
130
- <ry-button-group value="monthly">
131
- <ry-button value="monthly">Monthly</ry-button>
132
- <ry-button value="annually">Annually</ry-button>
133
- </ry-button-group>
205
+ CSS vars: `--ry-split-width`, `--ry-split-min-width`, `--ry-split-max-width`
206
+ Keyboard: Arrow (±10px), Shift+Arrow (±50px), Home/End, double-click reset
207
+ Event: `ry:resize` `{ width }`
208
+
209
+ ### Forms
134
210
 
135
- <!-- Form Field -->
136
- <ry-field label="Email" error="Invalid email">
137
- <input type="email" name="email">
211
+ ```html
212
+ <ry-field label="Email" hint="We'll never share your email">
213
+ <input type="email" placeholder="you@example.com">
138
214
  </ry-field>
139
215
 
140
- <!-- Toast (programmatic) -->
141
- <script>
142
- RyToast.success('Saved!');
143
- RyToast.error('Failed');
144
- </script>
216
+ <ry-field label="Password" error="Must be at least 8 characters">
217
+ <input type="password">
218
+ </ry-field>
145
219
  ```
146
220
 
147
- ## Themes
221
+ Error hides hint automatically. Set `error=""` to clear.
222
+
223
+ ### Button Group
148
224
 
149
225
  ```html
150
- <html data-ry-theme="dark">
226
+ <ry-button-group name="billing" value="monthly">
227
+ <ry-button value="monthly">Monthly</ry-button>
228
+ <ry-button value="annually">Annually</ry-button>
229
+ </ry-button-group>
151
230
  ```
152
231
 
153
- Available: `light`, `dark`, `ocean`
232
+ ### Button Variants
154
233
 
155
- ## CSS Architecture
234
+ ```html
235
+ <ry-button>Default</ry-button>
236
+ <ry-button variant="primary">Primary</ry-button>
237
+ <ry-button variant="secondary">Secondary</ry-button>
238
+ <ry-button variant="outline">Outline</ry-button>
239
+ <ry-button variant="ghost">Ghost</ry-button>
240
+ <ry-button variant="danger">Danger</ry-button>
241
+ <ry-button variant="accent">Accent</ry-button>
242
+ <ry-button size="sm">Small</ry-button>
243
+ <ry-button size="lg">Large</ry-button>
244
+ ```
156
245
 
157
- ry-ui separates **structure** (layout/behavior) from **theme** (visual styling):
246
+ ### Modal & Drawer
158
247
 
248
+ ```html
249
+ <ry-button modal="confirm">Open Modal</ry-button>
250
+ <ry-modal id="confirm" title="Confirm Action">
251
+ <p>Are you sure?</p>
252
+ <ry-cluster>
253
+ <ry-button variant="danger">Delete</ry-button>
254
+ <ry-button variant="ghost">Cancel</ry-button>
255
+ </ry-cluster>
256
+ </ry-modal>
257
+
258
+ <ry-button drawer="settings">Settings</ry-button>
259
+ <ry-drawer id="settings" position="right" size="400px">
260
+ <h3>Settings</h3>
261
+ </ry-drawer>
159
262
  ```
160
- ry-tokens.css # Design tokens (CSS variables)
161
- ry-structure.css # Pure layout - no colors, shadows, or borders
162
- ry-theme.css # All visual styling
163
- ry-ui.css # Bundled (all three combined)
263
+
264
+ ### Nav Bar
265
+
266
+ ```html
267
+ <ry-header sticky>
268
+ <ry-cluster>
269
+ <ry-logo>MyApp</ry-logo>
270
+ <ry-divider vertical></ry-divider>
271
+ <ry-nav>
272
+ <a href="/" aria-current="page">Home</a>
273
+ <a href="/docs">Docs</a>
274
+ </ry-nav>
275
+ </ry-cluster>
276
+ <ry-actions>
277
+ <ry-button variant="ghost" size="sm">Login</ry-button>
278
+ <ry-button size="sm">Sign Up</ry-button>
279
+ </ry-actions>
280
+ </ry-header>
164
281
  ```
165
282
 
166
- ### Custom Themes
283
+ ### Hero
284
+
285
+ ```html
286
+ <ry-hero>
287
+ <h1>Build faster with ry-ui</h1>
288
+ <p>Framework-agnostic components for any app.</p>
289
+ <ry-cluster>
290
+ <ry-button size="lg">Get Started</ry-button>
291
+ <ry-button variant="outline" size="lg">View Docs</ry-button>
292
+ </ry-cluster>
293
+ </ry-hero>
294
+ ```
167
295
 
168
- For completely custom styling, load only structure + your own theme:
296
+ ### Pricing
169
297
 
170
298
  ```html
171
- <!-- Use default look -->
172
- <link rel="stylesheet" href="ry-ui.css">
299
+ <ry-pricing>
300
+ <ry-pricing-card>
301
+ <h3>Free</h3>
302
+ <div class="ry-pricing__price">$0<span>/mo</span></div>
303
+ <ul class="ry-check-list">
304
+ <li>3 projects</li>
305
+ <li>Basic support</li>
306
+ </ul>
307
+ <ry-button variant="outline">Get Started</ry-button>
308
+ </ry-pricing-card>
309
+ <ry-pricing-card featured>
310
+ <h3>Pro</h3>
311
+ <div class="ry-pricing__price">$19<span>/mo</span></div>
312
+ <ul class="ry-check-list">
313
+ <li>Unlimited projects</li>
314
+ <li>Priority support</li>
315
+ </ul>
316
+ <ry-button>Upgrade</ry-button>
317
+ </ry-pricing-card>
318
+ </ry-pricing>
319
+ ```
173
320
 
174
- <!-- OR: Custom theme -->
175
- <link rel="stylesheet" href="ry-structure.css">
176
- <link rel="stylesheet" href="your-tokens.css">
177
- <link rel="stylesheet" href="your-theme.css">
321
+ ### Interactive Card Grid
322
+
323
+ ```html
324
+ <ry-grid cols="3">
325
+ <ry-card interactive href="/feature-a">
326
+ <h3>Feature A</h3>
327
+ <p>Description</p>
328
+ </ry-card>
329
+ </ry-grid>
178
330
  ```
179
331
 
180
- Structure CSS contains only:
181
- - Display modes, positioning, flexbox, grid
182
- - Sizing, padding, margins, gaps
183
- - State transitions (opacity, visibility, transform)
332
+ ### Dropdown Menu
184
333
 
185
- Theme CSS contains:
186
- - Colors, backgrounds, borders
187
- - Shadows, border-radius
188
- - Typography styling
189
- - Focus rings
334
+ ```html
335
+ <ry-dropdown>
336
+ <ry-button slot="trigger">Actions</ry-button>
337
+ <ry-menu>
338
+ <ry-menu-item>Edit</ry-menu-item>
339
+ <ry-menu-item>Duplicate</ry-menu-item>
340
+ <ry-menu-item>Delete</ry-menu-item>
341
+ </ry-menu>
342
+ </ry-dropdown>
343
+ ```
190
344
 
191
345
  ---
192
346
 
193
- ## Development
347
+ ## Events
194
348
 
195
- ```bash
196
- npm install
197
- npm run dev # Dev server with HMR
198
- npm run build # Production build
199
- npm run typecheck
349
+ All events prefixed with `ry:`:
350
+
351
+ ```javascript
352
+ element.addEventListener('ry:change', (e) => console.log(e.detail));
353
+ element.addEventListener('ry:open', () => {});
354
+ element.addEventListener('ry:close', () => {});
355
+ element.addEventListener('ry:click', () => {});
200
356
  ```
201
357
 
202
- ## Publishing
358
+ ### Programmatic Control
203
359
 
204
- ```bash
205
- npm run release
360
+ ```javascript
361
+ document.querySelector('ry-modal').open();
362
+ document.querySelector('ry-modal').close();
363
+ document.querySelector('ry-drawer').toggle();
364
+ document.querySelector('ry-select').value = 'new-value';
206
365
  ```
207
366
 
208
- Automatically available on CDN via [unpkg](https://unpkg.com/@ryanhelsing/ry-ui/) and [jsdelivr](https://cdn.jsdelivr.net/npm/@ryanhelsing/ry-ui/).
367
+ ---
209
368
 
210
- ## TypeScript Philosophy
369
+ ## CSS Token System
211
370
 
212
- ry-ui is written in strict TypeScript with a "vanilla-first" approach:
371
+ All visual properties use CSS custom properties. Override in your own CSS to customize.
213
372
 
214
- ### No Decorators, No Magic
215
- We use plain class syntax with private fields (`#field`) instead of decorators. The code reads like standard JavaScript with type annotations.
373
+ ### Colors
216
374
 
217
- ```typescript
218
- // What we do
219
- class RySelect extends RyElement {
220
- #highlightedIndex = -1;
221
- #typeahead = '';
375
+ | Token | Purpose |
376
+ |-------|---------|
377
+ | `--ry-color-primary` / `-hover` / `-active` | Primary action color |
378
+ | `--ry-color-secondary` / `-hover` / `-active` | Secondary muted color |
379
+ | `--ry-color-accent` / `-hover` / `-active` | Accent/highlight color |
380
+ | `--ry-color-success` | Green for positive states |
381
+ | `--ry-color-warning` | Yellow/orange for caution |
382
+ | `--ry-color-danger` / `-hover` | Red for destructive actions |
383
+ | `--ry-color-info` | Blue for informational |
384
+ | `--ry-color-text` / `-muted` / `-inverse` | Text colors |
385
+ | `--ry-color-bg` / `-subtle` / `-muted` | Background colors |
386
+ | `--ry-color-border` / `-strong` | Border colors |
387
+ | `--ry-color-overlay` | Modal/drawer backdrop |
222
388
 
223
- setup(): void { ... }
224
- }
389
+ Each color also has `-bg` and `-text` variants for alert/badge backgrounds.
225
390
 
226
- // What we avoid
227
- @customElement('ry-select')
228
- class RySelect extends LitElement {
229
- @property() value = '';
230
- @state() private _open = false;
231
- }
391
+ ### Spacing
392
+
393
+ `--ry-space-{0,1,2,3,4,5,6,8,10,12,16,20}` 0 to 5rem
394
+
395
+ ### Typography
396
+
397
+ | Token | Value |
398
+ |-------|-------|
399
+ | `--ry-font-sans` | system-ui stack |
400
+ | `--ry-font-mono` | ui-monospace stack |
401
+ | `--ry-text-{xs,sm,base,lg,xl,2xl,3xl,4xl}` | 0.75rem to 2.25rem |
402
+ | `--ry-font-{normal,medium,semibold,bold}` | 400 to 700 |
403
+
404
+ ### Borders & Shadows
405
+
406
+ | Token | Value |
407
+ |-------|-------|
408
+ | `--ry-radius-{none,sm,md,lg,xl,2xl,full}` | 0 to 9999px |
409
+ | `--ry-shadow-{sm,md,lg,xl}` | Elevation shadows |
410
+ | `--ry-border-width` | 1px |
411
+
412
+ ### Transitions
413
+
414
+ | Token | Value |
415
+ |-------|-------|
416
+ | `--ry-duration-{fast,normal,slow}` | 100ms, 200ms, 300ms |
417
+ | `--ry-ease` / `-in` / `-out` | Cubic bezier easing |
418
+
419
+ ### Z-Index
420
+
421
+ | Token | Value |
422
+ |-------|-------|
423
+ | `--ry-z-dropdown` | 1000 |
424
+ | `--ry-z-sticky` | 1020 |
425
+ | `--ry-z-modal-backdrop` | 1040 |
426
+ | `--ry-z-modal` | 1050 |
427
+ | `--ry-z-tooltip` | 1070 |
428
+ | `--ry-z-toast` | 1080 |
429
+
430
+ ---
431
+
432
+ ## Theming
433
+
434
+ Three CSS layers, loaded in order:
435
+
436
+ 1. **ry-tokens.css** — CSS custom properties (colors, spacing, etc.)
437
+ 2. **ry-structure.css** — Pure layout (no colors)
438
+ 3. **ry-theme.css** — All visual styling (colors, shadows, borders)
439
+
440
+ ### Custom Theme
441
+
442
+ Load structure-only and bring your own:
443
+
444
+ ```html
445
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ryanhelsing/ry-ui/dist/css/ry-structure.css">
446
+ <link rel="stylesheet" href="your-tokens.css">
447
+ <link rel="stylesheet" href="your-theme.css">
232
448
  ```
233
449
 
234
- ### Strict Mode, No Exceptions
235
- ```json
236
- {
237
- "strict": true,
238
- "noUncheckedIndexedAccess": true,
239
- "noImplicitReturns": true
450
+ ### Override Tokens
451
+
452
+ No build step needed:
453
+
454
+ ```css
455
+ :root {
456
+ --ry-color-primary: oklch(0.541 0.218 293);
457
+ --ry-color-primary-hover: oklch(0.491 0.234 292);
458
+ --ry-radius-md: 0;
240
459
  }
241
460
  ```
242
461
 
243
- ### Type Patterns
462
+ ### Themes & Modes
244
463
 
245
- **Explicit over inferred for public APIs:**
246
- ```typescript
247
- get value(): string { return this.getAttribute('value') ?? ''; }
248
- emit<T = void>(name: string, detail?: T): boolean { ... }
249
- ```
464
+ Theme and mode are independent:
250
465
 
251
- **Union types for constrained values:**
252
- ```typescript
253
- type ToastVariant = 'info' | 'success' | 'warning' | 'error';
254
- static observedAttributes = ['value', 'disabled'] as const;
466
+ ```html
467
+ <html data-ry-theme="ocean" data-ry-mode="dark">
255
468
  ```
256
469
 
257
- **Extend globals for custom events:**
258
- ```typescript
259
- declare global {
260
- interface HTMLElementEventMap {
261
- 'ry:change': CustomEvent<{ value: string }>;
470
+ Themes: `default`, `ocean`, `none` (structure only)
471
+ Modes: `auto` (OS preference), `light`, `dark`
472
+
473
+ Use `<ry-theme-panel>` for an interactive floating selector.
474
+
475
+ ---
476
+
477
+ ## TypeScript
478
+
479
+ ```ts
480
+ import { RyElement, RyButton, RyToast } from '@ryanhelsing/ry-ui';
481
+
482
+ RyToast.success('Saved!');
483
+
484
+ document.querySelector('ry-select')?.addEventListener('ry:change', (e: CustomEvent) => {
485
+ console.log(e.detail.value);
486
+ });
487
+
488
+ // Extend components
489
+ class MyWidget extends RyElement {
490
+ setup() {
491
+ this.on(this, 'click', () => this.emit('activate'));
262
492
  }
263
493
  }
264
494
  ```
265
495
 
266
- ### Build Output
496
+ ## Icon Registry
497
+
498
+ Built-in: `settings`, `heart`, `star`, `chevron-up`, `chevron-down`, `chevron-left`, `chevron-right`, `check`, `x`, `plus`, `minus`, `search`, `sun`, `moon`, `copy`, `trash`, `edit`, `eye`, `folder`, `file`, `drag`
499
+
500
+ ```ts
501
+ import { registerIcon, registerIcons } from '@ryanhelsing/ry-ui';
267
502
 
268
- Single bundled ESM file for CDN simplicity:
503
+ registerIcon('custom', '<svg>...</svg>');
504
+ registerIcons({ 'app-logo': '<svg>...</svg>' });
269
505
  ```
270
- dist/
271
- ├── ry-ui.js # 32KB (7KB gzip)
272
- ├── ry-ui.js.map # Source maps
273
- └── ry-ui.d.ts # Type declarations
506
+
507
+ ## Vendoring
508
+
509
+ Copy into your project instead of using CDN:
510
+
511
+ ```bash
512
+ npm pack @ryanhelsing/ry-ui && tar -xf ryanhelsing-ry-ui-*.tgz
513
+ cp -r package/dist ./vendor/ry-ui && rm -rf package ryanhelsing-ry-ui-*.tgz
274
514
  ```
275
515
 
276
- Vite builds with esbuild for speed, Rollup for optimization, and generates `.d.ts` for library consumers.
516
+ ```html
517
+ <link rel="stylesheet" href="/vendor/ry-ui/css/ry-ui.css">
518
+ <script type="module" src="/vendor/ry-ui/ry-ui.js"></script>
519
+ ```
277
520
 
278
521
  ---
279
522
 
280
- See [CLAUDE.md](CLAUDE.md) for component development guide.
523
+ ## Detailed Docs
524
+
525
+ Per-component docs with full attributes, events, and examples:
526
+
527
+ | Doc | Components |
528
+ |-----|-----------|
529
+ | [layout](docs/components/layout.md) | page, header, main, footer, section, grid, stack, cluster, split, center, card, nav, divider |
530
+ | [button](docs/components/button.md) | button, toggle-button |
531
+ | [button-group](docs/components/button-group.md) | button-group |
532
+ | [accordion](docs/components/accordion.md) | accordion, accordion-item |
533
+ | [tabs](docs/components/tabs.md) | tabs, tab |
534
+ | [modal](docs/components/modal.md) | modal |
535
+ | [drawer](docs/components/drawer.md) | drawer |
536
+ | [dropdown](docs/components/dropdown.md) | dropdown, menu, menu-item |
537
+ | [tooltip](docs/components/tooltip.md) | tooltip |
538
+ | [toast](docs/components/toast.md) | toast |
539
+ | [forms](docs/components/forms.md) | field, select, switch |
540
+ | [slider](docs/components/slider.md) | slider |
541
+ | [knob](docs/components/knob.md) | knob |
542
+ | [number-select](docs/components/number-select.md) | number-select |
543
+ | [color](docs/components/color.md) | color-picker, color-input, gradient-picker |
544
+ | [tree](docs/components/tree.md) | tree |
545
+ | [display](docs/components/display.md) | badge, alert, icon, code |
546
+ | [theme-toggle](docs/components/theme-toggle.md) | theme-toggle |
547
+ | [theming](docs/theming.md) | tokens, custom themes, structure-only loading |
548
+
549
+ ## AI-Friendly
550
+
551
+ This package includes a `.claude/skills/ry-ui-builder` skill so Claude Code can build with these components automatically. The detailed docs above serve as the complete agent reference.
552
+
553
+ ## License
554
+
555
+ MIT