@codenhub/styles 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -69
- package/dist/aesthetics/chunky-tile.css +1 -1
- package/dist/aesthetics/index.css +1 -1
- package/dist/components.css +1 -1
- package/dist/index.css +1 -1
- package/dist/native.css +1 -1
- package/dist/theme.css +1 -1
- package/dist/tw/aesthetics/chunky-tile.css +20 -2
- package/dist/tw/box.css +45 -2
- package/dist/tw/components/button.css +38 -29
- package/dist/tw/components/feedback.css +58 -41
- package/dist/tw/components/form.css +139 -47
- package/dist/tw/components/index.css +16 -6
- package/dist/tw/components/loader.css +2 -16
- package/dist/tw/components/surface.css +122 -5
- package/dist/tw/components/tooltip.css +102 -91
- package/dist/tw/elevation.css +57 -14
- package/dist/tw/index.css +23 -5
- package/dist/tw/intent.css +36 -12
- package/dist/tw/native.css +4 -20
- package/dist/tw/reset.css +40 -32
- package/dist/tw/theme.css +46 -8
- package/dist/tw/utilities/content.css +104 -18
- package/dist/tw/utilities/index.css +1 -1
- package/dist/tw/utilities/layout.css +33 -0
- package/docs/accessibility.md +68 -48
- package/docs/changelog/0.1.0.md +37 -0
- package/docs/changelog/0.1.1.md +10 -0
- package/docs/changelog/0.2.0.md +41 -0
- package/docs/changelog/index.md +11 -0
- package/docs/concepts.md +21 -46
- package/docs/index.md +18 -45
- package/docs/integrating/astro.md +13 -7
- package/docs/integrating/index.md +25 -16
- package/docs/integrating/nextjs.md +31 -8
- package/docs/integrating/svelte.md +14 -8
- package/docs/integrating/tailwind.md +31 -15
- package/docs/integrating/vue.md +13 -7
- package/docs/setup.md +31 -32
- package/docs/usage/aesthetics.md +31 -107
- package/docs/usage/buttons.md +24 -36
- package/docs/usage/composing.md +31 -102
- package/docs/usage/content-and-layout.md +65 -83
- package/docs/usage/customizing.md +25 -90
- package/docs/usage/feedback.md +25 -56
- package/docs/usage/forms.md +48 -80
- package/docs/usage/index.md +9 -17
- package/docs/usage/theming.md +25 -81
- package/docs/usage/tooltips.md +55 -35
- package/llms-full.txt +1193 -1510
- 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-
|
|
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{--ui-elevation:initial;--elevation-y:initial;--elevation-blur:initial}}}: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-400),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-bubble,.tooltip-icon,.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-bubble),.tooltip-bubble.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%}@property --ui-elevation{syntax:"*";inherits:false}@property --elevation-y{syntax:"*";inherits:false}@property --elevation-blur{syntax:"*";inherits:false}.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
|
-
|
|
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
|
|
@@ -105,3 +107,19 @@
|
|
|
105
107
|
font-weight: 800;
|
|
106
108
|
letter-spacing: 0.04em;
|
|
107
109
|
}
|
|
110
|
+
|
|
111
|
+
/* `--tile-lift` alone cannot reach this: it resolves once, on `.chunky-tile`
|
|
112
|
+
itself (`--_tile-lift: var(--tile-lift, 4px)`), and inherits down already a
|
|
113
|
+
plain `4px` -- a nested `--tile-lift` on a descendant button is a fresh
|
|
114
|
+
declaration nothing downstream reads. `--ui-shadow-y` is what `box` actually
|
|
115
|
+
multiplies by elevation, so that is the token restated here, at half the
|
|
116
|
+
resting lift. A `.btn.icon` at `.dense`/`.p-xs` is close to the smallest
|
|
117
|
+
button the package draws (~20px, once the size floor both tiers drop for
|
|
118
|
+
`.icon` is gone -- see `button.css`), and a flat 4px bar under a target that
|
|
119
|
+
size sits close enough to the glyph's own footprint to read as a solid fill
|
|
120
|
+
behind it -- a `.ghost` button that looks `.solid`. 2px keeps the bar
|
|
121
|
+
legible as depth without swallowing the icon. */
|
|
122
|
+
.chunky-tile .btn.icon.dense,
|
|
123
|
+
.chunky-tile .btn.icon.p-xs {
|
|
124
|
+
--ui-shadow-y: 2px;
|
|
125
|
+
}
|
package/dist/tw/box.css
CHANGED
|
@@ -12,8 +12,30 @@
|
|
|
12
12
|
by `--intent-fill-max` -- neutral stops early because a full fill of the
|
|
13
13
|
page's ink is a slab. The cap lands before the hover step, so a capped
|
|
14
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
|
-
|
|
15
|
+
See model.md#the-ink-is-gated-by-the-plate-not-tied-to-the-fill.
|
|
16
|
+
|
|
17
|
+
`--_fill-cap` is the matching slot for a component that reinterprets what a
|
|
18
|
+
presentation means for itself rather than merely bounding it -- `.card.soft`
|
|
19
|
+
uses it to rest at 0% regardless of what `.soft` asks for. It has to be a
|
|
20
|
+
separate term rather than a rewrite of `--ui-fill`: `presentation.css`'s
|
|
21
|
+
`.soft` is unlayered CSS and `@utility card`'s own rule compiles into
|
|
22
|
+
Tailwind's `utilities` layer, and an unlayered declaration always wins over
|
|
23
|
+
a layered one for the same property, at any specificity -- so `.card.soft`
|
|
24
|
+
writing `--ui-fill: 0%` directly was silently losing to `.soft`'s `12%`
|
|
25
|
+
every time. `--_fill-cap` is a token `.soft` never touches, so nothing
|
|
26
|
+
contests it.
|
|
27
|
+
|
|
28
|
+
Restated here at `100%` -- a no-op -- for the same reason `--_d-ground`
|
|
29
|
+
is restated `transparent` below rather than left to its `var()` fallback:
|
|
30
|
+
`--_fill-cap` inherits like any custom property, so `.card.soft` setting
|
|
31
|
+
it to `0%` would otherwise reach every box nested inside, not just the
|
|
32
|
+
card itself -- a badge sitting in a `.data-table:not(.solid, .ghost)`
|
|
33
|
+
cell inherited the table's `0%` cap and rendered with no fill and no
|
|
34
|
+
visible chip at all. An own declaration beats an inherited one at any
|
|
35
|
+
specificity, so restating it here is what keeps a nested box reading its
|
|
36
|
+
own presentation instead of the container's reinterpretation of one. */
|
|
37
|
+
--_fill-cap: 100%;
|
|
38
|
+
--_capped: min(var(--ui-fill, var(--_d-fill)), var(--_fill-cap, 100%), var(--intent-fill-max, 100%));
|
|
17
39
|
--_fill: calc(var(--_capped) * var(--ui-bg-alpha, 1));
|
|
18
40
|
--_hover-fill: calc(min(100%, var(--_capped) + var(--hover-step)) * var(--ui-bg-alpha, 1));
|
|
19
41
|
/* How much of the foreground is the contrast ink: what the presentation asks
|
|
@@ -26,6 +48,17 @@
|
|
|
26
48
|
stays the ceiling, not the result, or the slot is declared and unread. */
|
|
27
49
|
--_on-fill: min(var(--ui-fg-on-fill, var(--_d-fg-on-fill)), max(0%, calc(var(--_capped) * 2 - 100%)));
|
|
28
50
|
|
|
51
|
+
/* The ground a partial fill is painted over. Every box rests on nothing unless
|
|
52
|
+
it says so, and it says so here rather than through the `var(--_d-ground,
|
|
53
|
+
transparent)` fallback on the line below: `--_d-ground` inherits like any
|
|
54
|
+
custom property, so a `surface` setting it to its own opaque plate would
|
|
55
|
+
reach every box nested inside -- a `ghost` button in a card would blend its
|
|
56
|
+
zero fill over the page colour and paint an opaque slab where it asked for
|
|
57
|
+
no fill, and the slab would stay while the card tints on hover. An own
|
|
58
|
+
declaration beats the inherited one at any specificity; `surface`, `kbd`,
|
|
59
|
+
`code`, `pre` and the tooltip each restate theirs after `@apply box`. */
|
|
60
|
+
--_d-ground: transparent;
|
|
61
|
+
|
|
29
62
|
--_bg: color-mix(in oklab, var(--intent-color) var(--_fill), var(--_d-ground, transparent));
|
|
30
63
|
/* The two ends answer different questions: `--intent-contrast` prints *on* the
|
|
31
64
|
fill, `--intent-strong` prints on the page (the slot for readable ink on a
|
|
@@ -106,6 +139,16 @@
|
|
|
106
139
|
--_shadow: var(--_shadow-parts);
|
|
107
140
|
--_focus-stack: var(--_shadow), inset 0 0 0 var(--ui-focus-inset) var(--focus-ring);
|
|
108
141
|
|
|
142
|
+
/* Every box sizes itself as `min-height`/`min-width: var(--control-height)`
|
|
143
|
+
plus its own padding and the border shorthand above, so its geometry is only
|
|
144
|
+
right under `box-sizing: border-box`. `reset.css` sets that on `*`, but only
|
|
145
|
+
the `.`, `/native` and `/tw/reset` entrypoints carry the reset -- and the
|
|
146
|
+
first two pull Tailwind's Preflight, which sets it anyway. `/components` and
|
|
147
|
+
the granular `/tw/*` component entrypoints ship neither, so a box there
|
|
148
|
+
inherited the `content-box` default and every sized control came out
|
|
149
|
+
inflated by its own padding and border. A component's own geometry is not an
|
|
150
|
+
external reset's job; `box` owns it. */
|
|
151
|
+
box-sizing: border-box;
|
|
109
152
|
background-color: var(--_bg);
|
|
110
153
|
color: var(--_fg);
|
|
111
154
|
border: var(--_width) solid var(--_edge);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
@reference "tailwindcss";
|
|
2
2
|
@import "../theme.css";
|
|
3
|
-
@reference "./loader.css";
|
|
4
3
|
@reference "../box.css";
|
|
5
4
|
|
|
6
5
|
/* A filled, edgeless, raised box you can press. `box` paints it; everything
|
|
@@ -33,18 +32,50 @@
|
|
|
33
32
|
min-width: var(--control-height);
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
/* `.p-sm`/`.p-lg` are the canonical names, matching the `sm`/`md`/`lg`
|
|
37
|
-
vocabulary the elevation modifier uses; `.compact`/`.spacious` are
|
|
35
|
+
/* `.p-xs`/`.p-sm`/`.p-lg` are the canonical names, matching the `sm`/`md`/`lg`
|
|
36
|
+
vocabulary the elevation modifier uses; `.dense`/`.compact`/`.spacious` are
|
|
38
37
|
first-class aliases, not a deprecation shim -- the same pattern
|
|
39
38
|
`.destructive`/`.danger`/`.error` uses on the intent axis. Bare `sm`/`lg`
|
|
40
39
|
are already the size modifier's class names, so `p-` prefixes here to
|
|
41
|
-
avoid colliding with a button's own size.
|
|
40
|
+
avoid colliding with a button's own size. `.p-xs` is the tightest step,
|
|
41
|
+
below `.compact`: a chip-sized button in a toolbar or a table cell.
|
|
42
|
+
|
|
43
|
+
`.icon` drops both floors -- `min-width` from its own base rule below and
|
|
44
|
+
`min-height` from `--control-height` -- at every padding tier, labeled
|
|
45
|
+
buttons keep the height floor everywhere but `.p-xs`/`.dense`. `.icon`
|
|
46
|
+
already asks to be square: `aspect-square` ties its width to whatever
|
|
47
|
+
height it lands on, so a height floor that survives keeps the button
|
|
48
|
+
bigger than its own glyph regardless of how tight the padding asked to be
|
|
49
|
+
-- the one combination `.p-xs`/`.dense` used to get right and the other two
|
|
50
|
+
tiers did not, leaving a `.icon.compact` floating in a box far bigger than
|
|
51
|
+
it needed and a `.icon.spacious` doing the same one step further out. */
|
|
52
|
+
&.p-xs,
|
|
53
|
+
&.dense {
|
|
54
|
+
@apply px-2 py-0.5;
|
|
55
|
+
|
|
56
|
+
/* The one padding step that also drops the size floor for a labeled
|
|
57
|
+
button. At this tier the button is a chip in a toolbar or a table
|
|
58
|
+
cell, where `--control-height` is taller than the row it sits in;
|
|
59
|
+
every other size and padding class keeps the floor, so a plain button
|
|
60
|
+
stays a comfortable target. */
|
|
61
|
+
min-height: 0;
|
|
62
|
+
|
|
63
|
+
&.icon {
|
|
64
|
+
@apply p-0.5;
|
|
65
|
+
|
|
66
|
+
min-width: 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
42
70
|
&.p-sm,
|
|
43
71
|
&.compact {
|
|
44
72
|
@apply px-2.5 py-1;
|
|
45
73
|
|
|
46
74
|
&.icon {
|
|
47
75
|
@apply p-1;
|
|
76
|
+
|
|
77
|
+
min-width: 0;
|
|
78
|
+
min-height: 0;
|
|
48
79
|
}
|
|
49
80
|
}
|
|
50
81
|
|
|
@@ -54,35 +85,13 @@
|
|
|
54
85
|
|
|
55
86
|
&.icon {
|
|
56
87
|
@apply p-3;
|
|
88
|
+
|
|
89
|
+
min-width: 0;
|
|
90
|
+
min-height: 0;
|
|
57
91
|
}
|
|
58
92
|
}
|
|
59
93
|
|
|
60
94
|
&.pill {
|
|
61
95
|
@apply rounded-full;
|
|
62
96
|
}
|
|
63
|
-
|
|
64
|
-
&.loading {
|
|
65
|
-
/* `text-transparent!` forces the label invisible whatever the intent
|
|
66
|
-
resolved to, so the spinner is the only thing left to read. */
|
|
67
|
-
@apply pointer-events-none text-transparent!;
|
|
68
|
-
|
|
69
|
-
& > * {
|
|
70
|
-
opacity: 0 !important;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/* The spinner takes the label's colour, which `box` already resolved
|
|
74
|
-
against this button's own intent and presentation. It reads `--_fg`
|
|
75
|
-
rather than `currentColor`: the rule above forces `color` to transparent
|
|
76
|
-
to hide the label, and a spinner painted in `currentColor` inherits that
|
|
77
|
-
and paints nothing at all. */
|
|
78
|
-
&::after {
|
|
79
|
-
@apply loader-mask absolute block size-4 -translate-x-1/2 -translate-y-1/2;
|
|
80
|
-
|
|
81
|
-
background-color: var(--_fg);
|
|
82
|
-
|
|
83
|
-
top: calc(50% + var(--border-width));
|
|
84
|
-
left: calc(50% + var(--border-width));
|
|
85
|
-
content: "";
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
97
|
}
|
|
@@ -30,54 +30,59 @@
|
|
|
30
30
|
/* A soft, edged surface, so an aesthetic that blurs surfaces blurs this too: an
|
|
31
31
|
alert is a container carrying a message, not a control. */
|
|
32
32
|
@utility alert {
|
|
33
|
-
@apply surface flex p-4 leading-5;
|
|
33
|
+
@apply surface flex items-start gap-3 p-4 leading-5;
|
|
34
34
|
|
|
35
35
|
--_d-fill: 12%;
|
|
36
36
|
--_d-fg-on-fill: 0%;
|
|
37
37
|
--_d-border: 100%;
|
|
38
38
|
--_d-elevation: 0;
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
&.warning::before {
|
|
63
|
-
mask-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z'/><path d='M12 9v4'/><path d='M12 17h.01'/></svg>");
|
|
64
|
-
-webkit-mask-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z'/><path d='M12 9v4'/><path d='M12 17h.01'/></svg>");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
&.destructive::before,
|
|
68
|
-
&.danger::before,
|
|
69
|
-
&.error::before {
|
|
70
|
-
mask-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='10'/><path d='m15 9-6 6'/><path d='m9 9 6 6'/></svg>");
|
|
71
|
-
-webkit-mask-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='10'/><path d='m15 9-6 6'/><path d='m9 9 6 6'/></svg>");
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
&.info::before {
|
|
75
|
-
mask-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='10'/><path d='M12 16v-4'/><path d='M12 8h.01'/></svg>");
|
|
76
|
-
-webkit-mask-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='10'/><path d='M12 16v-4'/><path d='M12 8h.01'/></svg>");
|
|
77
|
-
}
|
|
40
|
+
/* Same carve-out as `.panel`'s, and for the same reason: a neutral alert
|
|
41
|
+
mixes 12% of near-black ink onto the page, measured `217, 217, 217`
|
|
42
|
+
against `250, 250, 250` -- the identical slab `.panel` read before its
|
|
43
|
+
own fix, not a status-signaling problem, since this is scoped to no
|
|
44
|
+
named intent. A named intent keeps its ordinary wash; untinting
|
|
45
|
+
`.alert.soft.destructive` too would silence the status it exists to
|
|
46
|
+
carry rather than quiet it, which is why an earlier, unconditional
|
|
47
|
+
version of this fix was rejected for `.alert` specifically. This
|
|
48
|
+
version never reaches that case: `:not(...)` names every intent class,
|
|
49
|
+
plus `.solid`/`.ghost` so an explicit presentation on the element is
|
|
50
|
+
never clamped to a cap it did not ask for.
|
|
51
|
+
|
|
52
|
+
Grounds on `--color-foreground` through the public `--ui-surface-ground`
|
|
53
|
+
escape hatch, exactly like `.panel` and a resting `.card.soft` -- an
|
|
54
|
+
alert has no hover state to reveal a tint into either, so this is
|
|
55
|
+
permanent rather than revealed. Unlike a neutral panel or badge, an
|
|
56
|
+
alert cannot lose its visual identity from this: `--_d-border: 100%`
|
|
57
|
+
above holds regardless of fill, so a neutral alert still reads as a
|
|
58
|
+
framed box, just a quiet one instead of a grey slab. */
|
|
59
|
+
&:not(.primary, .secondary, .success, .warning, .destructive, .danger, .error, .info, .solid, .ghost) {
|
|
60
|
+
--_fill-cap: 0%;
|
|
61
|
+
--ui-surface-ground: var(--color-foreground);
|
|
78
62
|
}
|
|
79
63
|
}
|
|
80
64
|
|
|
65
|
+
/* No icon is shipped: the five hardcoded per-intent masks this used to paint on
|
|
66
|
+
a `::before` are gone, the same way the per-input-type icon was removed from
|
|
67
|
+
`.input-group` -- see `docs/internal/model.md#input-group-owns-the-box-the-control-inside-gives-it-up`.
|
|
68
|
+
`.alert` is
|
|
69
|
+
already `flex`, so an icon dropped in as a real child is spaced by `gap-3`
|
|
70
|
+
with nothing to trigger: `.alert-icon` only fixes its size and alignment, not
|
|
71
|
+
whether one is present. Any icon element works, the same contract
|
|
72
|
+
`.input-group` documents -- an inline `<svg>`, an `<img>`, or a class from an
|
|
73
|
+
icon set such as `@codenhub/icons`. */
|
|
74
|
+
@utility alert-icon {
|
|
75
|
+
@apply inline-flex flex-none items-center justify-center;
|
|
76
|
+
|
|
77
|
+
/* `!important`: an icon-set glyph (`@codenhub/icons` among them) carries its
|
|
78
|
+
own `width`/`height` on a selector this utility cannot outrank -- an
|
|
79
|
+
attribute selector reads more specific than one class regardless of
|
|
80
|
+
stylesheet order. Sizing what you drop in is this utility's one job, so it
|
|
81
|
+
has to win over the glyph's own opinion. */
|
|
82
|
+
width: 1.25rem !important;
|
|
83
|
+
height: 1.25rem !important;
|
|
84
|
+
}
|
|
85
|
+
|
|
81
86
|
/* A soft, edgeless chip. Small enough that the structural silhouette would be
|
|
82
87
|
most of its height, so it takes the tight one where an aesthetic ships a pair. */
|
|
83
88
|
@utility badge {
|
|
@@ -114,10 +119,22 @@
|
|
|
114
119
|
|
|
115
120
|
/* The same: intent only. The track is 10px tall, which is most of a border under
|
|
116
121
|
any aesthetic thick enough to have an opinion, so the bar draws no edge and the
|
|
117
|
-
ceilings that used to keep one survivable are gone with it.
|
|
122
|
+
ceilings that used to keep one survivable are gone with it.
|
|
123
|
+
|
|
124
|
+
`--progress-surface` used to mix `--intent-subtle` at 60% -- a tint of the
|
|
125
|
+
same tone the track already sits on (a bare page, or a `.soft` card), which
|
|
126
|
+
measured under 1.2:1 against both in either theme, well under the 3:1 WCAG
|
|
127
|
+
1.4.11 floor `registry.json` already cites for the checkbox, radio, and
|
|
128
|
+
switch. `--color-border` was tried next and measured no better (1.05:1): it
|
|
129
|
+
is calibrated for a line against the page, and a `.soft` card already sits
|
|
130
|
+
close to that same lightness, so a border-strength token disappears against
|
|
131
|
+
it too. Mixing `--color-text-secondary` instead is what actually separates
|
|
132
|
+
the track from both -- it is a step read against text at rest everywhere
|
|
133
|
+
else in the package, and stepping toward it here clears 3:1 (3.63-4.26:1
|
|
134
|
+
measured) on a bare page and a `.soft` card, in both themes. */
|
|
118
135
|
@utility progress {
|
|
119
136
|
--progress-color: var(--intent-color);
|
|
120
|
-
--progress-surface: color-mix(in srgb, var(--
|
|
137
|
+
--progress-surface: color-mix(in srgb, var(--color-text-secondary) 70%, transparent);
|
|
121
138
|
|
|
122
139
|
@apply relative h-2.5 w-full overflow-hidden bg-(--progress-surface);
|
|
123
140
|
/* Round by default; an aesthetic that declares no radius squares the track and
|