@ryanhelsing/ry-ui 1.0.10 → 1.0.12

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,294 +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
- - `<ry-theme-panel>` - Floating theme/mode selector (none, default, ocean + light/dark)
84
- - `<ry-testimonial>` - Customer testimonial / quote card with avatar and star rating
85
-
86
- ### Forms
87
- - `<ry-field>` - Form field wrapper with auto label, error, and hint
88
-
89
- ### Display
90
- - `<ry-card>` - Card containers
91
- - `<ry-badge>` - Status badges
92
- - `<ry-alert>` - Alert messages
93
-
94
93
  ## Clean Syntax
95
94
 
96
- Use `<ry>` wrapper to write unprefixed markup:
95
+ Wrap markup in `<ry>` to use unprefixed tags:
97
96
 
98
97
  ```html
99
98
  <ry>
100
99
  <accordion>
101
- <accordion-item title="FAQ" open>
102
- No ry- prefix needed inside the wrapper.
103
- </accordion-item>
100
+ <accordion-item title="FAQ" open>No ry- prefix needed.</accordion-item>
104
101
  </accordion>
105
102
  </ry>
106
103
  ```
107
104
 
108
- ## 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
109
197
 
110
198
  ```html
111
- <!-- Modal -->
112
- <ry-button modal="demo">Open Modal</ry-button>
113
- <ry-modal id="demo" title="Hello">Content here</ry-modal>
114
-
115
- <!-- Drawer -->
116
- <ry-button drawer="menu">Open</ry-button>
117
- <ry-drawer id="menu" side="left">Menu content</ry-drawer>
118
-
119
- <!-- Select -->
120
- <ry-select placeholder="Country" name="country">
121
- <ry-option value="us">United States</ry-option>
122
- <ry-option value="uk">United Kingdom</ry-option>
123
- </ry-select>
124
-
125
- <!-- Split Panel (resizable, persistent) -->
126
- <ry-split resizable persist="sidebar">
127
- <main>Main content</main>
128
- <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>
129
202
  </ry-split>
203
+ ```
130
204
 
131
- <!-- Button Group -->
132
- <ry-button-group value="monthly">
133
- <ry-button value="monthly">Monthly</ry-button>
134
- <ry-button value="annually">Annually</ry-button>
135
- </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
136
210
 
137
- <!-- Form Field -->
138
- <ry-field label="Email" error="Invalid email">
139
- <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">
140
214
  </ry-field>
141
215
 
142
- <!-- Toast (programmatic) -->
143
- <script>
144
- RyToast.success('Saved!');
145
- RyToast.error('Failed');
146
- </script>
216
+ <ry-field label="Password" error="Must be at least 8 characters">
217
+ <input type="password">
218
+ </ry-field>
147
219
  ```
148
220
 
149
- ## Themes
221
+ Error hides hint automatically. Set `error=""` to clear.
150
222
 
151
- Theme and mode are orthogonal — every theme supports light and dark:
223
+ ### Button Group
152
224
 
153
225
  ```html
154
- <!-- Theme controls visual skin -->
155
- <html data-ry-theme="ocean">
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>
230
+ ```
156
231
 
157
- <!-- Mode controls light/dark (independent of theme) -->
158
- <html data-ry-mode="dark">
232
+ ### Button Variants
159
233
 
160
- <!-- "none" theme shows pure structure (no visual styling) -->
161
- <html data-ry-theme="none">
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>
162
244
  ```
163
245
 
164
- Available themes: `none`, `default`, `ocean`
165
- Available modes: `auto` (OS preference), `light`, `dark`
166
-
167
- Use `<ry-theme-panel>` for an interactive floating selector.
246
+ ### Modal & Drawer
168
247
 
169
- ## CSS Architecture
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>
262
+ ```
170
263
 
171
- ry-ui separates **structure** (layout/behavior) from **theme** (visual styling):
264
+ ### Nav Bar
172
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>
173
281
  ```
174
- ry-tokens.css # Design tokens (CSS variables)
175
- ry-structure.css # Pure layout - no colors, shadows, or borders
176
- ry-theme.css # All visual styling
177
- ry-ui.css # Bundled (all three combined)
178
- ```
179
282
 
180
- ### 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
+ ```
181
295
 
182
- For completely custom styling, load only structure + your own theme:
296
+ ### Pricing
183
297
 
184
298
  ```html
185
- <!-- Use default look -->
186
- <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
+ ```
187
320
 
188
- <!-- OR: Custom theme -->
189
- <link rel="stylesheet" href="ry-structure.css">
190
- <link rel="stylesheet" href="your-tokens.css">
191
- <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>
192
330
  ```
193
331
 
194
- Structure CSS contains only:
195
- - Display modes, positioning, flexbox, grid
196
- - Sizing, padding, margins, gaps
197
- - State transitions (opacity, visibility, transform)
332
+ ### Dropdown Menu
198
333
 
199
- Theme CSS contains:
200
- - Colors, backgrounds, borders
201
- - Shadows, border-radius
202
- - Typography styling
203
- - 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
+ ```
204
344
 
205
345
  ---
206
346
 
207
- ## Development
347
+ ## Events
208
348
 
209
- ```bash
210
- npm install
211
- npm run dev # Dev server with HMR
212
- npm run build # Production build
213
- 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', () => {});
214
356
  ```
215
357
 
216
- ## Publishing
358
+ ### Programmatic Control
217
359
 
218
- ```bash
219
- 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';
220
365
  ```
221
366
 
222
- Automatically available on CDN via [unpkg](https://unpkg.com/@ryanhelsing/ry-ui/) and [jsdelivr](https://cdn.jsdelivr.net/npm/@ryanhelsing/ry-ui/).
367
+ ---
223
368
 
224
- ## TypeScript Philosophy
369
+ ## CSS Token System
225
370
 
226
- 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.
227
372
 
228
- ### No Decorators, No Magic
229
- We use plain class syntax with private fields (`#field`) instead of decorators. The code reads like standard JavaScript with type annotations.
373
+ ### Colors
230
374
 
231
- ```typescript
232
- // What we do
233
- class RySelect extends RyElement {
234
- #highlightedIndex = -1;
235
- #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 |
236
388
 
237
- setup(): void { ... }
238
- }
389
+ Each color also has `-bg` and `-text` variants for alert/badge backgrounds.
239
390
 
240
- // What we avoid
241
- @customElement('ry-select')
242
- class RySelect extends LitElement {
243
- @property() value = '';
244
- @state() private _open = false;
245
- }
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">
246
448
  ```
247
449
 
248
- ### Strict Mode, No Exceptions
249
- ```json
250
- {
251
- "strict": true,
252
- "noUncheckedIndexedAccess": true,
253
- "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;
254
459
  }
255
460
  ```
256
461
 
257
- ### Type Patterns
462
+ ### Themes & Modes
258
463
 
259
- **Explicit over inferred for public APIs:**
260
- ```typescript
261
- get value(): string { return this.getAttribute('value') ?? ''; }
262
- emit<T = void>(name: string, detail?: T): boolean { ... }
263
- ```
464
+ Theme and mode are independent:
264
465
 
265
- **Union types for constrained values:**
266
- ```typescript
267
- type ToastVariant = 'info' | 'success' | 'warning' | 'error';
268
- static observedAttributes = ['value', 'disabled'] as const;
466
+ ```html
467
+ <html data-ry-theme="ocean" data-ry-mode="dark">
269
468
  ```
270
469
 
271
- **Extend globals for custom events:**
272
- ```typescript
273
- declare global {
274
- interface HTMLElementEventMap {
275
- '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'));
276
492
  }
277
493
  }
278
494
  ```
279
495
 
280
- ### 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';
281
502
 
282
- Single bundled ESM file for CDN simplicity:
503
+ registerIcon('custom', '<svg>...</svg>');
504
+ registerIcons({ 'app-logo': '<svg>...</svg>' });
283
505
  ```
284
- dist/
285
- ├── ry-ui.js # 32KB (7KB gzip)
286
- ├── ry-ui.js.map # Source maps
287
- └── 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
288
514
  ```
289
515
 
290
- 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
+ ```
291
520
 
292
521
  ---
293
522
 
294
- 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