Theme editor
Edit hydroxy's design tokens live on top of a preset: colours, corner radii, spacing scale and fonts, with a scoped preview, WCAG contrast warnings and CSS or JSON export.
Colours
Corners
Spacing
Type
Invoice INV-2041
Northwind Traders · due 14 November · View PDF
PaidPartialOverdueDraft
Shown on the customer's statement
Card expires soon
The card on file expires at the end of the month.
£1,240.001 pair is below WCAG AA contrast.
- Muted text on Card is 3.6:1, needs 4.5:1
css
[data-theme="custom"] {
color-scheme: light;
--surface-page: #f5f3ef;
--surface-card: #ffffff;
--surface-sunken: #ebe9e5;
--surface-raised: #ffffff;
--text-1: #0b1229;
--text-2: #3f475d;
--text-3: #7f8698;
--text-link: #7059c2;
--border-1: #e0e4ef;
--border-2: #c5cad7;
--accent: #644fb1;
--action-primary-bg: #0b1229;
--action-primary-fg: #f5f3ef;
--action-accent-bg: #644fb1;
--action-accent-fg: #ffffff;
--status-success: #09684b;
--status-warning: #553e02;
--status-danger: #9a2929;
--status-info: #025c93;
--radius-xs: 3px;
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 16px;
--radius-xl: 24px;
--font-display: 'Unbounded', 'Hanken Grotesk', system-ui, sans-serif;
--font-body: 'Hanken Grotesk', system-ui, -apple-system, sans-serif;
--font-mono: 'JetBrains Mono', ui-monospace, 'SFMono-Regular', monospace;
--space-1: 2px;
--space-2: 4px;
--space-3: 8px;
--space-4: 12px;
--space-5: 16px;
--space-6: 20px;
--space-7: 24px;
--space-8: 32px;
--space-9: 40px;
--space-10: 48px;
--space-11: 64px;
--space-12: 80px;
--space-13: 96px;
--space-14: 128px;
}Usage
use hydroxy_leptos::theme_editor::ThemeEditor;
use leptos::prelude::*;
pub fn demo() -> AnyView {
let (edited, set_edited) = signal(0usize);
view! {
<div style="display: grid; gap: var(--space-5);">
// Start from a preset, edit tokens, watch the preview and contrast checks, then export.
<ThemeEditor default_preset="light" on_change=move |_tokens| set_edited.update(|n| *n += 1) />
<span style="font: var(--text-sm); color: var(--text-2);">{move || format!("{} edits", edited.get())}</span>
</div>
}
.into_any()
}use dioxus::prelude::*;
use hydroxy_dioxus::theme_editor::ThemeEditor;
#[component]
pub fn ThemeEditorDemo() -> Element {
let mut edited = use_signal(|| 0usize);
rsx! {
div { style: "display: grid; gap: var(--space-5);",
// Start from a preset, edit tokens, watch the preview and contrast checks, then export.
ThemeEditor { default_preset: "light", on_change: move |_tokens| edited += 1 }
span { style: "font: var(--text-sm); color: var(--text-2);", "{edited} edits" }
}
}
}
Anatomy
| Component | data-part | Renders |
|---|---|---|
ThemeEditor | root | role=group, the controls beside the output |
ThemeEditor | controls / toolbar / section | Preset select, Reset all, then Colours, Corners, Spacing and Type |
ThemeEditor | row | One token. data-modified when it differs from the preset |
ThemeEditor | swatch / name / value | Inside the colour row's popover trigger; the popover holds a ColorPicker |
ThemeEditor | warning | Contrast ratio when the token fails a check it is the text of |
ThemeEditor | reset | Puts one token back to the preset. Disabled when unchanged |
ThemeEditor | preview | role=region with the preset's data-theme and the edited tokens inline, so edits stay inside it |
ThemeEditor | contrast / live / issues | role=status summary of WCAG AA checks, then the failing pairs |
ThemeEditor | export | CSS or JSON choice and a code block with a copy button |
Keyboard
| Key | Does |
|---|---|
Enter / Space (colour row) | Open the colour picker for that token |
Escape (picker) | Close it and return to the row |
Arrows (sliders) | Change a radius or the spacing scale |
Tab | Move between tokens and their reset buttons |
Props
| Prop | Type | Notes |
|---|---|---|
default_preset | String | Preset to start from, as a data-theme value. Default light |
on_change | Callback<Vec<(String, String)>> | Every token (without --) and its value after each change |