actual-css 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/README.md +2 -1
- package/components.json +2 -0
- package/dist/actual.css +26 -6
- package/dist/actual.full.css +260 -74
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/dist/actual.min.css +1 -1
- package/docs/pages/components/table.md +3 -2
- package/docs/pages/examples/overview.md +1 -0
- package/docs/pages/forms/input-policy.md +59 -0
- package/docs/pages/forms/validation.md +37 -1
- package/docs/pages/foundations/tokens.md +23 -4
- package/docs/pages/foundations/typography.md +10 -0
- package/docs/pages/guides/introduction.md +2 -1
- package/docs/pages/guides/progressive-enhancement.md +15 -10
- package/package.json +160 -159
- package/src/css/components/accordion.css +2 -0
- package/src/css/components/actions.css +2 -0
- package/src/css/components/alert.css +2 -0
- package/src/css/components/app-nav.css +2 -0
- package/src/css/components/avatar.css +2 -0
- package/src/css/components/badge.css +2 -0
- package/src/css/components/breadcrumb.css +2 -0
- package/src/css/components/busy.css +2 -0
- package/src/css/components/button.css +2 -0
- package/src/css/components/card.css +2 -0
- package/src/css/components/chat.css +2 -0
- package/src/css/components/data-list.css +2 -0
- package/src/css/components/drawer.css +35 -22
- package/src/css/components/fab.css +2 -0
- package/src/css/components/flyout.css +2 -0
- package/src/css/components/indicator.css +2 -0
- package/src/css/components/join.css +2 -0
- package/src/css/components/key.css +2 -0
- package/src/css/components/list.css +2 -0
- package/src/css/components/meter.css +2 -0
- package/src/css/components/modal.css +47 -40
- package/src/css/components/navbar.css +2 -0
- package/src/css/components/pagination.css +2 -0
- package/src/css/components/progress.css +2 -0
- package/src/css/components/rating.css +2 -0
- package/src/css/components/scrollspy.css +2 -0
- package/src/css/components/skeleton.css +2 -0
- package/src/css/components/spinner.css +2 -0
- package/src/css/components/status-bar.css +2 -0
- package/src/css/components/steps.css +2 -0
- package/src/css/components/tab.css +2 -0
- package/src/css/components/table.css +2 -0
- package/src/css/components/tooltip.css +2 -0
- package/src/css/core/base.css +2 -0
- package/src/css/core/print.css +3 -0
- package/src/css/core/tokens.css +2 -0
- package/src/css/core/variants.css +19 -6
- package/src/css/effects/aura.css +2 -0
- package/src/css/forms/choice-card.css +4 -0
- package/src/css/forms/control.css +2 -0
- package/src/css/forms/floating-field.css +5 -1
- package/src/css/forms/form-actions.css +2 -0
- package/src/css/forms/form.css +8 -0
- package/src/css/forms/input-icon.css +2 -0
- package/src/css/forms/native.css +2 -0
- package/src/css/forms/otp.css +10 -0
- package/src/css/forms/range.css +2 -0
- package/src/css/forms/select.css +2 -0
- package/src/css/forms/switch.css +2 -0
- package/src/css/forms/validation.css +2 -0
- package/src/css/layout/app-layout.css +2 -0
- package/src/css/layout/app-shell.css +5 -1
- package/src/css/layout/center.css +2 -0
- package/src/css/layout/cluster.css +2 -0
- package/src/css/layout/column-layout.css +2 -0
- package/src/css/layout/frame.css +2 -0
- package/src/css/layout/grid.css +2 -0
- package/src/css/layout/media.css +2 -1
- package/src/css/layout/scroll-snap.css +2 -0
- package/src/css/layout/scroll-target.css +2 -0
- package/src/css/layout/sidebar-layout.css +2 -0
- package/src/css/layout/stack.css +2 -1
- package/src/css/layout/switcher.css +2 -0
- package/src/css/layout/topbar.css +2 -0
- package/src/css/typography/fluid.css +2 -0
- package/src/css/typography/lead.css +2 -1
- package/src/css/typography/overline.css +2 -0
- package/src/css/typography/prose.css +2 -0
- package/src/css/utilities/base.css +6 -1
- package/src/js/context-menu.js +56 -15
- package/src/js/enhancement-loader.js +5 -1
- package/src/js/escape.js +11 -1
- package/src/js/flyout.js +6 -1
- package/src/js/focus.js +8 -3
- package/src/js/surface.js +28 -0
- package/src/js/tooltip.js +10 -4
- package/src/js/validation.js +31 -0
package/dist/actual.min.css
CHANGED
|
@@ -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-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --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); --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: var(--font-size-md); --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"); --icon-plus: 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='12' y1='5' x2='12' y2='19'/%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3C/svg%3E"); --viewport-block: 100vh; --viewport-inline: 100vw; --z-sticky: 10; --z-menu: 20; --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, [data-theme] { --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); } :where([data-theme]:not([data-theme="light"]):not([data-theme="dark"])) { --primary-soft-fg: initial; --secondary-soft-fg: initial; --success-soft-fg: initial; --warning-soft-fg: initial; --danger-soft-fg: initial; --neutral-soft-fg: initial; } :root, [data-theme="light"], [data-theme="dark"] { --secondary-soft-fg: color-mix(in oklab, var(--text) 10%, var(--secondary) 90%); --success-soft-fg: color-mix(in oklab, var(--text) 15%, var(--success) 85%); --warning-soft-fg: color-mix(in oklab, var(--text) 25%, var(--warning) 75%); --danger-soft-fg: color-mix(in oklab, var(--text) 15%, var(--danger) 85%); --neutral-soft-fg: color-mix(in oklab, var(--text) 20%, var(--neutral) 80%); } :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(--hr-space, var(--space-60)); margin-inline: 0; } code, kbd, samp, pre { font-family: var(--font-mono); } pre { overflow-x: auto; } 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); --intent-soft-fg: var(--primary-soft-fg); } .secondary { --intent: var(--secondary); --intent-fg: var(--secondary-fg); --intent-soft-fg: var(--secondary-soft-fg); } .success { --intent: var(--success); --intent-fg: var(--success-fg); --intent-soft-fg: var(--success-soft-fg); } .warning { --intent: var(--warning); --intent-fg: var(--warning-fg); --intent-soft-fg: var(--warning-soft-fg); } .danger { --intent: var(--danger); --intent-fg: var(--danger-fg); --intent-soft-fg: var(--danger-soft-fg); } .neutral { --intent: var(--neutral); --intent-fg: var(--neutral-fg); --intent-soft-fg: var(--neutral-soft-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); } .compact { --gap: var(--space-20); --density-space: var(--space-20); --control-size: var(--control-size-sm); } .spacious { --gap: var(--space-50); --density-space: var(--space-50); --control-size: var(--control-size-lg); } :where( .btn, .input, .textarea, .select, .color, .file, .field, .choice, .input-icon, .otp, .join, .pagination, .tabs, .tab, .flyout ).sm { --control-size: var(--control-size-sm); --control-font-size: var(--font-size-sm); --control-pad-x: var(--font-size-sm); } :where( .btn, .input, .textarea, .select, .color, .file, .field, .choice, .input-icon, .otp, .join, .pagination, .tabs, .tab, .flyout ).lg { --control-size: var(--control-size-lg); --control-font-size: var(--font-size-lg); --control-pad-x: var(--font-size-lg); } @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: var( --intent-soft-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; --font-width: 100%; --font-width-dense: 95%; --line-height: 1.5; --line-height-tight: 1.25; --line-height-relaxed: 1.75; --font-weight-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --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); --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: var(--font-size-md); --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"); --icon-plus: 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='12' y1='5' x2='12' y2='19'/%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3C/svg%3E"); --viewport-block: 100vh; --viewport-inline: 100vw; --z-sticky: 10; --z-menu: 20; --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, [data-theme] { --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); } :where([data-theme]:not([data-theme="light"]):not([data-theme="dark"])) { --primary-soft-fg: initial; --secondary-soft-fg: initial; --success-soft-fg: initial; --warning-soft-fg: initial; --danger-soft-fg: initial; --neutral-soft-fg: initial; } :root, [data-theme="light"], [data-theme="dark"] { --secondary-soft-fg: color-mix(in oklab, var(--text) 10%, var(--secondary) 90%); --success-soft-fg: color-mix(in oklab, var(--text) 15%, var(--success) 85%); --warning-soft-fg: color-mix(in oklab, var(--text) 25%, var(--warning) 75%); --danger-soft-fg: color-mix(in oklab, var(--text) 15%, var(--danger) 85%); --neutral-soft-fg: color-mix(in oklab, var(--text) 20%, var(--neutral) 80%); } :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); font-stretch: var(--font-width); 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(--hr-space, var(--space-60)); margin-inline: 0; } code, kbd, samp, pre { font-family: var(--font-mono); } pre { overflow-x: auto; } 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); --intent-soft-fg: var(--primary-soft-fg); } .secondary { --intent: var(--secondary); --intent-fg: var(--secondary-fg); --intent-soft-fg: var(--secondary-soft-fg); } .success { --intent: var(--success); --intent-fg: var(--success-fg); --intent-soft-fg: var(--success-soft-fg); } .warning { --intent: var(--warning); --intent-fg: var(--warning-fg); --intent-soft-fg: var(--warning-soft-fg); } .danger { --intent: var(--danger); --intent-fg: var(--danger-fg); --intent-soft-fg: var(--danger-soft-fg); } .neutral { --intent: var(--neutral); --intent-fg: var(--neutral-fg); --intent-soft-fg: var(--neutral-soft-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); } .compact { --gap: var(--space-20); --density-space: var(--space-20); --control-size: var(--control-size-sm); font-stretch: var(--font-width-dense); } .spacious { --gap: var(--space-50); --density-space: var(--space-50); --control-size: var(--control-size-lg); font-stretch: var(--font-width); } :where( .btn, .input, .textarea, .select, .color, .file, .field, .choice, .choice-card, .input-icon, .floating-field, .otp, .join, .pagination, .tabs, .tab, .flyout ).sm { --control-size: var(--control-size-sm); --control-font-size: var(--font-size-sm); --control-pad-x: var(--font-size-sm); } :where( .btn, .input, .textarea, .select, .color, .file, .field, .choice, .choice-card, .input-icon, .floating-field, .otp, .join, .pagination, .tabs, .tab, .flyout ).lg { --control-size: var(--control-size-lg); --control-font-size: var(--font-size-lg); --control-pad-x: var(--font-size-lg); } @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: var( --intent-soft-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, [data-theme][data-theme], body { color-scheme: light; background: #fff; color: #000; } h1, h2, h3, h4, h5, h6 { break-after: avoid; } }
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
| ---------------- | --------- | ------------------------------------------------------------ |
|
|
17
17
|
| `.table-wrap` | Component | Scroll container and outer chrome (surface, border, radius). |
|
|
18
18
|
| `.table` | Component | The table itself; row and cell rules. |
|
|
19
|
-
| `.table.compact` | Density | Tighter cell padding;
|
|
19
|
+
| `.table.compact` | Density | Tighter cell padding; font size and icons unchanged. |
|
|
20
20
|
|
|
21
21
|
## Basic usage
|
|
22
22
|
|
|
@@ -101,7 +101,8 @@ table width before the wrapper scrolls horizontally.
|
|
|
101
101
|
## Compact tables
|
|
102
102
|
|
|
103
103
|
`.compact` lowers cell padding and establishes compact density for controls or
|
|
104
|
-
spacing helpers inside cells.
|
|
104
|
+
spacing helpers inside cells. Font size and icons stay unchanged; the framework's
|
|
105
|
+
denser font width may apply when the typeface supports it. It is the
|
|
105
106
|
same vocabulary as `.card.compact`.
|
|
106
107
|
|
|
107
108
|
```html demo
|
|
@@ -30,6 +30,7 @@ Whole pages, composed the way a real product would.
|
|
|
30
30
|
- [Settings modal](../../demo/templates/settings-modal.html) - two-pane preferences dialog, with the local CSS it needs audited rule by rule
|
|
31
31
|
- [Workspace settings](../../demo/templates/settings-pricing.html) - plans with visible native choices, preferences and a local segmented tab treatment
|
|
32
32
|
- [Card compositions](../../demo/templates/card-compositions.html) - editorial, team, sign-in and horizontal cards composed from existing primitives
|
|
33
|
+
- [Register form](../../demo/templates/register-form.html) - registration page with choice cards, masks, the password toggle, `tel-prefix` validation, an OTP verification dialog, and status feedback
|
|
33
34
|
|
|
34
35
|
### Layout references
|
|
35
36
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
> Native input intent first. Actual's runtime filters values only when explicitly asked, and fixed-shape masks stay opt-in through `data-mask`.
|
|
4
4
|
|
|
5
|
+
**Related terms:** telephone, phone, tel-prefix.
|
|
6
|
+
|
|
5
7
|
`inputmode` changes the virtual keyboard but does not constrain the value. Actual
|
|
6
8
|
does not filter plain `inputmode` fields by default because numeric entry often
|
|
7
9
|
needs temporary, signed, grouped, or locale-specific values while the user types.
|
|
@@ -89,3 +91,60 @@ Use `data-mask` when the input has a fixed shape. Tokens are `9` for a digit,
|
|
|
89
91
|
|
|
90
92
|
Masks enforce shape, not domain validity. Use application validation when a date
|
|
91
93
|
must reject impossible values such as `2026-13-40`.
|
|
94
|
+
|
|
95
|
+
### Telephone input
|
|
96
|
+
|
|
97
|
+
Do not mask telephone numbers by default. Phone formatting varies by country,
|
|
98
|
+
device, copied source, and user convention. Preserve the entered value, apply
|
|
99
|
+
only coarse client-side policy, and normalize or validate the actual number on
|
|
100
|
+
the server.
|
|
101
|
+
|
|
102
|
+
`tel-prefix` is that coarse policy: a local number passes, and an explicit
|
|
103
|
+
international number must match one of the allowed prefixes. It analyzes a copy
|
|
104
|
+
of the value and never rewrites it.
|
|
105
|
+
|
|
106
|
+
```html demo
|
|
107
|
+
<form class="needs-validation stack" data-enhance="validation" novalidate>
|
|
108
|
+
<label class="field">
|
|
109
|
+
<span class="field-label">Phone</span>
|
|
110
|
+
<input
|
|
111
|
+
class="input"
|
|
112
|
+
type="tel"
|
|
113
|
+
name="phone"
|
|
114
|
+
inputmode="tel"
|
|
115
|
+
autocomplete="tel"
|
|
116
|
+
data-validation-rules="tel-prefix +32"
|
|
117
|
+
aria-describedby="phone-help phone-error"
|
|
118
|
+
/>
|
|
119
|
+
<span class="field-help" id="phone-help">
|
|
120
|
+
Local number or +32 international number.
|
|
121
|
+
</span>
|
|
122
|
+
<span class="field-error" id="phone-error">
|
|
123
|
+
Use a local number or an allowed international prefix.
|
|
124
|
+
</span>
|
|
125
|
+
</label>
|
|
126
|
+
|
|
127
|
+
<div class="form-actions">
|
|
128
|
+
<button class="btn primary" type="submit">Submit</button>
|
|
129
|
+
<button class="btn neutral outline" type="reset">Reset</button>
|
|
130
|
+
</div>
|
|
131
|
+
</form>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
| Input | `tel-prefix +32` |
|
|
135
|
+
| -------------------- | ---------------- |
|
|
136
|
+
| `0470 12 34 56` | passes |
|
|
137
|
+
| `02/123.45.67` | passes |
|
|
138
|
+
| `+32 470 12 34 56` | passes |
|
|
139
|
+
| `0032 470 12 34 56` | passes |
|
|
140
|
+
| `(+32) 470 12 34 56` | passes |
|
|
141
|
+
| `0033 6 12 34 56 78` | fails |
|
|
142
|
+
| `+33 6 12 34 56 78` | fails |
|
|
143
|
+
|
|
144
|
+
A local number passes in any usual notation, but its digits are required: once
|
|
145
|
+
the separators are ignored, letters and stray symbols fail, so a pasted word is
|
|
146
|
+
never read as a local number. An explicit international number is normalized
|
|
147
|
+
for comparison — `0032…`, `(+32)…`, and `+32 (0)…` all read as `+32` — and a
|
|
148
|
+
foreign prefix fails. With no prefix, only local input passes. This is an origin
|
|
149
|
+
policy, not phone validation: region detection, number semantics, and E.164
|
|
150
|
+
normalization stay on the server. See [validation](validation.md).
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Inline error feedback that respects assistive technology and form state.
|
|
4
4
|
|
|
5
|
-
**Related terms:** inline validation, field error.
|
|
5
|
+
**Related terms:** inline validation, field error, telephone input.
|
|
6
6
|
|
|
7
7
|
## Class reference
|
|
8
8
|
|
|
@@ -162,10 +162,46 @@ Custom rules live in `data-validation-rules` as a comma-separated list; each rul
|
|
|
162
162
|
- `number` — Value is empty or a valid `Number`.
|
|
163
163
|
- `digits` — Value is empty or ASCII digits only.
|
|
164
164
|
- `alnum` — Value is empty or letters and digits only.
|
|
165
|
+
- `starts-with <token>…` — Value must begin with one of the literal tokens (`starts-with INV- CR-`). The value is trimmed before comparison; the match is case-sensitive.
|
|
166
|
+
- `ends-with <token>…` — Value must end with one of the literal tokens (`ends-with .pdf .csv`). Trimmed, case-sensitive.
|
|
165
167
|
- `date` — Value is a valid date in `yyyy-mm-dd`, `dd/mm/yyyy`, `mm/dd/yyyy`, `dd.mm.yyyy`, `mm.dd.yyyy`, `dd-mm-yyyy`, or `mm-dd-yyyy` shape; rejects non-existent dates like `2026-02-29`. ISO remains the recommended wire format because servers can parse it without guessing.
|
|
168
|
+
- `tel-prefix <prefix>…` — A local number (its digits, with the usual separators `space ( ) . / -` ignored) passes; an explicit international number is normalized (`00` to `+`) and must match one of the literal prefixes (`tel-prefix +32`, `tel-prefix +32 +33 +352`). A value that is not dialing digits fails. With no prefix, only local input passes. The entered value is never modified.
|
|
169
|
+
|
|
170
|
+
`starts-with` and `ends-with` options are literal tokens: no quoting, no escaping, and a token cannot contain whitespace. Without a token they fail rather than passing silently. `tel-prefix` without a prefix is instead a deliberate local-only policy.
|
|
166
171
|
|
|
167
172
|
Add your own with `FormValidator.registerRule(name, (value, el, ...opts) => boolean)`. The built-in rules treat empty values as valid so optional fields stay optional; custom rules should do the same when that behavior is wanted.
|
|
168
173
|
|
|
174
|
+
```html demo
|
|
175
|
+
<form class="needs-validation stack" data-enhance="validation" novalidate>
|
|
176
|
+
<label class="field">
|
|
177
|
+
<span class="field-label">Invoice reference</span>
|
|
178
|
+
<input class="input" name="reference"
|
|
179
|
+
data-validation-rules="starts-with INV- CR-"
|
|
180
|
+
aria-describedby="reference-help reference-error"
|
|
181
|
+
required />
|
|
182
|
+
<span class="field-help" id="reference-help">Start with INV- or CR-.</span>
|
|
183
|
+
<span class="field-error" id="reference-error">Use an INV- or CR- reference.</span>
|
|
184
|
+
</label>
|
|
185
|
+
|
|
186
|
+
<label class="field">
|
|
187
|
+
<span class="field-label">Attachment filename</span>
|
|
188
|
+
<input class="input" name="attachment"
|
|
189
|
+
data-validation-rules="ends-with .pdf .csv"
|
|
190
|
+
aria-describedby="attachment-help attachment-error"
|
|
191
|
+
required />
|
|
192
|
+
<span class="field-help" id="attachment-help">A .pdf or .csv filename.</span>
|
|
193
|
+
<span class="field-error" id="attachment-error">Choose a .pdf or .csv file.</span>
|
|
194
|
+
</label>
|
|
195
|
+
|
|
196
|
+
<div class="form-actions">
|
|
197
|
+
<button class="btn primary" type="submit">Submit</button>
|
|
198
|
+
<button class="btn neutral outline" type="reset">Reset</button>
|
|
199
|
+
</div>
|
|
200
|
+
</form>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
More specific telephone policies — determining the actual region, validating number semantics, or producing E.164 — belong in application or server code, registered through `FormValidator.registerRule()`.
|
|
204
|
+
|
|
169
205
|
The `date` rule pairs naturally with `data-mask` — the mask structures input, the rule validates meaning. Same rule, different formats:
|
|
170
206
|
|
|
171
207
|
```html demo
|
|
@@ -211,6 +211,8 @@ Typography tokens cover the document baseline and common component needs.
|
|
|
211
211
|
:root {
|
|
212
212
|
--font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
213
213
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
214
|
+
--font-width: 100%;
|
|
215
|
+
--font-width-dense: 95%;
|
|
214
216
|
|
|
215
217
|
--line-height: 1.5;
|
|
216
218
|
--font-weight-normal: 400;
|
|
@@ -264,24 +266,41 @@ Disabled controls and disabled-prone components read `--disabled-opacity` for th
|
|
|
264
266
|
|
|
265
267
|
### Density
|
|
266
268
|
|
|
267
|
-
Density contexts swap shared spacing and geometry
|
|
269
|
+
Density contexts swap shared spacing and geometry, plus a slight typographic
|
|
270
|
+
width bonus:
|
|
268
271
|
|
|
269
272
|
```css
|
|
273
|
+
:root {
|
|
274
|
+
--font-width: 100%;
|
|
275
|
+
--font-width-dense: 95%;
|
|
276
|
+
}
|
|
277
|
+
|
|
270
278
|
.compact {
|
|
271
279
|
--gap: var(--space-20);
|
|
272
280
|
--density-space: var(--space-20);
|
|
273
281
|
--control-size: var(--control-size-sm);
|
|
282
|
+
font-stretch: var(--font-width-dense);
|
|
274
283
|
}
|
|
275
284
|
|
|
276
285
|
.spacious {
|
|
277
286
|
--gap: var(--space-50);
|
|
278
287
|
--density-space: var(--space-50);
|
|
279
288
|
--control-size: var(--control-size-lg);
|
|
289
|
+
font-stretch: var(--font-width);
|
|
280
290
|
}
|
|
281
291
|
```
|
|
282
292
|
|
|
283
|
-
Density
|
|
284
|
-
|
|
293
|
+
Density never changes font size or icon size — it may only use the denser font
|
|
294
|
+
width. `font-stretch` uses a variable `wdth` axis when available, or
|
|
295
|
+
participates in normal width-face matching for families that expose multiple
|
|
296
|
+
widths. Layout must never depend on the narrowing being available. Compact
|
|
297
|
+
density may slightly narrow variable typefaces when they support a width axis.
|
|
298
|
+
This is a density enhancement, not an overflow strategy: never condense a
|
|
299
|
+
component just because its label overflows at some viewport — that hides a
|
|
300
|
+
composition problem. A theme whose typeface has no useful width axis opts out
|
|
301
|
+
with `--font-width-dense: 100%`.
|
|
302
|
+
|
|
303
|
+
Participation is opt-in and selective: controls consume `--control-size`
|
|
285
304
|
for geometry; spacing helpers and rhythms consume `--density-space` and `--gap`;
|
|
286
305
|
components whose size is intrinsic (badge, avatar, spinner, rating, key, prose,
|
|
287
306
|
inline choice/switch) keep their own scales. A local component size overrides
|
|
@@ -505,7 +524,7 @@ Because the native select chevron is an SVG background image and cannot inherit
|
|
|
505
524
|
|
|
506
525
|
Under `prefers-contrast: more`, the user's contrast preference wins over a theme's palette by contract: the core collapses `--text-muted`/`--text-subtle` to `--text` and thickens borders and focus so no theme can defeat a legibility requirement. This is a deliberate decision, not a specificity quirk — the repeated `[data-theme][data-theme]` attribute only outranks a preset theme stylesheet loaded after the core.
|
|
507
526
|
|
|
508
|
-
Under `forced-colors: active`, the browser automatically remaps `color`, `background`, and `border` properties to system colors. The
|
|
527
|
+
Under `forced-colors: active`, the browser automatically remaps `color`, `background`, and `border` properties to system colors. The core only adjusts what the remapping cannot express on its own — `--state-disabled` becomes `GrayText` and `--disabled-opacity` becomes `1` — declared on `:root` and `[data-theme]` so every theme boundary follows. Keep local forced-color rules only for custom control geometry, native bar parts, or selected/checked states that would otherwise be communicated only by background color.
|
|
509
528
|
|
|
510
529
|
## Component mapping
|
|
511
530
|
|
|
@@ -113,6 +113,16 @@ the document) and `--font-mono` (for `code` / `kbd` / `samp` / `pre`). There is
|
|
|
113
113
|
theme-level token in your own theme when a project actually needs one; the
|
|
114
114
|
baseline stays sans so app UI inherits predictably.
|
|
115
115
|
|
|
116
|
+
### Font width
|
|
117
|
+
|
|
118
|
+
Two public hooks carry the typographic side of density: `--font-width` (normal
|
|
119
|
+
width) and `--font-width-dense` (compact width). The document reads
|
|
120
|
+
`--font-width` through `font-stretch`; `.compact` applies
|
|
121
|
+
`--font-width-dense` and `.spacious` restores `--font-width`. A theme whose
|
|
122
|
+
typeface has no useful width axis sets `--font-width-dense: 100%`. Width is a
|
|
123
|
+
density token, not a responsive escape hatch — never condense text to fix an
|
|
124
|
+
overflowing label.
|
|
125
|
+
|
|
116
126
|
## Font weights
|
|
117
127
|
|
|
118
128
|
The framework exposes a small set of weights:
|
|
@@ -80,7 +80,8 @@ shared by buttons, badges, alerts, and cards; `.inverted` is a shared surface
|
|
|
80
80
|
modifier that paints any block with the inverse surface. `.ghost` and `.link`
|
|
81
81
|
are button-only. Size variants `.sm` and `.lg` scale typography and
|
|
82
82
|
participating component geometry consistently. Density contexts `.compact` and
|
|
83
|
-
`.spacious` change geometry and spacing without changing
|
|
83
|
+
`.spacious` change geometry and spacing without changing font size or icons;
|
|
84
|
+
`.compact` may slightly narrow typefaces that support a width axis.
|
|
84
85
|
Undocumented `is-*` classes are runtime internals.
|
|
85
86
|
|
|
86
87
|
```html
|
|
@@ -286,14 +286,14 @@ The runtime dispatches a small set of public `actual:*` events. All are
|
|
|
286
286
|
listenable on the document; cancelable events veto the associated action when
|
|
287
287
|
`preventDefault()` is called.
|
|
288
288
|
|
|
289
|
-
| Event | Dispatch
|
|
290
|
-
| ---------------------- |
|
|
291
|
-
| `actual:surface-open` | before a surface opens
|
|
292
|
-
| `actual:context-menu` | before a context menu opens
|
|
293
|
-
| `actual:status` | status module, or any code
|
|
294
|
-
| `actual:invalid` | after a submit was blocked
|
|
295
|
-
| `actual:dialog-cancel` | before a native cancel close
|
|
296
|
-
| `actual:dismiss` | after `--dismiss`
|
|
289
|
+
| Event | Dispatch | `detail` |
|
|
290
|
+
| ---------------------- | ----------------------------- | ------------------------------------ |
|
|
291
|
+
| `actual:surface-open` | before a surface opens | `{ surface, options }` |
|
|
292
|
+
| `actual:context-menu` | before a context menu opens | `{ menu, context, origin, trigger }` |
|
|
293
|
+
| `actual:status` | status module, or any code | `{ message, intent, duration }` |
|
|
294
|
+
| `actual:invalid` | after a submit was blocked | `{ form, firstInvalid, message }` |
|
|
295
|
+
| `actual:dialog-cancel` | before a native cancel close | `{ dialog, sourceEvent }` |
|
|
296
|
+
| `actual:dismiss` | after a `--dismiss` dismissal | `{ trigger }` |
|
|
297
297
|
|
|
298
298
|
`actual:surface-open`, `actual:context-menu` and `actual:dialog-cancel` are
|
|
299
299
|
cancelable. `actual:status` both shows and clears the status bar — omit
|
|
@@ -343,6 +343,10 @@ applications that unload the module owning the command.
|
|
|
343
343
|
The runtime includes a generic `--dismiss` command for hiding any resolved
|
|
344
344
|
target. It prevents the trigger's default action, sets `hidden`, and emits a
|
|
345
345
|
bubbling `actual:dismiss` event with the trigger in `event.detail.trigger`.
|
|
346
|
+
On an open surface (flyout, context menu), the surface consumes the event to
|
|
347
|
+
close through its own lifecycle instead of staying hidden — by the time the
|
|
348
|
+
event reaches document listeners, `hidden` has already been lifted again and
|
|
349
|
+
the surface reads as closed, not merely attribute-hidden.
|
|
346
350
|
|
|
347
351
|
```html demo
|
|
348
352
|
<section id="notice" class="alert">
|
|
@@ -781,8 +785,9 @@ document.addEventListener("actual:invalid", (event) => {
|
|
|
781
785
|
|
|
782
786
|
Custom rules go in `data-validation-rules` and resolve through
|
|
783
787
|
`FormValidator.registerRule`. Built-in rules are `same`, `number`, `digits`,
|
|
784
|
-
`alnum`, and `
|
|
785
|
-
errors flow back through
|
|
788
|
+
`alnum`, `starts-with`, `ends-with`, `date`, and `tel-prefix`. The library never
|
|
789
|
+
performs network validation; server errors flow back through
|
|
790
|
+
`FormValidator.setErrors(form, { name: message })` and
|
|
786
791
|
`FormValidator.clearFieldError(field)`.
|
|
787
792
|
|
|
788
793
|
```js
|