actual-css 0.4.1 → 0.5.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 (74) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +2 -2
  3. package/dist/actual.css +53 -14
  4. package/dist/actual.full.css +300 -91
  5. package/dist/actual.full.js +1 -1
  6. package/dist/actual.full.min.css +1 -1
  7. package/dist/actual.min.css +1 -1
  8. package/docs/design-notes/spacing-units.md +286 -0
  9. package/docs/navigation.json +1 -0
  10. package/docs/pages/components/alert.md +1 -1
  11. package/docs/pages/components/app-navigation.md +2 -0
  12. package/docs/pages/components/badge.md +17 -3
  13. package/docs/pages/components/button.md +9 -2
  14. package/docs/pages/components/card.md +13 -0
  15. package/docs/pages/components/data-list.md +72 -0
  16. package/docs/pages/components/dialog.md +2 -0
  17. package/docs/pages/components/drawer.md +2 -0
  18. package/docs/pages/components/fab.md +2 -0
  19. package/docs/pages/components/meter.md +56 -1
  20. package/docs/pages/components/progress.md +10 -0
  21. package/docs/pages/components/skeleton.md +2 -0
  22. package/docs/pages/components/steps.md +10 -2
  23. package/docs/pages/components/table.md +1 -0
  24. package/docs/pages/enhancements/flyout.md +2 -0
  25. package/docs/pages/enhancements/status-bar.md +10 -1
  26. package/docs/pages/examples/overview.md +2 -1
  27. package/docs/pages/forms/joined-controls.md +2 -0
  28. package/docs/pages/forms/overview.md +1 -1
  29. package/docs/pages/forms/range.md +2 -0
  30. package/docs/pages/forms/states-and-sizes.md +1 -1
  31. package/docs/pages/forms/validation.md +2 -0
  32. package/docs/pages/foundations/philosophy.md +11 -1
  33. package/docs/pages/foundations/tokens.md +7 -3
  34. package/docs/pages/foundations/typography.md +10 -2
  35. package/docs/pages/guides/introduction.md +1 -1
  36. package/docs/pages/layout/scroll-snap.md +2 -0
  37. package/docs/pages/layout/sidebar-layout.md +2 -0
  38. package/docs/pages/layout/topbar.md +2 -0
  39. package/docs/pages/utilities/accessibility.md +18 -0
  40. package/package.json +1 -1
  41. package/scripts/reserved-classes.json +4 -0
  42. package/src/css/components/actions.css +1 -0
  43. package/src/css/components/breadcrumb.css +1 -0
  44. package/src/css/components/data-list.css +27 -0
  45. package/src/css/components/drawer.css +30 -11
  46. package/src/css/components/flyout.css +17 -12
  47. package/src/css/components/index.css +1 -0
  48. package/src/css/components/meter.css +28 -1
  49. package/src/css/components/modal.css +28 -11
  50. package/src/css/components/navbar.css +2 -0
  51. package/src/css/components/pagination.css +1 -0
  52. package/src/css/components/progress.css +5 -0
  53. package/src/css/components/status-bar.css +31 -15
  54. package/src/css/components/steps.css +36 -9
  55. package/src/css/components/surface.css +5 -3
  56. package/src/css/components/tab.css +1 -0
  57. package/src/css/components/tooltip.css +5 -8
  58. package/src/css/core/base.css +7 -1
  59. package/src/css/core/intents.css +6 -0
  60. package/src/css/core/tokens.css +23 -9
  61. package/src/css/core/variants.css +17 -4
  62. package/src/css/forms/form-actions.css +1 -0
  63. package/src/css/forms/form.css +4 -4
  64. package/src/css/layout/cluster.css +1 -0
  65. package/src/css/layout/scroll-snap.css +1 -0
  66. package/src/css/layout/sidebar-layout.css +1 -0
  67. package/src/css/layout/switcher.css +1 -0
  68. package/src/css/layout/topbar.css +1 -0
  69. package/src/css/typography/prose.css +2 -3
  70. package/src/css/utilities/base.css +16 -0
  71. package/src/js/selectors.js +1 -0
  72. package/src/js/status.js +88 -7
  73. package/src/js/surface.js +2 -11
  74. package/src/js/transition.js +20 -0
@@ -1 +1 @@
1
- *, *::before, *::after { box-sizing: border-box; } html { -webkit-text-size-adjust: 100%; } body { margin: 0; } button, input, textarea, select { font: inherit; } [hidden]:not([hidden="until-found"]) { display: none !important; } :where(img, svg, video) { max-inline-size: 100%; block-size: auto; } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto !important; } *, *::before, *::after { animation-duration: 0.01ms !important; animation-delay: 0s !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; transition-delay: 0s !important; } } :root { --font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; --line-height: 1.5; --line-height-tight: 1.25; --line-height-relaxed: 1.75; --font-weight: 400; --font-weight-medium: 500; --font-weight-strong: 650; --font-weight-bold: 700; --font-size-xs: 0.8125rem; --font-size-sm: 0.875rem; --font-size-md: 1rem; --font-size-lg: 1.125rem; --gap: 0.75rem; --density-space: var(--space-40); --density-compact-size: 1.5rem; --space-10: 0.25rem; --space-20: 0.5rem; --space-30: 0.75rem; --space-40: 1rem; --space-50: 1.5rem; --space-60: 2rem; --border-width: 1px; --radius-full: 9999px; --shadow: 0 1px 3px rgb(0 0 0 / 0.05), 0 4px 12px rgb(0 0 0 / 0.04); --shadow-popout: 0 0.25rem 0.75rem rgb(0 0 0 / 0.15); --duration: 150ms; --duration-fast: 100ms; --duration-slow: 200ms; --duration-spin: 0.75s; --duration-shimmer: 1.2s; --ease: ease; --focus-outline-color: currentColor; --focus-outline: calc(var(--border-width) * 2) solid var(--focus-outline-color); --focus-outline-offset: 2px; --focus-ring-width: 3px; --indicator-offset: 14.65%; --backdrop-color: rgb(0 0 0); --backdrop-opacity: 0.45; --backdrop-fill: rgb(0 0 0 / var(--backdrop-opacity)); --bar-height: 0.65rem; --control-size-sm: 2rem; --control-size-md: 2.375rem; --control-size-lg: 2.75rem; --control-size: var(--control-size-md); --control-pad-x: 1em; --control-font-size: var(--font-size-md); --choice-control-size: 1.125em; --disabled-opacity: 0.65; } :root, [data-theme] { --state-selected: var(--primary); --state-selected-fg: var(--primary-fg); --state-disabled: var(--text-subtle); --indicator-ring: var(--surface); } :root { --soft-bg-mix: 88%; --soft-border-mix: 65%; --soft-hover-alpha: 12%; --soft-fg-mix: 100%; --icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%238a8594' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"); --icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E"); --icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3C/svg%3E"); --viewport-block: 100vh; --viewport-inline: 100vw; --z-sticky: 10; --z-menu: 20; --z-tooltip: 50; --z-status: 60; } @media (forced-colors: active) { :root, [data-theme] { --state-disabled: GrayText; --disabled-opacity: 1; } } @supports (block-size: 100dvh) { :root { --viewport-block: 100dvh; } } @supports (inline-size: 100vi) { :root { --viewport-inline: 100vi; } } @supports (color: color-mix(in oklch, red, white)) { :root, [data-theme] { --shadow: 0 1px 3px color-mix(in oklch, var(--shadow-color) 5%, transparent), 0 4px 12px color-mix(in oklch, var(--shadow-color) 4%, transparent); --shadow-popout: 0 0.25rem 0.75rem color-mix(in oklch, var(--shadow-color) 15%, transparent); } :root { --backdrop-fill: color-mix( in oklch, var(--backdrop-color) calc(var(--backdrop-opacity) * 100%), transparent ); } } :root { color-scheme: light dark; } [data-theme="light"] { color-scheme: light; } [data-theme="dark"] { color-scheme: dark; } @supports not (color: light-dark(red, blue)) { :root, [data-theme="light"], [data-theme="dark"] { color-scheme: light; } } :root, [data-theme="light"] { --primary: hsl(268 32% 33%); --primary-fg: hsl(0 0% 98%); --secondary: hsl(16 38% 38%); --secondary-fg: hsl(0 0% 98%); --success: hsl(150 40% 31%); --success-fg: hsl(0 0% 98%); --warning: hsl(36 68% 34%); --warning-fg: hsl(0 0% 98%); --danger: hsl(5 58% 43%); --danger-fg: hsl(0 0% 98%); --neutral: hsl(258 7% 43%); --neutral-fg: hsl(0 0% 98%); --surface: hsl(0 0% 100%); --surface-raised: hsl(0 0% 100%); --surface-subtle: hsl(262 24% 96%); --surface-solid: hsl(261 16% 13%); --text: hsl(261 20% 13%); --text-muted: hsl(259 9% 42%); --text-subtle: hsl(258 7% 57%); --border: hsl(260 15% 88%); --focus: var(--neutral); --focus-ring: hsl(258 7% 43% / 0.3); --hover-overlay: hsl(260 20% 10% / 0.04); --radius: 0.5rem; --radius-sm: 0.25rem; --radius-lg: 0.75rem; --shadow-color: hsl(260 25% 10%); --hgroup-gap: 0.25em; } :root, [data-theme] { --heading: var(--text); --selection-bg: var(--primary); --selection-fg: var(--primary-fg); --focus-ring-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring); } :root, [data-theme] { accent-color: var(--primary); } [data-tone="dark"] { --tone-fg: white; } [data-tone="light"] { --tone-fg: var(--text); } @supports (color: color-mix(in oklch, red, white)) { :root, [data-theme] { --focus-ring: color-mix(in oklch, var(--focus) 30%, transparent); } } @supports (color: light-dark(red, blue)) { :root, [data-theme="light"] { --primary: light-dark(hsl(268 32% 33%), hsl(268 42% 76%)); --primary-fg: light-dark(hsl(0 0% 98%), hsl(268 30% 12%)); --secondary: light-dark(hsl(16 38% 38%), hsl(18 45% 72%)); --secondary-fg: light-dark(hsl(0 0% 98%), hsl(18 32% 11%)); --success: light-dark(hsl(150 40% 31%), hsl(150 38% 62%)); --success-fg: light-dark(hsl(0 0% 98%), hsl(150 35% 8%)); --warning: light-dark(hsl(36 68% 34%), hsl(40 62% 64%)); --warning-fg: light-dark(hsl(0 0% 98%), hsl(38 35% 10%)); --danger: light-dark(hsl(5 58% 43%), hsl(4 60% 70%)); --danger-fg: light-dark(hsl(0 0% 98%), hsl(4 35% 10%)); --neutral: light-dark(hsl(258 7% 43%), hsl(258 10% 72%)); --neutral-fg: light-dark(hsl(0 0% 98%), hsl(260 18% 10%)); --surface: light-dark(hsl(0 0% 100%), hsl(263 17% 9%)); --surface-raised: light-dark(hsl(0 0% 100%), hsl(263 15% 12%)); --surface-subtle: light-dark(hsl(262 24% 96%), hsl(263 13% 16%)); --surface-solid: light-dark(hsl(261 16% 13%), hsl(0 0% 96%)); --text: light-dark(hsl(261 20% 13%), hsl(260 12% 95%)); --text-muted: light-dark(hsl(259 9% 42%), hsl(259 9% 67%)); --text-subtle: light-dark(hsl(258 7% 57%), hsl(258 7% 54%)); --border: light-dark(hsl(260 15% 88%), hsl(262 12% 24%)); --focus-ring: light-dark(hsl(258 7% 43% / 0.3), hsl(258 10% 72% / 0.28)); --hover-overlay: light-dark(hsl(260 20% 10% / 0.04), hsl(0 0% 100% / 0.06)); --shadow-color: light-dark(hsl(260 25% 10%), hsl(262 40% 2%)); } } @supports (color: color-mix(in oklch, red, white)) and (color: light-dark(red, blue)) { :root, [data-theme] { --focus-ring: light-dark( color-mix(in oklch, var(--focus) 30%, transparent), color-mix(in oklch, var(--focus) 28%, transparent) ); } } @media (prefers-contrast: more) { :root, [data-theme][data-theme] { --border-width: 2px; --focus-outline-offset: 3px; --focus-ring-width: 4px; --text-muted: var(--text); --text-subtle: var(--text); } } body { background: var(--surface); color: var(--text); font-family: var(--font-sans); line-height: var(--line-height); } a { color: inherit; text-underline-offset: 0.18em; } a:hover { text-decoration-thickness: 0.12em; } :where(h1, h2, h3, h4, h5, h6) { color: var(--heading, inherit); line-height: var(--line-height-tight); text-wrap: balance; } hgroup { display: grid; gap: var(--hgroup-gap); } hgroup > * { margin-block: 0; } hr { border: 0; border-block-start: var(--border-width) solid var(--border); margin-block: var(--space-60); } code, kbd, samp, pre { font-family: var(--font-mono); } small { font-size: 0.875em; } ::selection { background: var(--selection-bg, var(--primary)); color: var(--selection-fg, var(--primary-fg)); } .primary { --intent: var(--primary); --intent-fg: var(--primary-fg); } .secondary { --intent: var(--secondary); --intent-fg: var(--secondary-fg); } .success { --intent: var(--success); --intent-fg: var(--success-fg); } .warning { --intent: var(--warning); --intent-fg: var(--warning-fg); } .danger { --intent: var(--danger); --intent-fg: var(--danger-fg); } .neutral { --intent: var(--neutral); --intent-fg: var(--neutral-fg); } .solid { --ui-bg: var(--intent, var(--neutral)); --ui-fg: var(--intent-fg, var(--neutral-fg)); --ui-border: transparent; } .soft { --ui-bg: var(--surface-subtle); --ui-border: var(--border); --ui-fg: var(--intent, var(--text)); } .outline { --ui-bg: transparent; --ui-fg: var(--intent, currentColor); --ui-border: var(--intent, currentColor); } .inverted { --ui-bg: var(--surface-solid); --ui-fg: var(--surface); --ui-border: transparent; --ui-hover-bg: var(--surface-solid); --heading: var(--surface); --busy-overlay-bg: var(--surface-solid); background: var(--ui-bg); color: var(--ui-fg); border-color: var(--ui-border); } .sm { --gap: var(--space-20); --density-space: var(--space-20); --control-size: var(--control-size-sm); --density-compact-size: 1.25rem; } .lg { --gap: var(--space-50); --density-space: var(--space-50); --control-size: var(--control-size-lg); --density-compact-size: 1.875rem; } @supports (color: color-mix(in oklch, red, white)) { .soft { --ui-bg: color-mix(in oklab, var(--surface) var(--soft-bg-mix), var(--intent, var(--neutral))); --ui-border: color-mix( in oklab, var(--surface) var(--soft-border-mix), var(--intent, var(--neutral)) ); --ui-fg: color-mix(in oklab, var(--text), var(--intent, var(--text)) var(--soft-fg-mix)); } .inverted { --ui-hover-bg: color-mix(in oklch, var(--surface-solid) 88%, black); } } :focus { outline: var(--focus-outline); outline-offset: var(--focus-outline-offset); } @supports selector(:focus-visible) { :focus:not(:focus-visible) { outline: none; } } @media print { :root, body { color-scheme: light; background: #fff; color: #000; } h1, h2, h3, h4, h5, h6 { break-after: avoid; } }
1
+ *, *::before, *::after { box-sizing: border-box; } html { -webkit-text-size-adjust: 100%; } body { margin: 0; } button, input, textarea, select { font: inherit; } [hidden]:not([hidden="until-found"]) { display: none !important; } :where(img, svg, video) { max-inline-size: 100%; block-size: auto; } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto !important; } *, *::before, *::after { animation-duration: 0.01ms !important; animation-delay: 0s !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; transition-delay: 0s !important; } } :root { --font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; --line-height: 1.5; --line-height-tight: 1.25; --line-height-relaxed: 1.75; --font-weight: 400; --font-weight-medium: 500; --font-weight-strong: 650; --font-weight-bold: 700; --font-size-xs: 0.8125rem; --font-size-sm: 0.875rem; --font-size-md: 1rem; --font-size-lg: 1.125rem; --gap: 0.75rem; --density-space: var(--space-40); --density-compact-size: 1.5rem; --space-10: 0.25rem; --space-20: 0.5rem; --space-30: 0.75rem; --space-40: 1rem; --space-50: 1.5rem; --space-60: 2rem; --border-width: 1px; --radius-full: 9999px; --shadow: 0 1px 3px rgb(0 0 0 / 0.05), 0 4px 12px rgb(0 0 0 / 0.04); --shadow-popout: 0 0.25rem 0.75rem rgb(0 0 0 / 0.15); --duration: 150ms; --duration-fast: 100ms; --duration-slow: 200ms; --duration-spin: 0.75s; --duration-shimmer: 1.2s; --ease-enter: cubic-bezier(0.2, 0, 0, 1); --ease-exit: cubic-bezier(0.4, 0, 1, 1); --focus-outline-color: currentColor; --focus-outline: calc(var(--border-width) * 2) solid var(--focus-outline-color); --focus-outline-offset: 2px; --focus-ring-width: 3px; --indicator-offset: 14.65%; --backdrop-color: rgb(0 0 0); --backdrop-opacity: 0.45; --backdrop-fill: rgb(0 0 0 / var(--backdrop-opacity)); --bar-height: 0.65rem; --control-size-sm: 2rem; --control-size-md: 2.375rem; --control-size-lg: 2.75rem; --control-size: var(--control-size-md); --control-pad-x: 1em; --control-font-size: var(--font-size-md); --choice-control-size: 1.125em; --disabled-opacity: 0.65; } :root, [data-theme] { --state-selected: var(--primary); --state-selected-fg: var(--primary-fg); --state-disabled: var(--text-subtle); --indicator-ring: var(--surface); } :root { --soft-bg-mix: 88%; --soft-border-mix: 65%; --soft-hover-alpha: 12%; --soft-fg-mix: 100%; --icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%238a8594' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"); --icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E"); --icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3C/svg%3E"); --viewport-block: 100vh; --viewport-inline: 100vw; --z-sticky: 10; --z-menu: 20; --z-tooltip: 50; --z-status: 60; } @media (forced-colors: active) { :root, [data-theme] { --state-disabled: GrayText; --disabled-opacity: 1; } } @supports (block-size: 100dvh) { :root { --viewport-block: 100dvh; } } @supports (inline-size: 100vi) { :root { --viewport-inline: 100vi; } } @supports (color: color-mix(in oklch, red, white)) { :root, [data-theme] { --shadow: 0 1px 3px color-mix(in oklch, var(--shadow-color) 5%, transparent), 0 4px 12px color-mix(in oklch, var(--shadow-color) 4%, transparent); --shadow-popout: 0 0.25rem 0.75rem color-mix(in oklch, var(--shadow-color) 15%, transparent); } :root { --backdrop-fill: color-mix( in oklch, var(--backdrop-color) calc(var(--backdrop-opacity) * 100%), transparent ); } } :root { color-scheme: light dark; } [data-theme="light"] { color-scheme: light; } [data-theme="dark"] { color-scheme: dark; } @supports not (color: light-dark(red, blue)) { :root, [data-theme="light"], [data-theme="dark"] { color-scheme: light; } } :root, [data-theme="light"] { --primary: hsl(268 32% 33%); --primary-fg: hsl(0 0% 98%); --secondary: hsl(16 38% 38%); --secondary-fg: hsl(0 0% 98%); --success: hsl(150 40% 31%); --success-fg: hsl(0 0% 98%); --warning: hsl(36 68% 34%); --warning-fg: hsl(0 0% 98%); --danger: hsl(5 58% 43%); --danger-fg: hsl(0 0% 98%); --neutral: hsl(258 7% 43%); --neutral-fg: hsl(0 0% 98%); --surface: hsl(0 0% 100%); --surface-raised: hsl(0 0% 100%); --surface-subtle: hsl(262 24% 96%); --surface-solid: hsl(261 16% 13%); --text: hsl(261 20% 13%); --text-muted: hsl(259 9% 42%); --text-subtle: hsl(258 7% 57%); --border: hsl(260 15% 88%); --focus: var(--neutral); --focus-ring: hsl(258 7% 43% / 0.3); --hover-overlay: hsl(260 20% 10% / 0.04); --radius: 0.5rem; --radius-sm: 0.25rem; --radius-lg: 0.75rem; --shadow-color: hsl(260 25% 10%); --hgroup-gap: 0.25em; } :root, [data-theme] { --heading: var(--text); --selection-bg: var(--primary); --selection-fg: var(--primary-fg); --focus-ring-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring); } :root, [data-theme] { accent-color: var(--primary); } [data-tone="dark"] { --tone-fg: white; } [data-tone="light"] { --tone-fg: var(--text); } @supports (color: color-mix(in oklch, red, white)) { :root, [data-theme] { --focus-ring: color-mix(in oklch, var(--focus) 30%, transparent); } } @supports (color: light-dark(red, blue)) { :root, [data-theme="light"] { --primary: light-dark(hsl(268 32% 33%), hsl(268 42% 76%)); --primary-fg: light-dark(hsl(0 0% 98%), hsl(268 30% 12%)); --secondary: light-dark(hsl(16 38% 38%), hsl(18 45% 72%)); --secondary-fg: light-dark(hsl(0 0% 98%), hsl(18 32% 11%)); --success: light-dark(hsl(150 40% 31%), hsl(150 38% 62%)); --success-fg: light-dark(hsl(0 0% 98%), hsl(150 35% 8%)); --warning: light-dark(hsl(36 68% 34%), hsl(40 62% 64%)); --warning-fg: light-dark(hsl(0 0% 98%), hsl(38 35% 10%)); --danger: light-dark(hsl(5 58% 43%), hsl(4 60% 70%)); --danger-fg: light-dark(hsl(0 0% 98%), hsl(4 35% 10%)); --neutral: light-dark(hsl(258 7% 43%), hsl(258 10% 72%)); --neutral-fg: light-dark(hsl(0 0% 98%), hsl(260 18% 10%)); --surface: light-dark(hsl(0 0% 100%), hsl(263 17% 9%)); --surface-raised: light-dark(hsl(0 0% 100%), hsl(263 15% 12%)); --surface-subtle: light-dark(hsl(262 24% 96%), hsl(263 13% 16%)); --surface-solid: light-dark(hsl(261 16% 13%), hsl(0 0% 96%)); --text: light-dark(hsl(261 20% 13%), hsl(260 12% 95%)); --text-muted: light-dark(hsl(259 9% 42%), hsl(259 9% 67%)); --text-subtle: light-dark(hsl(258 7% 57%), hsl(258 7% 54%)); --border: light-dark(hsl(260 15% 88%), hsl(262 12% 24%)); --focus-ring: light-dark(hsl(258 7% 43% / 0.3), hsl(258 10% 72% / 0.28)); --hover-overlay: light-dark(hsl(260 20% 10% / 0.04), hsl(0 0% 100% / 0.06)); --shadow-color: light-dark(hsl(260 25% 10%), hsl(262 40% 2%)); } } @supports (color: color-mix(in oklch, red, white)) and (color: light-dark(red, blue)) { :root, [data-theme] { --focus-ring: light-dark( color-mix(in oklch, var(--focus) 30%, transparent), color-mix(in oklch, var(--focus) 28%, transparent) ); } } @media (prefers-contrast: more) { :root, [data-theme][data-theme] { --border-width: 2px; --focus-outline-offset: 3px; --focus-ring-width: 4px; --text-muted: var(--text); --text-subtle: var(--text); } } body { background: var(--surface); color: var(--text); font-family: var(--font-sans); line-height: var(--line-height); } a { color: var(--link, inherit); text-underline-offset: 0.18em; } a:hover { text-decoration-thickness: 0.12em; } :where(h1, h2, h3, h4, h5, h6) { color: var(--heading, inherit); line-height: var(--line-height-tight); text-wrap: balance; } hgroup { display: grid; gap: var(--hgroup-gap); } hgroup > * { margin-block: 0; } hr { border: 0; border-block-start: var(--border-width) solid var(--border); margin-block: var(--space-60); } code, kbd, samp, pre { font-family: var(--font-mono); } small { font-size: 0.875em; } ::selection { background: var(--selection-bg, var(--primary)); color: var(--selection-fg, var(--primary-fg)); } .primary { --intent: var(--primary); --intent-fg: var(--primary-fg); } .secondary { --intent: var(--secondary); --intent-fg: var(--secondary-fg); } .success { --intent: var(--success); --intent-fg: var(--success-fg); } .warning { --intent: var(--warning); --intent-fg: var(--warning-fg); } .danger { --intent: var(--danger); --intent-fg: var(--danger-fg); } .neutral { --intent: var(--neutral); --intent-fg: var(--neutral-fg); } .intent-color { color: var(--intent); } .solid { --ui-bg: var(--intent, var(--neutral)); --ui-fg: var(--intent-fg, var(--neutral-fg)); --ui-border: transparent; } .soft { --ui-bg: var(--surface-subtle); --ui-border: var(--border); --ui-fg: var(--intent, var(--text)); } .outline { --ui-bg: transparent; --ui-fg: var(--intent, currentColor); --ui-border: var(--intent, currentColor); } .surface { --ui-bg: var(--surface); --ui-fg: var(--intent, var(--text)); --ui-border: var(--border); } .inverted { --ui-bg: var(--surface-solid); --ui-fg: var(--surface); --ui-border: transparent; --ui-hover-bg: var(--surface-solid); --heading: var(--surface); --busy-overlay-bg: var(--surface-solid); background: var(--ui-bg); color: var(--ui-fg); border-color: var(--ui-border); } .sm { --gap: var(--space-20); --density-space: var(--space-20); --control-size: var(--control-size-sm); --density-compact-size: 1.25rem; } .lg { --gap: var(--space-50); --density-space: var(--space-50); --control-size: var(--control-size-lg); --density-compact-size: 1.875rem; } @supports (color: color-mix(in oklch, red, white)) { .soft { --ui-bg: color-mix(in oklab, var(--surface) var(--soft-bg-mix), var(--intent, var(--neutral))); --ui-border: color-mix( in oklab, var(--surface) var(--soft-border-mix), var(--intent, var(--neutral)) ); --ui-fg: color-mix(in oklab, var(--text), var(--intent, var(--text)) var(--soft-fg-mix)); } .inverted { --ui-hover-bg: color-mix(in oklch, var(--surface-solid) 88%, black); } } :focus { outline: var(--focus-outline); outline-offset: var(--focus-outline-offset); } @supports selector(:focus-visible) { :focus:not(:focus-visible) { outline: none; } } @media print { :root, body { color-scheme: light; background: #fff; color: #000; } h1, h2, h3, h4, h5, h6 { break-after: avoid; } }
@@ -0,0 +1,286 @@
1
+ # Spacing Units: When to Use `em`, `rem`, `ch`, `lh`, or `--space-*`
2
+
3
+ Actual does not require every relative length to come from a design token.
4
+
5
+ CSS relative units already carry useful semantics. A literal `0.5em`, `1lh`, `20rem`, or `65ch` can be more meaningful than replacing it with an unrelated spacing token.
6
+
7
+ The goal is not to eliminate literal values. The goal is to choose the unit that best expresses the relationship.
8
+
9
+ ## Core rule
10
+
11
+ Use `--space-*` when the value belongs to the **shared interface rhythm**.
12
+
13
+ Use a CSS relative unit when the value belongs to the **intrinsic geometry or typography of the component**.
14
+
15
+ A useful question is:
16
+
17
+ > If this value changes, is that a design-system decision or a local relationship inside the component?
18
+
19
+ If it is a design-system decision, prefer a token.
20
+
21
+ If it is a local relationship, prefer the appropriate CSS unit.
22
+
23
+ ## `--space-*`: interface rhythm
24
+
25
+ Use spacing tokens for layout distances that should remain consistent across components and respond to Actual's density system.
26
+
27
+ Typical cases:
28
+
29
+ ```css
30
+ .card {
31
+ padding: var(--space-40);
32
+ }
33
+
34
+ .cluster {
35
+ gap: var(--space-20);
36
+ }
37
+
38
+ .data-list {
39
+ gap: var(--space-20) var(--space-40);
40
+ }
41
+ ```
42
+
43
+ Good candidates include:
44
+
45
+ * component padding
46
+ * gaps between peer controls
47
+ * spacing between sections
48
+ * list and grid gaps
49
+ * toolbar spacing
50
+ * application layout rhythm
51
+
52
+ Using a spacing token also communicates that the value is expected to participate in density changes.
53
+
54
+ Do not use `--space-*` merely to avoid writing a literal relative unit.
55
+
56
+ ## `em`: relative to the local text
57
+
58
+ Use `em` when a dimension should scale with the font size of the element or component.
59
+
60
+ Typical cases:
61
+
62
+ ```css
63
+ .prose dd {
64
+ margin-inline-start: 1.5em;
65
+ }
66
+
67
+ .inline-icon {
68
+ margin-inline-end: 0.4em;
69
+ }
70
+ ```
71
+
72
+ Good candidates include:
73
+
74
+ * typographic indentation
75
+ * small offsets around inline icons
76
+ * decorative elements sized relative to text
77
+ * local relationships that should scale when the component font size changes
78
+
79
+ `em` is especially useful when the relationship is best understood as:
80
+
81
+ > some fraction or multiple of this text size
82
+
83
+ Do not use `em` for general application spacing when local font-size changes should not affect the layout.
84
+
85
+ ## `rem`: relative to the root scale
86
+
87
+ Use `rem` when a dimension should follow the user's/root font size but remain independent of a component's local typography.
88
+
89
+ Typical cases:
90
+
91
+ ```css
92
+ .sidebar {
93
+ inline-size: 18rem;
94
+ }
95
+
96
+ .dialog {
97
+ max-inline-size: 36rem;
98
+ }
99
+ ```
100
+
101
+ Good candidates include:
102
+
103
+ * component size constraints
104
+ * layout thresholds
105
+ * widths and heights tied to the overall UI scale
106
+ * values that should remain stable when nested inside differently sized text
107
+
108
+ A literal `18rem` does not need a custom property unless the value itself is part of the public API, reused significantly, or expected to be configured.
109
+
110
+ ## `ch`: text-measure relationships
111
+
112
+ Use `ch` when a dimension is naturally related to the approximate width of characters.
113
+
114
+ Typical cases:
115
+
116
+ ```css
117
+ .prose {
118
+ max-inline-size: 65ch;
119
+ }
120
+
121
+ .search-field {
122
+ inline-size: 24ch;
123
+ }
124
+ ```
125
+
126
+ Good candidates include:
127
+
128
+ * readable text measures
129
+ * text-oriented input widths
130
+ * content regions whose useful size is determined by expected text length
131
+
132
+ Do not use `ch` as a generic substitute for `rem`; its value depends on font metrics.
133
+
134
+ ## `lh`: line-height relationships
135
+
136
+ Use `lh` when a dimension should be directly related to the computed line height.
137
+
138
+ Typical cases:
139
+
140
+ ```css
141
+ .floating-label {
142
+ inset-block-start: calc(0.5lh + var(--border-width));
143
+ }
144
+ ```
145
+
146
+ Good candidates include:
147
+
148
+ * vertical positioning relative to a line of text
149
+ * text-area or field geometry
150
+ * offsets that conceptually represent one or part of one line
151
+
152
+ `lh` is preferable to reconstructing line height indirectly from font size when line height is the actual relationship being expressed.
153
+
154
+ ## Choosing between them
155
+
156
+ | Intent | Prefer |
157
+ | --------------------------------------- | ---------------- |
158
+ | Shared application spacing | `var(--space-*)` |
159
+ | Density-aware spacing | `var(--space-*)` |
160
+ | Local relationship to component text | `em` |
161
+ | Stable size relative to root typography | `rem` |
162
+ | Width related to text length | `ch` |
163
+ | Geometry related to line height | `lh` |
164
+
165
+ ## Examples
166
+
167
+ ### System rhythm
168
+
169
+ ```css
170
+ .alert {
171
+ padding: var(--space-30) var(--space-40);
172
+ gap: var(--space-30);
173
+ }
174
+ ```
175
+
176
+ The spacing is part of the visual density and should stay aligned with other components.
177
+
178
+ ### Intrinsic typography
179
+
180
+ ```css
181
+ .prose dd {
182
+ margin-inline-start: 1.5em;
183
+ }
184
+ ```
185
+
186
+ The indentation belongs to the description text itself. A spacing token would add no useful meaning.
187
+
188
+ ### Stable component width
189
+
190
+ ```css
191
+ .sidebar {
192
+ flex-basis: 18rem;
193
+ }
194
+ ```
195
+
196
+ The width belongs to the component geometry, not to the spacing scale.
197
+
198
+ ### Readable prose
199
+
200
+ ```css
201
+ .prose {
202
+ max-inline-size: 65ch;
203
+ }
204
+ ```
205
+
206
+ The width exists to control line length, so `ch` expresses the intention directly.
207
+
208
+ ### Line-relative positioning
209
+
210
+ ```css
211
+ .field-label {
212
+ inset-block-start: 0.5lh;
213
+ }
214
+ ```
215
+
216
+ The position is defined relative to the text line, not to global spacing.
217
+
218
+ ## Avoid token laundering
219
+
220
+ Do not replace meaningful CSS units with spacing tokens simply to make a stylesheet appear more tokenized.
221
+
222
+ Avoid patterns such as:
223
+
224
+ ```css
225
+ .icon {
226
+ translate: 0 var(--space-10);
227
+ }
228
+
229
+ .prose {
230
+ max-inline-size: var(--space-900);
231
+ }
232
+
233
+ .label {
234
+ margin-inline-end: var(--space-20);
235
+ }
236
+ ```
237
+
238
+ when the real relationships would be better expressed as:
239
+
240
+ ```css
241
+ .icon {
242
+ translate: 0 -0.1em;
243
+ }
244
+
245
+ .prose {
246
+ max-inline-size: 65ch;
247
+ }
248
+
249
+ .label {
250
+ margin-inline-end: 0.5em;
251
+ }
252
+ ```
253
+
254
+ Tokens should add meaning, not hide it.
255
+
256
+ ## Literal relative values are not a code smell
257
+
258
+ A well-chosen literal relative value is valid framework CSS.
259
+
260
+ Values such as:
261
+
262
+ ```css
263
+ 0.25em
264
+ 1.5em
265
+ 1lh
266
+ 18rem
267
+ 65ch
268
+ ```
269
+
270
+ can be more stable and understandable than introducing a custom property or mapping them artificially onto the spacing scale.
271
+
272
+ Promote a value to a token when at least one of these becomes true:
273
+
274
+ * it represents a shared design decision
275
+ * multiple components need the same semantic value
276
+ * themes or density modes should modify it
277
+ * users are expected to configure it
278
+ * naming the value improves understanding of the API
279
+
280
+ Otherwise, keep the direct CSS unit.
281
+
282
+ ## Guideline
283
+
284
+ > Use tokens for shared design rhythm. Use CSS relative units for intrinsic relationships.
285
+
286
+ Actual favors semantic CSS over tokenization for its own sake.
@@ -55,6 +55,7 @@
55
55
  "chat",
56
56
  "dialog",
57
57
  "drawer",
58
+ "data-list",
58
59
  "fab",
59
60
  "indicator",
60
61
  "key",
@@ -31,7 +31,7 @@ itself needs to change, not `.soft`.
31
31
  | `.alert-title` | Composition | Admonition title bar; hosts the dismiss button. |
32
32
  | `.alert-body` | Composition | Admonition body resting on the page surface. |
33
33
  | Shared intents | Intent | `.primary`, `.secondary`, `.success`, `.warning`, `.danger`. |
34
- | Shared variants | Variant | `.solid` and `.outline` emphasis. |
34
+ | Shared variants | Variant | `.solid`, `.outline`, and `.surface` emphasis. |
35
35
  | `.sm` / `.lg` | Size | Density; inline padding stays stable. |
36
36
 
37
37
  ## Basic usage
@@ -5,6 +5,8 @@
5
5
  Use one `.app-nav` landmark for the application's primary destinations. Links
6
6
  remain links, and `aria-current="page"` is the only current-page state.
7
7
 
8
+ **Related terms:** tab bar, bottom navigation, bottom nav, navigation rail, side navigation.
9
+
8
10
  ```html demo
9
11
  <nav class="app-nav" aria-label="Primary">
10
12
  <a href="#today" aria-current="page">
@@ -16,6 +16,8 @@ direct child of `.stack` (where flex children otherwise stretch). Use
16
16
  `inline-size: 100%` — or the optional `.inline-size-full` utility — when a
17
17
  full-width badge is intentional.
18
18
 
19
+ **Related terms:** chip, tag, pill, removable tag.
20
+
19
21
  ## Class reference
20
22
 
21
23
  | Class | Kind | Description |
@@ -23,7 +25,7 @@ full-width badge is intentional.
23
25
  | `.badge` | Component | Compact label; soft by default. |
24
26
  | `.badge:empty` | Composition | Truly-empty badge renders as a solid dot. |
25
27
  | Shared intents | Intent | `.primary`, `.secondary`, `.success`, `.warning`, `.danger`. |
26
- | Shared variants | Variant | `.solid` (filled) and `.outline` (bordered) emphasis. |
28
+ | Shared variants | Variant | `.solid` (filled), `.outline` (bordered), `.surface` (untinted) emphasis. |
27
29
  | `.sm` / `.lg` | Size | Compact density. |
28
30
 
29
31
  ## Basic usage
@@ -58,13 +60,25 @@ full-width badge is intentional.
58
60
  </button>
59
61
  ```
60
62
 
61
- ## Empty badge dot
63
+ ## Status dot
62
64
 
63
65
  ```html demo
64
66
  <span class="badge success" aria-label="Online"></span>
65
67
  ```
66
68
 
67
- The dot conveys no text, so an `aria-label` is required for it to mean anything.
69
+ An autonomous dot conveys state without visible text, so give it an accessible
70
+ name.
71
+
72
+ ## Color marker
73
+
74
+ Use an empty badge as a compact intent-colored marker in legends and status
75
+ labels. When adjacent text carries the same information, keep the marker
76
+ decorative.
77
+
78
+ ```html demo
79
+ <span class="badge primary" aria-hidden="true"></span>
80
+ Regular
81
+ ```
68
82
 
69
83
  ## Size variants
70
84
 
@@ -16,6 +16,7 @@ Actions and navigation with shared intents, variants, sizes, and loading, presse
16
16
  | `.soft` | Variant | Muted intent-tinted surface. |
17
17
  | `.outline` | Variant | Transparent with an intent-colored border. |
18
18
  | `.solid` | Variant | Filled intent background — the `.btn` default. |
19
+ | `.surface` | Variant | Page surface with a neutral border; the intent colors the label only. |
19
20
  | `.ghost` | Variant | Borderless text button (button-only). |
20
21
  | `.link` | Variant | Underlined text button (button-only). |
21
22
  | `.sm` / `.lg` | Size | Compact or large control height. |
@@ -53,8 +54,12 @@ Shared intent classes set color meaning. They compose with every variant.
53
54
 
54
55
  ## Variants
55
56
 
56
- `.ghost` and `.link` are button-only; `.soft`, `.outline`, and `.solid` are
57
- shared with other components.
57
+ `.ghost` and `.link` are button-only; `.soft`, `.outline`, `.solid`, and
58
+ `.surface` are shared with other components.
59
+
60
+ Reach for `.surface` when a secondary action must sit on an opaque, neutral
61
+ chip: `.outline` lets whatever is behind it show through, and `.soft` is the
62
+ intent-tinted treatment (its baseline fallback uses `--surface-subtle`).
58
63
 
59
64
  Transparent treatments (`.outline`, `.ghost`, `.link`) follow the surrounding
60
65
  foreground by default, including on a contrasting `.inverted` surface. A local
@@ -79,6 +84,8 @@ container. For a plain text link, use a bare `<a>` with no class.
79
84
  <button class="btn danger outline" type="button">Danger outline</button>
80
85
  <button class="btn soft" type="button">Soft</button>
81
86
  <button class="btn danger soft" type="button">Danger soft</button>
87
+ <button class="btn surface" type="button">Surface</button>
88
+ <button class="btn danger surface" type="button">Danger surface</button>
82
89
  <button class="btn ghost" type="button">Ghost</button>
83
90
  <button class="btn link" type="button">Button as link</button>
84
91
  </div>
@@ -9,6 +9,7 @@ Flexible content container with optional header, body, and footer regions, plus
9
9
  | `.card` | Component | Neutral raised surface for grouped content. |
10
10
  | `.raised` | Variant | Elevated surface with a soft shadow. |
11
11
  | `.subtle` | Variant | Lower contrast against the page surface. |
12
+ | `.surface` | Variant | Shared variant: the page surface instead of `--surface-raised`, with the intent in the text only. |
12
13
  | `.inverted` | Surface modifier | Inverse surface treatment: paints `--surface-solid` and swaps the foreground to `--surface`, so it inverts in dark themes instead of always reading as dark. Apply as `class="card inverted"`, or on any block. |
13
14
  | `.compact` | Variant | Tighter padding for dense contexts. |
14
15
 
@@ -162,6 +163,11 @@ the surface tokens, so `.card.subtle.inverted` renders a subtle background with
162
163
  inverted foreground. Treat them as mutually exclusive, like two competing
163
164
  variants.
164
165
 
166
+ `.surface` is the shared variant on a card: it drops the card to the page
167
+ surface instead of `--surface-raised`, keeps the theme border, and lets an
168
+ intent class color the text only. `.raised` and `.subtle` are the card-local
169
+ treatments on either side of it.
170
+
165
171
  ```html demo
166
172
  <section class="grid">
167
173
  <article class="card raised">
@@ -178,6 +184,13 @@ variants.
178
184
  </hgroup>
179
185
  </article>
180
186
 
187
+ <article class="card surface">
188
+ <hgroup>
189
+ <h3>Surface</h3>
190
+ <p class="muted">The page surface with the theme border.</p>
191
+ </hgroup>
192
+ </article>
193
+
181
194
  <article class="card inverted stack">
182
195
  <hgroup>
183
196
  <h3>Inverted</h3>
@@ -0,0 +1,72 @@
1
+ # Data list
2
+
3
+ > A compact key/value layout for object properties.
4
+
5
+ Use `.data-list` to present the properties of a single object as simple
6
+ term–value pairs on a `<dl>`. It expects alternating `<dt>` and `<dd>` children
7
+ — one term, one value per property. Use a plain description list for
8
+ relationships that involve multiple terms or descriptions, and a `<table>` to
9
+ compare the same property across several objects.
10
+
11
+ ```html demo
12
+ <dl class="data-list">
13
+ <dt>Name</dt>
14
+ <dd>Alice Martin</dd>
15
+
16
+ <dt>Email</dt>
17
+ <dd>alice@example.com</dd>
18
+
19
+ <dt>Role</dt>
20
+ <dd>Administrator</dd>
21
+ </dl>
22
+ ```
23
+
24
+ ## Rich values
25
+
26
+ Values accept inline content such as badges, links, dates, or a `.cluster` of
27
+ related items. Terms and values remain baseline-aligned.
28
+
29
+ ```html demo
30
+ <dl class="data-list">
31
+ <dt>Status</dt>
32
+ <dd><span class="badge success">Active</span></dd>
33
+
34
+ <dt>Plan</dt>
35
+ <dd>Professional</dd>
36
+
37
+ <dt>Renewal</dt>
38
+ <dd><time datetime="2026-09-14">14 Sep 2026</time></dd>
39
+ </dl>
40
+ ```
41
+
42
+ ## In a card
43
+
44
+ `.data-list` composes with `.card` and other layout primitives for business
45
+ metadata.
46
+
47
+ ```html demo
48
+ <article class="card stack">
49
+ <h3>Storage details</h3>
50
+
51
+ <dl class="data-list">
52
+ <dt>Used</dt>
53
+ <dd>6.7 GB</dd>
54
+
55
+ <dt>Available</dt>
56
+ <dd>1.3 GB</dd>
57
+
58
+ <dt>Files</dt>
59
+ <dd>12,492</dd>
60
+ </dl>
61
+ </article>
62
+ ```
63
+
64
+ ## Contract
65
+
66
+ `.data-list` styles one `<dt>` followed by one `<dd>` per property. A valid
67
+ `<dl>` may group several terms with one description or the reverse, but that
68
+ markup does not belong in `.data-list` — use a plain description list instead,
69
+ where `.prose` already covers the editorial cases.
70
+
71
+ Use `.data-list` for the properties of a single object. Use a `<table>` when
72
+ comparing the same properties across multiple objects.
@@ -2,6 +2,8 @@
2
2
 
3
3
  > Centered modal overlay for focused tasks, confirmations, or forms, built on the native `<dialog>` element.
4
4
 
5
+ **Related terms:** modal, alert dialog, confirmation dialog.
6
+
5
7
  ## Class reference
6
8
 
7
9
  | Class | Kind | Description |
@@ -2,6 +2,8 @@
2
2
 
3
3
  > Modal side-sheet that overlays the page for navigation or filters.
4
4
 
5
+ **Related terms:** off-canvas, side panel, side sheet, navigation drawer.
6
+
5
7
  ## Class reference
6
8
 
7
9
  | Class | Kind | Description |
@@ -7,6 +7,8 @@ The existing button component owns its size, shape, intent, and interaction.
7
7
  As a direct child of `.app-layout`, it instead overlays the main grid area so
8
8
  the layout keeps it clear of adaptive application navigation and safe areas.
9
9
 
10
+ **Related terms:** fab.
11
+
10
12
  ```html
11
13
  <div class="fab">
12
14
  <button class="btn circle primary lg icon-only" type="button" aria-label="Create">
@@ -3,6 +3,8 @@
3
3
  > Scalar measurement within a known range, not a progress indicator.
4
4
 
5
5
  - Use native `<meter class="meter">`.
6
+ - Use a segmented `<div class="meter" role="meter">` when the measurement
7
+ needs a category breakdown.
6
8
  - Shares progress styling DNA.
7
9
  - Represents a scalar measurement within a known range, not task completion.
8
10
  - Use explicit bar height to avoid padding issues.
@@ -18,6 +20,59 @@
18
20
  The three standard meter zones (optimum, suboptimum, even-less-good) map to the
19
21
  shared `--success`, `--warning`, and `--danger` colors.
20
22
 
21
- ## CSS hooks
23
+ Segmented meters keep the overall measurement on the meter itself and expose
24
+ the category breakdown as composition. A segmented `.meter` is a flex container
25
+ whose direct children are weighted by `--meter-value`; child intent classes
26
+ define segment colors. The values are relative weights, so they do not need to
27
+ be converted to percentages. Keep the segments decorative with
28
+ `aria-hidden="true"` and provide category names and values in a nearby legend.
29
+ An unintentional child remains transparent, so the meter background can
30
+ represent an available or otherwise unclassified portion. Match its legend
31
+ marker to that background rather than to the neutral intent.
32
+
33
+ ```html demo
34
+ <div class="stack">
35
+ <div
36
+ class="meter"
37
+ role="meter"
38
+ aria-label="Storage usage"
39
+ aria-valuemin="0"
40
+ aria-valuemax="8192"
41
+ aria-valuenow="6854.45"
42
+ aria-valuetext="6854.45 MB used of 8 GB"
43
+ >
44
+ <span class="primary" style="--meter-value: 4600" aria-hidden="true"></span>
45
+ <span class="secondary" style="--meter-value: 1350" aria-hidden="true"></span>
46
+ <span class="success" style="--meter-value: 904.45" aria-hidden="true"></span>
47
+ <span style="--meter-value: 1337.55" aria-hidden="true"></span>
48
+ </div>
49
+
50
+ <div class="cluster">
51
+ <span>
52
+ <span class="badge primary" aria-hidden="true"></span>
53
+ Regular <span class="muted">4600 MB</span>
54
+ </span>
55
+ <span>
56
+ <span class="badge secondary" aria-hidden="true"></span>
57
+ System <span class="muted">1350 MB</span>
58
+ </span>
59
+ <span>
60
+ <span class="badge success" aria-hidden="true"></span>
61
+ Shared <span class="muted">904.45 MB</span>
62
+ </span>
63
+ <span>
64
+ <span class="badge" style="--intent: var(--surface-subtle)" aria-hidden="true"></span>
65
+ Free <span class="muted">1337.55 MB</span>
66
+ </span>
67
+ </div>
68
+ </div>
69
+ ```
70
+
71
+ ### Hooks
22
72
 
23
73
  - `--bar-height` — bar thickness, shared with `.progress`.
74
+ - `--meter-value` — relative weight of a direct child in a segmented meter.
75
+
76
+ `.sm` / `.lg` are intentionally not supported: a bar keeps its inline size and only changes
77
+ thickness, and a design asks for a 4px or a 12px bar, not for a "small" one. Set the hook
78
+ on the element, or better, on an application class.
@@ -27,3 +27,13 @@
27
27
  - `--progress-track` — unfilled track color.
28
28
  - `--progress-value` — filled bar color. Prefer an intent class over setting this directly.
29
29
  - `--bar-height` — bar thickness, shared with `.meter`.
30
+
31
+ `.sm` / `.lg` are intentionally not supported: a bar keeps its inline size and only changes
32
+ thickness, and a design asks for a 4px or a 12px bar, not for a "small" one. Set the hook
33
+ on the element, or better, on an application class.
34
+
35
+ ```css
36
+ .storage-meter {
37
+ --bar-height: 0.375rem;
38
+ }
39
+ ```
@@ -8,6 +8,8 @@
8
8
  - Use layout utilities for placeholder arrangement.
9
9
  - Only relevant for simple placeholders - use more structural solutions like phantom-ui for complex cases.
10
10
 
11
+ **Related terms:** skeleton screen, shimmer, loading placeholder.
12
+
11
13
  ## Class reference
12
14
 
13
15
  | Class | Kind | Description |