@codenhub/styles 0.1.0 → 0.1.1

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 (45) hide show
  1. package/README.md +7 -69
  2. package/dist/aesthetics/chunky-tile.css +1 -1
  3. package/dist/aesthetics/index.css +1 -1
  4. package/dist/components.css +1 -1
  5. package/dist/index.css +1 -1
  6. package/dist/native.css +1 -1
  7. package/dist/theme.css +1 -1
  8. package/dist/tw/aesthetics/chunky-tile.css +4 -2
  9. package/dist/tw/box.css +21 -0
  10. package/dist/tw/components/button.css +23 -3
  11. package/dist/tw/components/form.css +0 -2
  12. package/dist/tw/components/index.css +16 -6
  13. package/dist/tw/components/surface.css +29 -5
  14. package/dist/tw/elevation.css +14 -7
  15. package/dist/tw/index.css +20 -5
  16. package/dist/tw/intent.css +20 -0
  17. package/dist/tw/native.css +4 -20
  18. package/dist/tw/reset.css +20 -33
  19. package/dist/tw/theme.css +12 -7
  20. package/dist/tw/utilities/content.css +14 -2
  21. package/docs/accessibility.md +58 -48
  22. package/docs/changelog/0.1.0.md +37 -0
  23. package/docs/changelog/0.1.1.md +10 -0
  24. package/docs/changelog/index.md +10 -0
  25. package/docs/concepts.md +20 -46
  26. package/docs/index.md +18 -45
  27. package/docs/integrating/astro.md +13 -7
  28. package/docs/integrating/index.md +25 -16
  29. package/docs/integrating/nextjs.md +31 -8
  30. package/docs/integrating/svelte.md +14 -8
  31. package/docs/integrating/tailwind.md +31 -15
  32. package/docs/integrating/vue.md +13 -7
  33. package/docs/setup.md +35 -55
  34. package/docs/usage/aesthetics.md +30 -107
  35. package/docs/usage/buttons.md +19 -35
  36. package/docs/usage/composing.md +25 -100
  37. package/docs/usage/content-and-layout.md +56 -81
  38. package/docs/usage/customizing.md +25 -90
  39. package/docs/usage/feedback.md +11 -49
  40. package/docs/usage/forms.md +16 -58
  41. package/docs/usage/index.md +9 -17
  42. package/docs/usage/theming.md +25 -81
  43. package/docs/usage/tooltips.md +15 -21
  44. package/llms-full.txt +1060 -1506
  45. package/package.json +121 -44
package/dist/theme.css CHANGED
@@ -1,2 +1,2 @@
1
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}
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))}:root,.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,.elevation-md,.floating{--elevation-y:1px;--elevation-blur:3px}.elevation-sm,.raised{--ui-elevation:1}.elevation-md,.floating{--ui-elevation:2}: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}
@@ -80,8 +80,10 @@
80
80
 
81
81
  /* Consumer-supplied, the way `.pixel` reads `--font-pixel`. The package ships no
82
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));
83
+ page's own stack until one is supplied -- `--font-default`, the token the
84
+ reset applies to `html`/`body`, so a consumer that themed the page font gets
85
+ it here too. `--font-sans` is Tailwind's own and the package never sets it. */
86
+ font-family: var(--font-rounded, var(--font-default, ui-sans-serif, system-ui, sans-serif));
85
87
  }
86
88
 
87
89
  /* The one R3 exception this aesthetic takes, recorded in the registry as
package/dist/tw/box.css CHANGED
@@ -26,6 +26,17 @@
26
26
  stays the ceiling, not the result, or the slot is declared and unread. */
27
27
  --_on-fill: min(var(--ui-fg-on-fill, var(--_d-fg-on-fill)), max(0%, calc(var(--_capped) * 2 - 100%)));
28
28
 
29
+ /* The ground a partial fill is painted over. Every box rests on nothing unless
30
+ it says so, and it says so here rather than through the `var(--_d-ground,
31
+ transparent)` fallback on the line below: `--_d-ground` inherits like any
32
+ custom property, so a `surface` setting it to its own opaque plate would
33
+ reach every box nested inside -- a `ghost` button in a card would blend its
34
+ zero fill over the page colour and paint an opaque slab where it asked for
35
+ no fill, and the slab would stay while the card tints on hover. An own
36
+ declaration beats the inherited one at any specificity; `surface`, `kbd`,
37
+ `code`, `pre` and the tooltip each restate theirs after `@apply box`. */
38
+ --_d-ground: transparent;
39
+
29
40
  --_bg: color-mix(in oklab, var(--intent-color) var(--_fill), var(--_d-ground, transparent));
30
41
  /* The two ends answer different questions: `--intent-contrast` prints *on* the
31
42
  fill, `--intent-strong` prints on the page (the slot for readable ink on a
@@ -106,6 +117,16 @@
106
117
  --_shadow: var(--_shadow-parts);
107
118
  --_focus-stack: var(--_shadow), inset 0 0 0 var(--ui-focus-inset) var(--focus-ring);
108
119
 
120
+ /* Every box sizes itself as `min-height`/`min-width: var(--control-height)`
121
+ plus its own padding and the border shorthand above, so its geometry is only
122
+ right under `box-sizing: border-box`. `reset.css` sets that on `*`, but only
123
+ the `.`, `/native` and `/tw/reset` entrypoints carry the reset -- and the
124
+ first two pull Tailwind's Preflight, which sets it anyway. `/components` and
125
+ the granular `/tw/*` component entrypoints ship neither, so a box there
126
+ inherited the `content-box` default and every sized control came out
127
+ inflated by its own padding and border. A component's own geometry is not an
128
+ external reset's job; `box` owns it. */
129
+ box-sizing: border-box;
109
130
  background-color: var(--_bg);
110
131
  color: var(--_fg);
111
132
  border: var(--_width) solid var(--_edge);
@@ -33,12 +33,32 @@
33
33
  min-width: var(--control-height);
34
34
  }
35
35
 
36
- /* `.p-sm`/`.p-lg` are the canonical names, matching the `sm`/`md`/`lg`
37
- vocabulary the elevation modifier uses; `.compact`/`.spacious` are
36
+ /* `.p-xs`/`.p-sm`/`.p-lg` are the canonical names, matching the `sm`/`md`/`lg`
37
+ vocabulary the elevation modifier uses; `.dense`/`.compact`/`.spacious` are
38
38
  first-class aliases, not a deprecation shim -- the same pattern
39
39
  `.destructive`/`.danger`/`.error` uses on the intent axis. Bare `sm`/`lg`
40
40
  are already the size modifier's class names, so `p-` prefixes here to
41
- avoid colliding with a button's own size. */
41
+ avoid colliding with a button's own size. `.p-xs` is the tightest step,
42
+ below `.compact`: a chip-sized button in a toolbar or a table cell. */
43
+ &.p-xs,
44
+ &.dense {
45
+ @apply px-2 py-0.5;
46
+
47
+ /* The one padding step that also drops the size floor. At this tier the
48
+ button is a chip in a toolbar or a table cell, where `--control-height`
49
+ is taller than the row it sits in; every other size and padding class
50
+ keeps the floor, so a plain button stays a comfortable target. The icon
51
+ variant gives up `min-width` with it -- with no floor on either axis the
52
+ box is padding around whatever the consumer sized their glyph to. */
53
+ min-height: 0;
54
+
55
+ &.icon {
56
+ @apply p-0.5;
57
+
58
+ min-width: 0;
59
+ }
60
+ }
61
+
42
62
  &.p-sm,
43
63
  &.compact {
44
64
  @apply px-2.5 py-1;
@@ -104,8 +104,6 @@
104
104
 
105
105
  @apply w-full appearance-none;
106
106
 
107
- box-sizing: border-box;
108
-
109
107
  &::placeholder {
110
108
  color: var(--color-text-secondary);
111
109
  }
@@ -1,10 +1,7 @@
1
1
  @reference "tailwindcss";
2
2
  @import "tailwindcss/utilities.css";
3
- @source not "../../dist";
4
- @source not "../../tests";
5
- @source not "../../dev";
6
- @source not "../../debug";
7
- @source not "../../playground";
3
+ /* Everything that is not `src/` -- see the note in `index.css`. Keeps a class
4
+ name in a docs code fence from shipping as a real rule in `dist/components.css`. */
8
5
  @import "../theme.css";
9
6
  @import "../typography.css";
10
7
  @import "../box.css";
@@ -15,4 +12,17 @@
15
12
  @import "./surface.css";
16
13
  @import "./tooltip.css";
17
14
 
18
- @source inline("text-display text-title-lg text-title text-title-sm text-label-lg text-label text-body btn field label hint ipt email password url tel search date datetime month week time icon left right textarea select checkbox radio switch alert badge skeleton progress tooltip card panel interactive flush compact spacious neutral solid soft ghost edged edgeless flat raised floating loader");
15
+ /* The composition utilities -- `box`, `box-hover`, `box-active`, `loader-mask`,
16
+ `text-control`, `surface`, `input-group` -- are `@utility` rules whose bodies
17
+ the content scan will not emit on its own. `box` and friends appear only as
18
+ `@utility` blocks and `@apply` targets; `text-control`, `surface` and
19
+ `input-group` appear only inside `:is()`/`:where()` argument lists in
20
+ `intent.css` and `form.css`, never as a selector of their own. Their standalone
21
+ rules shipped only because docs prose happened to name them, and excluding
22
+ `docs/` from the scan removed that -- `.input-group` (and, with it, its focus
23
+ ring, its flush inner control, and `:has()`-propagated invalid state) went
24
+ silently dark, caught only by the browser suite. Safelisting all seven here
25
+ makes the emission deliberate; the composition-body check in
26
+ `exports.test.ts` fails the build if one is dropped. `text-control` and
27
+ `surface` are also documented as consumer-usable on their own. */
28
+ @source inline("box box-hover box-active loader-mask text-control surface input-group text-display text-title-lg text-title text-title-sm text-label-lg text-label text-body btn field label hint ipt email password url tel search date datetime month week time icon left right textarea select checkbox radio switch alert badge skeleton progress tooltip card panel interactive flush compact spacious neutral solid soft ghost edged edgeless flat raised floating loader");
@@ -46,8 +46,13 @@
46
46
  --_d-border: 100%;
47
47
  --_d-elevation: 1;
48
48
 
49
- /* `.p-sm`/`.p-lg` are the canonical names; `.compact`/`.spacious` are
50
- first-class aliases -- see the identical note in `button.css`. */
49
+ /* `.p-xs`/`.p-sm`/`.p-lg` are the canonical names; `.dense`/`.compact`/`.spacious`
50
+ are first-class aliases -- see the identical note in `button.css`. */
51
+ &.p-xs,
52
+ &.dense {
53
+ --surface-padding: 0.5rem;
54
+ }
55
+
51
56
  &.p-sm,
52
57
  &.compact {
53
58
  --surface-padding: 0.75rem;
@@ -65,9 +70,23 @@
65
70
  /* Opt-in, because a card is only sometimes a control. Applications still owe
66
71
  it a real interactive element and an accessible name. Hover and press are
67
72
  the derived ones every other pressable box gets; there is nothing
68
- card-specific about either. */
69
- &.interactive {
70
- @apply box-hover box-active cursor-pointer;
73
+ card-specific about either.
74
+
75
+ `.interactive` is the whole opt-in and stays that way; `.hoverable` and
76
+ `.pressable` are its halves, for a card that answers one gesture and not
77
+ the other. A card with its own nested controls takes `.hoverable` for the
78
+ hover warmth without the press-scale that reads as "you activated the
79
+ card" when the click really landed on a nested button. `.hoverable` alone
80
+ does not set the pointer cursor: that travels with being a click target,
81
+ i.e. with `.pressable` / `.interactive`. */
82
+ &.interactive,
83
+ &.hoverable {
84
+ @apply box-hover;
85
+ }
86
+
87
+ &.interactive,
88
+ &.pressable {
89
+ @apply box-active cursor-pointer;
71
90
  }
72
91
  }
73
92
 
@@ -82,6 +101,11 @@
82
101
  --_d-border: 0%;
83
102
  --_d-elevation: 0;
84
103
 
104
+ &.p-xs,
105
+ &.dense {
106
+ --surface-padding: 0.25rem;
107
+ }
108
+
85
109
  &.p-sm,
86
110
  &.compact {
87
111
  --surface-padding: 0.5rem;
@@ -20,20 +20,27 @@
20
20
  --ui-elevation: 0;
21
21
  }
22
22
 
23
- /* The aesthetic's depth as authored, said explicitly. The geometry is the
24
- fallback for a page with no aesthetic in scope; an aesthetic that declares its
25
- own overrides it, and the elevation multiplies whichever one lands. */
23
+ /* The plain-step geometry both rungs multiply, declared once so the two cannot
24
+ drift. It is the fallback for a page with no aesthetic in scope; an aesthetic
25
+ that declares its own shadow parts overrides it, and the elevation multiplier
26
+ scales whichever one lands. `.floating` takes its extra depth from the
27
+ multiplier below, not a second copy of these parts. */
26
28
  .elevation-sm,
27
- .raised {
28
- --ui-elevation: 1;
29
+ .raised,
30
+ .elevation-md,
31
+ .floating {
29
32
  --elevation-y: 1px;
30
33
  --elevation-blur: 3px;
31
34
  }
32
35
 
36
+ /* The aesthetic's depth as authored, said explicitly. */
37
+ .elevation-sm,
38
+ .raised {
39
+ --ui-elevation: 1;
40
+ }
41
+
33
42
  /* Twice it, for menus and popovers. */
34
43
  .elevation-md,
35
44
  .floating {
36
45
  --ui-elevation: 2;
37
- --elevation-y: 1px;
38
- --elevation-blur: 3px;
39
46
  }
package/dist/tw/index.css CHANGED
@@ -1,9 +1,24 @@
1
1
  @import "tailwindcss";
2
- @source not "../dist";
3
- @source not "../tests";
4
- @source not "../dev";
5
- @source not "../debug";
6
- @source not "../playground";
2
+
3
+ /* The build has no markup to scan, so Tailwind's automatic content detection
4
+ sees only this package's own files. The `src/` scan is load-bearing -- it is
5
+ how the composition utilities (`box`, `surface`, `text-control`,
6
+ `input-group`, `loader-mask`) get emitted as standalone rules, since nothing
7
+ `@source inline`s them -- so `source(none)` is not an option. The cost is that
8
+ a handful of Tailwind utilities whose names collide with a CSS property or a
9
+ word in a comment (`.backdrop-filter`, `.filter`, `.transition`, `.container`,
10
+ ...) are emitted unused. They are inert and small; left as-is deliberately.
11
+
12
+ The `@source not` lines below keep everything that is not `src/` out of that
13
+ scan: the sibling app and fixture directories, the `docs/` tree, the build
14
+ scripts, and the root prose and data files (`README.md`, `llms*.txt`,
15
+ `registry*.json`, `package.json`). Without them a class name or `--color-*`
16
+ reference that appears only in a documentation example ships as real CSS --
17
+ the violet ramp in `docs/usage/theming.md`'s custom-intent example was
18
+ compiling into `dist/index.css` and `dist/native.css`, and any utility in a
19
+ docs code fence did the same. `tests/integration/exports.test.ts` guards it.
20
+ `scripts/build-tw.mjs` strips these lines from the `dist/tw/` copy, where they
21
+ resolve to nothing and the `/tw` consumer scans their own tree anyway. */
7
22
 
8
23
  @import "./theme.css";
9
24
  @import "./typography.css";
@@ -8,6 +8,26 @@
8
8
  tooltips, key caps, code and table heads sit on) and `--intent-border` to
9
9
  `--ui-ink` (how an aesthetic restates the neutral line without naming one). */
10
10
 
11
+ /* The same neutral values, at the root. This is the floor: an element that
12
+ carries no intent class and matches none of the resets below -- a component
13
+ `@apply`-ed onto a selector the package does not name, a bare `<code>` with
14
+ no native stylesheet loaded -- still composes against a real colour instead
15
+ of an undefined one that collapses its `color-mix()` to nothing. It is not
16
+ the barrier. `.neutral` and both resets below still write these slots on the
17
+ element itself, which is a matching rule and so beats this inherited value --
18
+ that is what stops a `.success` container's intent reaching a component
19
+ nested under it. An intent class on the element beats both. Values track
20
+ `.neutral` slot for slot; `registry.test.ts` fails the build on drift. */
21
+ :root {
22
+ --intent-color: var(--color-text);
23
+ --intent-contrast: var(--color-text-contrast);
24
+ --intent-hover: var(--color-text-hover);
25
+ --intent-strong: var(--color-text-strong);
26
+ --intent-subtle: var(--color-surface);
27
+ --intent-border: var(--ui-ink, var(--color-border));
28
+ --intent-fill-max: 20%;
29
+ }
30
+
11
31
  /* Neutral stops its fill short: its colour is the page's ink, so a full fill is
12
32
  a slab and `.neutral.solid` was pixel-for-pixel `.primary.solid`. 20% is
13
33
  chosen against `.soft`'s 12% -- a step past the tint, not a repeat.
@@ -1,10 +1,8 @@
1
1
  @import "./theme.css";
2
2
  @reference "tailwindcss";
3
- @source not "../dist";
4
- @source not "../tests";
5
- @source not "../dev";
6
- @source not "../debug";
7
- @source not "../playground";
3
+ /* Everything that is not `src/` -- see the note in `index.css`. Without the
4
+ docs and root-file excludes a utility used only in a documentation example
5
+ compiles into `dist/native.css`. */
8
6
  @import "./typography.css";
9
7
  @import "./utilities/index.css";
10
8
  @import "./components/index.css";
@@ -52,21 +50,7 @@
52
50
  }
53
51
 
54
52
  @layer base {
55
- h1,
56
- h2,
57
- h3,
58
- h4,
59
- h5,
60
- h6,
61
- p,
62
- figure,
63
- blockquote,
64
- dl,
65
- dd,
66
- ul,
67
- ol {
68
- margin: 0;
69
- }
53
+ /* The `h1`-`ol` margin reset lives in `reset.css`, imported above. */
70
54
 
71
55
  /* Headings */
72
56
  h1 {
package/dist/tw/reset.css CHANGED
@@ -3,16 +3,13 @@
3
3
  @import "./typography.css";
4
4
 
5
5
  @layer base {
6
- /* Every sizing utility this package ships — `min-height`/`min-width:
7
- var(--control-height)` on `.btn`, `.ipt`, `.select`, `.textarea` chief
8
- among them — means that as the control's total footprint, padding and
9
- border included. Without this, `apps/docs` reads correctly only because
10
- its own Tailwind pipeline supplies Preflight's border-box reset
11
- separately; this package only pulls in Tailwind's utility engine via
12
- `@reference`, never its Preflight, so a consumer with no Tailwind
13
- pipeline of its own — `apps/demo` imports this compiled stylesheet
14
- directly — got the content-box default instead, inflating every sized
15
- control by its own padding and border. */
6
+ /* The package's own components no longer lean on this: `@utility box` sets
7
+ `box-sizing: border-box` on itself, so a sized control is right in every
8
+ entrypoint including the reset-less `/components` and granular `/tw/*`
9
+ ones. What is left for the universal rule is the consumer's own markup --
10
+ a plain `<div>` laid out beside our components under the full reset -- so
11
+ it stays part of the opinionated baseline the reset already is, rather
12
+ than a correctness prop the components need. */
16
13
  *,
17
14
  *::before,
18
15
  *::after {
@@ -36,7 +33,7 @@
36
33
 
37
34
  html,
38
35
  body {
39
- @apply bg-background text-text font-default min-h-dvh;
36
+ @apply bg-background text-text font-default;
40
37
  }
41
38
 
42
39
  h1,
@@ -55,10 +52,6 @@
55
52
  margin: 0;
56
53
  }
57
54
 
58
- body {
59
- @apply flex flex-col;
60
- }
61
-
62
55
  button,
63
56
  input,
64
57
  optgroup,
@@ -74,10 +67,6 @@
74
67
  @apply enabled:cursor-pointer;
75
68
  }
76
69
 
77
- img {
78
- @apply select-none;
79
- }
80
-
81
70
  ::selection {
82
71
  background: var(--color-primary);
83
72
  color: var(--color-primary-contrast);
@@ -124,19 +113,17 @@
124
113
  }
125
114
  }
126
115
 
127
- @layer components {
128
- [aria-current="page"] {
129
- @apply text-primary font-bold;
130
- }
131
-
132
- @media (forced-colors: active) {
133
- .btn,
134
- .ipt,
135
- .textarea,
136
- .select,
137
- .alert,
138
- .badge {
139
- border-color: CanvasText;
140
- }
116
+ /* Unlayered on purpose, matching the `forced-colors` blocks in `native.css` and
117
+ `form.css`. An accessibility override has to win over any layered rule
118
+ whatever the specificity, and a `@layer components` wrapper here would let the
119
+ component utilities that draw a border overrule it. */
120
+ @media (forced-colors: active) {
121
+ .btn,
122
+ .ipt,
123
+ .textarea,
124
+ .select,
125
+ .alert,
126
+ .badge {
127
+ border-color: CanvasText;
141
128
  }
142
129
  }
package/dist/tw/theme.css CHANGED
@@ -2,11 +2,8 @@
2
2
  @import "./intent.css";
3
3
  @import "./presentation.css";
4
4
  @import "./elevation.css";
5
- @source not "../dist";
6
- @source not "../tests";
7
- @source not "../dev";
8
- @source not "../debug";
9
- @source not "../playground";
5
+ /* Everything that is not `src/` -- see the note in `index.css`. Keeps a
6
+ `--color-*` reference in a docs example from shipping as a real theme entry. */
10
7
 
11
8
  /* Color tokens are declared once with `light-dark()`, which resolves at the
12
9
  point of use against the consuming element's computed `color-scheme`. Theme
@@ -50,7 +47,9 @@
50
47
  --color-text-secondary: light-dark(var(--color-neutral-700), var(--color-neutral-400));
51
48
  --color-text-contrast: light-dark(var(--color-neutral-50), var(--color-neutral-950));
52
49
  --color-text-hover: light-dark(var(--color-neutral-700), var(--color-neutral-200));
53
- --color-text-subtle: light-dark(var(--color-neutral-200), var(--color-neutral-900));
50
+ /* No `--color-text-subtle`: the neutral intent points `--intent-subtle` at
51
+ `--color-surface`, not at a text-family tint, so the slot the other families
52
+ fill had no reader here. */
54
53
  --color-text-strong: light-dark(var(--color-neutral-900), var(--color-neutral-100));
55
54
 
56
55
  /* The tooltip bubble is the one component whose plate is chosen rather than
@@ -215,4 +214,10 @@
215
214
  color-scheme: dark;
216
215
  }
217
216
 
218
- @custom-variant dark (&:where(.dark, .dark *, [data-theme="dark"], [data-theme="dark"] *));
217
+ /* The selector list mirrors the `color-scheme: dark` block above, all three
218
+ spellings included -- a consumer writing `.theme-dark` for the token theme
219
+ was otherwise left without the matching `dark:` variant. System preference is
220
+ deliberately out: the variant follows an explicit theme selector, the same
221
+ rule the rest of the theme model states, so `dark:` utilities track the theme
222
+ a subtree was actually put in rather than the OS default. */
223
+ @custom-variant dark (&:where(.dark, .dark *, .theme-dark, .theme-dark *, [data-theme="dark"], [data-theme="dark"] *));
@@ -306,7 +306,11 @@
306
306
  --_d-fg-on-fill: 0%;
307
307
  --_d-border: 100%;
308
308
  --_d-elevation: 0;
309
- --quote-fill: calc(min(var(--ui-fill, var(--_d-fill)), var(--intent-fill-max, 100%)) * var(--ui-bg-alpha, 1));
309
+ /* Presentation capped by the intent, the same `--_capped` `box` composes, kept
310
+ separate from `--quote-fill` because the ink ramp below reads it before
311
+ `--ui-bg-alpha` thins it. */
312
+ --quote-capped: min(var(--ui-fill, var(--_d-fill)), var(--intent-fill-max, 100%));
313
+ --quote-fill: calc(var(--quote-capped) * var(--ui-bg-alpha, 1));
310
314
  --quote-bg: color-mix(in oklab, var(--intent-color) var(--quote-fill), transparent);
311
315
  /* The same edge blend `box` draws, in the same order and for the same reason:
312
316
  the blend is the line's colour, and how much line there is comes after it.
@@ -327,9 +331,17 @@
327
331
  background-color: var(--quote-bg);
328
332
  border-inline-start-color: var(--quote-edge);
329
333
  border-inline-start-width: var(--quote-width);
334
+ /* The ink is gated by the plate, not tied to the fill: `--ui-fg-on-fill` is the
335
+ ceiling, and the contrast tone only ramps in past a half fill, reaching full
336
+ at a full fill. Below that the ink stays `--intent-strong`, the tone chosen
337
+ to read on the page. This is `box`'s `--_on-fill` exactly -- `min(asked,
338
+ --intent-fill-max)` read plausibly and was wrong at the bottom of the range,
339
+ printing a capped-neutral `.solid` quote at ~6.6:1 where the ramp gives
340
+ ~11:1, and this renderer has to agree with `box`. */
330
341
  color: color-mix(
331
342
  in oklab,
332
- var(--intent-contrast) min(var(--ui-fg-on-fill, var(--_d-fg-on-fill)), var(--intent-fill-max, 100%)),
343
+ var(--intent-contrast)
344
+ min(var(--ui-fg-on-fill, var(--_d-fg-on-fill)), max(0%, calc(var(--quote-capped) * 2 - 100%))),
333
345
  var(--intent-strong)
334
346
  );
335
347