@claralight-design/react 0.0.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/dist/index.d.ts +12 -0
  2. package/dist/index.js +12 -0
  3. package/dist/lib/anchored.d.ts +152 -0
  4. package/dist/lib/anchored.js +821 -0
  5. package/dist/lib/anchored.js.map +1 -0
  6. package/dist/lib/morph.js +484 -0
  7. package/dist/lib/morph.js.map +1 -0
  8. package/dist/lib/squircle.d.ts +95 -0
  9. package/dist/lib/squircle.js +220 -0
  10. package/dist/lib/squircle.js.map +1 -0
  11. package/dist/lib/utils.d.ts +30 -0
  12. package/dist/lib/utils.js +76 -0
  13. package/dist/lib/utils.js.map +1 -0
  14. package/dist/ui/button.d.ts +40 -0
  15. package/dist/ui/button.js +104 -0
  16. package/dist/ui/button.js.map +1 -0
  17. package/dist/ui/card.d.ts +54 -0
  18. package/dist/ui/card.js +82 -0
  19. package/dist/ui/card.js.map +1 -0
  20. package/dist/ui/dialog.d.ts +82 -0
  21. package/dist/ui/dialog.js +129 -0
  22. package/dist/ui/dialog.js.map +1 -0
  23. package/dist/ui/input.d.ts +34 -0
  24. package/dist/ui/input.js +60 -0
  25. package/dist/ui/input.js.map +1 -0
  26. package/dist/ui/popover.d.ts +67 -0
  27. package/dist/ui/popover.js +93 -0
  28. package/dist/ui/popover.js.map +1 -0
  29. package/dist/ui/scroll-area.d.ts +110 -0
  30. package/dist/ui/scroll-area.js +125 -0
  31. package/dist/ui/scroll-area.js.map +1 -0
  32. package/dist/ui/select.d.ts +113 -0
  33. package/dist/ui/select.js +213 -0
  34. package/dist/ui/select.js.map +1 -0
  35. package/dist/ui/tooltip.d.ts +108 -0
  36. package/dist/ui/tooltip.js +142 -0
  37. package/dist/ui/tooltip.js.map +1 -0
  38. package/package.json +72 -0
  39. package/styles/anchored.css +115 -0
  40. package/styles/base.css +309 -0
  41. package/styles/fonts/README.md +63 -0
  42. package/styles/index.css +28 -0
  43. package/styles/scroll-area.css +299 -0
  44. package/styles/theme.css +540 -0
  45. package/styles/tooltip.css +156 -0
@@ -0,0 +1,115 @@
1
+ /**
2
+ * ClaraLight Design — the anchored overlay surface.
3
+ *
4
+ * Popover and Tooltip only. Split out of `base.css` because it is the one part
5
+ * of the language a project can decline: a build with neither component has no
6
+ * use for the tail padding, the probe or the grow-from-the-tail entrance.
7
+ *
8
+ * Tokens live in `theme.css`; the tail itself is constructed in
9
+ * `src/lib/anchored.tsx`, which is the other half of this file.
10
+ *
11
+ * @import "tailwindcss";
12
+ * @import "@claralight-design/react/styles.css";
13
+ */
14
+
15
+ @layer components {
16
+ /**
17
+ * Popovers and tooltips.
18
+ *
19
+ * The pointing arrow is *not* a second element. It is spliced into the
20
+ * surface's own smooth-corner path, so fill, backdrop blur and the 1px
21
+ * outline cross the join as one shape — see `src/lib/anchored.tsx`. A
22
+ * separate arrow element cannot do that here: ClaraLight's frost fill is
23
+ * translucent, so an overlap would composite into a visible dark seam and
24
+ * the body's outline would run straight through the arrow's mouth.
25
+ *
26
+ * The consequence for CSS is this padding. The tail lives *inside* the
27
+ * element's box, occupying `--cl-arrow-extent` on whichever side faces the
28
+ * anchor, so content has to be inset by that much again or it would sit in
29
+ * the tail. `[data-side]` is Base UI's, and names the side of the *anchor*
30
+ * the surface was placed on — so the tail is on the opposite edge.
31
+ */
32
+ .cl-anchored {
33
+ padding: var(--cl-anchored-padding-y) var(--cl-anchored-padding-x);
34
+ }
35
+
36
+ .cl-anchored-arrow[data-side="top"] {
37
+ padding-bottom: calc(var(--cl-anchored-padding-y) + var(--cl-arrow-extent));
38
+ }
39
+
40
+ .cl-anchored-arrow[data-side="bottom"] {
41
+ padding-top: calc(var(--cl-anchored-padding-y) + var(--cl-arrow-extent));
42
+ }
43
+
44
+ .cl-anchored-arrow[data-side="left"] {
45
+ padding-right: calc(var(--cl-anchored-padding-x) + var(--cl-arrow-extent));
46
+ }
47
+
48
+ .cl-anchored-arrow[data-side="right"] {
49
+ padding-left: calc(var(--cl-anchored-padding-x) + var(--cl-arrow-extent));
50
+ }
51
+
52
+ /**
53
+ * Base UI positions this as its arrow element; ClaraLight never paints it.
54
+ * It is a measurement probe: Floating UI writes the already-clamped arrow
55
+ * offset onto it as inline `left`/`top`, and `AnchoredSurface` reads its box
56
+ * to place the tail. Sized so that clamp accounts for the real tail width.
57
+ */
58
+ .cl-anchor-probe {
59
+ width: var(--cl-arrow-width);
60
+ height: var(--cl-arrow-extent);
61
+ pointer-events: none;
62
+ visibility: hidden;
63
+ }
64
+
65
+
66
+ /**
67
+ * The entrance and exit: the surface grows out of its own tail, then returns
68
+ * to that same point.
69
+ *
70
+ * A dialog nudges into place — `--cl-enter-scale` is 0.95 for it, because a
71
+ * modal has no fixed point to grow from. An anchored overlay does: the tail's
72
+ * tip sits against the anchor and does not move while the surface opens, so
73
+ * the surface starts at **zero** and unfolds from that point. That is what
74
+ * makes the overlay read as pointing at its anchor instead of appearing near
75
+ * it, and it is why this is a second entrance rather than a value change to
76
+ * `.cl-enter-root`.
77
+ *
78
+ * No opacity leg on the way in, unlike `.cl-enter-root`: at scale 0 there is
79
+ * nothing on screen to fade, and the surface ClaraLight draws arrives opaque
80
+ * and grows. Opacity is what the reduced-motion fallback uses instead.
81
+ *
82
+ * The origin is not here. It is a measured length that only the component
83
+ * knows — where on the edge the tail actually landed — so `AnchoredSurface`
84
+ * writes it inline; see `src/lib/anchored.tsx`.
85
+ */
86
+ .cl-anchored-root {
87
+ transition: scale var(--cl-duration-enter) var(--ease-cl-spring-overlay);
88
+ }
89
+
90
+ .cl-anchored-root:has(> [data-ending-style]) {
91
+ transition-duration: var(--cl-duration-exit);
92
+ }
93
+
94
+ .cl-anchored-root:has(> [data-starting-style]),
95
+ .cl-anchored-root:has(> [data-ending-style]) {
96
+ scale: 0;
97
+ }
98
+
99
+ /**
100
+ * Reduced motion drops the growth — the one part that is movement — and keeps
101
+ * the overlay legible with a short fade at the reduced duration. Same split as
102
+ * the Flutter overlay, which reserves opacity for exactly this case.
103
+ */
104
+ @media (prefers-reduced-motion: reduce) {
105
+ .cl-anchored-root {
106
+ transition: opacity var(--cl-duration-reduced) var(--ease-cl-out);
107
+ }
108
+
109
+ .cl-anchored-root:has(> [data-starting-style]),
110
+ .cl-anchored-root:has(> [data-ending-style]) {
111
+ scale: none;
112
+ opacity: 0;
113
+ }
114
+ }
115
+ }
@@ -0,0 +1,309 @@
1
+ /**
2
+ * ClaraLight Design — component primitives.
3
+ *
4
+ * Tokens live in `theme.css`. This file holds the handful of behaviours that
5
+ * are part of the design language itself and are too structural to express as
6
+ * a Tailwind class string: the signature press interaction, the frosted
7
+ * floating surface, and the focus ring.
8
+ *
9
+ * Kept as CSS rather than utilities so a component's class list stays
10
+ * readable and the physics stay in one place.
11
+ *
12
+ * Everything here is used by every build. What only one or two components need
13
+ * ships beside them instead — `anchored.css` for Popover and Tooltip,
14
+ * `tooltip.css` for the shared-tooltip morph, `scroll-area.css` for the scroll
15
+ * area's edges and scrollbars — so a copy-in project that skips those
16
+ * components does not carry their CSS. The npm entry point pulls in all four.
17
+ *
18
+ * @import "tailwindcss";
19
+ * @import "@claralight-design/react/styles.css";
20
+ */
21
+
22
+ /* Tailwind's built-in shadow compiler expands token values into literal
23
+ * --tw-shadow declarations. Keep these public utilities runtime-bound so a
24
+ * scoped --shadow-* override also reaches Squircle's SVG effect reader. */
25
+ @utility shadow-frost {
26
+ box-shadow: var(--shadow-frost);
27
+ }
28
+
29
+ @utility shadow-panel {
30
+ box-shadow: var(--shadow-panel);
31
+ }
32
+
33
+ @utility shadow-dialog {
34
+ box-shadow: var(--shadow-dialog);
35
+ }
36
+
37
+ @layer components {
38
+ /* -------------------------------------------------------------------------
39
+ * Press
40
+ * ---------------------------------------------------------------------- */
41
+
42
+ /**
43
+ * The signature ClaraLight press:
44
+ * the control scales *up* while held, then springs back with overshoot on
45
+ * release.
46
+ *
47
+ * CSS picks transition values from the state being entered, which is exactly
48
+ * the behaviour we want and needs no JS:
49
+ *
50
+ * press -> element becomes :active -> 170ms easeOutQuart
51
+ * release -> element stops being :active -> 550ms spring
52
+ *
53
+ * The two legs therefore live in different rules. Overriding the duration
54
+ * through custom properties keeps a single `transition` shorthand valid
55
+ * instead of fighting `transition-property` ordering.
56
+ *
57
+ * Set `--cl-press-scale` using the size tokens so shorter controls move
58
+ * further in relative terms and every size feels equally responsive.
59
+ */
60
+ .cl-press {
61
+ --cl-press-scale: var(--cl-press-scale-lg);
62
+ --cl-press-duration: var(--cl-duration-release);
63
+ --cl-press-ease: var(--ease-cl-spring-press);
64
+
65
+ transition:
66
+ color var(--cl-duration-press) var(--ease-cl-out),
67
+ background-color var(--cl-duration-press) var(--ease-cl-out),
68
+ border-color var(--cl-duration-press) var(--ease-cl-out),
69
+ box-shadow var(--cl-duration-press) var(--ease-cl-out),
70
+ opacity var(--cl-duration-press) var(--ease-cl-out),
71
+ outline-color var(--cl-duration-fast) var(--ease-cl-out),
72
+ transform var(--cl-press-duration) var(--cl-press-ease);
73
+ }
74
+
75
+ /* Disabled shaped children can still leave their wrapper :active when
76
+ * pointer-events are suppressed. Guard both wrapped and standalone controls. */
77
+ .cl-press:active:not(:where(:disabled, [disabled], [data-disabled], [aria-disabled="true"])):not(:has(> :where(:disabled, [disabled], [data-disabled], [aria-disabled="true"]))) {
78
+ --cl-press-duration: var(--cl-duration-press);
79
+ --cl-press-ease: var(--ease-cl-press-in);
80
+ transform: scale(var(--cl-press-scale));
81
+ }
82
+
83
+ /**
84
+ * ClaraLight drops the scale entirely under reduced motion rather than
85
+ * shrinking it, and keeps the opacity/surface feedback.
86
+ */
87
+ @media (prefers-reduced-motion: reduce) {
88
+ .cl-press:active {
89
+ transform: none;
90
+ }
91
+ }
92
+
93
+ /* -------------------------------------------------------------------------
94
+ * Frosted floating surface
95
+ * ---------------------------------------------------------------------- */
96
+
97
+ /**
98
+ * Menus, popovers, dialogs and sheets. The blur, outline and shadow all
99
+ * consume theme tokens; Squircle handles their smooth-corner geometry.
100
+ */
101
+ .cl-frost {
102
+ background-color: var(--cl-frost);
103
+ -webkit-backdrop-filter: blur(var(--blur-frost));
104
+ backdrop-filter: blur(var(--blur-frost));
105
+ border: 1px solid var(--cl-outline-strong);
106
+ box-shadow: var(--shadow-frost);
107
+ }
108
+
109
+ /** Same layer, without the shadow — for surfaces stacked inside a panel. */
110
+ .cl-panel {
111
+ background-color: var(--cl-panel);
112
+ border: 1px solid var(--cl-outline);
113
+ }
114
+
115
+ /* -------------------------------------------------------------------------
116
+ * Smooth-corner root
117
+ * ---------------------------------------------------------------------- */
118
+
119
+ /**
120
+ * The wrapper `Squircle` renders around a clipped element.
121
+ *
122
+ * It exists because Lisse re-renders borders and shadows as SVG overlays,
123
+ * which have to be appended to a positioned element that tightly wraps the
124
+ * shape. Two consequences worth knowing:
125
+ *
126
+ * 1. **Transforms belong here, not on the shape.** The overlays are siblings
127
+ * of the clipped element, so anything that moves the shape without moving
128
+ * the wrapper tears the 1px border away from the edge. The press scale and
129
+ * the dialog entrance therefore live on this element, which also reads
130
+ * better: fill, border and shadow scale as one object.
131
+ *
132
+ * 2. **The focus ring lives here too**, because `clip-path` crops `outline` to
133
+ * nothing on the shape itself — measured, a 2px ring at 2px offset goes
134
+ * from 436 lit pixels to 0. `:has()` lets the browser's own
135
+ * `:focus-visible` heuristic drive it, so there is no JS focus state and
136
+ * clicks still do not flash a ring.
137
+ *
138
+ * The radius is carried in a custom property so the ring clears the shape at
139
+ * every radius token; `Squircle` sets it.
140
+ */
141
+ .cl-squircle-root {
142
+ border-radius: calc(var(--cl-squircle-radius, 0px) + var(--cl-focus-offset));
143
+ outline: var(--cl-focus-width) solid transparent;
144
+ outline-offset: var(--cl-focus-offset);
145
+ /*
146
+ * Deliberately no `transition` here. `.cl-press` and `.cl-squircle-root`
147
+ * very often sit on the same element, and a second `transition` declaration
148
+ * wins by source order — which silently replaced the press spring with an
149
+ * outline fade. `.cl-press` owns the transition and lists `outline-color`
150
+ * alongside its own properties.
151
+ */
152
+ }
153
+
154
+ .cl-squircle-root:has(> [data-cl-squircle]:focus-visible) {
155
+ outline-color: var(--cl-accent);
156
+ }
157
+
158
+ /**
159
+ * Text fields and selects take focus on click, and the user needs to see which
160
+ * field got it, so they cannot use `:focus-visible`.
161
+ */
162
+ .cl-squircle-field:has(> [data-cl-squircle]:focus) {
163
+ outline-color: var(--cl-accent);
164
+ }
165
+
166
+ /* -------------------------------------------------------------------------
167
+ * Floating-layer entrance
168
+ * ---------------------------------------------------------------------- */
169
+
170
+ /**
171
+ * Base UI observes animations on the popup element when deciding when to
172
+ * unmount. ClaraLight deliberately animates the wrapper so the fill, SVG
173
+ * outline and shadow stay together, which puts the visible transition one
174
+ * level above that observer.
175
+ *
176
+ * This non-visual animation is the bridge between the two contracts. It runs
177
+ * only while Base UI marks the popup as ending and animates a custom property,
178
+ * never opacity, transform or layout. The wrapper remains the only thing that
179
+ * moves; Base UI now has a real animation on the popup whose completion it
180
+ * can await before `forceUnmount`.
181
+ */
182
+ @keyframes cl-overlay-exit-sentinel {
183
+ from {
184
+ --cl-overlay-exit-sentinel: 0;
185
+ }
186
+ to {
187
+ --cl-overlay-exit-sentinel: 1;
188
+ }
189
+ }
190
+
191
+ .cl-exit-sentinel[data-ending-style] {
192
+ animation: cl-overlay-exit-sentinel var(--cl-duration-exit) linear both;
193
+ }
194
+
195
+ /**
196
+ * Menus, popovers, dialogs and sheets.
197
+ *
198
+ * The transition runs on the **wrapper**, not on the popup, because the popup
199
+ * is the clipped element and the SVG border and shadow overlays are its
200
+ * siblings on the wrapper. Scaling the popup alone would leave the 1px border
201
+ * and the shadow at their final size — 5% of a 512px dialog is 25px, which is
202
+ * very visible over a 250ms entrance.
203
+ *
204
+ * Base UI drives this from the popup's `data-starting-style` /
205
+ * `data-ending-style`; `:has()` lifts that state onto the wrapper so the whole
206
+ * assembly — fill, border and shadow — moves as one object.
207
+ *
208
+ * Two durations, because a scrim that takes as long as the spring makes the
209
+ * page behind it feel stuck. The ClaraLight springs are `Curve` equivalents,
210
+ * so `linear()` encodes the shape and the component owns the timing.
211
+ */
212
+ .cl-enter-root {
213
+ transition:
214
+ opacity var(--cl-duration-surface) var(--ease-cl-out),
215
+ scale var(--cl-duration-enter) var(--ease-cl-spring-overlay);
216
+ }
217
+
218
+ .cl-enter-root:has(> [data-starting-style]),
219
+ .cl-enter-root:has(> [data-ending-style]) {
220
+ opacity: 0;
221
+ scale: var(--cl-enter-scale);
222
+ }
223
+
224
+ .cl-enter-root:has(> [data-ending-style]) {
225
+ transition:
226
+ opacity var(--cl-duration-surface) var(--ease-cl-out),
227
+ scale var(--cl-duration-exit) var(--ease-cl-spring-overlay);
228
+ }
229
+
230
+ /**
231
+ * A layer whose geometry belongs to `useSurfaceMorph`.
232
+ *
233
+ * The 5% nudge belongs to a layer that has no fixed point to grow from. A layer
234
+ * that has one is carried by a projective quad, and the nudge would be a second,
235
+ * coarser claim on the same geometry — measured through it, the morph's own
236
+ * resting box lands 5% small, and therefore that far off the trigger.
237
+ *
238
+ * Opacity moves here for the same reason: the morph reveals the content from
239
+ * the same progress that carries the geometry, so the two legs cannot disagree.
240
+ * Left to a transition, the `ease-out` fade would empty the layer in the first
241
+ * half of a dismissal and leave the collapse back to the trigger unwatchable.
242
+ *
243
+ * Declared in the markup rather than set from the effect that drives the morph,
244
+ * because *changing* a computed `scale` is itself what starts the transition
245
+ * above: a component that reported itself as morphed one frame late would
246
+ * animate the nudge it was replacing. Present from the first style resolution,
247
+ * neither value changes and neither transition starts. This block has to stay
248
+ * after the two above it — it replaces their `transition` at equal specificity.
249
+ *
250
+ * Scoped to `no-preference` so reduced motion keeps the fade it is asking for,
251
+ * which is the CSS leg with no geometry underneath it.
252
+ */
253
+ @media (prefers-reduced-motion: no-preference) {
254
+ .cl-enter-root.cl-morph,
255
+ .cl-enter-root.cl-morph:has(> [data-starting-style]),
256
+ .cl-enter-root.cl-morph:has(> [data-ending-style]) {
257
+ transition-property: scale;
258
+ transition-duration: var(--cl-duration-enter);
259
+ transition-timing-function: var(--ease-cl-spring-overlay);
260
+ opacity: 1;
261
+ scale: none;
262
+ }
263
+ }
264
+
265
+ /* The dialog/select wrapper also fades for `--cl-duration-surface`; its
266
+ * sentinel has to cover that longer leg, not just the 110ms scale return. */
267
+ .cl-enter-root > .cl-exit-sentinel[data-ending-style] {
268
+ animation-duration: var(--cl-duration-surface);
269
+ }
270
+
271
+ @media (prefers-reduced-motion: reduce) {
272
+ .cl-enter-root {
273
+ transition: opacity var(--cl-duration-reduced) var(--ease-cl-out);
274
+ }
275
+
276
+ .cl-enter-root:has(> [data-starting-style]),
277
+ .cl-enter-root:has(> [data-ending-style]) {
278
+ scale: none;
279
+ opacity: 0;
280
+ }
281
+ }
282
+
283
+ /* -------------------------------------------------------------------------
284
+ * Focus
285
+ * ---------------------------------------------------------------------- */
286
+
287
+ /**
288
+ * ClaraLight marks focus with the accent color. Uses `outline` rather than a
289
+ * ring so the indicator never shifts layout and survives `overflow: hidden`
290
+ * ancestors, and stays visible on both schemes because accent is the same
291
+ * blue in each.
292
+ *
293
+ * Two variants, because the trigger differs:
294
+ * cl-focus buttons and other pointer-activated controls, which
295
+ * should not flash an indicator on mouse click
296
+ * cl-focus-field text fields and selects, where focus follows the click
297
+ * and the user needs to see which field took it
298
+ */
299
+ .cl-focus,
300
+ .cl-focus-field {
301
+ outline: var(--cl-focus-width) solid transparent;
302
+ outline-offset: var(--cl-focus-offset);
303
+ }
304
+
305
+ .cl-focus:focus-visible,
306
+ .cl-focus-field:focus {
307
+ outline-color: var(--cl-accent);
308
+ }
309
+ }
@@ -0,0 +1,63 @@
1
+ # Fonts
2
+
3
+ `theme.css` names four preferred families. This package currently ships
4
+ **no font files** — the token layer supplies platform fallbacks. Rendering and
5
+ metrics depend on the fonts installed by the consumer; they are not guaranteed
6
+ to match the preferred faces.
7
+
8
+ ## What the design language expects
9
+
10
+ | Family | Role | Token | Source / license |
11
+ | --- | --- | --- | --- |
12
+ | MiSans | UI text, Chinese + Latin (variable) | `--font-sans` | [hyperos.mi.com/font](https://hyperos.mi.com/font) — MiSans 字体知识产权许可协议, free commercial use & redistribution |
13
+ | Sarasa Mono SC | Numeric values and units | `--font-mono` | [be5invis/Sarasa-Gothic](https://github.com/be5invis/Sarasa-Gothic) v1.0.40 — SIL OFL 1.1 |
14
+ | ChillDINGothic | Large display headings | `--font-display` | [Warren2060/ChillDIN-ChillDINGothic](https://github.com/Warren2060/ChillDIN-ChillDINGothic) v1.300 — SIL OFL 1.1 |
15
+ | Clara Serif Pro | Optional serif, not in the default ramp | `--font-serif` | Obtain the font and verify its license before redistribution |
16
+
17
+ Check the license accompanying each actual font file before shipping it.
18
+
19
+ ## To ship them
20
+
21
+ 1. Obtain licensed font files from their upstream sources and convert to
22
+ woff2 for web delivery. Keep a variable version when the required weight
23
+ range and download size justify it.
24
+
25
+ 2. Subset for the languages the application supports. Preserve required CJK
26
+ coverage; use separate `@font-face` declarations with `unicode-range`
27
+ splits where useful. Do not assume an upstream file is already subset.
28
+
29
+ 3. Add the files in this directory (or a CDN), create the corresponding
30
+ `@font-face` stylesheet, and import it from `index.css`.
31
+
32
+ ## MiSans' wght axis is not standard
33
+
34
+ Verify the named instances in the exact MiSans VF file you ship. The following
35
+ mapping has been used for MiSans files with non-standard weight positions;
36
+ it is not a guarantee for every release:
37
+
38
+ | CSS `font-weight` | MiSans `wght` axis | Named instance |
39
+ | --- | --- | --- |
40
+ | 400 | **330** | Regular |
41
+ | 500 | **380** | Medium |
42
+ | 600 | **450** | Demibold |
43
+ | 700 | **520** | Semibold |
44
+
45
+ For a file with this mapping, axis position 600 is above Semibold, not
46
+ Demibold. If explicit axis settings are needed, define the mapping as tokens
47
+ in `theme.css`, then consume them rather than repeating axis values:
48
+
49
+ ```css
50
+ /* Instead of relying on font-weight alone. */
51
+ .cl-weight-title {
52
+ font-variation-settings: "wght" var(--cl-font-axis-title);
53
+ }
54
+ ```
55
+
56
+ Because `font-variation-settings` is a single property, the token ramp's
57
+ `--text-*--font-weight` values are the *intent* (700/600/500/400). They land
58
+ according to the loaded font's metadata; on fallback fonts the browser uses
59
+ the available weights. The example axis token above is not shipped yet.
60
+
61
+ Rather than hand-maintaining a utility per step, add a
62
+ `@utility cl-wght-{400,500,600,700}` set in `theme.css` that maps to
63
+ 330/380/450/520 during the font drop-in.
@@ -0,0 +1,28 @@
1
+ /**
2
+ * ClaraLight Design — full stylesheet for npm consumers.
3
+ *
4
+ * Registry (copy-in) consumers import these files directly instead, and take
5
+ * only the ones their components need, so the files stay the single source of
6
+ * truth for both paths. The npm package cannot make that choice per consumer,
7
+ * so it imports all of them.
8
+ *
9
+ * @import "tailwindcss";
10
+ * @import "@claralight-design/react/styles.css";
11
+ */
12
+
13
+ @import "./theme.css";
14
+ @import "./base.css";
15
+ @import "./anchored.css";
16
+ @import "./tooltip.css";
17
+ @import "./scroll-area.css";
18
+
19
+ /**
20
+ * Tailwind v4 does not scan `node_modules`, so a consumer importing the built
21
+ * package would otherwise get components with every utility class stripped.
22
+ * `@source` is resolved relative to this file, which puts it at the package
23
+ * root next to `dist/`.
24
+ *
25
+ * Registry consumers do not need this: their copied components live inside the
26
+ * project and are scanned automatically.
27
+ */
28
+ @source "../dist";