@codenhub/styles 0.0.3 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/NOTICE +52 -0
- package/README.md +60 -90
- package/dist/aesthetics/chunky-tile.css +1 -0
- package/dist/aesthetics/glass.css +1 -0
- package/dist/aesthetics/index.css +2 -0
- package/dist/aesthetics/neobrutalism.css +1 -0
- package/dist/aesthetics/pixel.css +1 -0
- package/dist/components.css +2 -2
- package/dist/index.css +2 -2
- package/dist/native.css +2 -0
- package/dist/theme.css +2 -2
- package/dist/tw/aesthetics/chunky-tile.css +107 -0
- package/dist/tw/aesthetics/glass.css +66 -0
- package/dist/tw/aesthetics/index.css +9 -0
- package/dist/tw/aesthetics/neobrutalism.css +63 -0
- package/dist/tw/aesthetics/pixel.css +88 -0
- package/dist/tw/box.css +185 -0
- package/dist/tw/components/button.css +88 -0
- package/dist/tw/components/feedback.css +163 -0
- package/dist/tw/components/form.css +601 -0
- package/dist/tw/components/index.css +18 -0
- package/dist/tw/components/loader.css +122 -0
- package/dist/tw/components/surface.css +98 -0
- package/dist/tw/components/tooltip.css +132 -0
- package/dist/tw/elevation.css +39 -0
- package/dist/tw/index.css +12 -0
- package/dist/tw/intent.css +189 -0
- package/dist/tw/native.css +203 -0
- package/dist/tw/presentation.css +77 -0
- package/dist/tw/reset.css +142 -0
- package/dist/tw/theme.css +218 -0
- package/dist/tw/typography.css +34 -0
- package/dist/tw/utilities/content.css +349 -0
- package/dist/tw/utilities/index.css +6 -0
- package/dist/tw/utilities/layout.css +107 -0
- package/docs/accessibility.md +43 -14
- package/docs/concepts.md +94 -0
- package/docs/index.md +62 -73
- package/docs/integrating/astro.md +29 -0
- package/docs/integrating/index.md +32 -0
- package/docs/integrating/nextjs.md +44 -0
- package/docs/integrating/svelte.md +37 -0
- package/docs/integrating/tailwind.md +40 -0
- package/docs/integrating/vue.md +25 -0
- package/docs/setup.md +116 -0
- package/docs/usage/aesthetics.md +197 -0
- package/docs/usage/buttons.md +74 -0
- package/docs/usage/composing.md +218 -0
- package/docs/usage/content-and-layout.md +200 -0
- package/docs/usage/customizing.md +213 -0
- package/docs/usage/feedback.md +117 -0
- package/docs/usage/forms.md +177 -0
- package/docs/usage/index.md +32 -0
- package/docs/usage/theming.md +210 -0
- package/docs/usage/tooltips.md +45 -0
- package/llms-full.txt +2122 -0
- package/llms.txt +26 -0
- package/package.json +96 -32
- package/docs/classes.md +0 -198
- package/docs/roadmap.md +0 -34
- package/docs/tests.md +0 -128
- package/docs/tokens.md +0 -101
- package/src/components/button.css +0 -155
- package/src/components/feedback.css +0 -214
- package/src/components/form.css +0 -296
- package/src/components/index.css +0 -9
- package/src/components/loader.css +0 -56
- package/src/components/surface.css +0 -6
- package/src/components/tooltip.css +0 -60
- package/src/index.css +0 -7
- package/src/overrides.css +0 -76
- package/src/theme.css +0 -136
- package/src/typography.css +0 -30
- package/src/utilities.css +0 -40
package/dist/theme.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! tailwindcss v4.3.
|
|
2
|
-
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-amber-100:oklch(96.2% .059 95.617);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-800:oklch(47.3% .137 46.201);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-800:oklch(43.2% .095 166.913);--color-indigo-100:oklch(93% .034 272.788);--color-indigo-600:oklch(51.1% .262 276.966);--color-indigo-800:oklch(39.8% .195 277.366);--color-rose-100:oklch(94.1% .03 12.58);--color-rose-700:oklch(51.4% .222 16.935);--color-rose-800:oklch(45.5% .188 13.697);--color-neutral-50:oklch(98.5% 0 0);--color-neutral-100:oklch(97% 0 0);--color-neutral-200:oklch(92.2% 0 0);--color-neutral-300:oklch(87% 0 0);--color-neutral-400:oklch(70.8% 0 0);--color-neutral-500:oklch(55.6% 0 0);--color-neutral-600:oklch(43.9% 0 0);--color-neutral-700:oklch(37.1% 0 0);--color-neutral-800:oklch(26.9% 0 0);--color-neutral-900:oklch(20.5% 0 0);--color-neutral-950:oklch(14.5% 0 0);--spacing:.25rem;--breakpoint-2xl:90rem;--font-weight-medium:500;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--font-default:"Segoe UI", system-ui, sans-serif;--color-primary:var(--color-neutral-950);--color-primary-contrast:var(--color-neutral-50);--color-primary-hover:var(--color-neutral-700);--color-primary-subtle:var(--color-neutral-200);--color-primary-strong:var(--color-neutral-900);--color-accent:var(--color-neutral-600);--color-accent-contrast:var(--color-neutral-50);--color-accent-hover:var(--color-neutral-700);--color-accent-subtle:var(--color-neutral-100);--color-accent-strong:var(--color-neutral-800);--color-border:var(--color-neutral-300);--color-border-hover:var(--color-neutral-600);--color-background:var(--color-neutral-50);--color-foreground:var(--color-neutral-100);--color-surface:var(--color-neutral-200);--color-text:var(--color-neutral-950);--color-text-secondary:var(--color-neutral-700);--color-text-contrast:var(--color-neutral-50);--color-text-hover:var(--color-neutral-700);--color-text-subtle:var(--color-neutral-200);--color-text-strong:var(--color-neutral-900);--color-success:var(--color-emerald-600);--color-success-contrast:var(--color-neutral-50);--color-success-hover:#008d61}@supports (color:color-mix(in lab, red, red)){:root,:host{--color-success-hover:color-mix(in srgb, var(--color-success) 82%, var(--color-success-strong))}}:root,:host{--color-success-subtle:var(--color-emerald-100);--color-success-strong:var(--color-emerald-800);--color-warning:var(--color-amber-600);--color-warning-contrast:var(--color-neutral-50);--color-warning-hover:#d06a00}@supports (color:color-mix(in lab, red, red)){:root,:host{--color-warning-hover:color-mix(in srgb, var(--color-warning) 82%, var(--color-warning-strong))}}:root,:host{--color-warning-subtle:var(--color-amber-100);--color-warning-strong:var(--color-amber-800);--color-destructive:var(--color-rose-700);--color-destructive-contrast:var(--color-neutral-50);--color-destructive-hover:#bc0039}@supports (color:color-mix(in lab, red, red)){:root,:host{--color-destructive-hover:color-mix(in srgb, var(--color-destructive) 82%, var(--color-destructive-strong))}}:root,:host{--color-destructive-subtle:var(--color-rose-100);--color-destructive-strong:var(--color-rose-800);--color-info:var(--color-indigo-600);--color-info-contrast:var(--color-neutral-50);--color-info-hover:#4b36e9}@supports (color:color-mix(in lab, red, red)){:root,:host{--color-info-hover:color-mix(in srgb, var(--color-info) 82%, var(--color-info-strong))}}:root,:host{--color-info-subtle:var(--color-indigo-100);--color-info-strong:var(--color-indigo-800);--breakpoint-xs:30rem}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.fixed{position:fixed}.static{position:static}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.flex-wrap{flex-wrap:wrap}.rounded{border-radius:.25rem}.border{border-style:var(--tw-border-style);border-width:1px}.bg-background{background-color:var(--color-background)}.font-default{font-family:var(--font-default)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.text-text{color:var(--color-text)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}}:root{color-scheme:light;--container-narrow:48rem;--container-max:80rem;--container-wide:90rem;--layout-gutter:clamp(1rem, 4vw, 2rem);--layout-section-block:clamp(4rem, 9vw, 7rem);--layout-stack-gap:1rem;--layout-cluster-gap:.75rem;--layout-grid-min:16rem;--radius-small:.25rem;--radius-control:.5rem;--radius-surface:.875rem;--control-height:2.5rem;--border-width:1px;--elevation-low:0 1px 2px #0f172a0f, 0 1px 3px #0f172a14;--elevation-mid:0 4px 6px -1px #0f172a14, 0 2px 4px -2px #0f172a14;--elevation-high:0 20px 25px -5px #0f172a1a, 0 8px 10px -6px #0f172a1a;--focus-ring:#0a0a0aa3}@supports (color:color-mix(in lab, red, red)){:root{--focus-ring:color-mix(in srgb, var(--color-primary) 64%, transparent)}}:root{--focus-ring-offset:2px;--focus-ring-width:3px;--motion-duration-fast:.12s;--motion-duration-normal:.2s;--motion-duration-slow:.4s;--motion-ease:cubic-bezier(.2, 0, 0, 1);--z-popover:40;--surface-hover-transform:translateY(-1px)}.dark{color-scheme:dark;--color-primary:var(--color-neutral-50);--color-primary-contrast:var(--color-neutral-950);--color-primary-hover:var(--color-neutral-200);--color-primary-subtle:var(--color-neutral-900);--color-primary-strong:var(--color-neutral-100);--color-accent:var(--color-neutral-400);--color-accent-contrast:var(--color-neutral-950);--color-accent-hover:var(--color-neutral-300);--color-accent-subtle:var(--color-neutral-800);--color-accent-strong:var(--color-neutral-200);--color-border:var(--color-neutral-700);--color-border-hover:var(--color-neutral-500);--color-background:var(--color-neutral-950);--color-foreground:var(--color-neutral-900);--color-surface:var(--color-neutral-800);--color-text:var(--color-neutral-50);--color-text-secondary:var(--color-neutral-400);--color-text-contrast:var(--color-neutral-950);--color-text-hover:var(--color-neutral-200);--color-text-subtle:var(--color-neutral-900);--color-text-strong:var(--color-neutral-100);--color-success:var(--color-emerald-600);--color-success-contrast:var(--color-neutral-50);--color-success-hover:#008d61}@supports (color:color-mix(in lab, red, red)){.dark{--color-success-hover:color-mix(in srgb, var(--color-success) 82%, var(--color-success-strong))}}.dark{--color-success-subtle:var(--color-emerald-800);--color-success-strong:var(--color-emerald-100);--color-warning:var(--color-amber-600);--color-warning-contrast:var(--color-neutral-50);--color-warning-hover:#d06a00}@supports (color:color-mix(in lab, red, red)){.dark{--color-warning-hover:color-mix(in srgb, var(--color-warning) 82%, var(--color-warning-strong))}}.dark{--color-warning-subtle:var(--color-amber-800);--color-warning-strong:var(--color-amber-100);--color-destructive:var(--color-rose-700);--color-destructive-contrast:var(--color-neutral-50);--color-destructive-hover:#bc0039}@supports (color:color-mix(in lab, red, red)){.dark{--color-destructive-hover:color-mix(in srgb, var(--color-destructive) 82%, var(--color-destructive-strong))}}.dark{--color-destructive-subtle:var(--color-rose-800);--color-destructive-strong:var(--color-rose-100);--color-info:var(--color-indigo-600);--color-info-contrast:var(--color-neutral-50);--color-info-hover:#4b36e9}@supports (color:color-mix(in lab, red, red)){.dark{--color-info-hover:color-mix(in srgb, var(--color-info) 82%, var(--color-info-strong))}}.dark{--color-info-subtle:var(--color-indigo-800);--color-info-strong:var(--color-indigo-100)}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}
|
|
1
|
+
/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
|
|
2
|
+
:root,:host{--color-amber-100:oklch(96.2% .059 95.617);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-700:oklch(50.8% .118 165.612);--color-emerald-800:oklch(43.2% .095 166.913);--color-emerald-900:oklch(37.8% .077 168.94);--color-indigo-100:oklch(93% .034 272.788);--color-indigo-300:oklch(78.5% .115 274.713);--color-indigo-600:oklch(51.1% .262 276.966);--color-indigo-800:oklch(39.8% .195 277.366);--color-indigo-900:oklch(35.9% .144 278.697);--color-rose-100:oklch(94.1% .03 12.58);--color-rose-300:oklch(81% .117 11.638);--color-rose-700:oklch(51.4% .222 16.935);--color-rose-800:oklch(45.5% .188 13.697);--color-rose-900:oklch(41% .159 10.272);--color-neutral-50:oklch(98.5% 0 0);--color-neutral-100:oklch(97% 0 0);--color-neutral-200:oklch(92.2% 0 0);--color-neutral-300:oklch(87% 0 0);--color-neutral-400:oklch(70.8% 0 0);--color-neutral-500:oklch(55.6% 0 0);--color-neutral-600:oklch(43.9% 0 0);--color-neutral-700:oklch(37.1% 0 0);--color-neutral-800:oklch(26.9% 0 0);--color-neutral-900:oklch(20.5% 0 0);--color-neutral-950:oklch(14.5% 0 0);--color-white:#fff;--color-primary:light-dark(var(--color-neutral-950),var(--color-neutral-50));--color-primary-contrast:light-dark(var(--color-neutral-50),var(--color-neutral-950));--color-primary-hover:light-dark(var(--color-neutral-700),var(--color-neutral-200));--color-primary-subtle:light-dark(var(--color-neutral-200),var(--color-neutral-900));--color-primary-strong:light-dark(var(--color-neutral-900),var(--color-neutral-100));--color-accent:light-dark(var(--color-neutral-600),var(--color-neutral-400));--color-accent-contrast:light-dark(var(--color-neutral-50),var(--color-neutral-950));--color-accent-hover:light-dark(var(--color-neutral-700),var(--color-neutral-300));--color-accent-subtle:light-dark(var(--color-neutral-100),var(--color-neutral-800));--color-accent-strong:light-dark(var(--color-neutral-800),var(--color-neutral-200));--color-border:light-dark(var(--color-neutral-300),var(--color-neutral-700));--color-control-border:var(--color-neutral-500);--color-surface:light-dark(var(--color-neutral-200),var(--color-neutral-800));--color-text:light-dark(var(--color-neutral-950),var(--color-neutral-50));--color-text-contrast:light-dark(var(--color-neutral-50),var(--color-neutral-950));--color-text-hover:light-dark(var(--color-neutral-700),var(--color-neutral-200));--color-text-strong:light-dark(var(--color-neutral-900),var(--color-neutral-100));--color-tooltip:light-dark(var(--color-white),var(--color-neutral-700));--color-tooltip-contrast:light-dark(var(--color-neutral-900),var(--color-neutral-100));--color-success:var(--color-emerald-700);--color-success-contrast:var(--color-neutral-50);--color-success-hover:light-dark(#007552,#008e66);--color-success-subtle:light-dark(var(--color-emerald-100),var(--color-emerald-900));--color-success-strong:light-dark(var(--color-emerald-800),var(--color-emerald-300));--color-warning:var(--color-amber-600);--color-warning-contrast:var(--color-neutral-950);--color-warning-hover:light-dark(#d46700,#e78300);--color-warning-subtle:light-dark(var(--color-amber-100),var(--color-amber-900));--color-warning-strong:light-dark(var(--color-amber-800),var(--color-amber-300));--color-destructive:var(--color-rose-700);--color-destructive-contrast:var(--color-neutral-50);--color-destructive-hover:light-dark(#c00036,#d1054b);--color-destructive-subtle:light-dark(var(--color-rose-100),var(--color-rose-900));--color-destructive-strong:light-dark(var(--color-rose-800),var(--color-rose-300));--color-info:var(--color-indigo-600);--color-info-contrast:var(--color-neutral-50);--color-info-hover:light-dark(#4b36e9,#5e4ff9);--color-info-subtle:light-dark(var(--color-indigo-100),var(--color-indigo-900));--color-info-strong:light-dark(var(--color-indigo-800),var(--color-indigo-300))}.neutral{--intent-color:var(--color-text);--intent-contrast:var(--color-text-contrast);--intent-hover:var(--color-text-hover);--intent-strong:var(--color-text-strong);--intent-subtle:var(--color-surface);--intent-border:var(--ui-ink,var(--color-border));--intent-fill-max:20%}.primary{--intent-color:var(--color-primary);--intent-contrast:var(--color-primary-contrast);--intent-hover:var(--color-primary-hover);--intent-strong:var(--color-primary-strong);--intent-subtle:var(--color-primary-subtle);--intent-border:var(--color-primary);--intent-fill-max:100%}.secondary{--intent-color:var(--color-accent);--intent-contrast:var(--color-accent-contrast);--intent-hover:var(--color-accent-hover);--intent-strong:var(--color-accent-strong);--intent-subtle:var(--color-accent-subtle);--intent-border:var(--color-accent);--intent-fill-max:100%}.success{--intent-color:var(--color-success);--intent-contrast:var(--color-success-contrast);--intent-hover:var(--color-success-hover);--intent-strong:var(--color-success-strong);--intent-subtle:var(--color-success-subtle);--intent-border:var(--color-success);--intent-fill-max:100%}.warning{--intent-color:var(--color-warning);--intent-contrast:var(--color-warning-contrast);--intent-hover:var(--color-warning-hover);--intent-strong:var(--color-warning-strong);--intent-subtle:var(--color-warning-subtle);--intent-border:var(--color-warning);--intent-fill-max:100%}.destructive,.danger,.error{--intent-color:var(--color-destructive);--intent-contrast:var(--color-destructive-contrast);--intent-hover:var(--color-destructive-hover);--intent-strong:var(--color-destructive-strong);--intent-subtle:var(--color-destructive-subtle);--intent-border:var(--color-destructive);--intent-fill-max:100%}.info{--intent-color:var(--color-info);--intent-contrast:var(--color-info-contrast);--intent-hover:var(--color-info-hover);--intent-strong:var(--color-info-strong);--intent-subtle:var(--color-info-subtle);--intent-border:var(--color-info);--intent-fill-max:100%}:where(.btn,.ipt,.text-control,.surface,.textarea,.select,.input-group,.checkbox,.radio,.switch,.card,.panel,.alert,.data-table,.pre,.quote,.tooltip,.badge,.kbd,.code,.loader,.skeleton,.progress,.divider){--intent-color:var(--color-text);--intent-contrast:var(--color-text-contrast);--intent-hover:var(--color-text-hover);--intent-strong:var(--color-text-strong);--intent-subtle:var(--color-surface);--intent-border:var(--ui-ink,var(--color-border));--intent-fill-max:20%}:where(.text-control,.ipt,.textarea,.select,.input-group,.checkbox,.radio,.switch){--intent-border:var(--ui-ink,var(--color-control-border))}:where(.tooltip),.tooltip.neutral{--intent-color:var(--color-tooltip);--intent-contrast:var(--color-tooltip-contrast);--intent-hover:var(--color-tooltip);--intent-strong:var(--color-tooltip-contrast);--intent-subtle:var(--color-surface);--intent-border:var(--ui-ink,var(--color-border));--intent-fill-max:100%}:where(.loader,.skeleton,.progress,.divider){--intent-color:currentColor;--intent-border:currentColor}.solid{--ui-fill:100%;--ui-fg-on-fill:100%}.soft{--ui-fill:12%;--ui-fg-on-fill:0%}.ghost{--ui-fill:0%;--ui-fg-on-fill:0%}.edged{--ui-border:100%}.edgeless{--ui-border:0%}.elevation-none,.flat{--ui-elevation:0}.elevation-sm,.raised{--ui-elevation:1;--elevation-y:1px;--elevation-blur:3px}.elevation-md,.floating{--ui-elevation:2;--elevation-y:1px;--elevation-blur:3px}:root{color-scheme:light dark;--container-narrow:48rem;--container-max:80rem;--container-wide:90rem;--layout-gutter:clamp(1rem, 4vw, 2rem);--layout-section-block:clamp(4rem, 9vw, 7rem);--layout-gap:1rem;--layout-grid-min:16rem;--radius-small:.25rem;--radius-control:.5rem;--radius-surface:.875rem;--control-height:2.5rem;--border-width:1px;--elevation-color:light-dark(#0f172a14,#0000008c);--elevation-none:none;--elevation-sm:0 1px 2px var(--elevation-color), 0 1px 3px var(--elevation-color);--elevation-md:0 4px 6px -1px var(--elevation-color), 0 2px 4px -2px var(--elevation-color);--elevation-lg:0 20px 25px -5px var(--elevation-color), 0 8px 10px -6px var(--elevation-color);--focus-ring:color-mix(in srgb, light-dark(var(--color-neutral-950),var(--color-neutral-50)) 64%, transparent)}@supports (color:color-mix(in lab, red, red)){:root{--focus-ring:color-mix(in srgb, var(--color-primary) 64%, transparent)}}:root{--focus-ring-offset:calc(-1 * var(--border-width));--focus-ring-width:3px;--motion-duration-fast:.12s;--motion-duration-normal:.2s;--motion-duration-slow:.4s;--motion-ease:cubic-bezier(.2, 0, 0, 1);--z-popover:40;--hover-step:14%;--ui-active-transform:scale(.97)}@media (prefers-reduced-motion:reduce){:root{--ui-active-transform:none}}.light,.theme-light,[data-theme=light]{color-scheme:light}.dark,.theme-dark,[data-theme=dark]{color-scheme:dark}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/* Chunky tile: rounded slabs seated on a darker shade of themselves, pressed flat
|
|
2
|
+
on click. The look mobile learning and game UIs use -- Duolingo is the one most
|
|
3
|
+
people picture -- named for what it is made of rather than for a product, the
|
|
4
|
+
way the other three are.
|
|
5
|
+
|
|
6
|
+
Tier 1, plus one recorded selector list at the bottom. Load after the base
|
|
7
|
+
stylesheet. */
|
|
8
|
+
.chunky-tile {
|
|
9
|
+
/* Two knobs, read with a fallback so an ancestor can set them -- declared here
|
|
10
|
+
they would outrank `:root` (R8; see model.md#rules-for-aesthetics). One lift
|
|
11
|
+
drives everything: the bar is this deep and a press travels exactly this
|
|
12
|
+
far. 2px reads as a rendering seam, 8px as floating rather than seated. */
|
|
13
|
+
--_tile-lift: var(--tile-lift, 4px);
|
|
14
|
+
--_tile-radius: var(--tile-radius, 0.75rem);
|
|
15
|
+
|
|
16
|
+
/* The tile grey -- also the neutral bar, at the shadow ink below. Darker than
|
|
17
|
+
the look's own greys because `--ui-ink` is every text control's boundary,
|
|
18
|
+
drawn at `--_line-rest: 60%`: neutral-200/300 measure 1.21:1/1.42:1 against
|
|
19
|
+
the light page where WCAG 1.4.11 asks 3:1, and neutral-400 (2.48:1 whole) is
|
|
20
|
+
the heaviest grey that still leaves a field legible. */
|
|
21
|
+
--ui-ink: light-dark(var(--color-neutral-400), var(--color-neutral-600));
|
|
22
|
+
|
|
23
|
+
/* One knob for both radii, where glass splits them: a tile and the button
|
|
24
|
+
inside it share a corner, and drifting apart loses the single-object read.
|
|
25
|
+
Chips clamp it to `--radius-small` themselves. */
|
|
26
|
+
--ui-radius: var(--_tile-radius);
|
|
27
|
+
--ui-radius-surface: var(--_tile-radius);
|
|
28
|
+
|
|
29
|
+
/* Half the tile. A 1px border under a 4px bar reads as a shadow with a hairline
|
|
30
|
+
rather than as a slab with an edge. The ceiling is restated so a chunky region
|
|
31
|
+
nested inside `.pixel` does not inherit its `0px` and lose the line. */
|
|
32
|
+
--ui-border-width: 2px;
|
|
33
|
+
--ui-border-max: 100px;
|
|
34
|
+
|
|
35
|
+
/* What makes the bar a *shade*, not a repeat. `box` mixes the bar
|
|
36
|
+
`color-mix(in oklab, var(--intent-border) var(--ui-shadow-ink), var(--elevation-color))`,
|
|
37
|
+
and `--intent-border` is `--intent-color` for the hue intents, so at 100%
|
|
38
|
+
ink the bar equalled the plate exactly (`.btn.success` #007a55 on #007a55).
|
|
39
|
+
Lowering the ink alone fails: the shipped `--elevation-color` is nearly
|
|
40
|
+
transparent, so the mix composites *lighter* than the plate. Naming the
|
|
41
|
+
depth colour opaque points it at black; 72% is measured (`.btn.success`
|
|
42
|
+
#007a55 on #004c34, neutral `.card` #fafafa on #888888). On a near-black
|
|
43
|
+
dark card the bar lands lighter and reads as a rim, as the original does.
|
|
44
|
+
`.glass` resolves `--elevation-color` in its own `--ui-surface-shadow`, so a
|
|
45
|
+
nested glass region inherits this opaque black -- one entry in the per-token
|
|
46
|
+
negotiation nesting two aesthetics already is. */
|
|
47
|
+
--elevation-color: rgb(0 0 0);
|
|
48
|
+
--ui-shadow-ink: 72%;
|
|
49
|
+
|
|
50
|
+
/* Straight down, solid, unblurred, no spread. Zero x is what separates this from
|
|
51
|
+
neobrutalism, whose two-axis offset reads as a card lifted off the page where
|
|
52
|
+
this one is a slab seated on it. Spread stays zero so the bar cannot widen past
|
|
53
|
+
the element's own silhouette and put its corners out of step with the radius. */
|
|
54
|
+
--ui-shadow-x: 0px;
|
|
55
|
+
--ui-shadow-y: var(--_tile-lift);
|
|
56
|
+
--ui-shadow-blur: 0px;
|
|
57
|
+
--ui-shadow-spread: 0px;
|
|
58
|
+
|
|
59
|
+
/* The press, and the reason the lift is one number: the element travels exactly
|
|
60
|
+
the bar's depth and lands flat on the page. Hover holds still -- a seated slab
|
|
61
|
+
has one gesture and it belongs to the press -- and the resting shadow tokens
|
|
62
|
+
above already hold the bar under the pointer, so hover needs nothing of its
|
|
63
|
+
own. */
|
|
64
|
+
--ui-active-shadow-x: 0px;
|
|
65
|
+
--ui-active-shadow-y: 0px;
|
|
66
|
+
--ui-active-transform: translateY(var(--_tile-lift));
|
|
67
|
+
|
|
68
|
+
/* Nothing here clips, blurs, thins its fill, or draws its edge as a ring, and an
|
|
69
|
+
aesthetic nested inside one that does has to say so rather than inherit it.
|
|
70
|
+
`--ui-shadow-edge` is the load-bearing clear: inherited from `.pixel` it would
|
|
71
|
+
point the bar at `--_edge`, which is transparent at `.edgeless`, and the bar
|
|
72
|
+
would vanish from every `.solid` button on the page -- which is the default
|
|
73
|
+
`.btn`. */
|
|
74
|
+
--ui-clip: none;
|
|
75
|
+
--ui-clip-tight: none;
|
|
76
|
+
--ui-shadow-inset: ;
|
|
77
|
+
--ui-shadow-edge: initial;
|
|
78
|
+
--ui-backdrop: none;
|
|
79
|
+
--ui-bg-alpha: 1;
|
|
80
|
+
|
|
81
|
+
/* Consumer-supplied, the way `.pixel` reads `--font-pixel`. The package ships no
|
|
82
|
+
font binary, so the aesthetic has no rounded face to fall back to and takes the
|
|
83
|
+
page's own stack until one is supplied. */
|
|
84
|
+
font-family: var(--font-rounded, var(--font-sans, ui-sans-serif, system-ui, sans-serif));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* The one R3 exception this aesthetic takes, recorded in the registry as
|
|
88
|
+
`selectors`.
|
|
89
|
+
|
|
90
|
+
A heavy, slightly tracked label is what makes an action read as a tile rather
|
|
91
|
+
than as a button, and neither weight nor tracking is a value any material token
|
|
92
|
+
holds. Adding two would put typography into the material vocabulary for one
|
|
93
|
+
aesthetic's benefit, and a slot buys nothing besides: `.btn` is the only action
|
|
94
|
+
the package ships, so an action-scoped token would be this same list with an
|
|
95
|
+
indirection in front of it. The bare `button` is named beside the class so the
|
|
96
|
+
treatment reaches an unclassed element the way every Tier 1 token already does.
|
|
97
|
+
|
|
98
|
+
Casing is deliberately not here. The chunky-tile look is usually drawn with
|
|
99
|
+
uppercase labels and the earlier draft of this rule set `text-transform`, but
|
|
100
|
+
how a label is *worded* is the application's decision and an aesthetic silently
|
|
101
|
+
recasing it takes that away -- along with any acronym, proper noun, or
|
|
102
|
+
locale whose rules for case are not English's. A consumer who wants shouting
|
|
103
|
+
buttons writes it in their own stylesheet, where it is theirs to own. */
|
|
104
|
+
.chunky-tile :is(.btn, button) {
|
|
105
|
+
font-weight: 800;
|
|
106
|
+
letter-spacing: 0.04em;
|
|
107
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* Glass: translucent surfaces over a blurred backdrop with a hairline highlight
|
|
2
|
+
edge. It needs something behind it to blur -- on a flat page background it is a
|
|
3
|
+
translucent panel and nothing more.
|
|
4
|
+
|
|
5
|
+
Tier 1 only. The scope that used to take a hand-written selector list is now a
|
|
6
|
+
token: only `surface` resolves `--ui-backdrop`, so blur reaches cards,
|
|
7
|
+
panels, alerts and tooltips while controls stay solid. An active blur costs a
|
|
8
|
+
compositing layer apiece, and one under every control of a dense cluster reads
|
|
9
|
+
as noise. */
|
|
10
|
+
.glass {
|
|
11
|
+
/* Knobs read with a fallback so an ancestor can set them -- declared here they
|
|
12
|
+
outrank `:root` (R8; see model.md#rules-for-aesthetics). Glass is softer
|
|
13
|
+
than the base geometry because a translucent tight corner reads as a
|
|
14
|
+
cut-out; a control/surface pair rather than one value, because they do not
|
|
15
|
+
scale together and 12/16 keeps a button looking at home inside a card. */
|
|
16
|
+
--ui-radius: var(--glass-radius, 0.75rem);
|
|
17
|
+
--ui-radius-surface: var(--glass-radius-surface, 1rem);
|
|
18
|
+
--ui-border-width: 1px;
|
|
19
|
+
--ui-border-max: 100px;
|
|
20
|
+
|
|
21
|
+
/* Glass catches light from above whatever is under it, so the edge is a light
|
|
22
|
+
hairline in both themes rather than a theme-following ink. */
|
|
23
|
+
--ui-ink: light-dark(rgb(255 255 255 / 0.65), rgb(255 255 255 / 0.22));
|
|
24
|
+
|
|
25
|
+
--ui-bg-alpha: 0.8;
|
|
26
|
+
--ui-backdrop: blur(14px) saturate(1.6);
|
|
27
|
+
|
|
28
|
+
/* R6. Controls carry no shadow of their own -- the depth in this aesthetic is
|
|
29
|
+
the blur, and a drop shadow under a translucent button reads as grime. All
|
|
30
|
+
general parts are explicitly zero so inherited or future geometry cannot
|
|
31
|
+
leak onto controls. `--ui-surface-shadow` below replaces the shadow of
|
|
32
|
+
surfaces only. */
|
|
33
|
+
--ui-shadow-x: 0px;
|
|
34
|
+
--ui-shadow-y: 0px;
|
|
35
|
+
--ui-shadow-blur: 0px;
|
|
36
|
+
--ui-shadow-spread: 0px;
|
|
37
|
+
/* `--ui-bg-alpha` multiplies the fill, and a surface's default fill is 0%:
|
|
38
|
+
without a translucent ground the blur ends up behind an opaque background
|
|
39
|
+
and nothing shows through. */
|
|
40
|
+
--ui-surface-ground: color-mix(in srgb, var(--color-background) 45%, transparent);
|
|
41
|
+
|
|
42
|
+
/* Two layers pulling in with negative spread, so the shadow tucks under the
|
|
43
|
+
surface instead of haloing onto the backdrop. It is the one shipped aesthetic
|
|
44
|
+
that cannot be described with the parts, so it takes `surface`'s
|
|
45
|
+
complete-value slot -- which is also what keeps it off buttons and chips --
|
|
46
|
+
and the registry records that `.flat` does not reach it. */
|
|
47
|
+
--ui-surface-shadow: 0 1px 2px -1px var(--elevation-color), 0 18px 40px -16px var(--elevation-color);
|
|
48
|
+
|
|
49
|
+
--ui-clip: none;
|
|
50
|
+
--ui-clip-tight: none;
|
|
51
|
+
/* Nothing here draws its edge as a ring, and an aesthetic nested inside pixel
|
|
52
|
+
has to say so rather than inherit it. */
|
|
53
|
+
--ui-shadow-edge: initial;
|
|
54
|
+
--ui-hover-transform: none;
|
|
55
|
+
--ui-active-transform: none;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Transparency is the whole aesthetic, so the honest degradation is an opaque
|
|
59
|
+
surface rather than a softer blur. */
|
|
60
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
61
|
+
.glass {
|
|
62
|
+
--ui-bg-alpha: 1;
|
|
63
|
+
--ui-backdrop: none;
|
|
64
|
+
--ui-surface-ground: var(--color-background);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* Every shipped aesthetic. Import one file from `./<name>.css` instead to pay
|
|
2
|
+
for only the one in use: a cascading class has no static call site, so nothing
|
|
3
|
+
here can be tree-shaken.
|
|
4
|
+
|
|
5
|
+
Import after the base stylesheet in either form. */
|
|
6
|
+
@import "./neobrutalism.css";
|
|
7
|
+
@import "./glass.css";
|
|
8
|
+
@import "./pixel.css";
|
|
9
|
+
@import "./chunky-tile.css";
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/* Neobrutalism: thick ink outline, a hard unblurred offset shadow, and a press
|
|
2
|
+
that moves the element into its own shadow. Material tokens only, no component
|
|
3
|
+
named -- the ink reaches everything through the intent reset, the shadow is
|
|
4
|
+
colourless geometry each component colours against its own intent. Load after
|
|
5
|
+
the base stylesheet; source order decides against the zero-specificity reset. */
|
|
6
|
+
.neobrutalism {
|
|
7
|
+
/* Read with a fallback, not declared, so an ancestor (`:root`, where an app
|
|
8
|
+
themes) can set `--neo-offset`; the private carries the resolved value to
|
|
9
|
+
the six places below. See model.md#rules-for-aesthetics (R8). */
|
|
10
|
+
--_neo-offset: var(--neo-offset, 4px);
|
|
11
|
+
|
|
12
|
+
/* Pure black reads as ink on a light page and disappears on a dark one, so the
|
|
13
|
+
ink follows the theme rather than the palette. A thick border in the neutral
|
|
14
|
+
border grey is a thick grey box, not a brutalist one. */
|
|
15
|
+
--ui-ink: light-dark(var(--color-neutral-950), var(--color-neutral-50));
|
|
16
|
+
|
|
17
|
+
--ui-radius: 0;
|
|
18
|
+
--ui-radius-surface: 0;
|
|
19
|
+
--ui-border-width: 2px;
|
|
20
|
+
--ui-border-max: 100px;
|
|
21
|
+
|
|
22
|
+
/* Hard, unblurred, and offset. Scaled by each component's own elevation, so
|
|
23
|
+
the registry decides which components sit on a slab and `.flat` takes one
|
|
24
|
+
off without this file knowing which component it landed on. */
|
|
25
|
+
/* The slab is ink, not shade: a hard offset of the element's own line colour
|
|
26
|
+
is the whole look, so this aesthetic asks for the intent instead of the
|
|
27
|
+
neutral depth colour every other shadow uses. */
|
|
28
|
+
--ui-shadow-ink: 100%;
|
|
29
|
+
--ui-shadow-x: var(--_neo-offset);
|
|
30
|
+
--ui-shadow-y: var(--_neo-offset);
|
|
31
|
+
--ui-shadow-blur: 0px;
|
|
32
|
+
--ui-shadow-spread: 0px;
|
|
33
|
+
|
|
34
|
+
/* The press: travel the offset, shrink the shadow to nothing, so the element
|
|
35
|
+
lands flat where its slab was. Hover keeps the slab still under the pointer;
|
|
36
|
+
`box-hover`'s derived fill tint is the whole hover response. One token pair,
|
|
37
|
+
no `:hover` list, R3 intact. */
|
|
38
|
+
--ui-active-shadow-x: 0px;
|
|
39
|
+
--ui-active-shadow-y: 0px;
|
|
40
|
+
--ui-active-transform: translate(var(--_neo-offset), var(--_neo-offset));
|
|
41
|
+
|
|
42
|
+
/* Nothing here clips or blurs, and an aesthetic nested inside another has to
|
|
43
|
+
say so rather than inherit it. */
|
|
44
|
+
--ui-clip: none;
|
|
45
|
+
--ui-clip-tight: none;
|
|
46
|
+
--ui-shadow-inset: ;
|
|
47
|
+
/* The slab is depth cast in ink, not this element's line, so an `.edgeless`
|
|
48
|
+
button keeps it. Cleared rather than omitted, because a neobrutalist region
|
|
49
|
+
nested inside a pixel one would otherwise inherit pixel's answer and lose the
|
|
50
|
+
slab on everything the edge axis rests at zero. */
|
|
51
|
+
--ui-shadow-edge: initial;
|
|
52
|
+
--ui-backdrop: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* The one R3 exception, recorded in the registry as `selectors`. The slab here
|
|
56
|
+
is a second ink line, not depth, and an alert -- the one loud container the
|
|
57
|
+
registry rests flat -- looks unfinished without it. It writes the component's
|
|
58
|
+
resting default rather than the modifier, so `.flat`/`.raised`/`.floating`
|
|
59
|
+
still win on the element; a token would name the component through a longer
|
|
60
|
+
route. `--_d-elevation` inheriting is inert -- it equals `box`'s own fallback. */
|
|
61
|
+
.neobrutalism .alert {
|
|
62
|
+
--_d-elevation: 1;
|
|
63
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* Pixel: an 8-bit look built from a single-step `clip-path` corner and an inset
|
|
2
|
+
ring where the border would be.
|
|
3
|
+
|
|
4
|
+
Tier 1 only. `clip-path` clips a border, an outline and any outer shadow, so
|
|
5
|
+
the border ceiling goes to zero and the edge becomes an inset shadow instead --
|
|
6
|
+
which is exactly what `--ui-shadow-inset`, `--ui-shadow-spread` and
|
|
7
|
+
`--ui-shadow-edge` are for. The ring is still coloured by each component's own
|
|
8
|
+
intent, because the parts carry no colour. */
|
|
9
|
+
.pixel {
|
|
10
|
+
/* One pixel of the imaginary grid; the ring is one unit thick and the corner
|
|
11
|
+
one unit square, so scaling it scales the whole look. Read with a fallback
|
|
12
|
+
so an ancestor can set `--pixel-unit` -- declared here it outranks `:root`
|
|
13
|
+
(R8; see model.md#rules-for-aesthetics). */
|
|
14
|
+
--_pixel-unit: var(--pixel-unit, 4px);
|
|
15
|
+
|
|
16
|
+
--ui-ink: light-dark(var(--color-neutral-950), var(--color-neutral-50));
|
|
17
|
+
--ui-radius: 0;
|
|
18
|
+
--ui-radius-surface: 0;
|
|
19
|
+
|
|
20
|
+
/* A border would be clipped at the straight sides and paint a second line
|
|
21
|
+
beside the ring, so the ceiling takes it out and the ring does the drawing.
|
|
22
|
+
|
|
23
|
+
The width itself stays under the unit deliberately. Its one live reader is
|
|
24
|
+
the toggle cap in `form.css`, which reads it as the thinnest line this
|
|
25
|
+
aesthetic will accept where the full ring does not fit: a 16px checkbox
|
|
26
|
+
spending four pixels a side leaves an eight-pixel hole and an unchecked box
|
|
27
|
+
reads as a filled one. */
|
|
28
|
+
--ui-border-width: 2px;
|
|
29
|
+
--ui-border-max: 0px;
|
|
30
|
+
|
|
31
|
+
/* The ring is one unit deep, which is exactly the corner cut, so the staircase
|
|
32
|
+
is covered and the line does not read as broken at the corners. Spread is not
|
|
33
|
+
scaled by elevation, so a chip resting at zero keeps its edge. */
|
|
34
|
+
--ui-shadow-inset: inset;
|
|
35
|
+
/* The ring is this element's border, not depth, drawn where `clip-path` cannot
|
|
36
|
+
cut it. `--ui-shadow-edge` says so and `box` colours it from `--_edge`, so
|
|
37
|
+
the edge axis still decides: an `.edgeless` badge or `.solid` button gets no
|
|
38
|
+
ring, an `.edged` card does, a field keeps one whatever a container asks.
|
|
39
|
+
Declared empty because it is read for presence -- the percentage form nests
|
|
40
|
+
two `color-mix()` results that WebKit resolves as zero (see `box`). */
|
|
41
|
+
--ui-shadow-edge: ;
|
|
42
|
+
--ui-shadow-spread: var(--_pixel-unit);
|
|
43
|
+
--ui-shadow-x: 0px;
|
|
44
|
+
--ui-shadow-y: 0px;
|
|
45
|
+
--ui-shadow-blur: 0px;
|
|
46
|
+
|
|
47
|
+
/* The focus ring is a second inset layer, and shadows paint first-on-top, so it
|
|
48
|
+
has to be wider than the edge to show at all. */
|
|
49
|
+
--ui-focus-inset: calc(var(--_pixel-unit) + var(--focus-ring-width));
|
|
50
|
+
|
|
51
|
+
/* One square unit off each corner, and nothing else. The staircase this
|
|
52
|
+
replaces stepped twice at half the unit to approximate a curve, which is the
|
|
53
|
+
one thing a low-resolution grid cannot do: at any size it read as a bevel
|
|
54
|
+
that had not quite committed. A single cut of the grid's own unit is the
|
|
55
|
+
shape the look is actually made of.
|
|
56
|
+
|
|
57
|
+
The polygon resolves the unit here, on `.pixel`, and inherits down
|
|
58
|
+
already-resolved -- so a component cannot rescale the corner by setting
|
|
59
|
+
`--pixel-unit` on itself, and the knob belongs above rather than within. */
|
|
60
|
+
--ui-clip: polygon(
|
|
61
|
+
0 var(--_pixel-unit),
|
|
62
|
+
var(--_pixel-unit) var(--_pixel-unit),
|
|
63
|
+
var(--_pixel-unit) 0,
|
|
64
|
+
calc(100% - var(--_pixel-unit)) 0,
|
|
65
|
+
calc(100% - var(--_pixel-unit)) var(--_pixel-unit),
|
|
66
|
+
100% var(--_pixel-unit),
|
|
67
|
+
100% calc(100% - var(--_pixel-unit)),
|
|
68
|
+
calc(100% - var(--_pixel-unit)) calc(100% - var(--_pixel-unit)),
|
|
69
|
+
calc(100% - var(--_pixel-unit)) 100%,
|
|
70
|
+
var(--_pixel-unit) 100%,
|
|
71
|
+
var(--_pixel-unit) calc(100% - var(--_pixel-unit)),
|
|
72
|
+
0 calc(100% - var(--_pixel-unit))
|
|
73
|
+
);
|
|
74
|
+
/* Chips take the other half of the vocabulary: no radius at all. The cut is one
|
|
75
|
+
grid unit whatever the element is, and one unit off each corner of a 24px
|
|
76
|
+
badge or a 16px checkbox is a bite rather than a corner. Squared is the
|
|
77
|
+
honest small-scale answer, and it is still the pixel look -- the aesthetic is
|
|
78
|
+
a grid, and a square is on it. */
|
|
79
|
+
--ui-clip-tight: none;
|
|
80
|
+
|
|
81
|
+
--ui-hover-transform: none;
|
|
82
|
+
--ui-active-transform: none;
|
|
83
|
+
--ui-backdrop: none;
|
|
84
|
+
|
|
85
|
+
/* Consumer-supplied. The package ships no font binary, so the aesthetic has no
|
|
86
|
+
font to fall back to but a monospace stack. */
|
|
87
|
+
font-family: var(--font-pixel, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
|
88
|
+
}
|
package/dist/tw/box.css
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
@reference "tailwindcss";
|
|
2
|
+
|
|
3
|
+
/* How a box is painted, in one place: intent supplies the colour, presentation
|
|
4
|
+
how much of it, the aesthetic the material, and this composes the three. It
|
|
5
|
+
composes here, not on a container, because a custom property resolves its
|
|
6
|
+
`var()` on the element that declares it -- a token referencing an intent would
|
|
7
|
+
inherit down already-resolved against the wrong one. So the aesthetic ships
|
|
8
|
+
colourless geometry and the colour is mixed here. A component's resting values
|
|
9
|
+
arrive as `--_d-*`; everything else is the three axes. */
|
|
10
|
+
@utility box {
|
|
11
|
+
/* Presentation resolved against the component's registry default, then capped
|
|
12
|
+
by `--intent-fill-max` -- neutral stops early because a full fill of the
|
|
13
|
+
page's ink is a slab. The cap lands before the hover step, so a capped
|
|
14
|
+
intent still deepens on hover instead of pinning rest and hover together.
|
|
15
|
+
See model.md#the-ink-is-gated-by-the-plate-not-tied-to-the-fill. */
|
|
16
|
+
--_capped: min(var(--ui-fill, var(--_d-fill)), var(--intent-fill-max, 100%));
|
|
17
|
+
--_fill: calc(var(--_capped) * var(--ui-bg-alpha, 1));
|
|
18
|
+
--_hover-fill: calc(min(100%, var(--_capped) + var(--hover-step)) * var(--ui-bg-alpha, 1));
|
|
19
|
+
/* How much of the foreground is the contrast ink: what the presentation asks
|
|
20
|
+
for, gated by how much the plate can carry. The second term ramps in past a
|
|
21
|
+
half fill and reaches full at a full fill -- below that a partial plate
|
|
22
|
+
keeps `--intent-strong`, the tone picked to be read on the page. Legibility
|
|
23
|
+
is not linear, so this is not `min(asked, --intent-fill-max)`: a capped
|
|
24
|
+
neutral `.solid` fills to 20% and needs the darkest ink, not one walked 20%
|
|
25
|
+
toward the page (measured 6.63:1 where strong gives 10.9:1). `--ui-fg-on-fill`
|
|
26
|
+
stays the ceiling, not the result, or the slot is declared and unread. */
|
|
27
|
+
--_on-fill: min(var(--ui-fg-on-fill, var(--_d-fg-on-fill)), max(0%, calc(var(--_capped) * 2 - 100%)));
|
|
28
|
+
|
|
29
|
+
--_bg: color-mix(in oklab, var(--intent-color) var(--_fill), var(--_d-ground, transparent));
|
|
30
|
+
/* The two ends answer different questions: `--intent-contrast` prints *on* the
|
|
31
|
+
fill, `--intent-strong` prints on the page (the slot for readable ink on a
|
|
32
|
+
subtle fill). Reading `--intent-color` at the page end instead makes every
|
|
33
|
+
colour do double duty and prints a soft amber component at 3.1:1.
|
|
34
|
+
`--intent-contrast` keeps its own meaning -- `--_on-fill` reconciles it with
|
|
35
|
+
a capped intent -- rather than being bent so neutral's contrast is the ink,
|
|
36
|
+
which rendered a checked neutral checkbox black-on-black. */
|
|
37
|
+
--_fg: color-mix(in oklab, var(--intent-contrast) var(--_on-fill), var(--intent-strong));
|
|
38
|
+
/* P3: the line blends toward the fill by the fill amount, so a filled box has
|
|
39
|
+
a seamless edge. Blend first, amount after (`--_edge-amount` below) -- the
|
|
40
|
+
other order lands `.edgeless` on the fill colour, which the border box then
|
|
41
|
+
paints as a second translucent coat: a ring around an element that asked for
|
|
42
|
+
no edge. The blend runs toward `--_bg`, not raw `--intent-color`, so under
|
|
43
|
+
an aesthetic that thins the fill a translucent box does not keep an opaque
|
|
44
|
+
ring. */
|
|
45
|
+
--_line: color-mix(in oklab, var(--_bg) var(--ui-fill, var(--_d-fill)), var(--intent-border));
|
|
46
|
+
/* How much line there is, as an input rather than a result. A component that
|
|
47
|
+
must keep its line whatever the edge class says raises `--_edge-floor`
|
|
48
|
+
instead of rewriting `--_edge`, and the composition above still runs.
|
|
49
|
+
|
|
50
|
+
The distinction is not cosmetic. Rewriting the result drops every term the
|
|
51
|
+
result was composed from, silently and with nothing to report: that is how
|
|
52
|
+
`--ui-border` stopped reaching all six text controls, and how `--_d-border`
|
|
53
|
+
sat declared and unread on each of them for a release. Bounding the input
|
|
54
|
+
leaves the axis live, so `.edged` and `.edgeless` still mean what they say
|
|
55
|
+
on a component that happens to floor itself. */
|
|
56
|
+
--_edge-amount: max(var(--ui-border, var(--_d-border)), var(--_edge-floor, 0%));
|
|
57
|
+
--_edge: color-mix(in oklab, var(--_line) var(--_edge-amount), transparent);
|
|
58
|
+
--_width: min(var(--ui-border-width, var(--border-width)), var(--ui-border-max, 100px));
|
|
59
|
+
|
|
60
|
+
/* Elevation is a unitless multiplier over the aesthetic's shadow geometry, so
|
|
61
|
+
one number turns a raised element flat without knowing what the aesthetic
|
|
62
|
+
draws. Zero lengths are written `0px` because `calc(0 * 1)` yields a number
|
|
63
|
+
and a shadow position needs a length. */
|
|
64
|
+
--_elevation: var(--ui-elevation, var(--_d-elevation, 1));
|
|
65
|
+
--_sx: calc(var(--ui-shadow-x, var(--_d-shadow-x, 0px)) * var(--_elevation));
|
|
66
|
+
/* Three slots, most specific first: the aesthetic, then a component that draws
|
|
67
|
+
its own depth, then the plain step `.raised`/`.floating` supply on a page
|
|
68
|
+
with no aesthetic. The order lets a raised card under `.neobrutalism` keep
|
|
69
|
+
the slab. Nothing declares the last slot until something asks for depth, so
|
|
70
|
+
a plain unmodified page has no shadow anywhere. */
|
|
71
|
+
--_sy: calc(var(--ui-shadow-y, var(--_d-shadow-y, var(--elevation-y, 0px))) * var(--_elevation));
|
|
72
|
+
--_sb: calc(var(--ui-shadow-blur, var(--_d-shadow-blur, var(--elevation-blur, 0px))) * var(--_elevation));
|
|
73
|
+
/* Spread is deliberately not scaled. An aesthetic that draws its edge as an
|
|
74
|
+
inset ring spends spread on it, and scaling that by elevation would erase
|
|
75
|
+
the edge of every component the registry rests at zero. Elevation is depth:
|
|
76
|
+
offset and blur. */
|
|
77
|
+
--_ss: var(--ui-shadow-spread, var(--_d-shadow-spread, 0px));
|
|
78
|
+
/* A shadow is a neutral depth cue by default, the intent's own ink only where
|
|
79
|
+
an aesthetic asks (`--ui-shadow-ink`, a bare percentage so it inherits and
|
|
80
|
+
mixes where the intent lives). An intent-coloured shadow at full opacity is
|
|
81
|
+
a smear of the component's hue, not depth; neobrutalism's slab, which *is*
|
|
82
|
+
ink, asks for it by name. */
|
|
83
|
+
--_depth-color: color-mix(in oklab, var(--intent-border) var(--ui-shadow-ink, 0%), var(--elevation-color));
|
|
84
|
+
/* Or the shadow *is* the edge. An aesthetic that draws its line as an inset
|
|
85
|
+
ring (`.pixel`) puts the border where `clip-path` cannot cut it, so that
|
|
86
|
+
ring must answer the edge axis like a real border -- `--_edge` already does,
|
|
87
|
+
carrying P3's blend and `--_edge-amount` as its alpha, transparent at
|
|
88
|
+
`.edgeless`. Kept separate from `--ui-shadow-ink`, which means the opposite:
|
|
89
|
+
neobrutalism's slab is depth in ink and stays under `.edgeless`.
|
|
90
|
+
|
|
91
|
+
The switch is presence, not a percentage, and that is measured: written as
|
|
92
|
+
`color-mix(... var(--_edge) var(--ui-shadow-edge), var(--_depth-color))`,
|
|
93
|
+
WebKit alone resolves the nested `color-mix()` as though the percentage were
|
|
94
|
+
zero. So the aesthetic declares the token empty to mean "my shadow is my
|
|
95
|
+
edge", and an undeclared token makes the line invalid at computed-value time
|
|
96
|
+
and hands `var()` its fallback -- the idiom `--_focus-stack` also uses. */
|
|
97
|
+
--_edge-shadow-color: var(--ui-shadow-edge) var(--_edge);
|
|
98
|
+
--_shadow-color: var(--_edge-shadow-color, var(--_depth-color));
|
|
99
|
+
--_shadow-parts: var(--ui-shadow-inset,) var(--_sx) var(--_sy) var(--_sb) var(--_ss) var(--_shadow-color);
|
|
100
|
+
/* Indirection so a component can substitute a complete value of its own
|
|
101
|
+
without restating the parts -- `surface` does, for the multi-layer case.
|
|
102
|
+
There is deliberately no `--ui-shadow` wrapper: a complete value has nothing
|
|
103
|
+
for the elevation multiplier to reach, so `.flat` would silently stop working
|
|
104
|
+
on anything that set one. `--ui-surface-shadow` is that escape hatch narrowed
|
|
105
|
+
to the single case that needs it. */
|
|
106
|
+
--_shadow: var(--_shadow-parts);
|
|
107
|
+
--_focus-stack: var(--_shadow), inset 0 0 0 var(--ui-focus-inset) var(--focus-ring);
|
|
108
|
+
|
|
109
|
+
background-color: var(--_bg);
|
|
110
|
+
color: var(--_fg);
|
|
111
|
+
border: var(--_width) solid var(--_edge);
|
|
112
|
+
border-radius: var(--ui-radius, var(--radius-control));
|
|
113
|
+
box-shadow: var(--_shadow);
|
|
114
|
+
clip-path: var(--ui-clip, none);
|
|
115
|
+
transition:
|
|
116
|
+
background-color var(--motion-duration-normal) var(--motion-ease),
|
|
117
|
+
border-color var(--motion-duration-normal) var(--motion-ease),
|
|
118
|
+
box-shadow var(--motion-duration-normal) var(--motion-ease),
|
|
119
|
+
color var(--motion-duration-normal) var(--motion-ease),
|
|
120
|
+
opacity var(--motion-duration-normal) var(--motion-ease),
|
|
121
|
+
transform var(--motion-duration-normal) var(--motion-ease);
|
|
122
|
+
|
|
123
|
+
/* An aesthetic that clips cannot show the global focus outline, so it declares
|
|
124
|
+
`--ui-focus-inset` and gets an inset layer instead. With no such aesthetic
|
|
125
|
+
the token is undefined, which makes the whole stack invalid at computed-value
|
|
126
|
+
time -- so `var()` takes the fallback and the box keeps the shadow it already
|
|
127
|
+
had, while the outline from `reset.css` does the work.
|
|
128
|
+
|
|
129
|
+
The stack has to be built as one custom property. Written inline, the
|
|
130
|
+
invalid `var()` would poison the `box-shadow` declaration itself, and a
|
|
131
|
+
declaration invalid at computed-value time becomes `unset` -- which for a
|
|
132
|
+
non-inherited property is `initial`, that is `none`. Every focused box would
|
|
133
|
+
lose its shadow rather than keep it. */
|
|
134
|
+
&:focus-visible {
|
|
135
|
+
box-shadow: var(--_focus-stack, var(--_shadow));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
&:disabled,
|
|
139
|
+
&.disabled,
|
|
140
|
+
&[aria-disabled="true"],
|
|
141
|
+
&[data-disabled] {
|
|
142
|
+
@apply cursor-not-allowed;
|
|
143
|
+
|
|
144
|
+
opacity: 0.58;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Hover is derived from the resting fill rather than declared: one formula, no
|
|
149
|
+
tokens, no branches. `ghost` picks up a tint, `soft` deepens, `solid` stays
|
|
150
|
+
full and darkens because the colour changed.
|
|
151
|
+
|
|
152
|
+
Separate from `box` because tinting on hover is right for something you press
|
|
153
|
+
and wrong for something you type into, so a component opts in. */
|
|
154
|
+
@utility box-hover {
|
|
155
|
+
&:not(:disabled, .disabled, [aria-disabled="true"], [data-disabled]):hover {
|
|
156
|
+
--_bg: color-mix(in oklab, var(--intent-hover) var(--_hover-fill), var(--_d-ground, transparent));
|
|
157
|
+
/* P3 again, against the hover tone. Only the ink end has to move: the fill
|
|
158
|
+
end is `--_bg`, which the line above already redefined, so the resting
|
|
159
|
+
formula follows the hover on its own. Without this the edge blends toward
|
|
160
|
+
the resting ink -- a ring of the colour the box just left. */
|
|
161
|
+
--_line: color-mix(in oklab, var(--_bg) var(--ui-fill, var(--_d-fill)), var(--intent-hover));
|
|
162
|
+
--_sx: calc(var(--ui-hover-shadow-x, var(--ui-shadow-x, var(--_d-shadow-x, 0px))) * var(--_elevation));
|
|
163
|
+
--_sy: calc(
|
|
164
|
+
var(--ui-hover-shadow-y, var(--ui-shadow-y, var(--_d-shadow-y, var(--elevation-y, 0px)))) * var(--_elevation)
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
transform: var(--ui-hover-transform, none);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* The press, beside the hover for the same reason: moving on `:active` is right
|
|
172
|
+
for something you click and meaningless on something you type into, so a
|
|
173
|
+
component opts in. `btn` and `.card.interactive` both do. The gesture is the
|
|
174
|
+
aesthetic's -- neobrutalism travels into its offset shadow, chunky-tile drops
|
|
175
|
+
onto its bar -- and with no aesthetic it is the base `scale(0.97)` from
|
|
176
|
+
`theme.css`, dropped under reduced motion. The `--ui-active-shadow-*` slots
|
|
177
|
+
stay no-ops until an aesthetic sets them. */
|
|
178
|
+
@utility box-active {
|
|
179
|
+
&:active:not(:disabled, .disabled, [aria-disabled="true"], [data-disabled]) {
|
|
180
|
+
--_sx: calc(var(--ui-active-shadow-x, var(--ui-shadow-x, var(--_d-shadow-x, 0px))) * var(--_elevation));
|
|
181
|
+
--_sy: calc(var(--ui-active-shadow-y, var(--ui-shadow-y, var(--_d-shadow-y, 0px))) * var(--_elevation));
|
|
182
|
+
|
|
183
|
+
transform: var(--ui-active-transform, none);
|
|
184
|
+
}
|
|
185
|
+
}
|