@codenhub/styles 0.5.0 → 0.6.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 +7 -7
- package/dist/aesthetics/chunky-tile.css +1 -1
- package/dist/aesthetics/cyber.css +1 -1
- package/dist/aesthetics/glass.css +1 -1
- package/dist/aesthetics/index.css +1 -1
- package/dist/aesthetics/neobrutalism.css +1 -1
- package/dist/aesthetics/pixel.css +1 -1
- package/dist/aesthetics/sketch.css +1 -1
- package/dist/components.css +1 -1
- package/dist/index.css +1 -1
- package/dist/native.css +1 -1
- package/dist/palette.css +1 -1
- package/dist/theme.css +1 -1
- package/dist/tw/aesthetics/chunky-tile.css +39 -39
- package/dist/tw/aesthetics/cyber.css +16 -11
- package/dist/tw/aesthetics/glass.css +235 -10
- package/dist/tw/aesthetics/index.css +2 -1
- package/dist/tw/aesthetics/neobrutalism.css +23 -11
- package/dist/tw/aesthetics/pixel.css +18 -8
- package/dist/tw/aesthetics/sketch.css +14 -7
- package/dist/tw/box.css +49 -27
- package/dist/tw/components/button.css +35 -14
- package/dist/tw/components/feedback.css +59 -56
- package/dist/tw/components/form.css +200 -126
- package/dist/tw/components/index.css +3 -7
- package/dist/tw/components/loader.css +4 -5
- package/dist/tw/components/surface.css +71 -68
- package/dist/tw/components/tooltip.css +75 -41
- package/dist/tw/elevation.css +14 -22
- package/dist/tw/index.css +15 -19
- package/dist/tw/intent.css +31 -24
- package/dist/tw/native.css +15 -5
- package/dist/tw/palette.css +8 -5
- package/dist/tw/presentation.css +16 -30
- package/dist/tw/reset.css +21 -28
- package/dist/tw/surface.css +11 -12
- package/dist/tw/theme.css +58 -86
- package/dist/tw/utilities/content.css +268 -199
- package/dist/tw/utilities/layout.css +15 -25
- package/docs/accessibility.md +50 -33
- package/docs/changelog/0.6.0.md +52 -0
- package/docs/changelog/index.md +1 -0
- package/docs/concepts.md +6 -6
- package/docs/index.md +9 -0
- package/docs/integrating/nextjs.md +1 -1
- package/docs/integrating/tailwind.md +14 -6
- package/docs/setup.md +6 -6
- package/docs/usage/aesthetics.md +35 -8
- package/docs/usage/buttons.md +1 -1
- package/docs/usage/composing.md +1 -1
- package/docs/usage/content-and-layout.md +12 -11
- package/docs/usage/customizing.md +8 -9
- package/docs/usage/feedback.md +5 -3
- package/docs/usage/forms.md +51 -35
- package/docs/usage/theming.md +39 -40
- package/docs/usage/tooltips.md +37 -17
- package/llms-full.txt +331 -183
- package/llms.txt +6 -0
- package/package.json +5 -5
|
@@ -34,43 +34,32 @@
|
|
|
34
34
|
--surface-padding: 0;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/* `.soft` rests untinted for no named intent
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
a second fill declaration, for the no-intent case: resting at 0% means
|
|
64
|
-
hover lands at exactly the hover step, 14%, in `--intent-hover` as
|
|
65
|
-
always. The ground steps to `--color-surface` at the same moment,
|
|
66
|
-
through `--_d-surface-ground`, the default `surface` reads beneath the
|
|
67
|
-
public `--ui-surface-ground` -- so a consumer's own override, or glass's
|
|
68
|
-
ground, still wins over both states. Scoped to `.interactive` and `.hoverable`,
|
|
69
|
-
the two classes that already opt a card into `box-hover` -- a plain
|
|
70
|
-
`.card.soft` with neither has no hover state to reveal into and stays at
|
|
71
|
-
the quiet, untinted rest. A named intent needs neither special case: it
|
|
72
|
-
already rests tinted, so the same generic `box-hover` formula deepens it
|
|
73
|
-
on hover (12% to 26%) with no ground shift required. */
|
|
37
|
+
/* `.soft` rests untinted for no named intent instead of mixing its 12%: 12%
|
|
38
|
+
of `--color-text` onto any of `--color-background`, `--color-foreground`,
|
|
39
|
+
or `--color-surface` lands within a few units of the others (a near-black
|
|
40
|
+
ink dominates the result), so swapping the ground token alone does not read
|
|
41
|
+
as quieter. A resting no-intent `.card.soft` is `--color-foreground` with
|
|
42
|
+
no ink mixed in -- a plain step up from the page that marks a different
|
|
43
|
+
container without drawing attention to it.
|
|
44
|
+
|
|
45
|
+
A named intent keeps its ordinary 12% wash: 12% of an actual hue does not
|
|
46
|
+
read as a slab the way 12% of near-black ink does (`.card.soft.destructive`
|
|
47
|
+
is a gentle `249, 225, 225`), and untinting it would silence it rather than
|
|
48
|
+
quiet it. `:not(...)` names every intent class for that reason; `.panel`'s
|
|
49
|
+
carve-out below is scoped the same way.
|
|
50
|
+
|
|
51
|
+
`--_fill-cap: 0%` (box.css's override slot, not `--ui-fill` directly -- see
|
|
52
|
+
the comment there) composed with `box-hover`'s formula (`min(100%, resting
|
|
53
|
+
+ 14%)`) reveals the intent on hover with no second fill declaration for
|
|
54
|
+
the no-intent case: resting at 0% means hover lands at exactly the hover
|
|
55
|
+
step, 14%, in `--intent-hover`. The ground steps to `--color-surface` at
|
|
56
|
+
the same moment, through `--_d-surface-ground`, the default `surface` reads
|
|
57
|
+
beneath the public `--ui-surface-ground`, so a consumer's override, or
|
|
58
|
+
glass's ground, wins over both states. Scoped to `.interactive` and
|
|
59
|
+
`.hoverable`, the two classes that opt a card into `box-hover`: a plain
|
|
60
|
+
`.card.soft` has no hover state to reveal into and stays at the quiet rest.
|
|
61
|
+
A named intent needs neither special case: it rests tinted, so the generic
|
|
62
|
+
`box-hover` formula deepens it on hover (12% to 26%). */
|
|
74
63
|
&.soft:not(.primary, .secondary, .success, .warning, .destructive, .info) {
|
|
75
64
|
--_fill-cap: 0%;
|
|
76
65
|
--_d-surface-ground: var(--color-foreground);
|
|
@@ -134,39 +123,53 @@
|
|
|
134
123
|
--surface-padding: 0;
|
|
135
124
|
}
|
|
136
125
|
|
|
137
|
-
/* Neutral panels are the common case --
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
Grounds on `--color-foreground` through `--_d-surface-ground`, the same
|
|
161
|
-
default `.card.soft` rests on -- so a bare `.panel` rests at exactly the
|
|
162
|
-
plate a `.card.soft` does (measured: `245, 245, 245` light, `23, 23, 23`
|
|
163
|
-
dark, identical both ways), and a consumer's `--ui-surface-ground` or
|
|
164
|
-
glass's translucent ground still wins over it: a neutral panel under
|
|
165
|
-
`.glass` is glass, like every other surface there. Permanent rather than
|
|
166
|
-
hover-revealed: neither a panel nor `data-table` has an interaction
|
|
167
|
-
state to reveal a tint into, so both rest quiet rather than reveal. */
|
|
126
|
+
/* Neutral panels are the common case -- layout wells, sidebars, toolbars with
|
|
127
|
+
no named intent -- and 12% of `--color-text` (near-black) mixed onto the
|
|
128
|
+
page is far more assertive than 12% of a hue (`217, 217, 217` against a
|
|
129
|
+
`250, 250, 250` page): the same "neutral ink reads louder" effect
|
|
130
|
+
`.card.soft` answers, and the reason `--intent-fill-max` stops neutral's
|
|
131
|
+
fill short everywhere else.
|
|
132
|
+
|
|
133
|
+
Scoped to no named intent rather than untinted unconditionally, as
|
|
134
|
+
`data-table` is: a panel is not interactive, so there is no hover to reveal
|
|
135
|
+
a colored intent into, and it is edgeless by default, so the fill is the
|
|
136
|
+
only channel a `.primary` bulk-actions bar or a `.destructive` danger-zone
|
|
137
|
+
panel has to say what it is (a resting `.panel.destructive` is a gentle
|
|
138
|
+
`249, 225, 225` wash). `.card.soft` carries the same scoping. `:not(...)`
|
|
139
|
+
names every intent class, plus `.solid`/`.ghost` so an explicit
|
|
140
|
+
presentation on the element is never clamped to a cap it did not ask for --
|
|
141
|
+
the guard `data-table`'s `:not(.solid, .ghost)` also uses.
|
|
142
|
+
|
|
143
|
+
Grounds on `--color-foreground` through `--_d-surface-ground`, the default
|
|
144
|
+
`.card.soft` rests on, so a bare `.panel` rests at exactly the plate a
|
|
145
|
+
`.card.soft` does, and a consumer's `--ui-surface-ground` or glass's
|
|
146
|
+
translucent ground wins over it: a neutral panel under `.glass` is glass.
|
|
147
|
+
Permanent rather than hover-revealed: neither a panel nor `data-table` has
|
|
148
|
+
an interaction state to reveal a tint into. */
|
|
168
149
|
&:not(.primary, .secondary, .success, .warning, .destructive, .info, .solid, .ghost) {
|
|
169
150
|
--_fill-cap: 0%;
|
|
170
151
|
--_d-surface-ground: var(--color-foreground);
|
|
171
152
|
}
|
|
172
153
|
}
|
|
154
|
+
|
|
155
|
+
/* The user agent already promises both of these for a native `<dialog>` --
|
|
156
|
+
`dialog:not([open]) { display: none }` and a centred `margin: auto` on the
|
|
157
|
+
open box -- but both are user-agent origin, which loses to any author rule
|
|
158
|
+
at all. `.card`'s own `display: block` beats the first outright, so a
|
|
159
|
+
`<dialog class="card">` rendered open with no `open` attribute and no
|
|
160
|
+
`.showModal()` call, and a margin reset beats the second, so an opened one
|
|
161
|
+
pinned to the top-left. The registry has no dialog component, and these
|
|
162
|
+
three are the containers a consumer reaches for.
|
|
163
|
+
|
|
164
|
+
Here rather than in the reset, so every entrypoint that ships `.card` ships
|
|
165
|
+
the fix, `/components` included. Unlayered, because a behaviour override has
|
|
166
|
+
to win over any layered rule whatever its specificity, and scoped to the
|
|
167
|
+
package's own containers, so a dialog the package does not style keeps
|
|
168
|
+
whatever margin its author gives it. */
|
|
169
|
+
dialog:is(.card, .panel, .surface):not([open]) {
|
|
170
|
+
display: none;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
dialog:is(.card, .panel, .surface)[open] {
|
|
174
|
+
margin: auto;
|
|
175
|
+
}
|
|
@@ -4,21 +4,24 @@
|
|
|
4
4
|
@reference "../surface.css";
|
|
5
5
|
|
|
6
6
|
/* The host is a positioning wrapper and nothing else -- it paints no fill, no
|
|
7
|
-
edge, no material of its own. The bubble is a real element beside the
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
bubble. See docs/usage/tooltips.md. */
|
|
7
|
+
edge, no material of its own. The bubble is a real element beside the trigger
|
|
8
|
+
rather than a pseudo-element, so intent, presentation, aesthetic, and
|
|
9
|
+
elevation classes go on `.tooltip-bubble`, the thing actually shown, not on
|
|
10
|
+
the wrapper -- the same rule every other component follows (theming.md:
|
|
11
|
+
"Intent does not cascade").
|
|
12
|
+
|
|
13
|
+
A real element is also what makes the tooltip accessible: `content:
|
|
14
|
+
attr(data-tooltip)` text is not reliably exposed to assistive tech and cannot
|
|
15
|
+
be the target of an `aria-describedby`, which needs a real element with a
|
|
16
|
+
real `id`. A real bubble can carry `role="tooltip"` and be referenced by the
|
|
17
|
+
trigger's own `aria-describedby`; wiring that association is the consumer's,
|
|
18
|
+
this package only styles the bubble. See docs/usage/tooltips.md. */
|
|
20
19
|
@utility tooltip {
|
|
21
20
|
--tooltip-gap: 0.5rem;
|
|
21
|
+
/* How long the bubble stays after the pointer leaves the trigger: long
|
|
22
|
+
enough to cross `--tooltip-gap` onto the bubble, which keeps it open
|
|
23
|
+
(WCAG 1.4.13). */
|
|
24
|
+
--tooltip-linger: 300ms;
|
|
22
25
|
--tooltip-top: calc(var(--tooltip-gap) * -1);
|
|
23
26
|
--tooltip-right: auto;
|
|
24
27
|
--tooltip-bottom: auto;
|
|
@@ -52,24 +55,31 @@
|
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
/* The bubble. It is a surface, so an aesthetic that blurs surfaces blurs it
|
|
55
|
-
too, and it rests on a ground of its own, edged, and flat -- depth is
|
|
56
|
-
|
|
57
|
-
that it is one. No proxy inheritance is needed to carry elevation across a
|
|
58
|
-
host-to-pseudo-element gap: there is no gap, `.tooltip-bubble.raised` reads
|
|
59
|
-
`--ui-elevation` the ordinary way every other component does. See
|
|
58
|
+
too, and it rests on a ground of its own, edged, and flat -- depth is opt-in
|
|
59
|
+
through its own `.raised`/`.floating`, like any other component. See
|
|
60
60
|
model.md#elevation. */
|
|
61
61
|
@utility tooltip-bubble {
|
|
62
|
-
@apply surface
|
|
62
|
+
@apply surface absolute z-(--z-popover) w-max max-w-72 px-2.5 py-2 text-sm font-normal whitespace-normal opacity-0;
|
|
63
|
+
|
|
64
|
+
/* Hidden rather than merely transparent, so a closed bubble is nothing
|
|
65
|
+
under the pointer, and it lingers before it goes: the host stays hovered
|
|
66
|
+
once the pointer is over the bubble, so crossing the gap in
|
|
67
|
+
`--tooltip-linger` keeps it open. Visibility switches at the end of the
|
|
68
|
+
fade, so the bubble is there to land on for the whole of it. */
|
|
69
|
+
visibility: hidden;
|
|
70
|
+
transition-property: opacity, transform, visibility;
|
|
71
|
+
transition-duration: var(--motion-duration-normal);
|
|
72
|
+
transition-timing-function: var(--motion-ease);
|
|
73
|
+
transition-delay: var(--tooltip-linger, 0s);
|
|
63
74
|
|
|
64
75
|
/* Filled, over a ground of its own. An intent floods the fill
|
|
65
76
|
(`.tooltip-bubble.primary` is the primary colour); with no intent
|
|
66
77
|
`intent.css` resolves the bubble onto `--color-tooltip` and lifts the
|
|
67
|
-
neutral cap
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
read first so a glass tooltip stays glass. */
|
|
78
|
+
neutral cap. The ground stays the surface tone, not the plate: pointed at
|
|
79
|
+
both, the fill axis goes inert (`axes.spec.ts` catches it). An opaque
|
|
80
|
+
ground is also what keeps a cascaded `.ghost` from leaving the bubble
|
|
81
|
+
boundaryless. `--ui-surface-ground` is read first so a glass tooltip stays
|
|
82
|
+
glass. */
|
|
73
83
|
--_d-fill: 100%;
|
|
74
84
|
--_d-fg-on-fill: 100%;
|
|
75
85
|
--_d-border: 100%;
|
|
@@ -105,40 +115,58 @@
|
|
|
105
115
|
&.edgeless {
|
|
106
116
|
--_edge-floor: 0%;
|
|
107
117
|
}
|
|
118
|
+
|
|
119
|
+
/* The bubble appears without growing under reduced motion, for the entries
|
|
120
|
+
without the reset; see `box.css`. */
|
|
121
|
+
@media (prefers-reduced-motion: reduce) {
|
|
122
|
+
transition-duration: 0.01ms;
|
|
123
|
+
}
|
|
108
124
|
}
|
|
109
125
|
|
|
110
126
|
/* Shown on hover or focus anywhere in the host, or pinned open with
|
|
111
127
|
`data-state="open"`. `:focus-within` rather than the trigger's own
|
|
112
|
-
`:focus-visible`, the same substitution `.input-group` makes
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
specificity -- see the seams note in `form.css`. */
|
|
128
|
+
`:focus-visible`, the same substitution `.input-group` makes: the bubble is a
|
|
129
|
+
sibling of the trigger, so only the shared ancestor can see both at once. The
|
|
130
|
+
cost is the one `.input-group` accepts -- the bubble also shows on a mouse
|
|
131
|
+
click.
|
|
132
|
+
|
|
133
|
+
In `utilities`, where the bubble's own `@utility` is, winning on specificity
|
|
134
|
+
-- see the seams note in `form.css`. */
|
|
120
135
|
@layer utilities {
|
|
121
136
|
.tooltip:hover .tooltip-bubble,
|
|
122
137
|
.tooltip:focus-within .tooltip-bubble,
|
|
123
138
|
.tooltip[data-state="open"] .tooltip-bubble {
|
|
124
139
|
opacity: 1;
|
|
125
140
|
transform: var(--tooltip-transform) scale(1);
|
|
141
|
+
visibility: visible;
|
|
142
|
+
transition-delay: 0s;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Dismissed: a hover message has to close without the pointer moving
|
|
146
|
+
(WCAG 1.4.13), and CSS cannot hear Escape, so the consumer's handler
|
|
147
|
+
writes `data-state="closed"` and clears it when the pointer and focus
|
|
148
|
+
leave. Later and as specific as the rule above, so it wins over hover and
|
|
149
|
+
focus alike. */
|
|
150
|
+
.tooltip[data-state="closed"] .tooltip-bubble {
|
|
151
|
+
opacity: 0;
|
|
152
|
+
transform: var(--tooltip-transform) scale(0);
|
|
153
|
+
visibility: hidden;
|
|
154
|
+
transition-delay: 0s;
|
|
126
155
|
}
|
|
127
156
|
}
|
|
128
157
|
|
|
129
158
|
/* The trigger badge, for a message with no other element to hang it on. Quiet
|
|
130
159
|
-- ghost, edged, on `--intent-subtle`, a key cap's resting shape -- because
|
|
131
160
|
it marks that there is a message, it is not one. The line earns its keep:
|
|
132
|
-
`--intent-subtle` is a near-page tint that reads the same across families,
|
|
133
|
-
|
|
161
|
+
`--intent-subtle` is a near-page tint that reads the same across families, so
|
|
162
|
+
a plate alone leaves three of eight intents indistinguishable;
|
|
134
163
|
`--intent-border` is the slot that differs.
|
|
135
164
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
circle instead of taking `.pixel`'s stepped silhouette. */
|
|
165
|
+
Its corner is the aesthetic's, through the pill corner and then the control
|
|
166
|
+
corner: round by default, a diamond under `.cyber`, square under
|
|
167
|
+
`.neobrutalism` and `.pixel`, whose corner is zero. Only the silhouette
|
|
168
|
+
clip is refused -- `clip-path: none`, as `.radio` has it -- because
|
|
169
|
+
`.pixel`'s stepped polygon would bite a 20px chip rather than shape it. */
|
|
142
170
|
@utility tooltip-icon {
|
|
143
171
|
@apply box inline-flex size-5 cursor-default items-center justify-center text-sm font-bold;
|
|
144
172
|
|
|
@@ -148,6 +176,12 @@
|
|
|
148
176
|
--_d-elevation: 0;
|
|
149
177
|
--_d-ground: var(--intent-subtle);
|
|
150
178
|
|
|
179
|
+
/* The trigger is best a real `<button>`, which is a `.btn` under `/native`
|
|
180
|
+
and carries the user agent's padding under `/components`; either would
|
|
181
|
+
stretch the chip past its own size. */
|
|
182
|
+
min-width: 0;
|
|
183
|
+
min-height: 0;
|
|
184
|
+
padding: 0;
|
|
151
185
|
border-radius: var(--ui-radius-pill, var(--ui-radius, var(--ui-corner, calc(infinity * 1px))));
|
|
152
186
|
clip-path: none;
|
|
153
187
|
}
|
package/dist/tw/elevation.css
CHANGED
|
@@ -3,31 +3,23 @@
|
|
|
3
3
|
docs/internal/model.md#cascade-layers. */
|
|
4
4
|
|
|
5
5
|
/* Elevation is a modifier, not an axis: depth is a per-element decision (raised
|
|
6
|
-
tiles beside a flat panel in one aesthetic), so one unitless number
|
|
7
|
-
the aesthetic's shadow geometry where the component composes it.
|
|
8
|
-
removes a part-based slab or blur without a conditional (complete
|
|
9
|
-
shadows like glass remain). See model.md#elevation.
|
|
6
|
+
tiles beside a flat panel in one aesthetic), so one unitless number
|
|
7
|
+
multiplies the aesthetic's shadow geometry where the component composes it.
|
|
8
|
+
`.flat` removes a part-based slab or blur without a conditional (complete
|
|
9
|
+
surface shadows like glass remain). See model.md#elevation.
|
|
10
10
|
|
|
11
11
|
`--ui-elevation`, and the plain-page fallback geometry (`--elevation-y`,
|
|
12
12
|
`--elevation-blur`) below, are registered non-inheriting: a class sets them
|
|
13
|
-
on the element it is written on and nothing else picks them up.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
itself more raised, it is just resting normally inside an already-raised
|
|
21
|
-
surface. `--ui-shadow-*` -- what an aesthetic supplies -- is unaffected and
|
|
22
|
-
still cascades normally; that is a different mechanism (the material tokens
|
|
23
|
-
in `src/aesthetics/*.css`), and an aesthetic still has to reach every nested
|
|
24
|
-
component uniformly the way it always did. Registering these three is what
|
|
25
|
-
scopes the change to the elevation modifier alone.
|
|
13
|
+
on the element it is written on and nothing else picks them up. Inherited, a
|
|
14
|
+
`.floating` dialog's ambient x2 would reach its own unclassed buttons and
|
|
15
|
+
double their shadow with nothing in the markup asking for it. Depth is not
|
|
16
|
+
the same kind of thing as color or material: a button inside a raised card is
|
|
17
|
+
not itself more raised. `--ui-shadow-*` -- what an aesthetic supplies -- is a
|
|
18
|
+
different mechanism and still cascades normally, so an aesthetic reaches
|
|
19
|
+
every nested component uniformly.
|
|
26
20
|
|
|
27
21
|
`.flat`/`.raised`/`.floating` name what they do rather than a scale step:
|
|
28
|
-
bare `sm`/`md`/`lg` would collide with the size modifier
|
|
29
|
-
`.elevation-*` spellings, which borrowed the raw `--elevation-*` shadows'
|
|
30
|
-
names for a different mechanism, were removed in `0.5.0`. */
|
|
22
|
+
bare `sm`/`md`/`lg` would collide with the size modifier. */
|
|
31
23
|
|
|
32
24
|
/* `syntax: "*"` with no `initial-value` is deliberate: a numeric syntax would
|
|
33
25
|
require one (CSS Properties and Values API), and any initial-value at all
|
|
@@ -36,8 +28,8 @@
|
|
|
36
28
|
1))` in `box.css` needs to fall through to a component's own default. With
|
|
37
29
|
no initial-value and `inherits: false`, an element that does not set the
|
|
38
30
|
property directly computes it as guaranteed-invalid, the same as if it had
|
|
39
|
-
never been registered at all -- `var()` takes its fallback
|
|
40
|
-
|
|
31
|
+
never been registered at all -- `var()` takes its fallback where an
|
|
32
|
+
inherited value would otherwise be handed in. */
|
|
41
33
|
@property --ui-elevation {
|
|
42
34
|
syntax: "*";
|
|
43
35
|
inherits: false;
|
package/dist/tw/index.css
CHANGED
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
|
|
3
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
|
|
4
|
+
sees only this package's own files. The `src/` scan is load-bearing: it is
|
|
5
5
|
how the composition utilities (`box`, `surface`, `text-control`,
|
|
6
|
-
`input-group`, `loader-mask`)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
verified against Tailwind 4.3.2, none of them emit.
|
|
12
|
-
`tests/integration/exports.test.ts` guards it, so a scanner change that
|
|
13
|
-
starts emitting one is caught rather than shipping silently.
|
|
6
|
+
`input-group`, `loader-mask`) are emitted as standalone rules, so
|
|
7
|
+
`source(none)` is not an option. Tailwind utility names that collide with a
|
|
8
|
+
CSS property or a word these comments use (`backdrop-filter`, `filter`,
|
|
9
|
+
`transition`, `transform`, `container`) do not emit, verified against
|
|
10
|
+
Tailwind 4.3.2; `tests/integration/exports.test.ts` guards it.
|
|
14
11
|
|
|
15
|
-
The `@source not` lines
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
resolve to nothing and the `/tw` consumer scans their own tree anyway. */
|
|
12
|
+
The `@source not` lines keep everything that is not `src/` out of the scan:
|
|
13
|
+
the sibling app and fixture directories, the `docs/` tree, the build scripts,
|
|
14
|
+
and the root prose, data, config, and license files. Without them a class
|
|
15
|
+
name or `--color-*` reference that appears only in a documentation example
|
|
16
|
+
ships as real CSS -- the license's "contents" once shipped as
|
|
17
|
+
`.contents{display:contents}`.
|
|
18
|
+
`exports.test.ts` guards it. `scripts/build-tw.mjs` strips these lines from
|
|
19
|
+
the `dist/tw/` copy, where they resolve to nothing and the `/tw` consumer
|
|
20
|
+
scans their own tree. */
|
|
25
21
|
|
|
26
22
|
@import "./theme.css";
|
|
27
23
|
@import "./typography.css";
|
package/dist/tw/intent.css
CHANGED
|
@@ -33,12 +33,11 @@
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/* Neutral stops its fill short: its colour is the page's ink, so a full fill is
|
|
36
|
-
a slab and `.neutral.solid`
|
|
36
|
+
a slab and `.neutral.solid` would be pixel-for-pixel `.primary.solid`. 20% is
|
|
37
37
|
chosen against `.soft`'s 12% -- a step past the tint, not a repeat.
|
|
38
38
|
`--intent-contrast` stays the page colour; a `.neutral.solid` badge's ink is
|
|
39
39
|
`box`'s question, answered by the same `--ui-fg-on-fill` cap. Pointing this
|
|
40
|
-
slot at `--color-text`
|
|
41
|
-
black-on-black. */
|
|
40
|
+
slot at `--color-text` renders a checked neutral checkbox black-on-black. */
|
|
42
41
|
.neutral {
|
|
43
42
|
--intent-color: var(--color-text);
|
|
44
43
|
--intent-contrast: var(--color-text-contrast);
|
|
@@ -171,34 +170,42 @@
|
|
|
171
170
|
`--ui-ink`: the theme keeps a control border apart from a surface border, and
|
|
172
171
|
an aesthetic whose neutral line suits a pane but not a control's boundary --
|
|
173
172
|
`.glass`'s white hairline, `.chunky-tile`'s tile grey -- names a control line
|
|
174
|
-
of its own. See docs/internal/model.md#presentation.
|
|
175
|
-
|
|
173
|
+
of its own. See docs/internal/model.md#presentation.
|
|
174
|
+
|
|
175
|
+
The second selector is the tooltip bubble's below, for the same reason:
|
|
176
|
+
`.neutral` carries 0-1-0 and beat `:where()`, so a `.neutral` field under
|
|
177
|
+
`.glass` drew the pane's hairline while an unclassed one drew the control
|
|
178
|
+
line, and writing the intent out changed the box. */
|
|
179
|
+
:where(.text-control, .ipt, .textarea, .select, .input-group, .checkbox, .radio, .switch),
|
|
180
|
+
:is(.text-control, .ipt, .textarea, .select, .input-group, .checkbox, .radio, .switch).neutral {
|
|
176
181
|
--intent-border: var(--ui-control-ink, var(--ui-ink, var(--color-control-border)));
|
|
177
182
|
}
|
|
178
183
|
|
|
184
|
+
/* A table's lines -- its frame, head rule, and row rules -- are drawn on a
|
|
185
|
+
plate, not at a pane's edge against the backdrop, so an aesthetic whose
|
|
186
|
+
neutral line is a highlight names a rule line of its own: glass's white
|
|
187
|
+
hairline vanishes on the table's light plate. Neutral only, and `.neutral`
|
|
188
|
+
included, the way the control line above is. */
|
|
189
|
+
:where(.data-table),
|
|
190
|
+
.data-table.neutral {
|
|
191
|
+
--intent-border: var(--ui-rule-ink, var(--ui-ink, var(--color-border)));
|
|
192
|
+
}
|
|
193
|
+
|
|
179
194
|
/* The bubble a tooltip draws is the one plate in the package that is chosen
|
|
180
195
|
rather than composed, so it resolves the no-intent case onto its own pair
|
|
181
|
-
instead of
|
|
182
|
-
ink over `--color-surface`, which lifts off a dark page and sinks into a light
|
|
183
|
-
one -- see `--color-tooltip` in `theme.css` for the measurements.
|
|
196
|
+
instead of the shared neutral -- see `--color-tooltip` in `theme.css`.
|
|
184
197
|
|
|
185
198
|
The cap goes with it. Neutral stops its fill at 20% because its colour is the
|
|
186
|
-
page's ink
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
Two selectors, because `none` and `neutral` have to agree here. `:where()`
|
|
197
|
-
loses to any intent class, so `.tooltip-bubble.primary` still floods with
|
|
198
|
-
primary; `.tooltip-bubble.neutral` carries 0-2-0 and beats `.neutral`, so
|
|
199
|
-
writing the intent out gives the same bubble as writing nothing. Without the
|
|
200
|
-
second selector the two would differ on a component where they mean the
|
|
201
|
-
same thing. */
|
|
199
|
+
page's ink; `--color-tooltip` is a plate rather than an ink, so a full fill
|
|
200
|
+
of it is the plate and there is nothing to stop short of. `--intent-subtle`
|
|
201
|
+
stays the surface token, the tone every other unclassed component sits on.
|
|
202
|
+
`.tooltip-icon` is a separate element that never fills past `0%`, so the
|
|
203
|
+
generic neutral reset above is enough for it.
|
|
204
|
+
|
|
205
|
+
Two selectors, so `none` and `neutral` agree. `:where()` loses to any intent
|
|
206
|
+
class, so `.tooltip-bubble.primary` still floods with primary;
|
|
207
|
+
`.tooltip-bubble.neutral` carries 0-2-0 and beats `.neutral`, so writing the
|
|
208
|
+
intent out gives the same bubble as writing nothing. */
|
|
202
209
|
:where(.tooltip-bubble),
|
|
203
210
|
.tooltip-bubble.neutral {
|
|
204
211
|
--intent-color: var(--color-tooltip);
|
package/dist/tw/native.css
CHANGED
|
@@ -26,11 +26,13 @@
|
|
|
26
26
|
--intent-fill-max: 20%;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
/* Native text controls
|
|
30
|
-
counterparts, without changing buttons
|
|
29
|
+
/* Native text controls and toggles take the same stronger neutral boundary as
|
|
30
|
+
their classed counterparts, without changing buttons or other inputs --
|
|
31
31
|
control ink included, as `intent.css`'s class reset reads it. */
|
|
32
32
|
:where(
|
|
33
33
|
input:not([type]),
|
|
34
|
+
input[type="checkbox"],
|
|
35
|
+
input[type="radio"],
|
|
34
36
|
input[type="text"],
|
|
35
37
|
input[type="number"],
|
|
36
38
|
input[type="email"],
|
|
@@ -49,6 +51,12 @@
|
|
|
49
51
|
--intent-border: var(--ui-control-ink, var(--ui-ink, var(--color-control-border)));
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
/* A bare table's lines take the rule line, as `.data-table`'s do in
|
|
55
|
+
`intent.css`. */
|
|
56
|
+
:where(table) {
|
|
57
|
+
--intent-border: var(--ui-rule-ink, var(--ui-ink, var(--color-border)));
|
|
58
|
+
}
|
|
59
|
+
|
|
52
60
|
:where(hr) {
|
|
53
61
|
--intent-color: currentColor;
|
|
54
62
|
--intent-border: currentColor;
|
|
@@ -80,9 +88,10 @@
|
|
|
80
88
|
@apply text-label;
|
|
81
89
|
}
|
|
82
90
|
|
|
91
|
+
/* Links are left out: a link takes the text it sits in, so one inside a
|
|
92
|
+
heading stays heading-sized. */
|
|
83
93
|
p,
|
|
84
|
-
li
|
|
85
|
-
a {
|
|
94
|
+
li {
|
|
86
95
|
@apply text-body;
|
|
87
96
|
}
|
|
88
97
|
|
|
@@ -173,7 +182,8 @@
|
|
|
173
182
|
transition: none;
|
|
174
183
|
}
|
|
175
184
|
|
|
176
|
-
input[type="checkbox"]:checked
|
|
185
|
+
input[type="checkbox"]:checked,
|
|
186
|
+
input[type="checkbox"]:indeterminate {
|
|
177
187
|
background-color: Highlight;
|
|
178
188
|
border-color: Highlight;
|
|
179
189
|
|
package/dist/tw/palette.css
CHANGED
|
@@ -7,11 +7,14 @@
|
|
|
7
7
|
@layer theme, base, components, utilities;
|
|
8
8
|
|
|
9
9
|
@layer theme {
|
|
10
|
-
:root
|
|
11
|
-
|
|
10
|
+
:root,
|
|
11
|
+
.light,
|
|
12
|
+
.theme-light,
|
|
13
|
+
[data-theme="light"] {
|
|
14
|
+
--palette-neutral-solid-bg: oklab(0.817 0 0);
|
|
12
15
|
--palette-neutral-solid-fg: oklab(0.205 0 0);
|
|
13
16
|
--palette-neutral-solid-edge: oklab(0 0 0 / 0);
|
|
14
|
-
--palette-neutral-solid-bg-hover: oklab(0.
|
|
17
|
+
--palette-neutral-solid-bg-hover: oklab(0.77624 0 0);
|
|
15
18
|
--palette-neutral-solid-edge-hover: oklab(0 0 0 / 0);
|
|
16
19
|
--palette-neutral-soft-fg: oklab(0.205 0 0);
|
|
17
20
|
--palette-neutral-soft-bg: oklab(0.145 0 0 / 0.12);
|
|
@@ -233,10 +236,10 @@
|
|
|
233
236
|
.dark,
|
|
234
237
|
.theme-dark,
|
|
235
238
|
[data-theme="dark"] {
|
|
236
|
-
--palette-neutral-solid-bg: oklab(0.
|
|
239
|
+
--palette-neutral-solid-bg: oklab(0.313 0 0);
|
|
237
240
|
--palette-neutral-solid-fg: oklab(0.97 0 0);
|
|
238
241
|
--palette-neutral-solid-edge: oklab(0 0 0 / 0);
|
|
239
|
-
--palette-neutral-solid-bg-hover: oklab(0.
|
|
242
|
+
--palette-neutral-solid-bg-hover: oklab(0.40918 0 0);
|
|
240
243
|
--palette-neutral-solid-edge-hover: oklab(0 0 0 / 0);
|
|
241
244
|
--palette-neutral-soft-fg: oklab(0.97 0 0);
|
|
242
245
|
--palette-neutral-soft-bg: oklab(0.985 0 0 / 0.12);
|