@bearmenu/ui 0.8.2 → 0.8.3
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/package.json +1 -1
- package/src/globals.css +162 -40
package/package.json
CHANGED
package/src/globals.css
CHANGED
|
@@ -33,34 +33,77 @@ h6 {
|
|
|
33
33
|
--sab: env(safe-area-inset-bottom);
|
|
34
34
|
--sal: env(safe-area-inset-left);
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
/* ─── Light neutral ramp (0.8.3 — "warmth has edges") ─────────────────
|
|
37
|
+
Every surface and text token below is TRUE NEUTRAL: chroma exactly 0,
|
|
38
|
+
8-bit R−B spread exactly 0. Grey. Not "warm grey", not "off-white" —
|
|
39
|
+
grey. That is deliberate and hard-won; read this before adding a tint.
|
|
40
|
+
|
|
41
|
+
These used to carry chroma 0.008-0.025 at hue 45-80, on the theory that a
|
|
42
|
+
faint warm tint everywhere expressed a warm brand. In practice a low-chroma
|
|
43
|
+
tint spread across large soft areas — page, chip fills, hover fills,
|
|
44
|
+
hairlines, shadows — does not read as warmth. It reads as a colour cast:
|
|
45
|
+
"tan", "peachy", "hurts my eyes", "unprofessional for a serious app".
|
|
46
|
+
|
|
47
|
+
It was walked down in stages — 0.012 → 0.005 → 0.0025 → 0.003 → 0 — and
|
|
48
|
+
was still being rejected at chroma 0.0025, an R−B spread of THREE, which
|
|
49
|
+
is about as small as an 8-bit tint gets. That is the finding: on a large
|
|
50
|
+
light field there is no amount of warm chroma small enough to be safe.
|
|
51
|
+
Any nonzero value at hue 45-80 eventually gets described as brown.
|
|
52
|
+
|
|
53
|
+
The rule that replaces it: warmth needs an EDGE and real saturation. It
|
|
54
|
+
lives in --accent, --gradient-primary, badges, CTAs and photography —
|
|
55
|
+
bounded things at chroma >= 0.09. Never in a fill, a blur or body text.
|
|
56
|
+
The contrast between a neutral field and a saturated accent is what makes
|
|
57
|
+
the accent read as intentional. Do not re-warm these. */
|
|
58
|
+
--background: oklch(0.986 0 0);
|
|
59
|
+
--foreground: oklch(0.18 0 0);
|
|
60
|
+
--card: oklch(1 0 0);
|
|
61
|
+
--card-foreground: oklch(0.18 0 0);
|
|
62
|
+
--popover: oklch(1 0 0);
|
|
63
|
+
--popover-foreground: oklch(0.18 0 0);
|
|
42
64
|
--primary: oklch(0.25 0.05 35);
|
|
43
65
|
--primary-foreground: oklch(0.98 0 0);
|
|
44
|
-
--secondary: oklch(0.
|
|
45
|
-
--secondary-foreground: oklch(0.
|
|
46
|
-
--muted: oklch(0.
|
|
47
|
-
--muted-foreground: oklch(0.
|
|
48
|
-
--accent
|
|
49
|
-
|
|
66
|
+
--secondary: oklch(0.955 0 0);
|
|
67
|
+
--secondary-foreground: oklch(0.18 0 0);
|
|
68
|
+
--muted: oklch(0.955 0 0);
|
|
69
|
+
--muted-foreground: oklch(0.52 0 0);
|
|
70
|
+
/* BREAKING in 0.8.3 — see CHANGELOG. --accent was oklch(0.94 0.04 50), a
|
|
71
|
+
pale peach FILL paired with a dark --accent-foreground. It is now the
|
|
72
|
+
brand terracotta, used as text and as a filled surface with WHITE
|
|
73
|
+
foreground. A consumer rendering `bg-accent text-accent-foreground` gets a
|
|
74
|
+
dark brown chip where it used to get a pale one. bearmenu-fe already ran
|
|
75
|
+
these values as a local override; promoting them stops Storybook and the
|
|
76
|
+
product disagreeing about what "accent" means. */
|
|
77
|
+
--accent: oklch(0.51 0.095 45);
|
|
78
|
+
--accent-foreground: oklch(1 0 0);
|
|
79
|
+
/* Recessed-well companion to --accent: a tinted surface for active chips,
|
|
80
|
+
wells and highlighted rows. Neutral on purpose — its call sites pair it
|
|
81
|
+
with `text-accent`, and the text is what carries the meaning. */
|
|
82
|
+
--accent-subtle: oklch(0.945 0 0);
|
|
50
83
|
--destructive: oklch(0.577 0.245 27.325);
|
|
51
84
|
--destructive-foreground: oklch(0.985 0 0);
|
|
52
|
-
--border: oklch(0.
|
|
53
|
-
--input: oklch(0.
|
|
85
|
+
--border: oklch(0.895 0 0);
|
|
86
|
+
--input: oklch(0.895 0 0);
|
|
54
87
|
--ring: oklch(0.25 0.05 35);
|
|
55
88
|
--chart-1: oklch(0.646 0.222 41.116);
|
|
56
89
|
--chart-2: oklch(0.6 0.118 184.704);
|
|
57
90
|
--chart-3: oklch(0.398 0.07 227.392);
|
|
58
91
|
--chart-4: oklch(0.828 0.189 84.429);
|
|
59
92
|
--chart-5: oklch(0.769 0.188 70.08);
|
|
60
|
-
--surface: oklch(0
|
|
93
|
+
--surface: oklch(1 0 0);
|
|
61
94
|
--surface-foreground: var(--foreground);
|
|
62
|
-
--surface-2: oklch(0.
|
|
63
|
-
--line-soft: oklch(0.
|
|
95
|
+
--surface-2: oklch(0.968 0 0);
|
|
96
|
+
--line-soft: oklch(0.935 0 0);
|
|
97
|
+
/* The top plane. In light mode white IS the ceiling — "elevated" means card
|
|
98
|
+
+ shadow, there is nothing above it to reach for — so this equals --card.
|
|
99
|
+
Dark reaches the same idea from below (see .dark). The asymmetry is
|
|
100
|
+
correct; do not "fix" the two into agreement. */
|
|
101
|
+
--surface-elevated: oklch(1 0 0);
|
|
102
|
+
/* Text tiers. --ink-secondary and --ink-tertiary sit between --foreground
|
|
103
|
+
and --muted-foreground for captions, counts and metadata. */
|
|
104
|
+
--ink-primary: var(--foreground);
|
|
105
|
+
--ink-secondary: oklch(0.45 0 0);
|
|
106
|
+
--ink-tertiary: oklch(0.52 0 0);
|
|
64
107
|
--radius: 0.625rem;
|
|
65
108
|
--sidebar: oklch(0.985 0 0);
|
|
66
109
|
--sidebar-foreground: oklch(0.145 0 0);
|
|
@@ -75,6 +118,26 @@ h6 {
|
|
|
75
118
|
--warning: oklch(0.72 0.18 68);
|
|
76
119
|
--warning-foreground: oklch(0.18 0.04 45);
|
|
77
120
|
|
|
121
|
+
/* ─── Brand gradient ──────────────────────────────────────────────────
|
|
122
|
+
The terracotta ramp used by the FAB, primary buttons, banner CTAs and
|
|
123
|
+
featured surfaces. This — not any surface tint — is where the brand's
|
|
124
|
+
warmth lives: chroma 0.09-0.10, on bounded elements. */
|
|
125
|
+
--gradient-primary: linear-gradient(180deg, oklch(0.58 0.1 45) 0%, oklch(0.44 0.09 42) 100%);
|
|
126
|
+
--gradient-primary-from: oklch(0.58 0.1 45);
|
|
127
|
+
--gradient-primary-to: oklch(0.44 0.09 42);
|
|
128
|
+
|
|
129
|
+
/* ─── Generic elevation ───────────────────────────────────────────────
|
|
130
|
+
Pure neutral (chroma 0) at L 0.52. These were a saturated brown, which put
|
|
131
|
+
a soft-edged tan patch under every card, row and overlay — the single
|
|
132
|
+
largest warm area on a light page, and the hardest to attribute because a
|
|
133
|
+
blur has no boundary to blame it on. Alphas are the calibrated set; only
|
|
134
|
+
the tint changed. Apps compose these; they must not re-tint them. */
|
|
135
|
+
--shadow-card: 0 1px 2px oklch(0.52 0 0 / 0.05), 0 2px 6px -1px oklch(0.52 0 0 / 0.08);
|
|
136
|
+
--shadow-card-hover:
|
|
137
|
+
0 2px 4px -1px oklch(0.52 0 0 / 0.07), 0 10px 24px -6px oklch(0.52 0 0 / 0.16);
|
|
138
|
+
--shadow-overlay:
|
|
139
|
+
0 4px 12px -2px oklch(0.52 0 0 / 0.1), 0 16px 48px -12px oklch(0.52 0 0 / 0.2);
|
|
140
|
+
|
|
78
141
|
/* Brown variant — replaces hardcoded #482E1F across button/badge */
|
|
79
142
|
--brown: oklch(0.31 0.05 45);
|
|
80
143
|
--brown-foreground: oklch(0.985 0 0);
|
|
@@ -90,10 +153,18 @@ h6 {
|
|
|
90
153
|
--shadow-alpha-2xl: 0.25;
|
|
91
154
|
|
|
92
155
|
/* ─── Card cover shadows (companion to lib/card-hover.ts) ─────────────
|
|
93
|
-
The
|
|
156
|
+
The lift under a card's cover region, and its deepened hover state.
|
|
94
157
|
Deliberately separate from the --shadow-alpha-* ladder above: that is a
|
|
95
|
-
|
|
96
|
-
|
|
158
|
+
pure-black ramp for surfaces, this is a softer, lighter cast tuned for
|
|
159
|
+
photography and generated cover art.
|
|
160
|
+
|
|
161
|
+
0.8.3: was rgba(120, 80, 50, …) — an 8-bit R−B spread of 70, by far the
|
|
162
|
+
most saturated shadow in the system, compositing to spread 18-23 under
|
|
163
|
+
every card cover. It survived an entire light-theme neutralisation pass
|
|
164
|
+
because it lives here and nothing overrode it. Now oklch(0.45 0 0),
|
|
165
|
+
matched on LUMINANCE (0.091 vs the old 0.100) so the visual weight is
|
|
166
|
+
unchanged, with the tint gone entirely. Same reasoning as the light ramp
|
|
167
|
+
above: a wide blur cannot hold a tint without reading as a stain.
|
|
97
168
|
|
|
98
169
|
--shadow-menu-cover-inset is the pressed-in "well" edge carried by covers
|
|
99
170
|
that render a generated illustration instead of a photo. It has to live in
|
|
@@ -104,8 +175,8 @@ h6 {
|
|
|
104
175
|
Consumer apps must not redefine these: their stylesheet is imported after
|
|
105
176
|
this one and would silently win, which is the drift these tokens exist to
|
|
106
177
|
end. */
|
|
107
|
-
--shadow-card-image: 0 4px 14px
|
|
108
|
-
--shadow-card-image-hover: 0 12px 28px
|
|
178
|
+
--shadow-card-image: 0 4px 14px oklch(0.45 0 0 / 0.22);
|
|
179
|
+
--shadow-card-image-hover: 0 12px 28px oklch(0.45 0 0 / 0.3);
|
|
109
180
|
--shadow-menu-cover-inset:
|
|
110
181
|
inset 0 1px 2px oklch(1 0 0 / 0.15), inset 0 -1px 4px oklch(0 0 0 / 0.18);
|
|
111
182
|
|
|
@@ -148,35 +219,52 @@ h6 {
|
|
|
148
219
|
}
|
|
149
220
|
|
|
150
221
|
.dark {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
--
|
|
156
|
-
--
|
|
157
|
-
--
|
|
158
|
-
--
|
|
222
|
+
/* Dark keeps its warmth: on a dark canvas a low-chroma warm tint reads as
|
|
223
|
+
"espresso", not as a cast — the light-mode problem is a light-mode
|
|
224
|
+
problem. These are bearmenu-fe's shipped dark values, promoted in 0.8.3
|
|
225
|
+
alongside the light ramp so both themes have one source of truth. */
|
|
226
|
+
--background: oklch(0.155 0.01 45);
|
|
227
|
+
--foreground: oklch(0.96 0.005 60);
|
|
228
|
+
--card: oklch(0.205 0.01 45);
|
|
229
|
+
--card-foreground: oklch(0.96 0.005 60);
|
|
230
|
+
--popover: oklch(0.205 0.01 45);
|
|
231
|
+
--popover-foreground: oklch(0.96 0.005 60);
|
|
232
|
+
--primary: oklch(0.66 0.09 45);
|
|
233
|
+
--primary-foreground: oklch(0.155 0.01 45);
|
|
159
234
|
--secondary: oklch(0.24 0.018 45);
|
|
160
|
-
--secondary-foreground: oklch(0.
|
|
161
|
-
--muted: oklch(0.
|
|
162
|
-
--muted-foreground: oklch(0.
|
|
163
|
-
--accent
|
|
164
|
-
--accent
|
|
235
|
+
--secondary-foreground: oklch(0.96 0.005 60);
|
|
236
|
+
--muted: oklch(0.22 0.01 45);
|
|
237
|
+
--muted-foreground: oklch(0.72 0.008 60);
|
|
238
|
+
/* BREAKING alongside light: --accent is the terracotta, not a dark tint. */
|
|
239
|
+
--accent: oklch(0.66 0.09 45);
|
|
240
|
+
--accent-foreground: oklch(0.155 0.01 45);
|
|
241
|
+
--accent-subtle: oklch(0.255 0.022 45);
|
|
165
242
|
/* AA-compliant on dark surfaces but dialed back from 0.68 to soften eye strain */
|
|
166
243
|
--destructive: oklch(0.58 0.17 25.7);
|
|
167
244
|
--destructive-foreground: oklch(0.985 0 0);
|
|
168
|
-
--border: oklch(0.
|
|
169
|
-
--input: oklch(0.
|
|
245
|
+
--border: oklch(0.32 0.01 45);
|
|
246
|
+
--input: oklch(0.32 0.01 45);
|
|
170
247
|
--ring: oklch(0.98 0 0);
|
|
171
248
|
--chart-1: oklch(0.488 0.243 264.376);
|
|
172
249
|
--chart-2: oklch(0.696 0.17 162.48);
|
|
173
250
|
--chart-3: oklch(0.769 0.188 70.08);
|
|
174
251
|
--chart-4: oklch(0.627 0.265 303.9);
|
|
175
252
|
--chart-5: oklch(0.645 0.246 16.439);
|
|
176
|
-
--surface: oklch(0.
|
|
253
|
+
--surface: oklch(0.205 0.01 45);
|
|
177
254
|
--surface-foreground: var(--foreground);
|
|
178
255
|
--surface-2: oklch(0.24 0.018 45);
|
|
179
256
|
--line-soft: oklch(0.26 0.014 45);
|
|
257
|
+
/* Dark reaches "top plane" from BELOW — a step up off the canvas — where
|
|
258
|
+
light reaches it by being white. Same token, opposite direction. */
|
|
259
|
+
--surface-elevated: oklch(0.225 0.012 45);
|
|
260
|
+
--ink-primary: var(--foreground);
|
|
261
|
+
--ink-secondary: oklch(0.78 0.005 60);
|
|
262
|
+
--ink-tertiary: oklch(0.6 0.006 60);
|
|
263
|
+
/* Black, not the light ramp's near-neutral grey: on a 0.155 canvas a grey
|
|
264
|
+
shadow has nothing to darken. */
|
|
265
|
+
--shadow-card: 0 1px 3px oklch(0 0 0 / 0.2), 0 1px 2px oklch(0 0 0 / 0.15);
|
|
266
|
+
--shadow-card-hover: 0 4px 16px oklch(0 0 0 / 0.3), 0 1px 4px oklch(0 0 0 / 0.2);
|
|
267
|
+
--shadow-overlay: 0 8px 32px oklch(0 0 0 / 0.4), 0 2px 8px oklch(0 0 0 / 0.25);
|
|
180
268
|
--sidebar: oklch(0.205 0 0);
|
|
181
269
|
--sidebar-foreground: oklch(0.985 0 0);
|
|
182
270
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
@@ -262,7 +350,12 @@ h6 {
|
|
|
262
350
|
--color-surface: var(--surface);
|
|
263
351
|
--color-surface-foreground: var(--surface-foreground);
|
|
264
352
|
--color-surface-2: var(--surface-2);
|
|
353
|
+
--color-surface-elevated: var(--surface-elevated);
|
|
265
354
|
--color-line-soft: var(--line-soft);
|
|
355
|
+
--color-accent-subtle: var(--accent-subtle);
|
|
356
|
+
--color-ink-primary: var(--ink-primary);
|
|
357
|
+
--color-ink-secondary: var(--ink-secondary);
|
|
358
|
+
--color-ink-tertiary: var(--ink-tertiary);
|
|
266
359
|
|
|
267
360
|
/* Theme-aware shadow utilities — override Tailwind defaults so they
|
|
268
361
|
stay visible on dark surfaces */
|
|
@@ -294,15 +387,44 @@ h6 {
|
|
|
294
387
|
@apply border-border outline-ring/50;
|
|
295
388
|
}
|
|
296
389
|
|
|
390
|
+
/* Reserve the scrollbar gutter so content does not jump when a page grows
|
|
391
|
+
* past one viewport, or when a modal locks scrolling.
|
|
392
|
+
*
|
|
393
|
+
* This was `overflow-y: scroll`, which reserved the same space by forcing a
|
|
394
|
+
* permanent scrollbar — and silently disabled every scroll lock in every
|
|
395
|
+
* consumer app.
|
|
396
|
+
*
|
|
397
|
+
* WHY IT BROKE THEM. `react-remove-scroll` — which Radix Dialog and Sheet
|
|
398
|
+
* use, and which vaul's Drawer sits on top of — locks the page by setting
|
|
399
|
+
* `overflow: hidden !important` on BODY. That only reaches the viewport
|
|
400
|
+
* through CSS overflow propagation, and the propagation rule applies ONLY
|
|
401
|
+
* while the ROOT element's overflow is `visible`: give <html> any explicit
|
|
402
|
+
* overflow and the viewport takes its value from <html> instead, so the body
|
|
403
|
+
* rule merely clips the body box while the document scrolls on behind the
|
|
404
|
+
* open dialog. Symptom: the page scrolls under every drawer and sheet, in
|
|
405
|
+
* every browser except Safari — where vaul has a second, `position: fixed`
|
|
406
|
+
* lock that does not depend on propagation (`usePositionFixed`, Safari-gated),
|
|
407
|
+
* which is what makes this look intermittent.
|
|
408
|
+
*
|
|
409
|
+
* `scrollbar-gutter: stable` reserves the same space without touching
|
|
410
|
+
* overflow, so <html> stays `visible` and the lock works as designed. Safari
|
|
411
|
+
* below 18.2 ignores the property, but Safari's overlay scrollbars take no
|
|
412
|
+
* layout space there, so there is nothing to reserve and nothing to shift.
|
|
413
|
+
*
|
|
414
|
+
* DO NOT put an `overflow` of any kind back on <html>. An app that needs to
|
|
415
|
+
* clip an axis puts it on `body`, which propagates rather than blocking. */
|
|
297
416
|
html {
|
|
298
|
-
|
|
417
|
+
scrollbar-gutter: stable;
|
|
299
418
|
}
|
|
300
419
|
|
|
301
420
|
body {
|
|
302
421
|
@apply bg-background text-foreground;
|
|
303
422
|
}
|
|
304
423
|
|
|
305
|
-
/*
|
|
424
|
+
/* `react-remove-scroll` compensates for the scrollbar it removes by adding an
|
|
425
|
+
* equal `margin-right`. The gutter above is reserved permanently and never
|
|
426
|
+
* goes away, so that compensation would double up and shove the page sideways
|
|
427
|
+
* by a scrollbar width every time a dialog opens. Keep this. */
|
|
306
428
|
body[data-scroll-locked] {
|
|
307
429
|
margin-right: 0px !important;
|
|
308
430
|
}
|