opui-css 5.0.0 → 5.1.0

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.
Files changed (114) hide show
  1. package/CHANGELOG.md +317 -276
  2. package/LICENSE +21 -21
  3. package/MIGRATING.md +138 -127
  4. package/README.md +87 -87
  5. package/astro/index.ts +46 -46
  6. package/components/AGENTS.md +248 -248
  7. package/components/Accordion/Accordion.astro +52 -42
  8. package/components/Anchor/Anchor.astro +55 -51
  9. package/components/Avatar/Avatar.astro +38 -38
  10. package/components/Badge/Badge.astro +59 -59
  11. package/components/Button/Button.astro +45 -45
  12. package/components/ButtonGroup/ButtonGroup.astro +36 -29
  13. package/components/Callout/Callout.astro +75 -75
  14. package/components/Card/Card.astro +38 -36
  15. package/components/Checkbox/Checkbox.astro +76 -76
  16. package/components/Checkbox/CheckboxInput.astro +32 -32
  17. package/components/Chip/Chip.astro +37 -37
  18. package/components/ClassicSelect/ClassicSelect.astro +83 -83
  19. package/components/DescriptionList/Description.astro +12 -12
  20. package/components/DescriptionList/DescriptionList.astro +22 -22
  21. package/components/DescriptionList/Item.astro +12 -12
  22. package/components/DescriptionList/Term.astro +12 -12
  23. package/components/DescriptionList/index.ts +12 -12
  24. package/components/Dialog/Dialog.astro +43 -41
  25. package/components/Divider/Divider.astro +17 -17
  26. package/components/Drawer/Drawer.astro +49 -49
  27. package/components/Drawer/DrawerFooter.astro +14 -14
  28. package/components/Drawer/DrawerHeader.astro +31 -31
  29. package/components/FieldDescription/FieldDescription.astro +13 -13
  30. package/components/FieldGroup/FieldGroup.astro +31 -31
  31. package/components/FieldLegend/FieldLegend.astro +15 -15
  32. package/components/FieldSet/FieldSet.astro +15 -15
  33. package/components/Form/Form.astro +15 -15
  34. package/components/IconButton/IconButton.astro +26 -26
  35. package/components/List/List.astro +36 -36
  36. package/components/ListItem/ListItem.astro +118 -118
  37. package/components/Progress/Progress.astro +41 -40
  38. package/components/Radio/Radio.astro +70 -70
  39. package/components/Radio/RadioInput.astro +24 -24
  40. package/components/Range/Range.astro +136 -136
  41. package/components/Select/Select.astro +106 -106
  42. package/components/Switch/Switch.astro +101 -101
  43. package/components/Switch/SwitchInput.astro +25 -25
  44. package/components/Table/Body.astro +13 -13
  45. package/components/Table/Cell.astro +13 -13
  46. package/components/Table/Column.astro +15 -15
  47. package/components/Table/ColumnGroup.astro +13 -13
  48. package/components/Table/Head.astro +13 -13
  49. package/components/Table/HeaderCell.astro +13 -13
  50. package/components/Table/Row.astro +13 -13
  51. package/components/Table/Table.astro +18 -18
  52. package/components/Table/index.ts +20 -20
  53. package/components/Tabs/Tabs.astro +28 -28
  54. package/components/Tabs/TabsItem.astro +47 -47
  55. package/components/Tabs/TabsPanel.astro +21 -21
  56. package/components/Tabs/TabsTab.astro +19 -19
  57. package/components/Tabs/index.ts +14 -14
  58. package/components/TextField/TextField.astro +128 -128
  59. package/components/Textarea/Textarea.astro +106 -106
  60. package/components/Toast/Toast.astro +42 -42
  61. package/components/ToggleButton/ToggleButton.astro +53 -53
  62. package/components/ToggleGroup/ToggleGroup.astro +49 -49
  63. package/components/Tooltip/Tooltip.astro +30 -30
  64. package/core/normalize.css +200 -194
  65. package/core/palette.css +110 -59
  66. package/core/utils.css +71 -71
  67. package/css/components/accordion.css +174 -174
  68. package/css/components/anchor.css +27 -27
  69. package/css/components/avatar.css +65 -65
  70. package/css/components/badge.css +107 -107
  71. package/css/components/button-group.css +193 -179
  72. package/css/components/button.css +227 -215
  73. package/css/components/callout.css +112 -109
  74. package/css/components/card.css +115 -117
  75. package/css/components/checkbox.css +211 -205
  76. package/css/components/chip.css +92 -87
  77. package/css/components/description-list.css +81 -80
  78. package/css/components/dialog.css +69 -68
  79. package/css/components/divider.css +20 -20
  80. package/css/components/drawer.css +171 -171
  81. package/css/components/form.css +147 -146
  82. package/css/components/icon-button.css +158 -139
  83. package/css/components/link.css +21 -21
  84. package/css/components/list.css +343 -345
  85. package/css/components/progress.css +103 -97
  86. package/css/components/radio.css +170 -172
  87. package/css/components/range.css +342 -337
  88. package/css/components/select.css +172 -169
  89. package/css/components/spinner.css +38 -37
  90. package/css/components/switch.css +271 -268
  91. package/css/components/table.css +173 -179
  92. package/css/components/tabs.css +84 -83
  93. package/css/components/text-field.css +449 -450
  94. package/css/components/text-input.css +68 -64
  95. package/css/components/textarea.css +26 -24
  96. package/css/components/toast.css +160 -161
  97. package/css/components/toggle-button.css +91 -87
  98. package/css/components/toggle-group.css +56 -56
  99. package/css/components/tooltip.css +70 -67
  100. package/css/components/typography.css +585 -585
  101. package/css/components.css +41 -41
  102. package/css/imports.css +17 -16
  103. package/css/js/checkbox.js +21 -21
  104. package/css/js/toast.js +94 -94
  105. package/css/theme.css +133 -132
  106. package/dist/op.css +4 -66
  107. package/dist/op.css.map +1 -1
  108. package/dist/opui.components.css +5234 -5154
  109. package/dist/opui.components.css.map +1 -1
  110. package/dist/opui.css +5798 -5663
  111. package/dist/opui.css.map +1 -1
  112. package/env.d.ts +14 -14
  113. package/open-props.css +8 -9
  114. package/package.json +81 -81
@@ -1,41 +1,41 @@
1
- /*
2
- * Components are divided into two categories - if they are stand-alone (root) or if they are built on top of others (extended).
3
- */
4
-
5
- /*** Root components (no dependencies) */
6
- @import "./components/button.css";
7
- @import "./components/callout.css";
8
- @import "./components/icon-button.css";
9
- @import "./components/tabs.css";
10
- @import "./components/toggle-group.css";
11
- @import "./components/toggle-button.css";
12
- @import "./components/anchor.css";
13
- @import "./components/avatar.css";
14
- @import "./components/card.css";
15
- @import "./components/chip.css";
16
- @import "./components/description-list.css";
17
- @import "./components/divider.css";
18
- @import "./components/link.css";
19
- @import "./components/table.css";
20
- @import "./components/progress.css";
21
- @import "./components/spinner.css";
22
- @import "./components/checkbox.css";
23
- @import "./components/radio.css";
24
- @import "./components/range.css";
25
- @import "./components/switch.css";
26
- @import "./components/toast.css";
27
- @import "./components/typography.css";
28
-
29
- /*** Extended components (has dependencies) */
30
- @import "./components/badge.css";
31
- @import "./components/button-group.css";
32
- @import "./components/accordion.css";
33
- @import "./components/list.css";
34
- @import "./components/dialog.css";
35
- @import "./components/drawer.css";
36
- @import "./components/form.css";
37
- @import "./components/text-field.css";
38
- @import "./components/select.css";
39
- @import "./components/text-input.css";
40
- @import "./components/textarea.css";
41
- @import "./components/tooltip.css";
1
+ /*
2
+ * Components are divided into two categories - if they are stand-alone (root) or if they are built on top of others (extended).
3
+ */
4
+
5
+ /*** Root components (no dependencies) */
6
+ @import "./components/button.css";
7
+ @import "./components/callout.css";
8
+ @import "./components/icon-button.css";
9
+ @import "./components/tabs.css";
10
+ @import "./components/toggle-group.css";
11
+ @import "./components/toggle-button.css";
12
+ @import "./components/anchor.css";
13
+ @import "./components/avatar.css";
14
+ @import "./components/card.css";
15
+ @import "./components/chip.css";
16
+ @import "./components/description-list.css";
17
+ @import "./components/divider.css";
18
+ @import "./components/link.css";
19
+ @import "./components/table.css";
20
+ @import "./components/progress.css";
21
+ @import "./components/spinner.css";
22
+ @import "./components/checkbox.css";
23
+ @import "./components/radio.css";
24
+ @import "./components/range.css";
25
+ @import "./components/switch.css";
26
+ @import "./components/toast.css";
27
+ @import "./components/typography.css";
28
+
29
+ /*** Extended components (has dependencies) */
30
+ @import "./components/badge.css";
31
+ @import "./components/button-group.css";
32
+ @import "./components/accordion.css";
33
+ @import "./components/list.css";
34
+ @import "./components/dialog.css";
35
+ @import "./components/drawer.css";
36
+ @import "./components/form.css";
37
+ @import "./components/text-field.css";
38
+ @import "./components/select.css";
39
+ @import "./components/text-input.css";
40
+ @import "./components/textarea.css";
41
+ @import "./components/tooltip.css";
package/css/imports.css CHANGED
@@ -1,16 +1,17 @@
1
- @layer openprops, normalize, theme, components.root, components.extended, utils;
2
-
3
- /* Open Props */
4
- @import "../open-props.css";
5
-
6
- /* Normalize */
7
- @import "../core/normalize.css";
8
-
9
- /* Theme */
10
- @import "./theme.css";
11
-
12
- /* Components */
13
- @import "./components.css";
14
-
15
- /* Utils */
16
- @import "../core/utils.css";
1
+ @layer openprops, theme, normalize, components.root, components.extended, utils;
2
+
3
+ /* Open Props */
4
+ @import "../open-props.css";
5
+
6
+ /* Theme */
7
+ @import "../core/palette.css";
8
+ @import "./theme.css";
9
+
10
+ /* Normalize */
11
+ @import "../core/normalize.css";
12
+
13
+ /* Components */
14
+ @import "./components.css";
15
+
16
+ /* Utils */
17
+ @import "../core/utils.css";
@@ -1,21 +1,21 @@
1
- /**
2
- * Checkbox runtime helpers.
3
- *
4
- * `indeterminate` is a JavaScript-only property on HTMLInputElement — the
5
- * attribute alone has no effect. We mark inputs with `data-indeterminate` at
6
- * render time and apply the property here, both on initial load and after
7
- * Astro's view transitions swap the DOM.
8
- */
9
-
10
- export function activateIndeterminate(root = document) {
11
- root
12
- .querySelectorAll('input[type="checkbox"][data-indeterminate]')
13
- .forEach((el) => {
14
- el.indeterminate = true
15
- })
16
- }
17
-
18
- export function initCheckbox() {
19
- activateIndeterminate()
20
- document.addEventListener("astro:after-swap", () => activateIndeterminate())
21
- }
1
+ /**
2
+ * Checkbox runtime helpers.
3
+ *
4
+ * `indeterminate` is a JavaScript-only property on HTMLInputElement — the
5
+ * attribute alone has no effect. We mark inputs with `data-indeterminate` at
6
+ * render time and apply the property here, both on initial load and after
7
+ * Astro's view transitions swap the DOM.
8
+ */
9
+
10
+ export function activateIndeterminate(root = document) {
11
+ root
12
+ .querySelectorAll('input[type="checkbox"][data-indeterminate]')
13
+ .forEach((el) => {
14
+ el.indeterminate = true
15
+ })
16
+ }
17
+
18
+ export function initCheckbox() {
19
+ activateIndeterminate()
20
+ document.addEventListener("astro:after-swap", () => activateIndeterminate())
21
+ }
package/css/js/toast.js CHANGED
@@ -1,94 +1,94 @@
1
- /**
2
- * Toast manager.
3
- *
4
- * - HTML owns structure (via <template id="toast-template">).
5
- * - CSS owns lifetime (via attr(data-duration type(<time>))).
6
- * - JS owns lifecycle (clone template, fill slots, append, remove on animationend).
7
- *
8
- * No setTimeout. No popovertargetaction. No innerHTML of user data.
9
- */
10
-
11
- const FALLBACK_TEMPLATE = `<div class="ui-toast" role="alert">
12
- <span class="ui-icon" data-toast-icon></span>
13
- <div class="ui-content">
14
- <div class="ui-title" data-toast-title></div>
15
- <div class="ui-description" data-toast-description></div>
16
- </div>
17
- <button class="ui-close-button" data-toast-close type="button" aria-label="Close">
18
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
19
- </button>
20
- </div>`
21
-
22
- export function initToastManager() {
23
- const manager = document.getElementById("toast-manager")
24
- if (!manager) return
25
-
26
- try {
27
- manager.showPopover()
28
- } catch {
29
- /* already open or unsupported */
30
- }
31
-
32
- manager.addEventListener("command", (event) => {
33
- if (event.command !== "--show-toast") return
34
- const trigger = event.source
35
- if (!trigger) return
36
-
37
- const data = trigger.dataset || {}
38
- showToast(
39
- {
40
- description: data.description,
41
- duration: data.duration,
42
- severity: data.severity,
43
- template: data.template,
44
- title: data.title || trigger.textContent?.trim() || "",
45
- },
46
- manager,
47
- )
48
- })
49
-
50
- window.showToast = (options) => showToast(options || {}, manager)
51
- }
52
-
53
- function showToast(options, manager) {
54
- const node = buildToast(options.template || "toast-template")
55
- if (!node) return
56
-
57
- fillSlot(node, "[data-toast-title]", options.title)
58
- fillSlot(node, "[data-toast-description]", options.description)
59
-
60
- if (options.severity) node.dataset.severity = options.severity
61
- if (options.duration) node.dataset.duration = options.duration
62
-
63
- wireToast(node)
64
- manager.appendChild(node)
65
- }
66
-
67
- function buildToast(templateId) {
68
- const tpl = document.getElementById(templateId)
69
- if (tpl?.content?.firstElementChild) {
70
- return tpl.content.firstElementChild.cloneNode(true)
71
- }
72
- const wrap = document.createElement("div")
73
- wrap.innerHTML = FALLBACK_TEMPLATE.trim()
74
- return wrap.firstElementChild
75
- }
76
-
77
- function fillSlot(root, selector, text) {
78
- const el = root.querySelector(selector)
79
- if (!el) return
80
- if (text == null || text === "") {
81
- el.remove()
82
- return
83
- }
84
- el.textContent = text
85
- }
86
-
87
- function wireToast(node) {
88
- node.querySelector("[data-toast-close]")?.addEventListener("click", () => {
89
- node.classList.add("ui-exiting")
90
- })
91
- node.addEventListener("animationend", (event) => {
92
- if (event.animationName === "toast-exit") node.remove()
93
- })
94
- }
1
+ /**
2
+ * Toast manager.
3
+ *
4
+ * - HTML owns structure (via <template id="toast-template">).
5
+ * - CSS owns lifetime (via attr(data-duration type(<time>))).
6
+ * - JS owns lifecycle (clone template, fill slots, append, remove on animationend).
7
+ *
8
+ * No setTimeout. No popovertargetaction. No innerHTML of user data.
9
+ */
10
+
11
+ const FALLBACK_TEMPLATE = `<div class="ui-toast" role="alert">
12
+ <span class="ui-icon" data-toast-icon></span>
13
+ <div class="ui-content">
14
+ <div class="ui-title" data-toast-title></div>
15
+ <div class="ui-description" data-toast-description></div>
16
+ </div>
17
+ <button class="ui-close-button" data-toast-close type="button" aria-label="Close">
18
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
19
+ </button>
20
+ </div>`
21
+
22
+ export function initToastManager() {
23
+ const manager = document.getElementById("toast-manager")
24
+ if (!manager) return
25
+
26
+ try {
27
+ manager.showPopover()
28
+ } catch {
29
+ /* already open or unsupported */
30
+ }
31
+
32
+ manager.addEventListener("command", (event) => {
33
+ if (event.command !== "--show-toast") return
34
+ const trigger = event.source
35
+ if (!trigger) return
36
+
37
+ const data = trigger.dataset || {}
38
+ showToast(
39
+ {
40
+ description: data.description,
41
+ duration: data.duration,
42
+ severity: data.severity,
43
+ template: data.template,
44
+ title: data.title || trigger.textContent?.trim() || "",
45
+ },
46
+ manager,
47
+ )
48
+ })
49
+
50
+ window.showToast = (options) => showToast(options || {}, manager)
51
+ }
52
+
53
+ function showToast(options, manager) {
54
+ const node = buildToast(options.template || "toast-template")
55
+ if (!node) return
56
+
57
+ fillSlot(node, "[data-toast-title]", options.title)
58
+ fillSlot(node, "[data-toast-description]", options.description)
59
+
60
+ if (options.severity) node.dataset.severity = options.severity
61
+ if (options.duration) node.dataset.duration = options.duration
62
+
63
+ wireToast(node)
64
+ manager.appendChild(node)
65
+ }
66
+
67
+ function buildToast(templateId) {
68
+ const tpl = document.getElementById(templateId)
69
+ if (tpl?.content?.firstElementChild) {
70
+ return tpl.content.firstElementChild.cloneNode(true)
71
+ }
72
+ const wrap = document.createElement("div")
73
+ wrap.innerHTML = FALLBACK_TEMPLATE.trim()
74
+ return wrap.firstElementChild
75
+ }
76
+
77
+ function fillSlot(root, selector, text) {
78
+ const el = root.querySelector(selector)
79
+ if (!el) return
80
+ if (text == null || text === "") {
81
+ el.remove()
82
+ return
83
+ }
84
+ el.textContent = text
85
+ }
86
+
87
+ function wireToast(node) {
88
+ node.querySelector("[data-toast-close]")?.addEventListener("click", () => {
89
+ node.classList.add("ui-exiting")
90
+ })
91
+ node.addEventListener("animationend", (event) => {
92
+ if (event.animationName === "toast-exit") node.remove()
93
+ })
94
+ }
package/css/theme.css CHANGED
@@ -1,132 +1,133 @@
1
- /*
2
- theme three theme setup
3
- */
4
- @layer theme {
5
-
6
- /* 1. Color scheme */
7
- .ui-light {
8
- --color-scheme: light;
9
- }
10
-
11
- .ui-dark {
12
- --color-scheme: dark;
13
- }
14
-
15
- :where(html) {
16
- color-scheme: var(--color-scheme, light dark);
17
-
18
- /* 2. Palette source - one value to rule them all. Every other color is derived from this one.
19
- Both light/dark branches are wired up via light-dark() so prefers-color-scheme works
20
- automatically. Set them to different OKLCH values if you want a distinct dark-mode palette. */
21
- --palette-source: light-dark(oklch(0.58 calc(0.21 * 0.5) var(--hue-blue)),
22
- oklch(0.58 calc(0.21 * 0.5) var(--hue-blue)));
23
- --palette-hue-rotate-by: 0;
24
-
25
- /* Gray ramp lives in core/palette.css. Override --gray-chroma or
26
- --gray-hue here if this theme wants warmer/cooler grays. */
27
-
28
- /* 3. Named colors (no severity meaning) — use when you literally want a green/red/etc. dot */
29
- --blue: oklch(from var(--color-9) l 0.2 210);
30
- --green: oklch(from var(--color-9) l 0.2 145);
31
- --orange: oklch(from var(--color-7) l 0.2 75);
32
- --red: oklch(from var(--color-9) l 0.2 25);
33
-
34
- /* 4. Intent tokens
35
- Severity (have scope classes below): --success, --info, --warning, --critical
36
- Non-severity (value-only): --primary, --neutral */
37
- --success: var(--green);
38
- --info: var(--blue);
39
- --warning: var(--orange);
40
- --critical: var(--red);
41
- --neutral: var(--gray-9);
42
-
43
- /* 5. Primary */
44
- --primary: var(--color-8);
45
- --primary-light: oklch(from var(--primary) calc(l * 1.25) c h);
46
- --primary-dark: oklch(from var(--primary) calc(l * 0.75) c h);
47
- --primary-contrast: var(--gray-1);
48
-
49
- /* 6. Text */
50
- --text-primary: light-dark(var(--gray-15), var(--gray-1));
51
- --text-primary-contrast: light-dark(var(--gray-2), var(--gray-15));
52
- --text-muted: light-dark(var(--gray-13), var(--gray-4));
53
- --text-muted-contrast: light-dark(var(--gray-4), var(--gray-13));
54
-
55
- /* 7. Surfaces */
56
- --surface-default: light-dark(var(--gray-1), var(--gray-13));
57
- --surface-filled: light-dark(var(--gray-4), var(--gray-15));
58
- --surface-tonal: light-dark(var(--gray-3), var(--gray-12));
59
- --surface-elevated: light-dark(var(--gray-1), var(--gray-12));
60
-
61
- /* 8. Borders */
62
- --border-color: light-dark(var(--gray-4), var(--gray-12));
63
- --border-radius: var(--size-2);
64
- --border-width: 1px;
65
-
66
- /* 9. Focus ring — components consume these and may override locally
67
- (e.g. .ui-button.ui-filled { --focus-ring-offset: 3px; }, .ui-checkbox { --focus-ring-offset: 0; }) */
68
- --focus-ring-color: var(--primary);
69
- --focus-ring-width: 2px;
70
- --focus-ring-offset: 2px;
71
- --focus-ring-style: solid;
72
-
73
- /* 10. Typography */
74
- --font-size-h1: var(--font-size-fluid-3);
75
- --font-size-h2: var(--font-size-fluid-2);
76
- --font-size-h3: var(--font-size-fluid-1);
77
- --font-size-h4: var(--font-size-3);
78
- --font-size-h5: var(--font-size-2);
79
- --font-size-h6: var(--font-size-fluid-0);
80
-
81
- /* Sneaking in an extra font size I want but Open Props doesn't provide. You can do whatever you want with your own theme. It's just CSS. */
82
- --font-size-05: 0.875rem;
83
-
84
- /* 11. Control sizes — shared scale for fields and buttons so they line up
85
- visually. --field-size-* and --button-size-* alias from --control-size-*. */
86
- --control-size-x-small: 28px;
87
- --control-size-small: 32px;
88
- --control-size: 40px;
89
- --control-size-large: 46px;
90
-
91
- --field-size-x-small: var(--control-size-x-small);
92
- --field-size-small: var(--control-size-small);
93
- --field-size: var(--control-size);
94
- --field-size-large: var(--control-size-large);
95
-
96
- --button-size-x-small: var(--control-size-x-small);
97
- --button-size-small: var(--control-size-small);
98
- --button-size: var(--control-size);
99
- --button-size-large: var(--control-size-large);
100
-
101
- /* 12. Field / input */
102
- --field-border-color: var(--border-color);
103
- --field-border-radius: var(--size-2);
104
- --field-border-width: 1px;
105
-
106
- /* 13. Button */
107
- --button-border-radius: var(--size-2);
108
- }
109
-
110
- /* 14. Severity scope classes — re-source the palette inside these contexts.
111
- Authoring `<div class="ui-warning">` paints the entire subtree warning-orange.
112
- Non-severity tokens (--primary, --neutral) deliberately have no scope class. */
113
- :where(.ui-critical, [data-invalid], del) {
114
- --palette-source: oklch(0.58 0.21 var(--hue-red));
115
- --palette-hue-rotate-by: 1;
116
- }
117
-
118
- :where(.ui-info, abbr, dfn) {
119
- --palette-source: oklch(0.58 0.21 var(--hue-blue));
120
- --palette-hue-rotate-by: 1;
121
- }
122
-
123
- :where(.ui-success, ins) {
124
- --palette-source: oklch(0.58 0.21 var(--hue-green));
125
- --palette-hue-rotate-by: 1;
126
- }
127
-
128
- :where(.ui-warning) {
129
- --palette-source: oklch(0.58 0.21 var(--hue-orange));
130
- --palette-hue-rotate-by: 1;
131
- }
132
- }
1
+ /*
2
+ theme three theme setup
3
+ */
4
+ @layer theme {
5
+ /* 1. Color scheme */
6
+ .ui-light {
7
+ --color-scheme: light;
8
+ }
9
+
10
+ .ui-dark {
11
+ --color-scheme: dark;
12
+ }
13
+
14
+ :where(html) {
15
+ color-scheme: var(--color-scheme, light dark);
16
+
17
+ /* 2. Palette source - one value to rule them all. Every other color is derived from this one.
18
+ Both light/dark branches are wired up via light-dark() so prefers-color-scheme works
19
+ automatically. Set them to different OKLCH values if you want a distinct dark-mode palette. */
20
+ --palette-source: light-dark(
21
+ oklch(0.58 calc(0.21 * 0.5) var(--hue-blue)),
22
+ oklch(0.58 calc(0.21 * 0.5) var(--hue-blue))
23
+ );
24
+ --palette-hue-rotate-by: 0;
25
+
26
+ /* Gray ramp lives in core/palette.css. Override --gray-chroma or
27
+ --gray-hue here if this theme wants warmer/cooler grays. */
28
+
29
+ /* 3. Named colors (no severity meaning) — use when you literally want a green/red/etc. dot */
30
+ --blue: oklch(from var(--color-9) l 0.2 210);
31
+ --green: oklch(from var(--color-9) l 0.2 145);
32
+ --orange: oklch(from var(--color-7) l 0.2 75);
33
+ --red: oklch(from var(--color-9) l 0.2 25);
34
+
35
+ /* 4. Intent tokens
36
+ Severity (have scope classes below): --success, --info, --warning, --critical
37
+ Non-severity (value-only): --primary, --neutral */
38
+ --success: var(--green);
39
+ --info: var(--blue);
40
+ --warning: var(--orange);
41
+ --critical: var(--red);
42
+ --neutral: var(--gray-9);
43
+
44
+ /* 5. Primary */
45
+ --primary: var(--color-8);
46
+ --primary-light: oklch(from var(--primary) calc(l * 1.25) c h);
47
+ --primary-dark: oklch(from var(--primary) calc(l * 0.75) c h);
48
+ --primary-contrast: var(--gray-1);
49
+
50
+ /* 6. Text */
51
+ --text-primary: light-dark(var(--gray-15), var(--gray-1));
52
+ --text-primary-contrast: light-dark(var(--gray-2), var(--gray-15));
53
+ --text-muted: light-dark(var(--gray-13), var(--gray-4));
54
+ --text-muted-contrast: light-dark(var(--gray-4), var(--gray-13));
55
+
56
+ /* 7. Surfaces */
57
+ --surface-default: light-dark(var(--gray-1), var(--gray-13));
58
+ --surface-filled: light-dark(var(--gray-4), var(--gray-15));
59
+ --surface-tonal: light-dark(var(--gray-3), var(--gray-12));
60
+ --surface-elevated: light-dark(var(--gray-1), var(--gray-12));
61
+
62
+ /* 8. Borders */
63
+ --border-color: light-dark(var(--gray-4), var(--gray-12));
64
+ --border-radius: var(--size-2);
65
+ --border-width: 1px;
66
+
67
+ /* 9. Focus ring — components consume these and may override locally
68
+ (e.g. .ui-button.ui-filled { --focus-ring-offset: 3px; }, .ui-checkbox { --focus-ring-offset: 0; }) */
69
+ --focus-ring-color: var(--primary);
70
+ --focus-ring-width: 2px;
71
+ --focus-ring-offset: 2px;
72
+ --focus-ring-style: solid;
73
+
74
+ /* 10. Typography */
75
+ --font-size-h1: var(--font-size-fluid-3);
76
+ --font-size-h2: var(--font-size-fluid-2);
77
+ --font-size-h3: var(--font-size-fluid-1);
78
+ --font-size-h4: var(--font-size-3);
79
+ --font-size-h5: var(--font-size-2);
80
+ --font-size-h6: var(--font-size-fluid-0);
81
+
82
+ /* Sneaking in an extra font size I want but Open Props doesn't provide. You can do whatever you want with your own theme. It's just CSS. */
83
+ --font-size-05: 0.875rem;
84
+
85
+ /* 11. Control sizes — shared scale for fields and buttons so they line up
86
+ visually. --field-size-* and --button-size-* alias from --control-size-*. */
87
+ --control-size-x-small: 28px;
88
+ --control-size-small: 32px;
89
+ --control-size: 40px;
90
+ --control-size-large: 46px;
91
+
92
+ --field-size-x-small: var(--control-size-x-small);
93
+ --field-size-small: var(--control-size-small);
94
+ --field-size: var(--control-size);
95
+ --field-size-large: var(--control-size-large);
96
+
97
+ --button-size-x-small: var(--control-size-x-small);
98
+ --button-size-small: var(--control-size-small);
99
+ --button-size: var(--control-size);
100
+ --button-size-large: var(--control-size-large);
101
+
102
+ /* 12. Field / input */
103
+ --field-border-color: var(--border-color);
104
+ --field-border-radius: var(--size-2);
105
+ --field-border-width: 1px;
106
+
107
+ /* 13. Button */
108
+ --button-border-radius: var(--size-2);
109
+ }
110
+
111
+ /* 14. Severity scope classes — re-source the palette inside these contexts.
112
+ Authoring `<div class="ui-warning">` paints the entire subtree warning-orange.
113
+ Non-severity tokens (--primary, --neutral) deliberately have no scope class. */
114
+ :where(.ui-critical, [data-invalid], del) {
115
+ --palette-source: oklch(0.58 0.21 var(--hue-red));
116
+ --palette-hue-rotate-by: 1;
117
+ }
118
+
119
+ :where(.ui-info, abbr, dfn) {
120
+ --palette-source: oklch(0.58 0.21 var(--hue-blue));
121
+ --palette-hue-rotate-by: 1;
122
+ }
123
+
124
+ :where(.ui-success, ins) {
125
+ --palette-source: oklch(0.58 0.21 var(--hue-green));
126
+ --palette-hue-rotate-by: 1;
127
+ }
128
+
129
+ :where(.ui-warning) {
130
+ --palette-source: oklch(0.58 0.21 var(--hue-orange));
131
+ --palette-hue-rotate-by: 1;
132
+ }
133
+ }