@saasflare/ui 3.1.2 → 3.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 +68 -2
- package/dist/{button-0Bdl7Nqm.d.ts → button-BA7OcXqy.d.mts} +12 -17
- package/dist/{button-Brb4BhPO.d.mts → button-Bfg2Tnvx.d.ts} +12 -17
- package/dist/{chunk-D5LKWKG7.js → chunk-2GOPD64T.js} +117 -89
- package/dist/{chunk-56PMDC5F.mjs → chunk-2ONA6OMO.mjs} +29 -40
- package/dist/{chunk-RW2S3KNB.mjs → chunk-5C65JNGY.mjs} +7 -6
- package/dist/{chunk-NPNSPYTX.js → chunk-7UD3SGPP.js} +28 -39
- package/dist/chunk-GI6VN7XU.mjs +2143 -0
- package/dist/{chunk-FT66KYRN.js → chunk-ITALEYDI.js} +2 -2
- package/dist/{chunk-4BOMMZEY.js → chunk-JC7EIEGI.js} +14 -13
- package/dist/chunk-N65HIOBD.js +234 -0
- package/dist/{chunk-EJHYM2HP.mjs → chunk-OZAWULTM.mjs} +1 -1
- package/dist/chunk-R3AVBLJ3.js +2207 -0
- package/dist/{chunk-WRONFPRI.mjs → chunk-RMQBB72G.mjs} +118 -91
- package/dist/chunk-XNDTCYSO.mjs +211 -0
- package/dist/{dialog-BmY55WSX.d.ts → dialog-CZRwrqDa.d.ts} +2 -2
- package/dist/{dialog-CcaHMAsS.d.mts → dialog-Cr0becOL.d.mts} +2 -2
- package/dist/entries/calendar.d.mts +3 -3
- package/dist/entries/calendar.d.ts +3 -3
- package/dist/entries/calendar.js +13 -214
- package/dist/entries/calendar.mjs +5 -196
- package/dist/entries/carousel.d.mts +3 -3
- package/dist/entries/carousel.d.ts +3 -3
- package/dist/entries/carousel.js +17 -14
- package/dist/entries/carousel.mjs +10 -7
- package/dist/entries/chart.d.mts +1 -1
- package/dist/entries/chart.d.ts +1 -1
- package/dist/entries/chart.js +11 -11
- package/dist/entries/chart.mjs +1 -1
- package/dist/entries/command.d.mts +3 -3
- package/dist/entries/command.d.ts +3 -3
- package/dist/entries/command.js +21 -19
- package/dist/entries/command.mjs +8 -6
- package/dist/entries/drawer.d.mts +1 -1
- package/dist/entries/drawer.d.ts +1 -1
- package/dist/entries/drawer.js +9 -9
- package/dist/entries/drawer.mjs +2 -2
- package/dist/entries/input-otp.d.mts +2 -2
- package/dist/entries/input-otp.d.ts +2 -2
- package/dist/entries/input-otp.js +10 -8
- package/dist/entries/input-otp.mjs +6 -4
- package/dist/entries/resizable.d.mts +3 -2
- package/dist/entries/resizable.d.ts +3 -2
- package/dist/entries/resizable.js +8 -6
- package/dist/entries/resizable.mjs +6 -4
- package/dist/index.d.mts +974 -31
- package/dist/index.d.ts +974 -31
- package/dist/index.js +2992 -554
- package/dist/index.mjs +2486 -199
- package/dist/{use-saasflare-props-NrM2Glmp.d.mts → use-saasflare-props-BrjMhU0U.d.mts} +53 -4
- package/dist/{use-saasflare-props-NrM2Glmp.d.ts → use-saasflare-props-BrjMhU0U.d.ts} +53 -4
- package/package.json +4 -3
- package/styles/aurora.css +47 -0
- package/styles/palettes.css +483 -3
- package/styles/surfaces.css +89 -10
- package/styles/theme.css +11 -5
package/styles/palettes.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview 20 preset brand palettes — activate via [data-palette="id"] on <html>.
|
|
3
3
|
* @module packages/ui/styles/palettes
|
|
4
4
|
* @package ui
|
|
5
5
|
* @reviewed 2026-04-19
|
|
@@ -134,18 +134,498 @@
|
|
|
134
134
|
:root[data-palette="ruby"] { --primary-h: 10; --primary-c: 0.21; --primary-l: 0.58; }
|
|
135
135
|
:root[data-palette="ruby"].dark { --primary-l: 0.68; }
|
|
136
136
|
|
|
137
|
+
/* ─── Sky (HeroUI-style pastel blue) ────────────── */
|
|
138
|
+
:root[data-palette="sky"] { --primary-h: 210; --primary-c: 0.12; --primary-l: 0.72; }
|
|
139
|
+
:root[data-palette="sky"].dark { --primary-l: 0.80; }
|
|
140
|
+
|
|
141
|
+
/* ─── Lavender (HeroUI-style pastel violet) ─────── */
|
|
142
|
+
:root[data-palette="lavender"] { --primary-h: 280; --primary-c: 0.10; --primary-l: 0.72; }
|
|
143
|
+
:root[data-palette="lavender"].dark { --primary-l: 0.80; }
|
|
144
|
+
|
|
145
|
+
/* ─── Mint (HeroUI-style pastel green) ──────────── */
|
|
146
|
+
:root[data-palette="mint"] { --primary-h: 155; --primary-c: 0.10; --primary-l: 0.72; }
|
|
147
|
+
:root[data-palette="mint"].dark { --primary-l: 0.80; }
|
|
148
|
+
|
|
149
|
+
/* ─── Snow (near-white, ultra-low chroma) ───────── */
|
|
150
|
+
/* For an outline-forward, low-contrast feel. Primary lands near-white so the
|
|
151
|
+
* UI leans on borders/dividers instead of filled accent surfaces. */
|
|
152
|
+
:root[data-palette="snow"] {
|
|
153
|
+
--primary-h: 0;
|
|
154
|
+
--primary-c: 0.005;
|
|
155
|
+
--primary-l: 0.93;
|
|
156
|
+
--neutral-h: 0;
|
|
157
|
+
--neutral-c: 0;
|
|
158
|
+
--primary-foreground: oklch(0.2 0 0); /* dark text on near-white */
|
|
159
|
+
}
|
|
160
|
+
:root[data-palette="snow"].dark {
|
|
161
|
+
--primary-l: 0.95;
|
|
162
|
+
--primary-foreground: oklch(0.2 0 0);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* ─── Craivo (cool cyan → magenta brand) ─────────── */
|
|
166
|
+
/* Brand hue anchored in cool cyan-blue; gradient-text / progress should
|
|
167
|
+
* layer to magenta via component-level from/to props. Neutrals are pure
|
|
168
|
+
* grey (no brand tint). Dark canvas is pushed below the system default
|
|
169
|
+
* to land near-black, matching the Craivo landing page. */
|
|
170
|
+
:root[data-palette="craivo"] {
|
|
171
|
+
--primary-h: 200;
|
|
172
|
+
--primary-c: 0.18;
|
|
173
|
+
--primary-l: 0.62;
|
|
174
|
+
--neutral-h: 200;
|
|
175
|
+
--neutral-c: 0;
|
|
176
|
+
}
|
|
177
|
+
:root[data-palette="craivo"].dark {
|
|
178
|
+
--primary-l: 0.72;
|
|
179
|
+
--background: oklch(0.12 0 0); /* near-black canvas */
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* Chart palette: spans cyan → magenta with a lime success-accent, matching
|
|
183
|
+
* the electric-cool spectrum used across Craivo's metric/progress visuals. */
|
|
184
|
+
:root[data-palette="craivo"] {
|
|
185
|
+
--chart-1: oklch(0.70 0.18 200); /* cyan (brand) */
|
|
186
|
+
--chart-2: oklch(0.68 0.22 330); /* magenta */
|
|
187
|
+
--chart-3: oklch(0.78 0.20 140); /* lime */
|
|
188
|
+
--chart-4: oklch(0.72 0.20 60); /* amber */
|
|
189
|
+
--chart-5: oklch(0.66 0.22 290); /* violet */
|
|
190
|
+
}
|
|
191
|
+
|
|
137
192
|
/* ============================================
|
|
138
193
|
* Chart palette overrides for achromatic palettes
|
|
139
194
|
*
|
|
140
195
|
* With --primary-c: 0, the derived chart colors collapse to grayscale.
|
|
141
|
-
* Give Ink and
|
|
196
|
+
* Give Ink, Stone, Black and Snow a fixed distinguishable 5-hue palette.
|
|
142
197
|
* ============================================ */
|
|
143
198
|
:root[data-palette="ink"],
|
|
144
199
|
:root[data-palette="stone"],
|
|
145
|
-
:root[data-palette="black"]
|
|
200
|
+
:root[data-palette="black"],
|
|
201
|
+
:root[data-palette="snow"] {
|
|
146
202
|
--chart-1: oklch(0.60 0.18 230); /* blue */
|
|
147
203
|
--chart-2: oklch(0.65 0.17 55); /* orange */
|
|
148
204
|
--chart-3: oklch(0.60 0.15 155); /* green */
|
|
149
205
|
--chart-4: oklch(0.58 0.20 25); /* red */
|
|
150
206
|
--chart-5: oklch(0.60 0.18 290); /* violet */
|
|
151
207
|
}
|
|
208
|
+
|
|
209
|
+
/* ─── Colorful (minimal at rest, soft pastel-gradient glow on hover) ─── */
|
|
210
|
+
/* Inspired by htgf.de — surfaces are quiet and outline-forward at rest,
|
|
211
|
+
* then on hover bloom with a SOFT pastel gradient (peach → pink → lavender
|
|
212
|
+
* → cyan) wrapped in a matching color-aura glow. Text stays dark; the
|
|
213
|
+
* effect reads as "warm cloud appears behind the button." Token --primary
|
|
214
|
+
* is kept as a warm red so swatches in palette pickers stay legible; the
|
|
215
|
+
* pastel sweep lives only in the hover rules below. */
|
|
216
|
+
:root[data-palette="colorful"] {
|
|
217
|
+
--primary-h: 25;
|
|
218
|
+
--primary-c: 0.21;
|
|
219
|
+
--primary-l: 0.58;
|
|
220
|
+
--neutral-h: 0;
|
|
221
|
+
--neutral-c: 0;
|
|
222
|
+
|
|
223
|
+
/* --primary itself is transparent — primary surfaces don't carry a
|
|
224
|
+
* solid color. Instead, the resting state gets the gradient as an
|
|
225
|
+
* almost-transparent background-image wash (see rules below). On
|
|
226
|
+
* hover/active, the full-opacity gradient kicks in. */
|
|
227
|
+
--primary: transparent;
|
|
228
|
+
--primary-foreground: oklch(0.2 0 0);
|
|
229
|
+
|
|
230
|
+
/* Full-opacity pastel sweep — peach, blush, lavender, cyan. Used on
|
|
231
|
+
* hover and on active/checked states. */
|
|
232
|
+
--colorful-gradient: linear-gradient(95deg, #ffd4a3 0%, #ffb8c5 35%, #d4bce8 65%, #b8d8e5 100%);
|
|
233
|
+
|
|
234
|
+
/* Almost-transparent variant of the same sweep for resting "primary
|
|
235
|
+
* fill" surfaces (~18% alpha). Visible enough to identify the
|
|
236
|
+
* surface, quiet enough not to compete with surrounding content. */
|
|
237
|
+
--colorful-gradient-faint: linear-gradient(95deg,
|
|
238
|
+
rgba(255, 212, 163, 0.20) 0%,
|
|
239
|
+
rgba(255, 184, 197, 0.20) 35%,
|
|
240
|
+
rgba(212, 188, 232, 0.20) 65%,
|
|
241
|
+
rgba(184, 216, 229, 0.20) 100%);
|
|
242
|
+
}
|
|
243
|
+
:root[data-palette="colorful"].dark {
|
|
244
|
+
--primary-l: 0.68;
|
|
245
|
+
--primary: transparent;
|
|
246
|
+
--primary-foreground: oklch(0.95 0 0);
|
|
247
|
+
/* Slightly stronger alpha in dark mode so the faint wash still
|
|
248
|
+
* reads against the darker canvas. */
|
|
249
|
+
--colorful-gradient-faint: linear-gradient(95deg,
|
|
250
|
+
rgba(255, 212, 163, 0.14) 0%,
|
|
251
|
+
rgba(255, 184, 197, 0.14) 35%,
|
|
252
|
+
rgba(212, 188, 232, 0.14) 65%,
|
|
253
|
+
rgba(184, 216, 229, 0.14) 100%);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* Apply the faint gradient as the resting "primary fill". Covers both
|
|
257
|
+
* the token-driven path (Buttons use bg-[var(--intent)]) and the direct
|
|
258
|
+
* utility-class path (Tooltip / Calendar selected days / PricingCard
|
|
259
|
+
* pill / scroll-to-top / etc. use bare bg-primary). Hover and active
|
|
260
|
+
* states win via higher-specificity rules further down. */
|
|
261
|
+
:root[data-palette="colorful"] [data-slot="button"][data-intent="primary"][data-variant="solid"]:not(:hover):not(:disabled),
|
|
262
|
+
:root[data-palette="colorful"] [data-slot="button"][data-intent="primary"][data-variant="soft"]:not(:hover):not(:disabled),
|
|
263
|
+
:root[data-palette="colorful"] [data-slot="button"][data-intent="primary"][data-variant="shadow"]:not(:hover):not(:disabled),
|
|
264
|
+
:root[data-palette="colorful"] .bg-primary:not(:hover) {
|
|
265
|
+
background-image: var(--colorful-gradient-faint);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* Palette-scoped hover retrofits. Specificity 0,2,0 from the
|
|
269
|
+
* :root[data-palette] prefix beats the variant utility's 0,1,0 — same
|
|
270
|
+
* mechanism every preset above uses to override base tokens. Scoped via
|
|
271
|
+
* the data-slot + data-variant attributes the components already emit. */
|
|
272
|
+
|
|
273
|
+
/* With --primary: transparent, the primary-intent token chain resolves
|
|
274
|
+
* --intent-text = --primary = transparent, which would render invisible
|
|
275
|
+
* text on every non-filled variant (outline, ghost, link, glass). Route
|
|
276
|
+
* primary-intent text through --foreground so it always reads against
|
|
277
|
+
* the page background. The fill (--intent) stays transparent so solid
|
|
278
|
+
* surfaces still behave as the user requested. */
|
|
279
|
+
:root[data-palette="colorful"] [data-intent="primary"] {
|
|
280
|
+
--intent-text: var(--foreground);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* Discoverability for transparent primary buttons. Solid / soft / shadow
|
|
284
|
+
* variants of primary intent would otherwise be invisible at rest — the
|
|
285
|
+
* fill is transparent and they ship without a border. Add a hairline
|
|
286
|
+
* border + soft drop shadow so the button's footprint reads against the
|
|
287
|
+
* page without compromising the "quiet at rest" feel. Outline already
|
|
288
|
+
* has its own border; glass has the surface treatment; ghost / link are
|
|
289
|
+
* intentionally chromeless and stay so. */
|
|
290
|
+
:root[data-palette="colorful"] [data-slot="button"][data-variant="solid"][data-intent="primary"]:not(:hover):not(:disabled),
|
|
291
|
+
:root[data-palette="colorful"] [data-slot="button"][data-variant="soft"][data-intent="primary"]:not(:hover):not(:disabled),
|
|
292
|
+
:root[data-palette="colorful"] [data-slot="button"][data-variant="shadow"][data-intent="primary"]:not(:hover):not(:disabled) {
|
|
293
|
+
border: 1px solid oklch(0 0 0 / 0.12);
|
|
294
|
+
box-shadow:
|
|
295
|
+
0 1px 2px oklch(0 0 0 / 0.06),
|
|
296
|
+
0 4px 12px -2px oklch(0 0 0 / 0.05);
|
|
297
|
+
}
|
|
298
|
+
:root[data-palette="colorful"].dark [data-slot="button"][data-variant="solid"][data-intent="primary"]:not(:hover):not(:disabled),
|
|
299
|
+
:root[data-palette="colorful"].dark [data-slot="button"][data-variant="soft"][data-intent="primary"]:not(:hover):not(:disabled),
|
|
300
|
+
:root[data-palette="colorful"].dark [data-slot="button"][data-variant="shadow"][data-intent="primary"]:not(:hover):not(:disabled) {
|
|
301
|
+
border: 1px solid oklch(1 0 0 / 0.14);
|
|
302
|
+
box-shadow:
|
|
303
|
+
0 1px 2px oklch(0 0 0 / 0.25),
|
|
304
|
+
0 4px 12px -2px oklch(0 0 0 / 0.20);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* Colorful hover bloom — shared across every Button variant when the
|
|
308
|
+
* intent is primary, plus every outline-variant button regardless of
|
|
309
|
+
* intent. Variants targeted here:
|
|
310
|
+
* - outline (all intents): minimal-at-rest is already the variant's
|
|
311
|
+
* resting style; hover swaps the passive tint for the pastel sweep.
|
|
312
|
+
* - solid / soft / ghost / shadow / glass with intent="primary": the
|
|
313
|
+
* filled/tinted resting state is invisible under --primary:transparent,
|
|
314
|
+
* so the gradient is what gives these variants a hover personality.
|
|
315
|
+
* (link stays a pure-text variant — the underline is its own affordance.)
|
|
316
|
+
* Multi-layer box-shadow simulates the diffused warm/cool color aura
|
|
317
|
+
* around the button seen on htgf.de. */
|
|
318
|
+
:root[data-palette="colorful"] [data-slot="button"][data-variant="outline"]:hover:not(:disabled),
|
|
319
|
+
:root[data-palette="colorful"] [data-slot="button"][data-variant="solid"][data-intent="primary"]:hover:not(:disabled),
|
|
320
|
+
:root[data-palette="colorful"] [data-slot="button"][data-variant="soft"][data-intent="primary"]:hover:not(:disabled),
|
|
321
|
+
:root[data-palette="colorful"] [data-slot="button"][data-variant="ghost"][data-intent="primary"]:hover:not(:disabled),
|
|
322
|
+
:root[data-palette="colorful"] [data-slot="button"][data-variant="shadow"][data-intent="primary"]:hover:not(:disabled),
|
|
323
|
+
:root[data-palette="colorful"] [data-slot="button"][data-variant="glass"][data-intent="primary"]:hover:not(:disabled) {
|
|
324
|
+
background-image: var(--colorful-gradient);
|
|
325
|
+
background-color: transparent;
|
|
326
|
+
border-color: transparent;
|
|
327
|
+
color: #1a1a1a;
|
|
328
|
+
box-shadow:
|
|
329
|
+
-8px 0 24px -4px rgba(255, 180, 140, 0.55),
|
|
330
|
+
8px 0 24px -4px rgba(160, 210, 230, 0.55),
|
|
331
|
+
0 4px 16px -2px rgba(220, 180, 230, 0.40);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/* Rest-state safety net: with --primary: transparent, any utility that
|
|
335
|
+
* resolves to var(--primary) (bg-primary, text-primary, border-primary)
|
|
336
|
+
* would render as invisible. Add a neutral fallback only where the
|
|
337
|
+
* components ship with primary-tinted resting surfaces, so icons and
|
|
338
|
+
* chips remain visible until the hover gradient takes over. */
|
|
339
|
+
:root[data-palette="colorful"] [data-slot="feature-card"] > div:first-of-type {
|
|
340
|
+
background-color: oklch(0 0 0 / 0.05);
|
|
341
|
+
color: oklch(0.2 0 0);
|
|
342
|
+
}
|
|
343
|
+
:root[data-palette="colorful"].dark [data-slot="feature-card"] > div:first-of-type {
|
|
344
|
+
background-color: oklch(1 0 0 / 0.08);
|
|
345
|
+
color: oklch(0.95 0 0);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/* Cards: pastel gradient border ring on hover. background-clip trick keeps
|
|
349
|
+
* the inner fill as the card surface and lets only the 1px border bloom
|
|
350
|
+
* into the gradient. Mirrors the same warm-aura glow used on buttons. */
|
|
351
|
+
:root[data-palette="colorful"] [data-slot="feature-card"]:hover,
|
|
352
|
+
:root[data-palette="colorful"] [data-slot="testimonial-card"]:hover,
|
|
353
|
+
:root[data-palette="colorful"] [data-slot="team-card"]:hover,
|
|
354
|
+
:root[data-palette="colorful"] [data-slot="spotlight-card"]:hover {
|
|
355
|
+
border-color: transparent;
|
|
356
|
+
background-image:
|
|
357
|
+
linear-gradient(var(--card), var(--card)),
|
|
358
|
+
var(--colorful-gradient);
|
|
359
|
+
background-origin: border-box;
|
|
360
|
+
background-clip: padding-box, border-box;
|
|
361
|
+
box-shadow:
|
|
362
|
+
-12px 0 32px -6px rgba(255, 180, 140, 0.30),
|
|
363
|
+
12px 0 32px -6px rgba(160, 210, 230, 0.30),
|
|
364
|
+
0 8px 24px -4px rgba(220, 180, 230, 0.25);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/* Feature-card icon chip: soft tint at rest, pastel gradient fill on card hover.
|
|
368
|
+
* Resting style at feature-card.tsx:64 is bg-primary/10 text-primary; this
|
|
369
|
+
* swaps the chip surface to the pastel sweep and keeps the icon dark. */
|
|
370
|
+
:root[data-palette="colorful"] [data-slot="feature-card"]:hover > div:first-of-type {
|
|
371
|
+
background-image: var(--colorful-gradient);
|
|
372
|
+
color: #1a1a1a;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/* ── "On / checked / filled" state identity ──────────────────────────────
|
|
376
|
+
* Every form control that ships with bg-primary as its active-state fill
|
|
377
|
+
* would render invisibly under --primary: transparent. Instead, use the
|
|
378
|
+
* pastel gradient as the brand identity for the active state — switches
|
|
379
|
+
* on, checkboxes checked, radio dots, sliders filled, progress bars,
|
|
380
|
+
* avatar badges, the pricing card's "popular" pill. Resting/empty states
|
|
381
|
+
* fall back to a neutral muted surface so the control is still readable. */
|
|
382
|
+
|
|
383
|
+
/* Switch — checked = gradient, thumb tinted dark for contrast. */
|
|
384
|
+
:root[data-palette="colorful"] [data-slot="switch"][data-state="checked"] {
|
|
385
|
+
background-image: var(--colorful-gradient);
|
|
386
|
+
background-color: transparent;
|
|
387
|
+
}
|
|
388
|
+
:root[data-palette="colorful"] [data-slot="switch"][data-state="checked"] [data-slot="switch-thumb"] {
|
|
389
|
+
background-color: oklch(0.2 0 0);
|
|
390
|
+
}
|
|
391
|
+
:root[data-palette="colorful"].dark [data-slot="switch"][data-state="checked"] [data-slot="switch-thumb"] {
|
|
392
|
+
background-color: oklch(0.98 0 0);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* Checkbox — checked = gradient, indicator (the tick) flips to dark/light. */
|
|
396
|
+
:root[data-palette="colorful"] [data-slot="checkbox"][data-state="checked"] {
|
|
397
|
+
background-image: var(--colorful-gradient);
|
|
398
|
+
background-color: transparent;
|
|
399
|
+
border-color: transparent;
|
|
400
|
+
color: oklch(0.2 0 0);
|
|
401
|
+
}
|
|
402
|
+
:root[data-palette="colorful"].dark [data-slot="checkbox"][data-state="checked"] {
|
|
403
|
+
color: oklch(0.2 0 0);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/* Radio group — checked ring border + indicator dot need a visible color. */
|
|
407
|
+
:root[data-palette="colorful"] [data-slot="radio-group-item"][data-state="checked"] {
|
|
408
|
+
border-color: oklch(0.5 0.15 25);
|
|
409
|
+
}
|
|
410
|
+
:root[data-palette="colorful"] [data-slot="radio-group-item"] {
|
|
411
|
+
color: oklch(0.5 0.15 25);
|
|
412
|
+
}
|
|
413
|
+
:root[data-palette="colorful"].dark [data-slot="radio-group-item"][data-state="checked"] {
|
|
414
|
+
border-color: oklch(0.7 0.15 25);
|
|
415
|
+
}
|
|
416
|
+
:root[data-palette="colorful"].dark [data-slot="radio-group-item"] {
|
|
417
|
+
color: oklch(0.7 0.15 25);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/* Slider — filled range = gradient, thumb gets a visible border. */
|
|
421
|
+
:root[data-palette="colorful"] [data-slot="slider-range"] {
|
|
422
|
+
background-image: var(--colorful-gradient);
|
|
423
|
+
background-color: transparent;
|
|
424
|
+
}
|
|
425
|
+
:root[data-palette="colorful"] [data-slot="slider-thumb"] {
|
|
426
|
+
border-color: oklch(0.5 0.15 25 / 0.6);
|
|
427
|
+
}
|
|
428
|
+
:root[data-palette="colorful"].dark [data-slot="slider-thumb"] {
|
|
429
|
+
border-color: oklch(0.7 0.15 25 / 0.7);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/* Progress — track = soft muted, fill = gradient. */
|
|
433
|
+
:root[data-palette="colorful"] [data-slot="progress"] {
|
|
434
|
+
background-color: oklch(0 0 0 / 0.08);
|
|
435
|
+
}
|
|
436
|
+
:root[data-palette="colorful"].dark [data-slot="progress"] {
|
|
437
|
+
background-color: oklch(1 0 0 / 0.12);
|
|
438
|
+
}
|
|
439
|
+
:root[data-palette="colorful"] [data-slot="progress-indicator"] {
|
|
440
|
+
background-image: var(--colorful-gradient);
|
|
441
|
+
background-color: transparent;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/* Avatar status badge — solid dark/light dot since the surface is tiny
|
|
445
|
+
* and a gradient on it would be visually noisy at this scale. */
|
|
446
|
+
:root[data-palette="colorful"] [data-slot="avatar-badge"] {
|
|
447
|
+
background-color: oklch(0.2 0 0);
|
|
448
|
+
color: oklch(0.98 0 0);
|
|
449
|
+
}
|
|
450
|
+
:root[data-palette="colorful"].dark [data-slot="avatar-badge"] {
|
|
451
|
+
background-color: oklch(0.98 0 0);
|
|
452
|
+
color: oklch(0.2 0 0);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/* Pricing card "Popular" pill — the only badge that uses bg-primary
|
|
456
|
+
* directly. Render as the pastel gradient pill with dark text. */
|
|
457
|
+
:root[data-palette="colorful"] [data-slot="pricing-card"] > div:first-child {
|
|
458
|
+
background-image: var(--colorful-gradient);
|
|
459
|
+
background-color: transparent;
|
|
460
|
+
color: oklch(0.2 0 0);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/* Tooltip — bg-primary text-primary-foreground would be invisible. A
|
|
464
|
+
* dark slate surface reads cleanly without needing the gradient
|
|
465
|
+
* (gradients on tiny tooltip surfaces look noisy at this scale). */
|
|
466
|
+
:root[data-palette="colorful"] [data-slot="tooltip-content"] {
|
|
467
|
+
background-color: oklch(0.2 0 0);
|
|
468
|
+
color: oklch(0.98 0 0);
|
|
469
|
+
}
|
|
470
|
+
:root[data-palette="colorful"].dark [data-slot="tooltip-content"] {
|
|
471
|
+
background-color: oklch(0.98 0 0);
|
|
472
|
+
color: oklch(0.2 0 0);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/* Calendar — selected single, range-start, range-end days use bg-primary.
|
|
476
|
+
* Promote the selection to the pastel gradient so it stays consistent
|
|
477
|
+
* with switches / progress / checkboxes. */
|
|
478
|
+
:root[data-palette="colorful"] [data-slot="calendar"] [data-selected-single="true"],
|
|
479
|
+
:root[data-palette="colorful"] [data-slot="calendar"] [data-range-start="true"],
|
|
480
|
+
:root[data-palette="colorful"] [data-slot="calendar"] [data-range-end="true"] {
|
|
481
|
+
background-image: var(--colorful-gradient);
|
|
482
|
+
background-color: transparent;
|
|
483
|
+
color: oklch(0.2 0 0);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* Scroll-to-top button — pinned floating action that uses bg-primary as
|
|
487
|
+
* its filled surface. Promote to gradient + dark icon. */
|
|
488
|
+
:root[data-palette="colorful"] [data-slot="scroll-to-top-button"],
|
|
489
|
+
:root[data-palette="colorful"] [data-slot="scroll-to-top"] {
|
|
490
|
+
background-image: var(--colorful-gradient);
|
|
491
|
+
background-color: transparent;
|
|
492
|
+
color: oklch(0.2 0 0);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/* Generic text-primary safety net — Tailwind's text-primary utility
|
|
496
|
+
* resolves to var(--primary), which is transparent under this palette
|
|
497
|
+
* and would render invisible text wherever it's used directly (link
|
|
498
|
+
* hovers in <Empty>, custom prose, downstream apps, etc.). Route it
|
|
499
|
+
* through --foreground so the text stays readable. Specific data-slot
|
|
500
|
+
* rules above still win for active states that need the gradient. */
|
|
501
|
+
:root[data-palette="colorful"] .text-primary {
|
|
502
|
+
color: var(--foreground);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/* ─────────────────────────────────────────────────────────────────────
|
|
506
|
+
* Audit pass — close remaining utility + intent gaps so no element
|
|
507
|
+
* renders invisibly under --primary: transparent. See the audit table
|
|
508
|
+
* in the plan file for the per-component reasoning.
|
|
509
|
+
* ───────────────────────────────────────────────────────────────────── */
|
|
510
|
+
|
|
511
|
+
/* .border-primary — Steps circles, Card hover ring, Field checked,
|
|
512
|
+
* Timeline dots, PricingCard featured border. */
|
|
513
|
+
:root[data-palette="colorful"] .border-primary {
|
|
514
|
+
border-color: oklch(0.5 0.15 25 / 0.4);
|
|
515
|
+
}
|
|
516
|
+
:root[data-palette="colorful"].dark .border-primary {
|
|
517
|
+
border-color: oklch(0.7 0.15 25 / 0.5);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/* .fill-primary — inline SVG fills (e.g. icons.tsx success glyph). */
|
|
521
|
+
:root[data-palette="colorful"] .fill-primary {
|
|
522
|
+
fill: var(--foreground);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/* Text-selection color — input / textarea / native-select apply
|
|
526
|
+
* selection:bg-primary selection:text-primary-foreground, which is
|
|
527
|
+
* invisible without an explicit color. Use a soft warm-tinted highlight. */
|
|
528
|
+
:root[data-palette="colorful"] ::selection {
|
|
529
|
+
background-color: oklch(0.85 0.10 25 / 0.5);
|
|
530
|
+
color: oklch(0.2 0 0);
|
|
531
|
+
}
|
|
532
|
+
:root[data-palette="colorful"].dark ::selection {
|
|
533
|
+
background-color: oklch(0.55 0.12 25 / 0.55);
|
|
534
|
+
color: oklch(0.98 0 0);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/* PricingCard featured: ring-primary/20 uses --color-primary and goes
|
|
538
|
+
* invisible. Restore the elevated featured look with a soft pastel
|
|
539
|
+
* ring + ambient shadow. Targets the .border-primary class the
|
|
540
|
+
* component applies when featured (pricing-card.tsx:100). */
|
|
541
|
+
:root[data-palette="colorful"] [data-slot="pricing-card"].border-primary {
|
|
542
|
+
box-shadow:
|
|
543
|
+
0 1px 2px oklch(0 0 0 / 0.06),
|
|
544
|
+
0 0 0 1px oklch(0.5 0.15 25 / 0.25),
|
|
545
|
+
0 8px 24px -4px oklch(0.5 0.15 25 / 0.10);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/* Typewriter cursor — 2px wide blinking caret. The faint gradient is
|
|
549
|
+
* invisible at this scale; solid foreground keeps the cursor readable. */
|
|
550
|
+
:root[data-palette="colorful"] [data-slot="typewriter-text"] .bg-primary,
|
|
551
|
+
:root[data-palette="colorful"] .animate-pulse.bg-primary {
|
|
552
|
+
background-color: var(--foreground);
|
|
553
|
+
background-image: none;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/* Alert primary intent — all three derived utilities (bg/text/border)
|
|
557
|
+
* resolve through transparent --intent. Replace with a coherent
|
|
558
|
+
* gradient-tinted surface that reads as the primary-intent tone. */
|
|
559
|
+
:root[data-palette="colorful"] [data-slot="alert"][data-intent="primary"] {
|
|
560
|
+
background-image: var(--colorful-gradient-faint);
|
|
561
|
+
background-color: transparent;
|
|
562
|
+
border-color: oklch(0.5 0.15 25 / 0.3);
|
|
563
|
+
color: var(--foreground);
|
|
564
|
+
}
|
|
565
|
+
:root[data-palette="colorful"] [data-slot="alert"][data-intent="primary"] [data-slot="alert-description"] {
|
|
566
|
+
color: oklch(0.4 0 0);
|
|
567
|
+
}
|
|
568
|
+
:root[data-palette="colorful"].dark [data-slot="alert"][data-intent="primary"] [data-slot="alert-description"] {
|
|
569
|
+
color: oklch(0.75 0 0);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/* Badge primary intent — uses the full hover-color gradient
|
|
573
|
+
* (--colorful-gradient: peach → blush → lavender → cyan) as the brand
|
|
574
|
+
* identity, matching what the buttons + cards bloom into on hover.
|
|
575
|
+
* - solid: full gradient pill + dark text + soft drop shadow
|
|
576
|
+
* - soft: faint version of the same gradient + foreground text
|
|
577
|
+
* - outline: gradient border ring (background-clip trick) + dark text
|
|
578
|
+
* All three carry the same identity so the user can pick the weight
|
|
579
|
+
* (heavy / soft / outline) without losing the "colorful" feel. */
|
|
580
|
+
|
|
581
|
+
:root[data-palette="colorful"] [data-slot="badge"][data-intent="primary"][data-variant="solid"] {
|
|
582
|
+
/* Mid-alpha pastel sweep (~60%) — quieter than the full gradient
|
|
583
|
+
* used on buttons, so the badge doesn't compete with surrounding
|
|
584
|
+
* content. Dark mode keeps the full gradient since the dark canvas
|
|
585
|
+
* already absorbs intensity. */
|
|
586
|
+
background-image: linear-gradient(95deg,
|
|
587
|
+
rgba(255, 212, 163, 0.60) 0%,
|
|
588
|
+
rgba(255, 184, 197, 0.60) 35%,
|
|
589
|
+
rgba(212, 188, 232, 0.60) 65%,
|
|
590
|
+
rgba(184, 216, 229, 0.60) 100%);
|
|
591
|
+
background-color: transparent;
|
|
592
|
+
color: oklch(0.2 0 0);
|
|
593
|
+
border-color: transparent;
|
|
594
|
+
/* Warm-left / cool-right color aura, also softened for light mode. */
|
|
595
|
+
box-shadow:
|
|
596
|
+
inset 0 1px 0 oklch(1 0 0 / 0.40),
|
|
597
|
+
-6px 0 18px -4px rgba(255, 180, 140, 0.35),
|
|
598
|
+
6px 0 18px -4px rgba(160, 210, 230, 0.35),
|
|
599
|
+
0 3px 12px -2px rgba(220, 180, 230, 0.25);
|
|
600
|
+
letter-spacing: 0.01em;
|
|
601
|
+
}
|
|
602
|
+
:root[data-palette="colorful"].dark [data-slot="badge"][data-intent="primary"][data-variant="solid"] {
|
|
603
|
+
color: oklch(0.2 0 0);
|
|
604
|
+
box-shadow:
|
|
605
|
+
inset 0 1px 0 oklch(1 0 0 / 0.25),
|
|
606
|
+
-6px 0 18px -4px rgba(255, 180, 140, 0.35),
|
|
607
|
+
6px 0 18px -4px rgba(160, 210, 230, 0.35),
|
|
608
|
+
0 3px 12px -2px rgba(220, 180, 230, 0.25);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
:root[data-palette="colorful"] [data-slot="badge"][data-intent="primary"][data-variant="soft"] {
|
|
612
|
+
background-image: var(--colorful-gradient-faint);
|
|
613
|
+
background-color: transparent;
|
|
614
|
+
color: var(--foreground);
|
|
615
|
+
border-color: oklch(0.5 0.12 25 / 0.18);
|
|
616
|
+
letter-spacing: 0.01em;
|
|
617
|
+
}
|
|
618
|
+
:root[data-palette="colorful"].dark [data-slot="badge"][data-intent="primary"][data-variant="soft"] {
|
|
619
|
+
border-color: oklch(0.7 0.10 25 / 0.20);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
:root[data-palette="colorful"] [data-slot="badge"][data-intent="primary"][data-variant="outline"] {
|
|
623
|
+
background-image:
|
|
624
|
+
linear-gradient(var(--background), var(--background)),
|
|
625
|
+
var(--colorful-gradient);
|
|
626
|
+
background-origin: border-box;
|
|
627
|
+
background-clip: padding-box, border-box;
|
|
628
|
+
border-color: transparent;
|
|
629
|
+
color: var(--foreground);
|
|
630
|
+
letter-spacing: 0.01em;
|
|
631
|
+
}
|
package/styles/surfaces.css
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Surface system — flat / glass as semantic overlay.
|
|
2
|
+
* @fileoverview Surface system — flat / glass / clay as semantic overlay.
|
|
3
3
|
* @module packages/ui/styles/surfaces
|
|
4
4
|
* @package ui
|
|
5
|
-
* @reviewed 2026-
|
|
5
|
+
* @reviewed 2026-05-26
|
|
6
6
|
*
|
|
7
7
|
* Components bind to four surface tokens:
|
|
8
|
-
* --surface-bg background color or gradient
|
|
8
|
+
* --surface-bg background color or gradient (resolved via `background:`)
|
|
9
9
|
* --surface-border border color
|
|
10
10
|
* --surface-backdrop backdrop-filter value (blur/saturate) or "none"
|
|
11
|
-
* --surface-shadow box-shadow value
|
|
11
|
+
* --surface-shadow box-shadow value (may stack multiple layers)
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Two attributes activate the surface tokens:
|
|
14
|
+
* - [data-style="…"] — used on <html> by SaasflareShell (page-wide default)
|
|
15
|
+
* - [data-surface="…"] — used on individual components for per-element override
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
+
* Both feed the same rule blocks so swapping at either level swaps the tokens
|
|
18
|
+
* without touching any component. A component-level `data-surface` re-defines
|
|
19
|
+
* the tokens for its subtree, overriding the cascaded page-wide value — that's
|
|
20
|
+
* how `<Button surface="glass">` wins over `<html data-style="flat">`.
|
|
17
21
|
*
|
|
22
|
+
* Extend by adding a new selector block:
|
|
23
|
+
* [data-style="neumorphic"], [data-surface="neumorphic"] { --surface-bg: …; … }
|
|
18
24
|
* and registering the id in the StyleVariant union (types.ts).
|
|
19
25
|
*/
|
|
20
26
|
|
|
@@ -25,14 +31,16 @@
|
|
|
25
31
|
--surface-shadow: 0 1px 2px oklch(0 0 0 / 0.05);
|
|
26
32
|
}
|
|
27
33
|
|
|
28
|
-
[data-style="flat"]
|
|
34
|
+
[data-style="flat"],
|
|
35
|
+
[data-surface="flat"] {
|
|
29
36
|
--surface-bg: var(--card);
|
|
30
37
|
--surface-border: var(--border);
|
|
31
38
|
--surface-backdrop: none;
|
|
32
39
|
--surface-shadow: 0 1px 2px oklch(0 0 0 / 0.05);
|
|
33
40
|
}
|
|
34
41
|
|
|
35
|
-
[data-style="glass"]
|
|
42
|
+
[data-style="glass"],
|
|
43
|
+
[data-surface="glass"] {
|
|
36
44
|
--surface-bg: oklch(0.99 0.005 var(--neutral-h) / 0.6);
|
|
37
45
|
--surface-border: oklch(1 0 0 / 0.2);
|
|
38
46
|
--surface-backdrop: blur(12px) saturate(140%);
|
|
@@ -42,10 +50,81 @@
|
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
[data-style="glass"].dark,
|
|
45
|
-
.dark [data-style="glass"]
|
|
53
|
+
.dark [data-style="glass"],
|
|
54
|
+
[data-surface="glass"].dark,
|
|
55
|
+
.dark [data-surface="glass"] {
|
|
46
56
|
--surface-bg: oklch(0.2 0.015 var(--neutral-h) / 0.5);
|
|
47
57
|
--surface-border: oklch(1 0 0 / 0.1);
|
|
48
58
|
--surface-shadow:
|
|
49
59
|
0 8px 32px oklch(0 0 0 / 0.4),
|
|
50
60
|
inset 0 1px 0 oklch(1 0 0 / 0.08);
|
|
51
61
|
}
|
|
62
|
+
|
|
63
|
+
/* ============================================
|
|
64
|
+
* Clay — palette-agnostic pillow finish. Color comes from the active palette
|
|
65
|
+
* via --card (surface base) and --accent (shadow tint). Four-layer shadow
|
|
66
|
+
* stack creates the 3D pillow effect:
|
|
67
|
+
* 1. Outer drop — accent-tinted soft elevation
|
|
68
|
+
* 2. Outer ambient — neutral contact shadow
|
|
69
|
+
* 3. Inner top — bright highlight, like top-lit edge
|
|
70
|
+
* 4. Inner bottom — accent-tinted shade, like underside
|
|
71
|
+
* Background is a vertical micro-gradient so the surface feels rounded rather
|
|
72
|
+
* than printed-flat. NO transparency, NO backdrop-filter.
|
|
73
|
+
* ============================================ */
|
|
74
|
+
[data-style="clay"],
|
|
75
|
+
[data-surface="clay"] {
|
|
76
|
+
--surface-bg: linear-gradient(
|
|
77
|
+
180deg,
|
|
78
|
+
oklch(from var(--card) calc(l + 0.02) c h),
|
|
79
|
+
oklch(from var(--card) calc(l - 0.02) c h)
|
|
80
|
+
);
|
|
81
|
+
--surface-border: transparent;
|
|
82
|
+
--surface-backdrop: none;
|
|
83
|
+
--surface-shadow:
|
|
84
|
+
0 8px 24px -8px oklch(from var(--accent) 0.4 c h / 0.35),
|
|
85
|
+
0 2px 4px oklch(0 0 0 / 0.06),
|
|
86
|
+
inset 0 2px 3px oklch(1 0 0 / 0.6),
|
|
87
|
+
inset 0 -2px 4px oklch(from var(--accent) 0.3 c h / 0.15);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
[data-style="clay"].dark,
|
|
91
|
+
.dark [data-style="clay"],
|
|
92
|
+
[data-surface="clay"].dark,
|
|
93
|
+
.dark [data-surface="clay"] {
|
|
94
|
+
--surface-bg: linear-gradient(
|
|
95
|
+
180deg,
|
|
96
|
+
oklch(from var(--card) calc(l + 0.025) c h),
|
|
97
|
+
oklch(from var(--card) calc(l - 0.015) c h)
|
|
98
|
+
);
|
|
99
|
+
--surface-border: transparent;
|
|
100
|
+
--surface-backdrop: none;
|
|
101
|
+
--surface-shadow:
|
|
102
|
+
0 8px 24px -8px oklch(from var(--accent) 0.5 c h / 0.4),
|
|
103
|
+
0 2px 4px oklch(0 0 0 / 0.3),
|
|
104
|
+
inset 0 1px 0 oklch(1 0 0 / 0.06),
|
|
105
|
+
inset 0 -2px 4px oklch(from var(--accent) 0.4 c h / 0.18);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* ============================================
|
|
109
|
+
* Surface utility classes — let any component opt into the active surface
|
|
110
|
+
* tokens with one className, no useSaasflareProps roundtrip required.
|
|
111
|
+
*
|
|
112
|
+
* Use `.surface-card` on the root of card-like elements (FeatureCard,
|
|
113
|
+
* TestimonialCard, StatCard, …). The class consumes whichever
|
|
114
|
+
* `--surface-*` values are currently active in the cascade — `flat`
|
|
115
|
+
* defaults from :root, `glass` overrides from <html data-style="glass">,
|
|
116
|
+
* or a local `data-surface` attribute on an ancestor.
|
|
117
|
+
*
|
|
118
|
+
* Pair with Tailwind `border` to make `--surface-border` visible. Border
|
|
119
|
+
* width is intentionally not baked in so consumers can pick `border`,
|
|
120
|
+
* `border-2`, or `border-0` per design.
|
|
121
|
+
* ============================================ */
|
|
122
|
+
@layer utilities {
|
|
123
|
+
.surface-card {
|
|
124
|
+
background: var(--surface-bg);
|
|
125
|
+
border-color: var(--surface-border);
|
|
126
|
+
backdrop-filter: var(--surface-backdrop);
|
|
127
|
+
-webkit-backdrop-filter: var(--surface-backdrop);
|
|
128
|
+
box-shadow: var(--surface-shadow);
|
|
129
|
+
}
|
|
130
|
+
}
|
package/styles/theme.css
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
@import "tailwindcss";
|
|
27
27
|
@import "./motion.css";
|
|
28
28
|
@import "./surfaces.css";
|
|
29
|
+
@import "./aurora.css";
|
|
29
30
|
@import "./palettes.css";
|
|
30
31
|
|
|
31
32
|
/* Tailwind v4 source discovery — scan the package itself so consumers
|
|
@@ -330,20 +331,25 @@ textarea,
|
|
|
330
331
|
}
|
|
331
332
|
|
|
332
333
|
/* ============================================
|
|
333
|
-
* Radius axis — discrete preset switched via [data-radius]
|
|
334
|
+
* Radius axis — discrete preset switched via [data-radius].
|
|
334
335
|
*
|
|
335
336
|
* Baseline default is "rounded" (set in :root above as --radius: 0.625rem).
|
|
336
337
|
* "pill" overrides the entire derived scale explicitly: relying on calc()
|
|
337
338
|
* would give 9995–10003px for sm/lg, which is visually pill on tall elements
|
|
338
339
|
* but NOT pill on a 200px-wide component. Lock the whole scale instead.
|
|
339
340
|
*
|
|
341
|
+
* The selectors deliberately drop the `:root` prefix so a per-component
|
|
342
|
+
* `data-radius` attribute (e.g. `<Button radius="pill">`) creates its own
|
|
343
|
+
* cascade scope and overrides the inherited page-wide value. Without that
|
|
344
|
+
* scope a component-level `radius` prop would have no visible effect.
|
|
345
|
+
*
|
|
340
346
|
* Custom per-palette override (CustomPalette.radius) sets --radius inline
|
|
341
347
|
* and wins by specificity — intentional escape hatch.
|
|
342
348
|
* ============================================ */
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
349
|
+
[data-radius="sharp"] { --radius: 0; }
|
|
350
|
+
[data-radius="soft"] { --radius: 0.35rem; }
|
|
351
|
+
[data-radius="rounded"] { --radius: 0.625rem; }
|
|
352
|
+
[data-radius="pill"] {
|
|
347
353
|
--radius: 9999px;
|
|
348
354
|
--radius-sm: 9999px;
|
|
349
355
|
--radius-md: 9999px;
|