odt-ui-rails 2.2.0 → 2.2.1
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.
- checksums.yaml +4 -4
- data/README.md +308 -44
- data/app/assets/stylesheets/odt_ui/styles.css +420 -0
- data/app/javascript/controllers/odt_modal_controller.js +61 -0
- data/app/javascript/controllers/odt_toast_controller.js +40 -0
- data/lib/generators/odt/install_generator.rb +17 -0
- data/lib/generators/odt/templates/odt_modal_controller.js +61 -0
- data/lib/generators/odt/templates/odt_toast_controller.js +40 -0
- data/lib/odt_ui/helper.rb +265 -0
- data/lib/odt_ui/version.rb +1 -1
- data/sig/odt_ui.rbs +72 -0
- metadata +170 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c6a3351e3c566a02e0cc281f7c75d8ff5aee3b9c790b49a360907aeb5118ef67
|
|
4
|
+
data.tar.gz: c46e7e26fb322d840b5a569589d3c4d1d6ff4fa67e1215850aaff860d8ca82e0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 91490c4a98b07b40e122526c78b79baed1559f84a75edf07438a0cf1febe3b11026089a68ac76de7a86e15f1d1c9f79a5dc0bf7e69f53c79eee07867c669714d
|
|
7
|
+
data.tar.gz: 41c74c909825e0431a27e69775ec9d7e3c61576ee2e03f0c2fbf1d27219a9a139c2f662ea17a5b75a042702aad30bf65b807aff36bf2c5d0e7115633bdbd4e3a
|
data/README.md
CHANGED
|
@@ -1,36 +1,91 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# ODT UI for Ruby on Rails (`odt-ui-rails`)
|
|
4
|
+
|
|
5
|
+
**Standalone CSS-first design tokens, Hotwire-ready UI components, and ActionView helpers for Ruby on Rails.**
|
|
6
|
+
|
|
7
|
+
[](https://badge.fury.io/rb/odt-ui-rails)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://www.ruby-lang.org)
|
|
10
|
+
[](https://rubyonrails.org)
|
|
11
|
+
[](https://hotwired.dev)
|
|
12
|
+
[](sig/odt_ui.rbs)
|
|
13
|
+
|
|
14
|
+
</div>
|
|
4
15
|
|
|
5
16
|
---
|
|
6
17
|
|
|
7
|
-
##
|
|
18
|
+
## Overview
|
|
8
19
|
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
20
|
+
`odt-ui-rails` brings the **ODT Design System** directly into Ruby on Rails. It is architected from the ground up to provide a **Standalone CSS First** experience:
|
|
21
|
+
|
|
22
|
+
- 📦 **Zero Host Build Tools Required:** Ships pre-compiled CSS custom properties and `.odt-*` component styles. Works out-of-the-box with Propshaft, Sprockets, or no asset pipeline at all.
|
|
23
|
+
- ⚡ **Seamless Tailwind CSS v4 Interoperability:** Includes a complete `@theme` token configuration for Tailwind v4 projects, allowing custom utility classes (`class: "..."`) to merge effortlessly onto components.
|
|
24
|
+
- 🚀 **Hotwire & Turbo Native:** First-class helpers for Turbo Streams (`odt_toast_stream`), Flash message auto-toasts (`odt_flash_toasts`), and Stimulus-powered Modals (`odt_modal`).
|
|
25
|
+
- 💎 **Strict Type Signatures:** Full RBS definitions included for Ruby LSP, Steep, and Solargraph IDE autocompletion.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Table of Contents
|
|
30
|
+
|
|
31
|
+
- [Installation](#installation)
|
|
32
|
+
- [Asset Pipeline Configuration](#asset-pipeline-configuration)
|
|
33
|
+
- [Component Reference](#component-reference)
|
|
34
|
+
- [Feedback & Overlays](#feedback--overlays)
|
|
35
|
+
- [Toasts & Toaster](#toasts--toaster)
|
|
36
|
+
- [Modals](#modals)
|
|
37
|
+
- [Alerts](#alerts)
|
|
38
|
+
- [Actions & Indicators](#actions--indicators)
|
|
39
|
+
- [Buttons](#buttons)
|
|
40
|
+
- [Badges](#badges)
|
|
41
|
+
- [Form Controls](#form-controls)
|
|
42
|
+
- [Input Field](#input-field)
|
|
43
|
+
- [Textarea](#textarea)
|
|
44
|
+
- [Select Dropdown](#select-dropdown)
|
|
45
|
+
- [Checkbox & Radio](#checkbox--radio)
|
|
46
|
+
- [Form Group](#form-group)
|
|
47
|
+
- [Layout & Typography](#layout--typography)
|
|
48
|
+
- [Cards](#cards)
|
|
49
|
+
- [Typography (Heading & Text)](#typography-heading--text)
|
|
50
|
+
- [Avatars](#avatars)
|
|
51
|
+
- [Theming & Dark Mode](#theming--dark-mode)
|
|
52
|
+
- [License](#license)
|
|
13
53
|
|
|
14
54
|
---
|
|
15
55
|
|
|
16
56
|
## Installation
|
|
17
57
|
|
|
18
|
-
Add to your `Gemfile`:
|
|
58
|
+
Add the gem to your application's `Gemfile`:
|
|
19
59
|
|
|
20
60
|
```ruby
|
|
21
|
-
gem "odt-ui-rails", "~> 2.
|
|
61
|
+
gem "odt-ui-rails", "~> 2.2.0"
|
|
22
62
|
```
|
|
23
63
|
|
|
24
|
-
|
|
64
|
+
Install dependencies:
|
|
25
65
|
|
|
26
66
|
```bash
|
|
27
67
|
bundle install
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Run the automated install generator:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
28
73
|
bin/rails g odt:install
|
|
29
74
|
```
|
|
30
75
|
|
|
31
|
-
|
|
76
|
+
The generator will:
|
|
32
77
|
|
|
33
|
-
|
|
78
|
+
1. Detect your asset pipeline (Tailwind CSS v4, Propshaft, or Sprockets) and safely configure the required `@import` statements.
|
|
79
|
+
2. Copy `sig/odt_ui.rbs` for Ruby LSP IDE autocompletion.
|
|
80
|
+
3. Copy Stimulus controllers (`odt_toast_controller.js`, `odt_modal_controller.js`) to `app/javascript/controllers/` if Stimulus is present.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Asset Pipeline Configuration
|
|
85
|
+
|
|
86
|
+
If configuring stylesheets manually, use the configuration below:
|
|
87
|
+
|
|
88
|
+
### Option A: Tailwind CSS v4 (`app/assets/tailwind/application.css`)
|
|
34
89
|
|
|
35
90
|
```css
|
|
36
91
|
@import "tailwindcss";
|
|
@@ -39,7 +94,7 @@ bin/rails g odt:install
|
|
|
39
94
|
@import "odt_ui/theme.css";
|
|
40
95
|
```
|
|
41
96
|
|
|
42
|
-
|
|
97
|
+
### Option B: Propshaft / Sprockets / Standard Rails (`app/assets/stylesheets/application.css`)
|
|
43
98
|
|
|
44
99
|
```css
|
|
45
100
|
@import "odt_ui/tokens.css";
|
|
@@ -48,68 +103,277 @@ bin/rails g odt:install
|
|
|
48
103
|
|
|
49
104
|
---
|
|
50
105
|
|
|
51
|
-
##
|
|
106
|
+
## Component Reference
|
|
52
107
|
|
|
53
|
-
|
|
108
|
+
All helpers are globally available in all ERB templates, layouts, and helpers.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
### Feedback & Overlays
|
|
113
|
+
|
|
114
|
+
#### Toasts & Toaster
|
|
115
|
+
|
|
116
|
+
Provides viewport stacking containers, automated Flash message toasts on redirects, and Turbo Stream responses for async form actions.
|
|
54
117
|
|
|
55
118
|
```erb
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
<%=
|
|
59
|
-
<%=
|
|
60
|
-
|
|
61
|
-
|
|
119
|
+
<%# In app/views/layouts/application.html.erb %>
|
|
120
|
+
<body class="bg-surface text-fg font-sans antialiased">
|
|
121
|
+
<%= odt_toaster position: :top_right do %>
|
|
122
|
+
<%= odt_flash_toasts %>
|
|
123
|
+
<% end %>
|
|
124
|
+
|
|
125
|
+
<%= yield %>
|
|
126
|
+
</body>
|
|
62
127
|
```
|
|
63
128
|
|
|
64
|
-
|
|
129
|
+
**Controller Turbo Stream Action:**
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
def create
|
|
133
|
+
@todo = Todo.new(todo_params)
|
|
134
|
+
if @todo.save
|
|
135
|
+
respond_to do |format|
|
|
136
|
+
format.turbo_stream do
|
|
137
|
+
render turbo_stream: [
|
|
138
|
+
turbo_stream.prepend("todos", @todo),
|
|
139
|
+
odt_toast_stream("Todo created successfully!", variant: :success)
|
|
140
|
+
]
|
|
141
|
+
end
|
|
142
|
+
format.html { redirect_to todos_path, notice: "Todo created successfully!" }
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Individual Toast Helper:**
|
|
65
149
|
|
|
66
150
|
```erb
|
|
67
|
-
<%=
|
|
68
|
-
<%=
|
|
69
|
-
<%= odt_badge "Date", icon: "fa-regular fa-calendar" %>
|
|
151
|
+
<%= odt_toast "Settings updated", title: "Saved", variant: :success, duration: 4000 %>
|
|
152
|
+
<%= odt_toast "Failed to sync records", variant: :danger %>
|
|
70
153
|
```
|
|
71
154
|
|
|
72
|
-
|
|
155
|
+
| Parameter | Type | Default | Description |
|
|
156
|
+
| :------------ | :-------- | :--------- | :--------------------------------------------------------------------------- |
|
|
157
|
+
| `message` | `String` | `nil` | Toast description or body text |
|
|
158
|
+
| `title` | `String` | `nil` | Optional bold header title |
|
|
159
|
+
| `variant` | `Symbol` | `:success` | `:success`, `:warning`, `:danger`, `:error`, `:info`, `:loading`, `:frosted` |
|
|
160
|
+
| `duration` | `Integer` | `4000` | Auto-dismiss duration in ms (`0` to disable) |
|
|
161
|
+
| `dismissible` | `Boolean` | `true` | Renders close button (`×`) |
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
#### Modals
|
|
166
|
+
|
|
167
|
+
Accessible dialog containers with backdrop overlay, ESC key handling, scroll locking, and smooth scale transitions.
|
|
73
168
|
|
|
74
169
|
```erb
|
|
75
|
-
|
|
76
|
-
|
|
170
|
+
<%# Trigger Button %>
|
|
171
|
+
<%= odt_button "New Task", color: :primary, data: { action: "click->odt-modal#open" } %>
|
|
172
|
+
|
|
173
|
+
<%# Standard Modal %>
|
|
174
|
+
<%= odt_modal id: "new_task_modal", title: "Create Task", subtitle: "Add a task to your board", size: :md do %>
|
|
175
|
+
<%= render "form", todo: Todo.new %>
|
|
77
176
|
<% end %>
|
|
78
177
|
|
|
79
|
-
|
|
80
|
-
|
|
178
|
+
<%# Custom Composition Modal %>
|
|
179
|
+
<%= odt_modal id: "confirm_delete_modal", size: :sm do %>
|
|
180
|
+
<%= odt_modal_header title: "Delete Item?", subtitle: "This action cannot be undone." %>
|
|
181
|
+
<%= odt_modal_body do %>
|
|
182
|
+
<p class="text-sm text-fg-muted">Are you sure you want to permanently remove this record?</p>
|
|
183
|
+
<% end %>
|
|
184
|
+
<%= odt_modal_footer do %>
|
|
185
|
+
<%= odt_button "Cancel", variant: :ghost, data: { action: "click->odt-modal#close" } %>
|
|
186
|
+
<%= odt_button "Delete", color: :danger, data: { turbo_method: :delete } %>
|
|
187
|
+
<% end %>
|
|
81
188
|
<% end %>
|
|
82
189
|
```
|
|
83
190
|
|
|
84
|
-
|
|
191
|
+
| Parameter | Type | Default | Description |
|
|
192
|
+
| :----------------------- | :-------- | :------- | :------------------------------------------- |
|
|
193
|
+
| `id` | `String` | `nil` | HTML identifier for Stimulus/Turbo targeting |
|
|
194
|
+
| `title` / `subtitle` | `String` | `nil` | Header titles |
|
|
195
|
+
| `size` | `Symbol` | `:md` | `:sm`, `:md`, `:lg`, `:xl`, `:full` |
|
|
196
|
+
| `radius` | `Symbol` | `:"2xl"` | `:none` to `:"4xl"`, `:full` |
|
|
197
|
+
| `dismissible` | `Boolean` | `true` | Renders close button |
|
|
198
|
+
| `open` | `Boolean` | `false` | Initial visible state |
|
|
199
|
+
| `prevent_backdrop_close` | `Boolean` | `false` | Disables closing on overlay click |
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
#### Alerts
|
|
204
|
+
|
|
205
|
+
Inline banners for status notices, warning messages, and form callouts.
|
|
85
206
|
|
|
86
207
|
```erb
|
|
87
|
-
<%= odt_alert "
|
|
88
|
-
<%= odt_alert "Please
|
|
208
|
+
<%= odt_alert "Your subscription will renew on May 1st.", variant: :info %>
|
|
209
|
+
<%= odt_alert "Payment failed. Please update billing info.", variant: :danger, icon: "fa-solid fa-triangle-exclamation" %>
|
|
89
210
|
```
|
|
90
211
|
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
### Actions & Indicators
|
|
215
|
+
|
|
216
|
+
#### Buttons
|
|
217
|
+
|
|
218
|
+
Versatile action buttons supporting brand colors, frosted glass surfaces, loading states, and icon decorations.
|
|
219
|
+
|
|
220
|
+
```erb
|
|
221
|
+
<%# Standard Brand Action %>
|
|
222
|
+
<%= odt_button "Save Changes", color: :primary, variant: :filled, radius: :full %>
|
|
223
|
+
|
|
224
|
+
<%# Secondary & Frosted Glass %>
|
|
225
|
+
<%= odt_button "Secondary", variant: :subtle, color: :secondary %>
|
|
226
|
+
<%= odt_button "Frosted Glass", variant: :frosted, color: :primary %>
|
|
227
|
+
|
|
228
|
+
<%# Link as Button %>
|
|
229
|
+
<%= odt_button "View Project", href: project_path(@project), variant: :subtle %>
|
|
230
|
+
|
|
231
|
+
<%# Loading & Icons %>
|
|
232
|
+
<%= odt_button "Add Task", icon: "fa-solid fa-plus", color: :primary %>
|
|
233
|
+
<%= odt_button "Saving...", loading: true %>
|
|
234
|
+
<%= odt_button icon: "fa-solid fa-trash", icon_only: true, color: :danger, variant: :ghost %>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
| Option | Type | Default | Description |
|
|
238
|
+
| :-------------------- | :-------- | :--------- | :----------------------------------------------------------------------------------------------------------------- |
|
|
239
|
+
| `variant` | `Symbol` | `:filled` | `:filled`, `:subtle`, `:ghost`, `:outline`, `:capsule`, `:frosted`, `:light` |
|
|
240
|
+
| `color` | `Symbol` | `:primary` | `:primary`, `:secondary`, `:surface`, `:muted`, `:inverse`, `:success`, `:warning`, `:danger`, `:info`, `:neutral` |
|
|
241
|
+
| `size` | `Symbol` | `:md` | `:xs`, `:sm`, `:md`, `:lg`, `:xl` |
|
|
242
|
+
| `radius` | `Symbol` | `:"2xl"` | `:none`, `:xs`, `:sm`, `:md`, `:lg`, `:xl`, `:"2xl"`, `:"3xl"`, `:"4xl"`, `:full` |
|
|
243
|
+
| `icon` / `right_icon` | `String` | `nil` | FontAwesome class or inline SVG string |
|
|
244
|
+
| `icon_only` | `Boolean` | `false` | Square icon button formatting |
|
|
245
|
+
| `loading` | `Boolean` | `false` | Replaces icon with spinning loader |
|
|
246
|
+
| `disabled` | `Boolean` | `false` | HTML disabled state |
|
|
247
|
+
| `full_width` | `Boolean` | `false` | Expands width to 100% |
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
#### Badges
|
|
252
|
+
|
|
253
|
+
Compact status indicators with optional indicator dots and icons.
|
|
254
|
+
|
|
255
|
+
```erb
|
|
256
|
+
<%= odt_badge "Active", variant: :subtle, color: :success, dot: true %>
|
|
257
|
+
<%= odt_badge "High Priority", variant: :filled, color: :danger, radius: :full %>
|
|
258
|
+
<%= odt_badge "Calendar", icon: "fa-regular fa-calendar", variant: :outlined %>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
91
263
|
### Form Controls
|
|
92
264
|
|
|
265
|
+
Accessible form components complete with labels, helper text, error validations, icons, and input adornments.
|
|
266
|
+
|
|
93
267
|
```erb
|
|
94
|
-
<%=
|
|
268
|
+
<%= form_with model: @user, class: "space-y-4" do |f| %>
|
|
269
|
+
<%# Text Input with Left Icon %>
|
|
270
|
+
<%= odt_input(
|
|
271
|
+
label: "Email Address",
|
|
272
|
+
name: "user[email]",
|
|
273
|
+
value: @user.email,
|
|
274
|
+
type: "email",
|
|
275
|
+
placeholder: "you@example.com",
|
|
276
|
+
left_icon: "fa-solid fa-envelope",
|
|
277
|
+
error: @user.errors[:email].first,
|
|
278
|
+
required: true
|
|
279
|
+
) %>
|
|
280
|
+
|
|
281
|
+
<%# Textarea %>
|
|
282
|
+
<%= odt_textarea(
|
|
283
|
+
label: "Bio",
|
|
284
|
+
name: "user[bio]",
|
|
285
|
+
value: @user.bio,
|
|
286
|
+
rows: 4,
|
|
287
|
+
helper: "Brief summary for your profile"
|
|
288
|
+
) %>
|
|
289
|
+
|
|
290
|
+
<%# Select Dropdown %>
|
|
291
|
+
<%= odt_select(
|
|
292
|
+
label: "Country",
|
|
293
|
+
name: "user[country]",
|
|
294
|
+
options: [["Thailand", "TH"], ["United States", "US"], ["Japan", "JP"]],
|
|
295
|
+
selected: @user.country,
|
|
296
|
+
prompt: "Select country"
|
|
297
|
+
) %>
|
|
298
|
+
|
|
299
|
+
<%# Checkbox %>
|
|
300
|
+
<%= odt_checkbox(
|
|
301
|
+
label: "Subscribe to newsletter",
|
|
302
|
+
description: "Receive product updates once a week",
|
|
303
|
+
name: "user[subscribed]",
|
|
304
|
+
checked: true,
|
|
305
|
+
color: :primary
|
|
306
|
+
) %>
|
|
307
|
+
|
|
308
|
+
<%# Radio Group %>
|
|
309
|
+
<%= odt_radio label: "Monthly Billing", name: "plan", value: "monthly", checked: true %>
|
|
310
|
+
<%= odt_radio label: "Annual Billing (Save 20%)", name: "plan", value: "annual" %>
|
|
311
|
+
|
|
312
|
+
<%# Custom Slot Form Group %>
|
|
313
|
+
<%= odt_form_group label: "Custom Input", helper: "Unique value required" do %>
|
|
314
|
+
<input type="text" class="odt-input odt-input--outlined odt-input--md" name="custom" />
|
|
315
|
+
<% end %>
|
|
316
|
+
<% end %>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
95
320
|
|
|
96
|
-
|
|
321
|
+
### Layout & Typography
|
|
97
322
|
|
|
98
|
-
|
|
323
|
+
#### Cards
|
|
99
324
|
|
|
100
|
-
|
|
325
|
+
Versatile containers supporting header metadata, multiple surface styles, and hover transitions.
|
|
101
326
|
|
|
102
|
-
|
|
327
|
+
```erb
|
|
328
|
+
<%= odt_card title: "Analytics Overview", subtitle: "Monthly performance", variant: :elevated, radius: :"3xl" do %>
|
|
329
|
+
<p class="text-sm text-fg">Dashboard charts and statistics...</p>
|
|
330
|
+
<% end %>
|
|
103
331
|
|
|
104
|
-
<%=
|
|
105
|
-
<
|
|
332
|
+
<%= odt_card variant: :frosted, radius: :"2xl", hoverable: true do %>
|
|
333
|
+
<span>Interactive frosted glass card</span>
|
|
106
334
|
<% end %>
|
|
107
335
|
```
|
|
108
336
|
|
|
109
|
-
|
|
337
|
+
#### Typography (`odt_heading` & `odt_text`)
|
|
338
|
+
|
|
339
|
+
Standardized typographic elements mapped to the design system scale.
|
|
110
340
|
|
|
111
341
|
```erb
|
|
112
|
-
<%= odt_heading "Dashboard", as: :h1, size: :"4xl" %>
|
|
113
|
-
<%=
|
|
114
|
-
|
|
342
|
+
<%= odt_heading "Task Dashboard", as: :h1, size: :"4xl", weight: :bold, color: :strong %>
|
|
343
|
+
<%= odt_heading "Section Title", as: :h3, size: :xl %>
|
|
344
|
+
|
|
345
|
+
<%= odt_text "Manage all project deliverables in one workspace.", size: :sm, color: :muted %>
|
|
346
|
+
<%= odt_text "A very long single-line description...", truncate: true %>
|
|
115
347
|
```
|
|
348
|
+
|
|
349
|
+
#### Avatars
|
|
350
|
+
|
|
351
|
+
Renders image avatars or automatically computes two-letter initials from full names.
|
|
352
|
+
|
|
353
|
+
```erb
|
|
354
|
+
<%# Generates initials avatar "JD" %>
|
|
355
|
+
<%= odt_avatar name: "John Doe", size: :lg, color: :secondary %>
|
|
356
|
+
|
|
357
|
+
<%# Image avatar with fallback %>
|
|
358
|
+
<%= odt_avatar src: "https://avatar.vercel.sh/pixel", size: :md %>
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Theming & Dark Mode
|
|
364
|
+
|
|
365
|
+
The design system is fully dark mode ready. Apply `data-theme="dark"` or the `.dark` class to `<html>` or any sub-container:
|
|
366
|
+
|
|
367
|
+
```html
|
|
368
|
+
<html data-theme="dark" class="dark">
|
|
369
|
+
...
|
|
370
|
+
</html>
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
All design tokens (backgrounds, surfaces, borders, text, and shadows) adapt automatically without custom CSS.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## License
|
|
378
|
+
|
|
379
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE).
|