actual-css 0.9.1 → 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.
@@ -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, .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; } }
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; typography is unchanged. |
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. Typography and icons stay unchanged. It is the
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 covers spacing and geometry only — it never touches typography or icon
284
- size. Participation is opt-in and selective: controls consume `--control-size`
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
@@ -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 typography or icons.
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
@@ -785,8 +785,9 @@ document.addEventListener("actual:invalid", (event) => {
785
785
 
786
786
  Custom rules go in `data-validation-rules` and resolve through
787
787
  `FormValidator.registerRule`. Built-in rules are `same`, `number`, `digits`,
788
- `alnum`, and `date`. The library never performs network validation; server
789
- errors flow back through `FormValidator.setErrors(form, { name: message })` and
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
790
791
  `FormValidator.clearFieldError(field)`.
791
792
 
792
793
  ```js
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "actual-css",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "Plain CSS component framework with semantic classes, universal variants, small tokens, theme hooks, and progressive enhancements.",
5
5
  "type": "module",
6
6
  "packageManager": "bun@1.4.2",
@@ -144,6 +144,7 @@
144
144
  "probe": "bun scripts/probe.js",
145
145
  "serve": "bun scripts/serve.js",
146
146
  "check:sync": "bun scripts/check-sync.js",
147
+ "release:notes": "bun scripts/release-notes.js",
147
148
  "test": "bun test",
148
149
  "test:changed": "bun test --changed=master",
149
150
  "build:all": "bun run lint && bun run build:dist && bun run build:js && bun run build:size && bun run format:docs && bun run build:docs && bun run check:docs && bun run check:doc-classes && bun run check:compat && bun run check:css-api && bun run check:color-space && bun run check:architecture && bun run check:enhance && bun run generate:reserved && bun run check:reserved && bun run generate:catalog && bun run check:catalog && bun run check:sync && bun run check:links && bun run check:templates && bun run check:markup && bun run test",
@@ -7,12 +7,14 @@
7
7
  * Public hooks:
8
8
  * --link
9
9
  * --hr-space
10
+ * --font-width / --font-width-dense (see variants.css for the dense application)
10
11
  */
11
12
 
12
13
  body {
13
14
  background: var(--surface);
14
15
  color: var(--text);
15
16
  font-family: var(--font-sans);
17
+ font-stretch: var(--font-width);
16
18
  line-height: var(--line-height);
17
19
  }
18
20
 
@@ -19,6 +19,8 @@
19
19
  /* Typography */
20
20
  --font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
21
21
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
22
+ --font-width: 100%;
23
+ --font-width-dense: 95%;
22
24
  --line-height: 1.5;
23
25
  --line-height-tight: 1.25;
24
26
  --line-height-relaxed: 1.75;
@@ -16,9 +16,16 @@
16
16
  * components/alert.css — .alert default soft behind explicit --ui-*
17
17
  *
18
18
  * .compact/.spacious set inherited density tokens: spacing rhythm and geometry
19
- * only. Density never changes typography or icon size. Controls consume
20
- * --control-size; non-control components may consume --density-* when the
21
- * context should affect their local rhythm.
19
+ * only, plus a slight typographic width bonus. Density never changes font size
20
+ * or icon size. Controls consume --control-size; non-control components may
21
+ * consume --density-* when the context should affect their local rhythm.
22
+ * .compact applies --font-width-dense through font-stretch (a variable wdth
23
+ * axis when available, otherwise normal width-face matching); layout must
24
+ * never depend on the narrowing being available. .spacious restores
25
+ * --font-width, so a spacious island inside a compact context returns to the
26
+ * normal width exactly as it restores its own gap, spacing, and control size.
27
+ * Themes whose typeface has no useful width axis opt out with
28
+ * --font-width-dense: 100%.
22
29
  *
23
30
  * .sm/.lg are local size roles, not shared measurements. Shared control
24
31
  * participants below map both typography and geometry; components with
@@ -83,12 +90,14 @@
83
90
  --gap: var(--space-20);
84
91
  --density-space: var(--space-20);
85
92
  --control-size: var(--control-size-sm);
93
+ font-stretch: var(--font-width-dense);
86
94
  }
87
95
 
88
96
  .spacious {
89
97
  --gap: var(--space-50);
90
98
  --density-space: var(--space-50);
91
99
  --control-size: var(--control-size-lg);
100
+ font-stretch: var(--font-width);
92
101
  }
93
102
 
94
103
  /* Shared control scale. Wrappers are listed when their descendants form one
@@ -18,6 +18,12 @@
18
18
  */
19
19
  .field {
20
20
  display: grid;
21
+
22
+ /* why: a field stretched by a taller row sibling — the classic case is a
23
+ neighbour revealing its .field-error — would otherwise hand the extra
24
+ block size to its auto rows and inflate the control. Rows keep their
25
+ natural height; the slack stays at the end. */
26
+ align-content: start;
21
27
  gap: var(--space-10);
22
28
  color: var(--text);
23
29
  font-size: var(--control-font-size);
@@ -86,9 +86,40 @@ const rules = {
86
86
  alnum(v) {
87
87
  return /^[a-z0-9]+$/i.test(v);
88
88
  },
89
+ // Options are literal tokens: no quoting, no escaping, no whitespace. A
90
+ // missing option is a configuration error and fails rather than passing
91
+ // silently. Comparison is case-sensitive; the value is trimmed to compare.
92
+ "starts-with"(v, _el, ...prefixes) {
93
+ const value = v.trim();
94
+ return prefixes.length > 0 && prefixes.some((prefix) => value.startsWith(prefix));
95
+ },
96
+ "ends-with"(v, _el, ...suffixes) {
97
+ const value = v.trim();
98
+ return suffixes.length > 0 && suffixes.some((suffix) => value.endsWith(suffix));
99
+ },
89
100
  date(v) {
90
101
  return isValidDate(v);
91
102
  },
103
+ // Telephone origin policy, not telephone validation. A local number carries
104
+ // nothing but its digits after the usual separators are ignored, so a value
105
+ // with letters or stray symbols is not a number and fails instead of reading
106
+ // as local. Local numbering still belongs to the application and the server;
107
+ // an explicit international number must match an allowed prefix. No prefixes
108
+ // means local-only: local input passes, any explicit international notation
109
+ // fails. The entered value is analyzed, never rewritten.
110
+ "tel-prefix"(value, _el, ...prefixes) {
111
+ let v = value.trim().replace(/[\s()./-]+/g, "");
112
+ if (v.startsWith("00")) {
113
+ v = `+${v.slice(2)}`;
114
+ }
115
+ if (!/^\+?\d+$/.test(v)) {
116
+ return false;
117
+ }
118
+ if (!v.startsWith("+")) {
119
+ return true;
120
+ }
121
+ return prefixes.some((prefix) => v.startsWith(prefix));
122
+ },
92
123
  };
93
124
 
94
125
  function isFormElement(el) {