@codenhub/styles 0.0.4 → 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.
Files changed (71) hide show
  1. package/LICENSE +201 -201
  2. package/NOTICE +27 -0
  3. package/README.md +60 -92
  4. package/dist/aesthetics/chunky-tile.css +1 -0
  5. package/dist/aesthetics/glass.css +1 -0
  6. package/dist/aesthetics/index.css +2 -0
  7. package/dist/aesthetics/neobrutalism.css +1 -0
  8. package/dist/aesthetics/pixel.css +1 -0
  9. package/dist/components.css +2 -2
  10. package/dist/index.css +2 -2
  11. package/dist/native.css +2 -0
  12. package/dist/theme.css +2 -2
  13. package/dist/tw/aesthetics/chunky-tile.css +107 -0
  14. package/dist/tw/aesthetics/glass.css +66 -0
  15. package/dist/tw/aesthetics/index.css +9 -0
  16. package/dist/tw/aesthetics/neobrutalism.css +63 -0
  17. package/dist/tw/aesthetics/pixel.css +88 -0
  18. package/dist/tw/box.css +185 -0
  19. package/dist/tw/components/button.css +88 -0
  20. package/dist/tw/components/feedback.css +163 -0
  21. package/dist/tw/components/form.css +601 -0
  22. package/dist/tw/components/index.css +18 -0
  23. package/dist/tw/components/loader.css +122 -0
  24. package/dist/tw/components/surface.css +98 -0
  25. package/dist/tw/components/tooltip.css +132 -0
  26. package/dist/tw/elevation.css +39 -0
  27. package/dist/tw/index.css +12 -0
  28. package/dist/tw/intent.css +189 -0
  29. package/dist/tw/native.css +203 -0
  30. package/dist/tw/presentation.css +77 -0
  31. package/dist/tw/reset.css +142 -0
  32. package/dist/tw/theme.css +218 -0
  33. package/dist/tw/typography.css +34 -0
  34. package/dist/tw/utilities/content.css +349 -0
  35. package/dist/tw/utilities/index.css +6 -0
  36. package/dist/tw/utilities/layout.css +107 -0
  37. package/docs/accessibility.md +82 -0
  38. package/docs/concepts.md +94 -0
  39. package/docs/index.md +62 -0
  40. package/docs/integrating/astro.md +29 -0
  41. package/docs/integrating/index.md +32 -0
  42. package/docs/integrating/nextjs.md +44 -0
  43. package/docs/integrating/svelte.md +37 -0
  44. package/docs/integrating/tailwind.md +40 -0
  45. package/docs/integrating/vue.md +25 -0
  46. package/docs/setup.md +116 -0
  47. package/docs/usage/aesthetics.md +197 -0
  48. package/docs/usage/buttons.md +74 -0
  49. package/docs/usage/composing.md +218 -0
  50. package/docs/usage/content-and-layout.md +200 -0
  51. package/docs/usage/customizing.md +213 -0
  52. package/docs/usage/feedback.md +117 -0
  53. package/docs/usage/forms.md +177 -0
  54. package/docs/usage/index.md +32 -0
  55. package/docs/usage/theming.md +210 -0
  56. package/docs/usage/tooltips.md +45 -0
  57. package/llms-full.txt +2122 -0
  58. package/llms.txt +26 -0
  59. package/package.json +95 -29
  60. package/src/components/button.css +0 -155
  61. package/src/components/feedback.css +0 -214
  62. package/src/components/form.css +0 -303
  63. package/src/components/index.css +0 -9
  64. package/src/components/loader.css +0 -56
  65. package/src/components/surface.css +0 -6
  66. package/src/components/tooltip.css +0 -60
  67. package/src/index.css +0 -7
  68. package/src/overrides.css +0 -76
  69. package/src/theme.css +0 -234
  70. package/src/typography.css +0 -30
  71. package/src/utilities.css +0 -40
package/dist/theme.css CHANGED
@@ -1,2 +1,2 @@
1
- /*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
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}.contents{display:contents}.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)}.light,.theme-light,[data-theme=light]{color-scheme:light;--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)){.light,.theme-light,[data-theme=light]{--color-success-hover:color-mix(in srgb, var(--color-success) 82%, var(--color-success-strong))}}.light,.theme-light,[data-theme=light]{--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)){.light,.theme-light,[data-theme=light]{--color-warning-hover:color-mix(in srgb, var(--color-warning) 82%, var(--color-warning-strong))}}.light,.theme-light,[data-theme=light]{--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)){.light,.theme-light,[data-theme=light]{--color-destructive-hover:color-mix(in srgb, var(--color-destructive) 82%, var(--color-destructive-strong))}}.light,.theme-light,[data-theme=light]{--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)){.light,.theme-light,[data-theme=light]{--color-info-hover:color-mix(in srgb, var(--color-info) 82%, var(--color-info-strong))}}.light,.theme-light,[data-theme=light]{--color-info-subtle:var(--color-indigo-100);--color-info-strong:var(--color-indigo-800)}.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)}@media (prefers-color-scheme:dark){:root:not(.light,.theme-light,[data-theme=light]){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)){:root:not(.light,.theme-light,[data-theme=light]){--color-success-hover:color-mix(in srgb, var(--color-success) 82%, var(--color-success-strong))}}:root:not(.light,.theme-light,[data-theme=light]){--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)){:root:not(.light,.theme-light,[data-theme=light]){--color-warning-hover:color-mix(in srgb, var(--color-warning) 82%, var(--color-warning-strong))}}:root:not(.light,.theme-light,[data-theme=light]){--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)){:root:not(.light,.theme-light,[data-theme=light]){--color-destructive-hover:color-mix(in srgb, var(--color-destructive) 82%, var(--color-destructive-strong))}}:root:not(.light,.theme-light,[data-theme=light]){--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)){:root:not(.light,.theme-light,[data-theme=light]){--color-info-hover:color-mix(in srgb, var(--color-info) 82%, var(--color-info-strong))}}:root:not(.light,.theme-light,[data-theme=light]){--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
+ }
@@ -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
+ }