@ryanhelsing/ry-ui 1.0.2 → 1.0.3
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/AGENTS.md +56 -0
- package/README.md +26 -1
- package/dist/components/ry-carousel.d.ts +21 -0
- package/dist/components/ry-carousel.d.ts.map +1 -0
- package/dist/components/ry-feature.d.ts +21 -0
- package/dist/components/ry-feature.d.ts.map +1 -0
- package/dist/components/ry-hero.d.ts +16 -0
- package/dist/components/ry-hero.d.ts.map +1 -0
- package/dist/components/ry-number-select.d.ts.map +1 -1
- package/dist/components/ry-pricing.d.ts +21 -0
- package/dist/components/ry-pricing.d.ts.map +1 -0
- package/dist/components/ry-select.d.ts +8 -1
- package/dist/components/ry-select.d.ts.map +1 -1
- package/dist/components/ry-stat.d.ts +17 -0
- package/dist/components/ry-stat.d.ts.map +1 -0
- package/dist/components/ry-tag-input.d.ts +18 -0
- package/dist/components/ry-tag-input.d.ts.map +1 -0
- package/dist/components/ry-tag.d.ts +19 -0
- package/dist/components/ry-tag.d.ts.map +1 -0
- package/dist/core/ry-transform.d.ts.map +1 -1
- package/dist/css/ry-structure.css +620 -148
- package/dist/css/ry-theme.css +456 -180
- package/dist/css/ry-tokens.css +112 -24
- package/dist/css/ry-ui.css +4708 -1059
- package/dist/ry-ui.d.ts +7 -0
- package/dist/ry-ui.d.ts.map +1 -1
- package/dist/ry-ui.js +1071 -713
- package/dist/ry-ui.js.map +1 -1
- package/dist/themes/dark.css +7 -90
- package/dist/themes/light.css +6 -35
- package/dist/themes/ocean.css +22 -26
- package/docs/components/accordion.md +31 -0
- package/docs/components/button.md +65 -0
- package/docs/components/color.md +84 -0
- package/docs/components/display.md +69 -0
- package/docs/components/drawer.md +36 -0
- package/docs/components/dropdown.md +33 -0
- package/docs/components/forms.md +86 -0
- package/docs/components/knob.md +42 -0
- package/docs/components/layout.md +189 -0
- package/docs/components/modal.md +38 -0
- package/docs/components/number-select.md +42 -0
- package/docs/components/slider.md +48 -0
- package/docs/components/tabs.md +30 -0
- package/docs/components/theme-toggle.md +36 -0
- package/docs/components/toast.md +27 -0
- package/docs/components/tooltip.md +14 -0
- package/docs/components/tree.md +46 -0
- package/docs/theming.md +182 -0
- package/package.json +5 -3
- package/USING_CDN.md +0 -591
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Layout Components
|
|
2
|
+
|
|
3
|
+
CSS-only layout primitives. No JavaScript needed.
|
|
4
|
+
|
|
5
|
+
## `<ry-page>`
|
|
6
|
+
|
|
7
|
+
Top-level page shell. Contains header, main, footer.
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<ry-page>
|
|
11
|
+
<ry-header sticky>...</ry-header>
|
|
12
|
+
<ry-main>...</ry-main>
|
|
13
|
+
<ry-footer>...</ry-footer>
|
|
14
|
+
</ry-page>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## `<ry-header>`
|
|
18
|
+
|
|
19
|
+
| Attribute | Values | Description |
|
|
20
|
+
|-----------|--------|-------------|
|
|
21
|
+
| `sticky` | boolean | Sticks to top of viewport |
|
|
22
|
+
|
|
23
|
+
Horizontal flex with space-between. Two direct children = left and right.
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<ry-header sticky>
|
|
27
|
+
<ry-cluster><strong>App</strong></ry-cluster>
|
|
28
|
+
<ry-cluster><ry-theme-toggle themes="light,dark"></ry-theme-toggle></ry-cluster>
|
|
29
|
+
</ry-header>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## `<ry-grid>`
|
|
33
|
+
|
|
34
|
+
| Attribute | Values | Description |
|
|
35
|
+
|-----------|--------|-------------|
|
|
36
|
+
| `cols` | 1–6 | Number of columns |
|
|
37
|
+
|
|
38
|
+
```html
|
|
39
|
+
<ry-grid cols="3">
|
|
40
|
+
<ry-card>One</ry-card>
|
|
41
|
+
<ry-card>Two</ry-card>
|
|
42
|
+
<ry-card>Three</ry-card>
|
|
43
|
+
</ry-grid>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## `<ry-stack>`
|
|
47
|
+
|
|
48
|
+
Vertical flex layout.
|
|
49
|
+
|
|
50
|
+
| Attribute | Values | Description |
|
|
51
|
+
|-----------|--------|-------------|
|
|
52
|
+
| `gap` | sm \| md \| lg | Vertical spacing |
|
|
53
|
+
|
|
54
|
+
```html
|
|
55
|
+
<ry-stack gap="sm">
|
|
56
|
+
<p>Item 1</p>
|
|
57
|
+
<p>Item 2</p>
|
|
58
|
+
</ry-stack>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## `<ry-cluster>`
|
|
62
|
+
|
|
63
|
+
Horizontal flex, wraps.
|
|
64
|
+
|
|
65
|
+
| Attribute | Values | Description |
|
|
66
|
+
|-----------|--------|-------------|
|
|
67
|
+
| `gap` | sm \| md \| lg | Horizontal spacing |
|
|
68
|
+
|
|
69
|
+
```html
|
|
70
|
+
<ry-cluster>
|
|
71
|
+
<ry-badge>A</ry-badge>
|
|
72
|
+
<ry-badge>B</ry-badge>
|
|
73
|
+
</ry-cluster>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## `<ry-card>`
|
|
77
|
+
|
|
78
|
+
Content container with padding and border. Use `<ry-actions>` inside for button groups.
|
|
79
|
+
|
|
80
|
+
```html
|
|
81
|
+
<ry-card>
|
|
82
|
+
<h3>Title</h3>
|
|
83
|
+
<p>Content.</p>
|
|
84
|
+
<ry-actions>
|
|
85
|
+
<ry-button>Save</ry-button>
|
|
86
|
+
<ry-button variant="ghost">Cancel</ry-button>
|
|
87
|
+
</ry-actions>
|
|
88
|
+
</ry-card>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## `<ry-section>`
|
|
92
|
+
|
|
93
|
+
Content section block. Adds bottom margin between sections, removed on last child.
|
|
94
|
+
|
|
95
|
+
```html
|
|
96
|
+
<ry-main>
|
|
97
|
+
<ry-section><h2>Section 1</h2><p>Content.</p></ry-section>
|
|
98
|
+
<ry-section><h2>Section 2</h2><p>Content.</p></ry-section>
|
|
99
|
+
</ry-main>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## `<ry-aside>`
|
|
103
|
+
|
|
104
|
+
Sidebar / secondary content block.
|
|
105
|
+
|
|
106
|
+
```html
|
|
107
|
+
<ry-split>
|
|
108
|
+
<ry-main>Primary content</ry-main>
|
|
109
|
+
<ry-aside>Sidebar content</ry-aside>
|
|
110
|
+
</ry-split>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## `<ry-split>`
|
|
114
|
+
|
|
115
|
+
Two-pane layout: content (flexible) + sidebar (300px). Stacks vertically on mobile (<768px).
|
|
116
|
+
|
|
117
|
+
```html
|
|
118
|
+
<ry-split>
|
|
119
|
+
<div>Main content (flex: 1)</div>
|
|
120
|
+
<div>Sidebar (300px)</div>
|
|
121
|
+
</ry-split>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## `<ry-center>`
|
|
125
|
+
|
|
126
|
+
Centers content both horizontally and vertically.
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<ry-center>
|
|
130
|
+
<h1>Centered title</h1>
|
|
131
|
+
<p>Centered paragraph</p>
|
|
132
|
+
</ry-center>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## `<ry-nav>`
|
|
136
|
+
|
|
137
|
+
Horizontal navigation links. Use `aria-current="page"` to highlight the active link.
|
|
138
|
+
|
|
139
|
+
```html
|
|
140
|
+
<ry-nav>
|
|
141
|
+
<a href="/" aria-current="page">Home</a>
|
|
142
|
+
<a href="/about">About</a>
|
|
143
|
+
<a href="/contact">Contact</a>
|
|
144
|
+
</ry-nav>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## `<ry-logo>`
|
|
148
|
+
|
|
149
|
+
Inline logo element. Bold, large text. Typically used inside `<ry-header>`.
|
|
150
|
+
|
|
151
|
+
```html
|
|
152
|
+
<ry-header>
|
|
153
|
+
<ry-logo>My App</ry-logo>
|
|
154
|
+
<ry-nav>...</ry-nav>
|
|
155
|
+
</ry-header>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## `<ry-actions>`
|
|
159
|
+
|
|
160
|
+
Horizontal button group. Use inside cards, modals, or any container.
|
|
161
|
+
|
|
162
|
+
```html
|
|
163
|
+
<ry-actions>
|
|
164
|
+
<ry-button>Primary</ry-button>
|
|
165
|
+
<ry-button variant="ghost">Cancel</ry-button>
|
|
166
|
+
</ry-actions>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## `<ry-divider>`
|
|
170
|
+
|
|
171
|
+
Horizontal or vertical separator line.
|
|
172
|
+
|
|
173
|
+
| Attribute | Values | Description |
|
|
174
|
+
|-----------|--------|-------------|
|
|
175
|
+
| `vertical` | boolean | Vertical orientation (for use inside flex rows) |
|
|
176
|
+
|
|
177
|
+
```html
|
|
178
|
+
<ry-stack>
|
|
179
|
+
<p>Above</p>
|
|
180
|
+
<ry-divider></ry-divider>
|
|
181
|
+
<p>Below</p>
|
|
182
|
+
</ry-stack>
|
|
183
|
+
|
|
184
|
+
<ry-cluster>
|
|
185
|
+
<span>Left</span>
|
|
186
|
+
<ry-divider vertical></ry-divider>
|
|
187
|
+
<span>Right</span>
|
|
188
|
+
</ry-cluster>
|
|
189
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Modal
|
|
2
|
+
|
|
3
|
+
## `<ry-modal>`
|
|
4
|
+
|
|
5
|
+
| Attribute | Values | Description |
|
|
6
|
+
|-----------|--------|-------------|
|
|
7
|
+
| `id` | string | Identifier (match button's `modal` attr) |
|
|
8
|
+
| `title` | string | Header title |
|
|
9
|
+
|
|
10
|
+
Put `close` attribute on any button inside to dismiss. Uses focus trapping.
|
|
11
|
+
|
|
12
|
+
Events: `ry:open`, `ry:close`
|
|
13
|
+
API: `.open()`, `.close()`, `.state`
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<ry-button modal="demo">Open</ry-button>
|
|
17
|
+
|
|
18
|
+
<ry-modal id="demo" title="Confirm">
|
|
19
|
+
<p>Are you sure?</p>
|
|
20
|
+
<ry-actions slot="footer">
|
|
21
|
+
<ry-button variant="ghost" close>Cancel</ry-button>
|
|
22
|
+
<ry-button>OK</ry-button>
|
|
23
|
+
</ry-actions>
|
|
24
|
+
</ry-modal>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
JS:
|
|
28
|
+
```js
|
|
29
|
+
const modal = document.querySelector('ry-modal');
|
|
30
|
+
|
|
31
|
+
modal.addEventListener('ry:open', () => console.log('opened'));
|
|
32
|
+
modal.addEventListener('ry:close', () => console.log('closed'));
|
|
33
|
+
|
|
34
|
+
// Programmatic
|
|
35
|
+
modal.open();
|
|
36
|
+
modal.close();
|
|
37
|
+
modal.state; // "open" or "closed"
|
|
38
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Number Select
|
|
2
|
+
|
|
3
|
+
## `<ry-number-select>`
|
|
4
|
+
|
|
5
|
+
Numeric stepper with increment/decrement buttons and optional drag-to-adjust.
|
|
6
|
+
|
|
7
|
+
| Attribute | Values | Description |
|
|
8
|
+
|-----------|--------|-------------|
|
|
9
|
+
| `min` | number | Minimum (default: 0) |
|
|
10
|
+
| `max` | number | Maximum (default: 100) |
|
|
11
|
+
| `step` | number | Step (default: 1) |
|
|
12
|
+
| `value` | number | Current value |
|
|
13
|
+
| `arrows` | both \| start \| end \| stacked \| stacked-end \| stacked-start \| none | Button placement |
|
|
14
|
+
| `icons` | plus-minus \| chevron \| arrow | Icon style |
|
|
15
|
+
| `drag` | x \| y \| none | Drag direction |
|
|
16
|
+
| `prefix` | string | Before value ("$") |
|
|
17
|
+
| `suffix` | string | After value ("°", "%") |
|
|
18
|
+
| `editable` | boolean | Allow typing directly |
|
|
19
|
+
| `wrap` | boolean | Wrap around min/max |
|
|
20
|
+
| `size` | xs \| sm \| lg | Size |
|
|
21
|
+
| `disabled` | boolean | Disable |
|
|
22
|
+
|
|
23
|
+
Events: `ry:input` (during drag), `ry:change` (on commit)
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<ry-number-select min="0" max="100" value="50"></ry-number-select>
|
|
27
|
+
<ry-number-select min="0" max="360" value="90" suffix="°" arrows="stacked" icons="chevron"></ry-number-select>
|
|
28
|
+
<ry-number-select min="0" max="1000" value="50" prefix="$" editable></ry-number-select>
|
|
29
|
+
<ry-number-select min="0" max="7" value="3" wrap></ry-number-select>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
JS:
|
|
33
|
+
```js
|
|
34
|
+
const ns = document.querySelector('ry-number-select');
|
|
35
|
+
|
|
36
|
+
ns.addEventListener('ry:input', (e) => console.log(e.detail.value));
|
|
37
|
+
ns.addEventListener('ry:change', (e) => console.log(e.detail.value));
|
|
38
|
+
|
|
39
|
+
ns.value; // 50
|
|
40
|
+
ns.value = 75;
|
|
41
|
+
ns.step = 5;
|
|
42
|
+
```
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Slider
|
|
2
|
+
|
|
3
|
+
## `<ry-slider>`
|
|
4
|
+
|
|
5
|
+
| Attribute | Values | Description |
|
|
6
|
+
|-----------|--------|-------------|
|
|
7
|
+
| `min` | number | Minimum (default: 0) |
|
|
8
|
+
| `max` | number | Maximum (default: 100) |
|
|
9
|
+
| `step` | number | Step (default: 1, 0 = smooth) |
|
|
10
|
+
| `value` | number | Current value (single mode) |
|
|
11
|
+
| `start` / `end` | number | Range values (with `range`) |
|
|
12
|
+
| `range` | boolean | Dual-handle mode |
|
|
13
|
+
| `labeled` | boolean | Show min/max labels |
|
|
14
|
+
| `ticked` | boolean | Show tick marks |
|
|
15
|
+
| `tooltip` | boolean | Show value on hover |
|
|
16
|
+
| `vertical` | boolean | Vertical orientation |
|
|
17
|
+
| `reversed` | boolean | Reverse direction |
|
|
18
|
+
| `color` | primary \| secondary \| success \| warning \| danger \| info | Track color |
|
|
19
|
+
| `size` | sm \| lg | Size |
|
|
20
|
+
| `disabled` | boolean | Disable |
|
|
21
|
+
|
|
22
|
+
Events: `ry:input` (during drag), `ry:change` (on release)
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<ry-slider min="0" max="100" value="50"></ry-slider>
|
|
26
|
+
<ry-slider min="0" max="100" start="25" end="75" range labeled></ry-slider>
|
|
27
|
+
<ry-slider min="0" max="10" step="1" value="5" ticked labeled tooltip></ry-slider>
|
|
28
|
+
<ry-slider min="0" max="100" value="60" color="success"></ry-slider>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
JS:
|
|
32
|
+
```js
|
|
33
|
+
const slider = document.querySelector('ry-slider');
|
|
34
|
+
|
|
35
|
+
slider.addEventListener('ry:input', (e) => {
|
|
36
|
+
console.log(e.detail.value); // fires during drag
|
|
37
|
+
});
|
|
38
|
+
slider.addEventListener('ry:change', (e) => {
|
|
39
|
+
saveValue(e.detail.value); // fires on release
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
slider.value; // 50
|
|
43
|
+
slider.value = 75;
|
|
44
|
+
|
|
45
|
+
// Range slider
|
|
46
|
+
rangeSlider.start; // 25
|
|
47
|
+
rangeSlider.end; // 75
|
|
48
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Tabs
|
|
2
|
+
|
|
3
|
+
## `<ry-tabs>`
|
|
4
|
+
|
|
5
|
+
| Attribute (on tab) | Values | Description |
|
|
6
|
+
|---------------------|--------|-------------|
|
|
7
|
+
| `title` | string | Tab label |
|
|
8
|
+
| `active` | boolean | Initially selected |
|
|
9
|
+
|
|
10
|
+
Events: `ry:change` — `e.detail.index`, `e.detail.title`
|
|
11
|
+
|
|
12
|
+
```html
|
|
13
|
+
<ry-tabs>
|
|
14
|
+
<ry-tab title="Overview" active><p>Overview content.</p></ry-tab>
|
|
15
|
+
<ry-tab title="Settings"><p>Settings content.</p></ry-tab>
|
|
16
|
+
</ry-tabs>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
JS:
|
|
20
|
+
```js
|
|
21
|
+
const tabs = document.querySelector('ry-tabs');
|
|
22
|
+
|
|
23
|
+
tabs.addEventListener('ry:change', (e) => {
|
|
24
|
+
console.log(e.detail.index); // 0, 1, 2...
|
|
25
|
+
console.log(e.detail.title); // "Overview"
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Switch programmatically
|
|
29
|
+
tabs.activeIndex = 1;
|
|
30
|
+
```
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Theme Toggle
|
|
2
|
+
|
|
3
|
+
## `<ry-theme-toggle>`
|
|
4
|
+
|
|
5
|
+
Cycles through themes on click. Sets `data-ry-theme` on `<html>`.
|
|
6
|
+
|
|
7
|
+
| Attribute | Values | Description |
|
|
8
|
+
|-----------|--------|-------------|
|
|
9
|
+
| `themes` | string | Comma-separated theme names (default: "light,dark") |
|
|
10
|
+
|
|
11
|
+
Events: `ry:theme-change` — `e.detail.theme`
|
|
12
|
+
API: `.theme` (get/set), `.toggle()`
|
|
13
|
+
|
|
14
|
+
```html
|
|
15
|
+
<ry-theme-toggle themes="light,dark"></ry-theme-toggle>
|
|
16
|
+
<ry-theme-toggle themes="light,dark,ocean"></ry-theme-toggle>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
JS:
|
|
20
|
+
```js
|
|
21
|
+
const toggle = document.querySelector('ry-theme-toggle');
|
|
22
|
+
|
|
23
|
+
toggle.addEventListener('ry:theme-change', (e) => {
|
|
24
|
+
console.log(e.detail.theme); // "dark"
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Programmatic
|
|
28
|
+
toggle.theme; // "light"
|
|
29
|
+
toggle.theme = 'dark';
|
|
30
|
+
toggle.toggle(); // cycles to next
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or skip the component and set the theme directly:
|
|
34
|
+
```js
|
|
35
|
+
document.documentElement.dataset.ryTheme = 'dark';
|
|
36
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Toast
|
|
2
|
+
|
|
3
|
+
## `RyToast` (programmatic API)
|
|
4
|
+
|
|
5
|
+
No HTML element needed. Call globally after ry-ui loads.
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
RyToast.success('Saved!');
|
|
9
|
+
RyToast.error('Something went wrong');
|
|
10
|
+
RyToast.warning('Please review');
|
|
11
|
+
RyToast.info('New update available');
|
|
12
|
+
|
|
13
|
+
// With options
|
|
14
|
+
RyToast.success('Done!', { duration: 5000 }); // default: 3000ms
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Common pattern:
|
|
18
|
+
```js
|
|
19
|
+
async function saveData() {
|
|
20
|
+
try {
|
|
21
|
+
await api.save(data);
|
|
22
|
+
RyToast.success('Saved!');
|
|
23
|
+
} catch (err) {
|
|
24
|
+
RyToast.error(err.message);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Tooltip
|
|
2
|
+
|
|
3
|
+
## `<ry-tooltip>`
|
|
4
|
+
|
|
5
|
+
| Attribute | Values | Description |
|
|
6
|
+
|-----------|--------|-------------|
|
|
7
|
+
| `content` | string | Tooltip text |
|
|
8
|
+
| `position` | top \| bottom \| left \| right | Position |
|
|
9
|
+
|
|
10
|
+
```html
|
|
11
|
+
<ry-tooltip content="Save changes" position="top">
|
|
12
|
+
<ry-button>Save</ry-button>
|
|
13
|
+
</ry-tooltip>
|
|
14
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Tree
|
|
2
|
+
|
|
3
|
+
## `<ry-tree>`
|
|
4
|
+
|
|
5
|
+
File-tree / nested list with expand/collapse and optional drag-and-drop.
|
|
6
|
+
|
|
7
|
+
| Attribute | Values | Description |
|
|
8
|
+
|-----------|--------|-------------|
|
|
9
|
+
| `sortable` | boolean | Enable drag-and-drop (on tree) |
|
|
10
|
+
| `no-animate` | boolean | Disable animation |
|
|
11
|
+
| `label` | string | Item label (on tree-item) |
|
|
12
|
+
| `open` | boolean | Expanded folder (on tree-item with children) |
|
|
13
|
+
| `selected` | boolean | Selected item (on leaf tree-item) |
|
|
14
|
+
|
|
15
|
+
Events: `ry:select` (file click), `ry:toggle` (folder expand/collapse), `ry:move` (drag-drop)
|
|
16
|
+
API: `.toJSON()`, `.value`, `RyTree.from(json)`
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<ry-tree sortable>
|
|
20
|
+
<ry-tree-item label="src" open>
|
|
21
|
+
<ry-tree-item label="app">
|
|
22
|
+
<ry-tree-item label="page.tsx"></ry-tree-item>
|
|
23
|
+
</ry-tree-item>
|
|
24
|
+
<ry-tree-item label="utils.ts"></ry-tree-item>
|
|
25
|
+
</ry-tree-item>
|
|
26
|
+
</ry-tree>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
JS:
|
|
30
|
+
```js
|
|
31
|
+
const tree = document.querySelector('ry-tree');
|
|
32
|
+
|
|
33
|
+
tree.addEventListener('ry:select', (e) => {
|
|
34
|
+
console.log(e.detail.label); // "page.tsx"
|
|
35
|
+
});
|
|
36
|
+
tree.addEventListener('ry:toggle', (e) => {
|
|
37
|
+
console.log(e.detail.label, e.detail.open);
|
|
38
|
+
});
|
|
39
|
+
tree.addEventListener('ry:move', (e) => {
|
|
40
|
+
console.log(`Moved "${e.detail.item}" ${e.detail.position} "${e.detail.target}"`);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Serialize/deserialize
|
|
44
|
+
tree.toJSON(); // [{ label: "src", open: true, children: [...] }]
|
|
45
|
+
const copy = RyTree.from(tree.toJSON());
|
|
46
|
+
```
|
package/docs/theming.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# ry-ui Theming Guide
|
|
2
|
+
|
|
3
|
+
## Built-in Themes
|
|
4
|
+
|
|
5
|
+
Set on `<html>`:
|
|
6
|
+
```html
|
|
7
|
+
<html data-ry-theme="light"> <!-- light | dark | ocean -->
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Toggle at runtime:
|
|
11
|
+
```html
|
|
12
|
+
<ry-theme-toggle themes="light,dark"></ry-theme-toggle>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or programmatically:
|
|
16
|
+
```js
|
|
17
|
+
document.documentElement.setAttribute('data-ry-theme', 'dark');
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Dark Mode
|
|
21
|
+
|
|
22
|
+
Dark mode is built into the base bundle via `light-dark()` + `color-scheme`. No extra files needed.
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<!-- This is all you need — dark mode works automatically -->
|
|
26
|
+
<link rel="stylesheet" href="https://unpkg.com/@ryanhelsing/ry-ui/css/ry-ui.css">
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Without a `data-ry-theme` attribute, the OS preference is respected automatically. Setting `data-ry-theme="dark"` forces dark mode.
|
|
30
|
+
|
|
31
|
+
## Token Override (Quickest Customization)
|
|
32
|
+
|
|
33
|
+
Use the full bundle, override specific tokens:
|
|
34
|
+
|
|
35
|
+
```css
|
|
36
|
+
:root {
|
|
37
|
+
--ry-color-primary: oklch(0.541 0.218 293);
|
|
38
|
+
--ry-color-primary-hover: oklch(0.491 0.234 292);
|
|
39
|
+
--ry-radius-md: 0;
|
|
40
|
+
--ry-radius-lg: 0;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
> **Note:** Tokens use `oklch()` color values. Hex still works for overrides but won't participate in `light-dark()` automatic switching. For dark-aware overrides, use `light-dark()`:
|
|
45
|
+
> ```css
|
|
46
|
+
> :root {
|
|
47
|
+
> --ry-color-primary: light-dark(oklch(0.541 0.218 293), oklch(0.65 0.2 293));
|
|
48
|
+
> }
|
|
49
|
+
> ```
|
|
50
|
+
|
|
51
|
+
## Custom Theme (Full Control)
|
|
52
|
+
|
|
53
|
+
Load structure only, bring your own visuals:
|
|
54
|
+
|
|
55
|
+
```html
|
|
56
|
+
<link rel="stylesheet" href="https://unpkg.com/@ryanhelsing/ry-ui/css/ry-structure.css">
|
|
57
|
+
<link rel="stylesheet" href="/your-tokens.css">
|
|
58
|
+
<link rel="stylesheet" href="/your-theme.css">
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Structure CSS has layout only — no colors, shadows, borders. Your theme provides all visuals.
|
|
62
|
+
|
|
63
|
+
## Token Reference
|
|
64
|
+
|
|
65
|
+
### Colors
|
|
66
|
+
|
|
67
|
+
All color tokens use `oklch()` and `light-dark()` for automatic dark mode:
|
|
68
|
+
|
|
69
|
+
```css
|
|
70
|
+
--ry-color-primary: light-dark(oklch(0.623 0.188 259.8), oklch(0.714 0.143 254.6));
|
|
71
|
+
--ry-color-primary-hover: light-dark(oklch(0.546 0.215 262.9), oklch(0.623 0.188 259.8));
|
|
72
|
+
--ry-color-primary-active: light-dark(oklch(0.488 0.217 264.4), oklch(0.546 0.215 262.9));
|
|
73
|
+
--ry-color-secondary: light-dark(oklch(0.554 0.041 257.4), oklch(0.714 0.035 253.3));
|
|
74
|
+
--ry-color-success: light-dark(oklch(0.723 0.192 149.6), oklch(0.723 0.192 149.6));
|
|
75
|
+
--ry-color-warning: light-dark(oklch(0.769 0.165 70.1), oklch(0.769 0.165 70.1));
|
|
76
|
+
--ry-color-danger: light-dark(oklch(0.637 0.208 25.3), oklch(0.637 0.208 25.3));
|
|
77
|
+
--ry-color-info: light-dark(oklch(0.715 0.126 215.2), oklch(0.715 0.126 215.2));
|
|
78
|
+
--ry-color-text: light-dark(oklch(0.279 0.037 260), oklch(0.95 0.008 255));
|
|
79
|
+
--ry-color-text-muted: light-dark(oklch(0.554 0.041 257.4), oklch(0.714 0.035 253.3));
|
|
80
|
+
--ry-color-text-inverse: light-dark(oklch(1 0 0), oklch(0.279 0.037 260));
|
|
81
|
+
--ry-color-bg: light-dark(oklch(1 0 0), oklch(0.205 0.027 261));
|
|
82
|
+
--ry-color-bg-subtle: light-dark(oklch(0.984 0.003 248.2), oklch(0.249 0.03 261));
|
|
83
|
+
--ry-color-bg-muted: light-dark(oklch(0.968 0.007 248.1), oklch(0.279 0.033 261));
|
|
84
|
+
--ry-color-border: light-dark(oklch(0.929 0.013 255.6), oklch(0.372 0.039 257.3));
|
|
85
|
+
--ry-color-overlay: light-dark(oklch(0 0 0 / 0.5), oklch(0 0 0 / 0.7));
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Typography
|
|
89
|
+
```css
|
|
90
|
+
--ry-font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
91
|
+
--ry-font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
|
|
92
|
+
|
|
93
|
+
/* Sizes */
|
|
94
|
+
--ry-text-xs: 0.75rem;
|
|
95
|
+
--ry-text-sm: 0.875rem;
|
|
96
|
+
--ry-text-base: 1rem;
|
|
97
|
+
--ry-text-lg: 1.125rem;
|
|
98
|
+
--ry-text-xl: 1.25rem;
|
|
99
|
+
--ry-text-2xl: 1.5rem;
|
|
100
|
+
--ry-text-3xl: 1.875rem;
|
|
101
|
+
--ry-text-4xl: 2.25rem;
|
|
102
|
+
|
|
103
|
+
/* Weights */
|
|
104
|
+
--ry-font-normal: 400;
|
|
105
|
+
--ry-font-medium: 500;
|
|
106
|
+
--ry-font-semibold: 600;
|
|
107
|
+
--ry-font-bold: 700;
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Spacing
|
|
111
|
+
```css
|
|
112
|
+
--ry-space-0: 0;
|
|
113
|
+
--ry-space-1: 0.25rem;
|
|
114
|
+
--ry-space-2: 0.5rem;
|
|
115
|
+
--ry-space-3: 0.75rem;
|
|
116
|
+
--ry-space-4: 1rem;
|
|
117
|
+
--ry-space-5: 1.25rem;
|
|
118
|
+
--ry-space-6: 1.5rem;
|
|
119
|
+
--ry-space-8: 2rem;
|
|
120
|
+
--ry-space-10: 2.5rem;
|
|
121
|
+
--ry-space-12: 3rem;
|
|
122
|
+
--ry-space-16: 4rem;
|
|
123
|
+
--ry-space-20: 5rem;
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Border Radius
|
|
127
|
+
```css
|
|
128
|
+
--ry-radius-none: 0;
|
|
129
|
+
--ry-radius-sm: 0.25rem;
|
|
130
|
+
--ry-radius-md: 0.375rem;
|
|
131
|
+
--ry-radius-lg: 0.5rem;
|
|
132
|
+
--ry-radius-xl: 0.75rem;
|
|
133
|
+
--ry-radius-2xl: 1rem;
|
|
134
|
+
--ry-radius-full: 9999px;
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Shadows
|
|
138
|
+
|
|
139
|
+
Shadows use sub-tokens for dark-mode-aware intensity:
|
|
140
|
+
|
|
141
|
+
```css
|
|
142
|
+
--ry-shadow-color: light-dark(oklch(0 0 0 / 0.1), oklch(0 0 0 / 0.4));
|
|
143
|
+
--ry-shadow-sm: 0 1px 2px 0 var(--ry-shadow-color-sm);
|
|
144
|
+
--ry-shadow-md: 0 4px 6px -1px var(--ry-shadow-color), 0 2px 4px -2px var(--ry-shadow-color);
|
|
145
|
+
--ry-shadow-lg: 0 10px 15px -3px var(--ry-shadow-color), 0 4px 6px -4px var(--ry-shadow-color);
|
|
146
|
+
--ry-shadow-xl: 0 20px 25px -5px var(--ry-shadow-color), 0 8px 10px -6px var(--ry-shadow-color);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Transitions
|
|
150
|
+
```css
|
|
151
|
+
--ry-duration-fast: 100ms;
|
|
152
|
+
--ry-duration-normal: 200ms;
|
|
153
|
+
--ry-duration-slow: 300ms;
|
|
154
|
+
--ry-ease: cubic-bezier(0.4, 0, 0.2, 1);
|
|
155
|
+
--ry-ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
156
|
+
--ry-ease-out: cubic-bezier(0, 0, 0.2, 1);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Z-Index Layers
|
|
160
|
+
```css
|
|
161
|
+
--ry-z-dropdown: 1000;
|
|
162
|
+
--ry-z-sticky: 1020;
|
|
163
|
+
--ry-z-fixed: 1030;
|
|
164
|
+
--ry-z-modal-backdrop: 1040;
|
|
165
|
+
--ry-z-modal: 1050;
|
|
166
|
+
--ry-z-popover: 1060;
|
|
167
|
+
--ry-z-tooltip: 1070;
|
|
168
|
+
--ry-z-toast: 1080;
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Focus
|
|
172
|
+
```css
|
|
173
|
+
--ry-focus-ring: 0 0 0 3px light-dark(oklch(0.623 0.188 259.8 / 0.5), oklch(0.714 0.143 254.6 / 0.5));
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Best Practices
|
|
177
|
+
|
|
178
|
+
- Always use `--ry-*` tokens instead of hardcoding values
|
|
179
|
+
- Use `var(--ry-color-text)` and `var(--ry-color-bg)` on your own elements to stay theme-consistent
|
|
180
|
+
- Semantic colors (`success`, `warning`, `danger`, `info`) are available as both `--ry-color-*` tokens and component `variant`/`type`/`color` attributes
|
|
181
|
+
- For inline styles, reference tokens: `style="color: var(--ry-color-text-muted); padding: var(--ry-space-4);"`
|
|
182
|
+
- Set `background: var(--ry-color-bg); color: var(--ry-color-text);` on `<body>` for full-page theme support
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryanhelsing/ry-ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Framework-agnostic, Light DOM web components. CSS is the source of truth.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/ry-ui.js",
|
|
@@ -16,13 +16,15 @@
|
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"dist",
|
|
19
|
-
"
|
|
19
|
+
"AGENTS.md",
|
|
20
|
+
"docs/components",
|
|
21
|
+
"docs/theming.md"
|
|
20
22
|
],
|
|
21
23
|
"scripts": {
|
|
22
24
|
"dev": "vite",
|
|
23
25
|
"build": "vite build && npm run build:types && npm run build:css",
|
|
24
26
|
"build:types": "tsc --emitDeclarationOnly",
|
|
25
|
-
"build:css": "mkdir -p dist/css && cp src/css
|
|
27
|
+
"build:css": "mkdir -p dist/css && node -e \"const fs=require('fs'); const files=['src/css/ry-tokens.css','src/css/ry-structure.css','src/css/ry-theme.css']; const out=files.map(f=>fs.readFileSync(f,'utf8')).join('\\n'); fs.writeFileSync('dist/css/ry-ui.css',out);\" && cp src/css/ry-tokens.css src/css/ry-structure.css src/css/ry-theme.css dist/css/ && mkdir -p dist/themes && cp src/themes/*.css dist/themes/",
|
|
26
28
|
"preview": "vite preview",
|
|
27
29
|
"typecheck": "tsc --noEmit"
|
|
28
30
|
},
|