@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.
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/lib/anchored.d.ts +152 -0
- package/dist/lib/anchored.js +821 -0
- package/dist/lib/anchored.js.map +1 -0
- package/dist/lib/morph.js +484 -0
- package/dist/lib/morph.js.map +1 -0
- package/dist/lib/squircle.d.ts +95 -0
- package/dist/lib/squircle.js +220 -0
- package/dist/lib/squircle.js.map +1 -0
- package/dist/lib/utils.d.ts +30 -0
- package/dist/lib/utils.js +76 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/ui/button.d.ts +40 -0
- package/dist/ui/button.js +104 -0
- package/dist/ui/button.js.map +1 -0
- package/dist/ui/card.d.ts +54 -0
- package/dist/ui/card.js +82 -0
- package/dist/ui/card.js.map +1 -0
- package/dist/ui/dialog.d.ts +82 -0
- package/dist/ui/dialog.js +129 -0
- package/dist/ui/dialog.js.map +1 -0
- package/dist/ui/input.d.ts +34 -0
- package/dist/ui/input.js +60 -0
- package/dist/ui/input.js.map +1 -0
- package/dist/ui/popover.d.ts +67 -0
- package/dist/ui/popover.js +93 -0
- package/dist/ui/popover.js.map +1 -0
- package/dist/ui/scroll-area.d.ts +110 -0
- package/dist/ui/scroll-area.js +125 -0
- package/dist/ui/scroll-area.js.map +1 -0
- package/dist/ui/select.d.ts +113 -0
- package/dist/ui/select.js +213 -0
- package/dist/ui/select.js.map +1 -0
- package/dist/ui/tooltip.d.ts +108 -0
- package/dist/ui/tooltip.js +142 -0
- package/dist/ui/tooltip.js.map +1 -0
- package/package.json +72 -0
- package/styles/anchored.css +115 -0
- package/styles/base.css +309 -0
- package/styles/fonts/README.md +63 -0
- package/styles/index.css +28 -0
- package/styles/scroll-area.css +299 -0
- package/styles/theme.css +540 -0
- package/styles/tooltip.css +156 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClaraLight Design — the scroll area's edge effects and overlay scrollbars.
|
|
3
|
+
*
|
|
4
|
+
* Base UI's Scroll Area owns the viewport, the thumb geometry and the overflow
|
|
5
|
+
* signals; everything here is the ClaraLight treatment painted on top. Two of
|
|
6
|
+
* those signals do the real work, and they are deliberately used for different
|
|
7
|
+
* halves of the effect:
|
|
8
|
+
*
|
|
9
|
+
* `--scroll-area-overflow-*` the distance in px from each logical edge,
|
|
10
|
+
* rewritten on the viewport every scroll frame.
|
|
11
|
+
* `data-overflow-*` the same four edges as booleans, which change
|
|
12
|
+
* only when an edge is actually crossed.
|
|
13
|
+
*
|
|
14
|
+
* The alpha mask reads the continuous distance, so the band grows out of the
|
|
15
|
+
* first `--cl-scroll-edge` pixels of scroll and there is no animation to run —
|
|
16
|
+
* no transition, no timer, no re-render, and nothing to get out of step with
|
|
17
|
+
* the scroll position. The blur reads the boolean and cross-fades instead,
|
|
18
|
+
* because a `backdrop-filter` layer is worth compositing only while its edge is
|
|
19
|
+
* live, and opacity is the one channel that costs nothing to animate.
|
|
20
|
+
*
|
|
21
|
+
* Requires `theme.css` for the tokens.
|
|
22
|
+
*
|
|
23
|
+
* @import "tailwindcss";
|
|
24
|
+
* @import "@claralight-design/react/styles.css";
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
@layer components {
|
|
28
|
+
/* -------------------------------------------------------------------------
|
|
29
|
+
* The alpha mask
|
|
30
|
+
* ---------------------------------------------------------------------- */
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Four gradients composited together, one per logical edge. A band of zero
|
|
34
|
+
* collapses its gradient's two stops onto the same position, which resolves
|
|
35
|
+
* to fully opaque — so a disabled side, or an axis that is not scrollable,
|
|
36
|
+
* costs nothing and needs no separate rule.
|
|
37
|
+
*
|
|
38
|
+
* `min()` is what clamps the band: Base UI reports the true distance from
|
|
39
|
+
* the edge, which keeps growing as you scroll, and the band stops widening
|
|
40
|
+
* once it reaches the token.
|
|
41
|
+
*
|
|
42
|
+
* `black` in these gradients is a stencil, not a colour. A mask reads only
|
|
43
|
+
* alpha, so the hue never reaches the screen and there is no token for it
|
|
44
|
+
* to come from — every opaque value would render identically.
|
|
45
|
+
*/
|
|
46
|
+
.cl-scroll-viewport {
|
|
47
|
+
--cl-scroll-band-block-start: min(
|
|
48
|
+
var(--scroll-area-overflow-y-start, 0px),
|
|
49
|
+
var(--cl-scroll-edge-block-start)
|
|
50
|
+
);
|
|
51
|
+
--cl-scroll-band-block-end: min(
|
|
52
|
+
var(--scroll-area-overflow-y-end, 0px),
|
|
53
|
+
var(--cl-scroll-edge-block-end)
|
|
54
|
+
);
|
|
55
|
+
--cl-scroll-band-inline-start: min(
|
|
56
|
+
var(--scroll-area-overflow-x-start, 0px),
|
|
57
|
+
var(--cl-scroll-edge-inline-start)
|
|
58
|
+
);
|
|
59
|
+
--cl-scroll-band-inline-end: min(
|
|
60
|
+
var(--scroll-area-overflow-x-end, 0px),
|
|
61
|
+
var(--cl-scroll-edge-inline-end)
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
/* Gradients take a physical direction, so the inline pair is the only
|
|
65
|
+
* thing in this file that has to know about writing direction. */
|
|
66
|
+
--cl-scroll-to-inline-start: right;
|
|
67
|
+
--cl-scroll-to-inline-end: left;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.cl-scroll-viewport:dir(rtl) {
|
|
71
|
+
--cl-scroll-to-inline-start: left;
|
|
72
|
+
--cl-scroll-to-inline-end: right;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Applied only once an axis actually overflows, so a scroll area that fits
|
|
76
|
+
* its content is never promoted to a masked layer. */
|
|
77
|
+
.cl-scroll-viewport:is([data-has-overflow-x], [data-has-overflow-y]) {
|
|
78
|
+
mask-image: linear-gradient(
|
|
79
|
+
to bottom,
|
|
80
|
+
transparent,
|
|
81
|
+
black var(--cl-scroll-band-block-start)
|
|
82
|
+
),
|
|
83
|
+
linear-gradient(to top, transparent, black var(--cl-scroll-band-block-end)),
|
|
84
|
+
linear-gradient(
|
|
85
|
+
to var(--cl-scroll-to-inline-start),
|
|
86
|
+
transparent,
|
|
87
|
+
black var(--cl-scroll-band-inline-start)
|
|
88
|
+
),
|
|
89
|
+
linear-gradient(
|
|
90
|
+
to var(--cl-scroll-to-inline-end),
|
|
91
|
+
transparent,
|
|
92
|
+
black var(--cl-scroll-band-inline-end)
|
|
93
|
+
);
|
|
94
|
+
mask-composite: intersect;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* -------------------------------------------------------------------------
|
|
98
|
+
* The progressive blur
|
|
99
|
+
* ---------------------------------------------------------------------- */
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* One element per edge, each holding three stacked `backdrop-filter` layers
|
|
103
|
+
* whose sigma doubles and whose masks nest, so the blur ramps from full
|
|
104
|
+
* strength at the edge to nothing at the far side of the band. This is the
|
|
105
|
+
* web's stand-in for the single spatially-varying sigma a shader would use.
|
|
106
|
+
*
|
|
107
|
+
* These have to be **descendants of the clipped element**. `clip-path`
|
|
108
|
+
* establishes a backdrop root, so an edge placed as a sibling of the clipped
|
|
109
|
+
* surface cannot see the scrolled content behind it and renders no blur at
|
|
110
|
+
* all — silently, with no error and no warning.
|
|
111
|
+
*
|
|
112
|
+
* That same backdrop root is why **the surface's fill has to be opaque**.
|
|
113
|
+
* Inside it the backdrop is only the surface's own fill plus the scrolled
|
|
114
|
+
* content; everything painted behind the scroll area is excluded. Blur a
|
|
115
|
+
* backdrop that is mostly transparent and the engine lifts what little
|
|
116
|
+
* colour is there instead of preserving it — measured on a 10%-white fill,
|
|
117
|
+
* the band's mean brightness goes from 49 to 87 out of 255, once per
|
|
118
|
+
* overlapping layer, which reads as a white halo around the frame. An
|
|
119
|
+
* opaque fill holds the mean to within 1.3.
|
|
120
|
+
*
|
|
121
|
+
* There is no way to have all three of a squircle-clipped surface, a
|
|
122
|
+
* translucent fill and this blur: clipping the layers themselves instead
|
|
123
|
+
* fixes the halo but then nothing shapes the content, and moving the clip
|
|
124
|
+
* anywhere else stops the blur from seeing the content at all. So a
|
|
125
|
+
* translucent surface takes `edge="mask"`, which has none of this coupling.
|
|
126
|
+
*/
|
|
127
|
+
.cl-scroll-edge {
|
|
128
|
+
position: absolute;
|
|
129
|
+
pointer-events: none;
|
|
130
|
+
opacity: 0;
|
|
131
|
+
transition: opacity var(--cl-duration-surface) var(--ease-cl-in);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.cl-scroll-edge[data-edge="block-start"] {
|
|
135
|
+
inset-inline: 0;
|
|
136
|
+
inset-block-start: 0;
|
|
137
|
+
block-size: var(--cl-scroll-edge-block-start);
|
|
138
|
+
--cl-scroll-edge-to: bottom;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.cl-scroll-edge[data-edge="block-end"] {
|
|
142
|
+
inset-inline: 0;
|
|
143
|
+
inset-block-end: 0;
|
|
144
|
+
block-size: var(--cl-scroll-edge-block-end);
|
|
145
|
+
--cl-scroll-edge-to: top;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.cl-scroll-edge[data-edge="inline-start"] {
|
|
149
|
+
inset-block: 0;
|
|
150
|
+
inset-inline-start: 0;
|
|
151
|
+
inline-size: var(--cl-scroll-edge-inline-start);
|
|
152
|
+
--cl-scroll-edge-to: right;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.cl-scroll-edge[data-edge="inline-end"] {
|
|
156
|
+
inset-block: 0;
|
|
157
|
+
inset-inline-end: 0;
|
|
158
|
+
inline-size: var(--cl-scroll-edge-inline-end);
|
|
159
|
+
--cl-scroll-edge-to: left;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.cl-scroll-edge[data-edge="inline-start"]:dir(rtl) {
|
|
163
|
+
--cl-scroll-edge-to: left;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.cl-scroll-edge[data-edge="inline-end"]:dir(rtl) {
|
|
167
|
+
--cl-scroll-edge-to: right;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* Arriving and leaving get the two halves of the ClaraLight motion pair, the
|
|
171
|
+
* same way the Flutter edge controllers animate in and out. */
|
|
172
|
+
.cl-scroll-area[data-overflow-y-start] > .cl-scroll-edge[data-edge="block-start"],
|
|
173
|
+
.cl-scroll-area[data-overflow-y-end] > .cl-scroll-edge[data-edge="block-end"],
|
|
174
|
+
.cl-scroll-area[data-overflow-x-start] > .cl-scroll-edge[data-edge="inline-start"],
|
|
175
|
+
.cl-scroll-area[data-overflow-x-end] > .cl-scroll-edge[data-edge="inline-end"] {
|
|
176
|
+
opacity: 1;
|
|
177
|
+
transition-timing-function: var(--ease-cl-out);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.cl-scroll-edge-layer {
|
|
181
|
+
position: absolute;
|
|
182
|
+
inset: 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* Weakest and widest first, strongest and narrowest last, so the layers
|
|
186
|
+
* paint in falloff order. Per-edge sigma is reachable without another
|
|
187
|
+
* token: scope `--cl-scroll-blur` to one `[data-edge]` and only that side
|
|
188
|
+
* changes. */
|
|
189
|
+
.cl-scroll-edge-layer:nth-child(1) {
|
|
190
|
+
backdrop-filter: blur(calc(var(--cl-scroll-blur) * var(--cl-scroll-blur-far)));
|
|
191
|
+
mask-image: linear-gradient(to var(--cl-scroll-edge-to), black, transparent);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.cl-scroll-edge-layer:nth-child(2) {
|
|
195
|
+
backdrop-filter: blur(calc(var(--cl-scroll-blur) * var(--cl-scroll-blur-mid)));
|
|
196
|
+
mask-image: linear-gradient(
|
|
197
|
+
to var(--cl-scroll-edge-to),
|
|
198
|
+
black,
|
|
199
|
+
transparent var(--cl-scroll-reach-mid)
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.cl-scroll-edge-layer:nth-child(3) {
|
|
204
|
+
backdrop-filter: blur(var(--cl-scroll-blur));
|
|
205
|
+
mask-image: linear-gradient(
|
|
206
|
+
to var(--cl-scroll-edge-to),
|
|
207
|
+
black,
|
|
208
|
+
transparent var(--cl-scroll-reach-near)
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Dropping to the mask alone is the honest degradation: the edge keeps
|
|
214
|
+
* saying "there is more behind this" at a fraction of the cost, which is
|
|
215
|
+
* also why `edge="mask"` exists as a deliberate choice.
|
|
216
|
+
*/
|
|
217
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
218
|
+
.cl-scroll-edge-layer {
|
|
219
|
+
backdrop-filter: none;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* -------------------------------------------------------------------------
|
|
224
|
+
* Overlay scrollbars
|
|
225
|
+
* ---------------------------------------------------------------------- */
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* The visibility policy is two transitions rather than a timer: the hidden
|
|
229
|
+
* state carries the fade-out delay, and the revealed state overrides the
|
|
230
|
+
* delay away. Hovering the area or scrolling either axis reveals both bars,
|
|
231
|
+
* and letting go schedules the fade — which is exactly what the Flutter
|
|
232
|
+
* overlay's hide timer does, with nothing to cancel.
|
|
233
|
+
*/
|
|
234
|
+
/* No z-index anywhere here: the viewport is static and both the edges and
|
|
235
|
+
* the bars are absolutely positioned, so paint order already runs
|
|
236
|
+
* viewport, edges, bars — exactly the order the DOM is written in. */
|
|
237
|
+
.cl-scrollbar {
|
|
238
|
+
opacity: 0;
|
|
239
|
+
pointer-events: none;
|
|
240
|
+
transition: opacity var(--cl-scrollbar-fade-duration) var(--ease-cl-in)
|
|
241
|
+
var(--cl-scrollbar-fade-delay);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.cl-scrollbar:is([data-hovering], [data-scrolling]) {
|
|
245
|
+
opacity: 1;
|
|
246
|
+
pointer-events: auto;
|
|
247
|
+
transition-duration: var(--cl-duration-surface);
|
|
248
|
+
transition-timing-function: var(--ease-cl-out);
|
|
249
|
+
transition-delay: 0s;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/* Base UI unmounts a scrollbar whose axis cannot scroll, so `always` still
|
|
253
|
+
* means "whenever this axis has somewhere to go". */
|
|
254
|
+
.cl-scrollbar[data-cl-visibility="always"] {
|
|
255
|
+
opacity: 1;
|
|
256
|
+
pointer-events: auto;
|
|
257
|
+
transition: none;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* The inset is a margin, not an offset: Base UI writes the scrollbar's
|
|
262
|
+
* `inset-*` inline, and the thumb maths already reads margins back out.
|
|
263
|
+
*
|
|
264
|
+
* Each track is a flex line along its own axis, which is what gives the
|
|
265
|
+
* thumb its thickness. Base UI sizes the thumb along the scrolling axis
|
|
266
|
+
* only — the other axis is left to the track, and `stretch` is the flex
|
|
267
|
+
* default, so the cross size follows `--cl-scrollbar-thickness` without
|
|
268
|
+
* either element restating it.
|
|
269
|
+
*/
|
|
270
|
+
.cl-scrollbar[data-orientation="vertical"] {
|
|
271
|
+
display: flex;
|
|
272
|
+
flex-direction: column;
|
|
273
|
+
inline-size: var(--cl-scrollbar-thickness);
|
|
274
|
+
margin-block: var(--cl-scrollbar-inset);
|
|
275
|
+
margin-inline-end: var(--cl-scrollbar-inset);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.cl-scrollbar[data-orientation="horizontal"] {
|
|
279
|
+
display: flex;
|
|
280
|
+
block-size: var(--cl-scrollbar-thickness);
|
|
281
|
+
margin-inline: var(--cl-scrollbar-inset);
|
|
282
|
+
margin-block-end: var(--cl-scrollbar-inset);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/* A 4px bar makes the design's 17px corner a pill, so there is no squircle
|
|
286
|
+
* to draw here — the two shapes coincide below twice the thickness. */
|
|
287
|
+
.cl-scroll-thumb {
|
|
288
|
+
border-radius: var(--radius-capsule);
|
|
289
|
+
background-color: var(--cl-selection);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.cl-scroll-thumb[data-orientation="vertical"] {
|
|
293
|
+
margin-block: var(--cl-scrollbar-margin);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.cl-scroll-thumb[data-orientation="horizontal"] {
|
|
297
|
+
margin-inline: var(--cl-scrollbar-margin);
|
|
298
|
+
}
|
|
299
|
+
}
|