@evolve.ui/theme-system 1.1.0 → 1.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/{allThemes-CFS_9-tk.cjs → allThemes-3d8nhymo.cjs} +12 -2
- package/allThemes-3d8nhymo.cjs.map +1 -0
- package/{allThemes-OV-ayn4C.mjs → allThemes-Abn6KDm7.mjs} +7 -3
- package/allThemes-Abn6KDm7.mjs.map +1 -0
- package/allThemes.cjs +1 -1
- package/allThemes.d.cts +1 -0
- package/allThemes.d.cts.map +1 -1
- package/allThemes.d.mts +1 -0
- package/allThemes.d.mts.map +1 -1
- package/allThemes.mjs +1 -1
- package/brands-CE0wIkKX.d.cts +58 -0
- package/brands-CE0wIkKX.d.cts.map +1 -0
- package/brands-CE0wIkKX.d.mts +58 -0
- package/brands-CE0wIkKX.d.mts.map +1 -0
- package/brands.cjs +16 -13
- package/brands.cjs.map +1 -1
- package/brands.d.cts +2 -2
- package/brands.d.mts +2 -2
- package/brands.mjs +16 -13
- package/brands.mjs.map +1 -1
- package/css/EverydayHarbor.css +377 -0
- package/css/EverydayHarbor.effects.css +650 -0
- package/{default-b-tgfyxR.cjs → default-B6VWF0Js.cjs} +237 -36
- package/default-B6VWF0Js.cjs.map +1 -0
- package/{default-B2n4PnsJ.mjs → default-BogJDhKE.mjs} +238 -37
- package/default-BogJDhKE.mjs.map +1 -0
- package/effects.cjs +26 -0
- package/effects.cjs.map +1 -0
- package/effects.d.cts +22 -0
- package/effects.d.cts.map +1 -0
- package/effects.d.mts +22 -0
- package/effects.d.mts.map +1 -0
- package/effects.mjs +22 -0
- package/effects.mjs.map +1 -0
- package/evolve-ui.entries.json +5 -0
- package/index.cjs +12 -5
- package/index.cjs.map +1 -1
- package/index.d.cts +108 -4
- package/index.d.cts.map +1 -1
- package/index.d.mts +108 -4
- package/index.d.mts.map +1 -1
- package/index.mjs +9 -6
- package/index.mjs.map +1 -1
- package/package.json +5 -1
- package/{plugin-DPtvp4lp.cjs → plugin-B3PF4RMI.cjs} +299 -110
- package/plugin-B3PF4RMI.cjs.map +1 -0
- package/{plugin-D4eCyUJe.mjs → plugin-D6aLCeNB.mjs} +258 -111
- package/plugin-D6aLCeNB.mjs.map +1 -0
- package/src/effects/__fixtures__/fixtureLayer.ts +88 -0
- package/src/effects/brands/EverydayHarbor.ts +392 -0
- package/src/effects/classes.ts +19 -0
- package/src/effects/emit.ts +314 -0
- package/src/effects/hooks.ts +493 -0
- package/src/effects/index.ts +2 -0
- package/src/effects/layers.ts +18 -0
- package/src/effects/rules.ts +90 -0
- package/src/effects/rules.typecheck.ts +50 -0
- package/src/effects/tokens.ts +148 -0
- package/src/effects/validate.ts +683 -0
- package/src/effects/vocabulary.ts +32 -0
- package/src/index.ts +3 -0
- package/src/tailwind/animation.ts +30 -0
- package/src/tailwind/borderWidth.ts +14 -0
- package/src/tailwind/plugin.ts +230 -239
- package/src/tailwind/reducedMotion.ts +164 -0
- package/src/tailwind/text.ts +4 -2
- package/src/themeValues.snapshot.ts +6735 -0
- package/src/themes/EverydayHarbor.ts +147 -0
- package/src/themes/allThemes.ts +2 -0
- package/src/themes/brandSources.ts +37 -0
- package/src/themes/brands.ts +41 -15
- package/src/themes/contrastFloors.ts +26 -0
- package/src/themes/inkRamps.ts +43 -0
- package/src/themes/runtime/buildTheme.ts +6 -19
- package/src/themes/runtime/everydayHarbor.ts +3 -0
- package/src/themes/semanticColorTokenSteps.ts +5 -2
- package/src/themes/themeValues.ts +374 -0
- package/src/themes/wcag.ts +48 -0
- package/tailwind/plugin.cjs +5 -1
- package/tailwind/plugin.d.cts +458 -1
- package/tailwind/plugin.d.cts.map +1 -1
- package/tailwind/plugin.d.mts +458 -1
- package/tailwind/plugin.d.mts.map +1 -1
- package/tailwind/plugin.mjs +2 -2
- package/themes/runtime/default.cjs +1 -1
- package/themes/runtime/default.mjs +1 -1
- package/themes/themeType.d.cts +1 -1
- package/themes/themeType.d.mts +1 -1
- package/allThemes-CFS_9-tk.cjs.map +0 -1
- package/allThemes-OV-ayn4C.mjs.map +0 -1
- package/brands-n30rolHQ.d.cts +0 -33
- package/brands-n30rolHQ.d.cts.map +0 -1
- package/brands-n30rolHQ.d.mts +0 -33
- package/brands-n30rolHQ.d.mts.map +0 -1
- package/default-B2n4PnsJ.mjs.map +0 -1
- package/default-b-tgfyxR.cjs.map +0 -1
- package/plugin-D4eCyUJe.mjs.map +0 -1
- package/plugin-DPtvp4lp.cjs.map +0 -1
- package/src/themes/generate.ts +0 -46
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everyday Harbor effect layer: glass v2 on the ink canvas.
|
|
3
|
+
*
|
|
4
|
+
* `harborGlass` is the one data object. The `--effect-*` token strings are rendered from it by the pure functions
|
|
5
|
+
* below, and `glassContrast.spec.ts` reads the same object, so the contrast proof measures the values that ship.
|
|
6
|
+
* Values follow the approved render (.g2 .feat .navglow .dlg2 .btnglow); change them here, not in the tokens.
|
|
7
|
+
*
|
|
8
|
+
* Structural hooks (select.menu, toast, dropzone*, dropzoneOverlay*) are bound here and each one has a
|
|
9
|
+
* `witness('<id>'` call in apps/docs/stories/Brands/EverydayHarbor (scripts/lib/effect-witness.spec.ts enforces it).
|
|
10
|
+
* `modal.backdrop` is deliberately NOT bound: a filtered backdrop becomes the backdrop root, so the dialog would
|
|
11
|
+
* blur the scrim instead of the page.
|
|
12
|
+
*
|
|
13
|
+
* Pseudo-slot plan: every material edge lands on the first free slot (`::before`, or `::after` on bottomSheet).
|
|
14
|
+
* The only decorations are the dropzone's inner dashed frame and the drag overlay's frame, both on `::after`.
|
|
15
|
+
* The validator does not cross-check two hook ids on one element, so the dropzone state rules rely on source order:
|
|
16
|
+
* disabled, then dragging, then error, each with a `hover` twin because `:hover` out-ranks the state class.
|
|
17
|
+
*/
|
|
18
|
+
import type { BrandEffectRule, EffectLayer } from '../rules';
|
|
19
|
+
|
|
20
|
+
import { fx, type EffectTokenValues } from '../tokens';
|
|
21
|
+
|
|
22
|
+
export type Rgb = readonly [number, number, number];
|
|
23
|
+
|
|
24
|
+
export type GlassTier = {
|
|
25
|
+
/** Alpha of the tint over the backdrop. */
|
|
26
|
+
readonly alpha: number;
|
|
27
|
+
/** Alpha of the white sheen that starts the 160deg gradient. */
|
|
28
|
+
readonly sheen: number;
|
|
29
|
+
readonly blur: number;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type Glow = {
|
|
33
|
+
readonly rgb: Rgb;
|
|
34
|
+
readonly alpha: number;
|
|
35
|
+
readonly at: `${number}% ${number}%`;
|
|
36
|
+
readonly reach: `${number}%`;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type Shadow = { readonly alpha: number; readonly blur: number; readonly y: number };
|
|
40
|
+
|
|
41
|
+
export type Stop = { readonly alpha: number; readonly at: number | null; readonly rgb: Rgb };
|
|
42
|
+
|
|
43
|
+
/** The whole data object. Every value the tokens render from is typed here, so a typo cannot slip through as `unknown`. */
|
|
44
|
+
export type HarborGlass = {
|
|
45
|
+
readonly ink: Rgb;
|
|
46
|
+
readonly solid: Rgb;
|
|
47
|
+
readonly tint: Rgb;
|
|
48
|
+
readonly saturate: number;
|
|
49
|
+
readonly surface: GlassTier;
|
|
50
|
+
readonly raised: GlassTier;
|
|
51
|
+
readonly surfaceRadius: number;
|
|
52
|
+
readonly shine: { readonly raised: number; readonly surface: number };
|
|
53
|
+
readonly rim: number;
|
|
54
|
+
readonly raisedHalo: { readonly alpha: number; readonly blur: number; readonly rgb: Rgb };
|
|
55
|
+
readonly drop: {
|
|
56
|
+
readonly raised: Shadow;
|
|
57
|
+
readonly surface: Shadow;
|
|
58
|
+
/** Hover, selected, success and error cards: the surface drop, lighter. */
|
|
59
|
+
readonly state: Shadow;
|
|
60
|
+
};
|
|
61
|
+
readonly edge: { readonly angle: number; readonly stops: readonly Stop[] };
|
|
62
|
+
readonly scrim: { readonly alpha: number; readonly blur: number };
|
|
63
|
+
readonly hoverTint: { readonly alpha: number; readonly rgb: Rgb; readonly sheen: number };
|
|
64
|
+
readonly featured: {
|
|
65
|
+
readonly rgb: Rgb;
|
|
66
|
+
readonly ringAlpha: number;
|
|
67
|
+
readonly glowAlpha: number;
|
|
68
|
+
readonly glowBlur: number;
|
|
69
|
+
};
|
|
70
|
+
readonly halo: { readonly rgb: Rgb; readonly alpha: number; readonly blur: number };
|
|
71
|
+
readonly glow: { readonly rgb: Rgb; readonly alpha: number; readonly blur: number };
|
|
72
|
+
readonly hover: { readonly rgb: Rgb; readonly alpha: number; readonly blur: number; readonly shine: number };
|
|
73
|
+
readonly selected: {
|
|
74
|
+
readonly rgb: Rgb;
|
|
75
|
+
readonly ringAlpha: number;
|
|
76
|
+
readonly glowAlpha: number;
|
|
77
|
+
readonly glowBlur: number;
|
|
78
|
+
};
|
|
79
|
+
readonly success: { readonly rgb: Rgb; readonly ringAlpha: number };
|
|
80
|
+
readonly error: { readonly ringAlpha: number };
|
|
81
|
+
readonly accept: {
|
|
82
|
+
readonly rgb: Rgb;
|
|
83
|
+
readonly glowAlpha: number;
|
|
84
|
+
readonly insetAlpha: number;
|
|
85
|
+
readonly blur: number;
|
|
86
|
+
readonly dashAlpha: number;
|
|
87
|
+
};
|
|
88
|
+
readonly reject: {
|
|
89
|
+
readonly glowAlpha: number;
|
|
90
|
+
readonly insetAlpha: number;
|
|
91
|
+
readonly blur: number;
|
|
92
|
+
readonly dashAlpha: number;
|
|
93
|
+
};
|
|
94
|
+
readonly dash: {
|
|
95
|
+
readonly rgb: Rgb;
|
|
96
|
+
readonly rest: number;
|
|
97
|
+
readonly strong: number;
|
|
98
|
+
readonly disabledRgb: Rgb;
|
|
99
|
+
readonly disabled: number;
|
|
100
|
+
};
|
|
101
|
+
readonly cta: {
|
|
102
|
+
readonly angle: number;
|
|
103
|
+
readonly stops: readonly [string, string, string];
|
|
104
|
+
readonly mid: number;
|
|
105
|
+
readonly hoverAngle: number;
|
|
106
|
+
readonly hoverMid: number;
|
|
107
|
+
readonly ink: Rgb;
|
|
108
|
+
};
|
|
109
|
+
readonly mutedOnGlass: Rgb;
|
|
110
|
+
readonly mutedOnSolid: Rgb;
|
|
111
|
+
readonly criticalOnGlass: Rgb;
|
|
112
|
+
readonly criticalOnSolid: Rgb;
|
|
113
|
+
readonly successOnGlass: Rgb;
|
|
114
|
+
readonly successOnSolid: Rgb;
|
|
115
|
+
readonly glows: readonly Glow[];
|
|
116
|
+
readonly serif: string;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const harborGlass: HarborGlass = {
|
|
120
|
+
/** #0B1626, the page canvas and the scrim. */
|
|
121
|
+
ink: [11, 22, 38],
|
|
122
|
+
/** #12223A, what every translucent fill collapses to in the fallbacks. */
|
|
123
|
+
solid: [18, 34, 58],
|
|
124
|
+
/** rgba(14,28,48,a): the glass tint. */
|
|
125
|
+
tint: [14, 28, 48],
|
|
126
|
+
saturate: 180,
|
|
127
|
+
/** .g2 */
|
|
128
|
+
surface: { alpha: 0.42, blur: 18, sheen: 0.09 },
|
|
129
|
+
/** .dlg2 */
|
|
130
|
+
raised: { alpha: 0.58, blur: 26, sheen: 0.1 },
|
|
131
|
+
surfaceRadius: 18,
|
|
132
|
+
/** Inner highlight on each tier (.g2 .22, .dlg2 .25). */
|
|
133
|
+
shine: { raised: 0.25, surface: 0.22 },
|
|
134
|
+
/** Hairline ring inside .g2. */
|
|
135
|
+
rim: 0.06,
|
|
136
|
+
/** .dlg2 halo and drops. */
|
|
137
|
+
raisedHalo: { alpha: 0.22, blur: 40, rgb: [142, 197, 232] },
|
|
138
|
+
drop: {
|
|
139
|
+
raised: { alpha: 0.55, blur: 80, y: 30 },
|
|
140
|
+
surface: { alpha: 0.45, blur: 60, y: 20 },
|
|
141
|
+
state: { alpha: 0.4, blur: 60, y: 20 },
|
|
142
|
+
},
|
|
143
|
+
/** .g2::before gradient border. */
|
|
144
|
+
edge: {
|
|
145
|
+
angle: 135,
|
|
146
|
+
stops: [
|
|
147
|
+
{ alpha: 0.45, at: null, rgb: [255, 255, 255] },
|
|
148
|
+
{ alpha: 0.06, at: 35, rgb: [255, 255, 255] },
|
|
149
|
+
{ alpha: 0.1, at: 65, rgb: [142, 197, 232] },
|
|
150
|
+
{ alpha: 0.4, at: null, rgb: [142, 197, 232] },
|
|
151
|
+
],
|
|
152
|
+
},
|
|
153
|
+
/** Scrim tier. Not in the brief's CSS; the fixture values. */
|
|
154
|
+
scrim: { alpha: 0.45, blur: 10 },
|
|
155
|
+
hoverTint: { alpha: 0.3, rgb: [46, 107, 166], sheen: 0.12 },
|
|
156
|
+
/** .feat */
|
|
157
|
+
featured: { rgb: [227, 179, 65], ringAlpha: 0.45, glowAlpha: 0.28, glowBlur: 36 },
|
|
158
|
+
/** .navglow */
|
|
159
|
+
halo: { rgb: [46, 107, 166], alpha: 0.35, blur: 28 },
|
|
160
|
+
/** .btnglow */
|
|
161
|
+
glow: { rgb: [46, 107, 166], alpha: 0.55, blur: 24 },
|
|
162
|
+
hover: { rgb: [46, 107, 166], alpha: 0.4, blur: 28, shine: 0.25 },
|
|
163
|
+
selected: { rgb: [227, 179, 65], ringAlpha: 0.55, glowAlpha: 0.28, glowBlur: 32 },
|
|
164
|
+
/** Olive on glass, #B4C15A. */
|
|
165
|
+
success: { rgb: [180, 193, 90], ringAlpha: 0.35 },
|
|
166
|
+
/** Error uses the theme's critical border channels, not a literal red. */
|
|
167
|
+
error: { ringAlpha: 0.45 },
|
|
168
|
+
accept: { rgb: [227, 179, 65], glowAlpha: 0.3, insetAlpha: 0.12, blur: 36, dashAlpha: 0.9 },
|
|
169
|
+
reject: { glowAlpha: 0.28, insetAlpha: 0.1, blur: 36, dashAlpha: 0.9 },
|
|
170
|
+
dash: { rgb: [142, 197, 232], rest: 0.4, strong: 0.85, disabledRgb: [185, 197, 211], disabled: 0.35 },
|
|
171
|
+
cta: {
|
|
172
|
+
/** Base angle is not in the brief (only the hover is); 90deg follows the fixture. */
|
|
173
|
+
angle: 90,
|
|
174
|
+
stops: ['#f2cf7a', '#e3b341', '#d9963e'],
|
|
175
|
+
mid: 55,
|
|
176
|
+
/** Brief: linear-gradient(180deg,#f2cf7a,#e3b341 40%,#d9963e). */
|
|
177
|
+
hoverAngle: 180,
|
|
178
|
+
hoverMid: 40,
|
|
179
|
+
ink: [11, 22, 38],
|
|
180
|
+
},
|
|
181
|
+
/** #B9C5D3 on glass, #A9B6C6 on solid. */
|
|
182
|
+
mutedOnGlass: [185, 197, 211],
|
|
183
|
+
mutedOnSolid: [169, 182, 198],
|
|
184
|
+
/** Olive text on glass, #B4C15A (4.03:1 at the shipped #A3B04E against the brightest glow). */
|
|
185
|
+
successOnGlass: [180, 193, 90],
|
|
186
|
+
/** #A3B04E, the theme's success-400, on the opaque fallback fill. */
|
|
187
|
+
successOnSolid: [163, 176, 78],
|
|
188
|
+
/** Critical text on glass, #F7A29D (theme critical #EA756F is 4.21:1 on the brightest list card). */
|
|
189
|
+
criticalOnGlass: [247, 162, 157],
|
|
190
|
+
/** The theme critical, #EA756F, on the opaque fallback fill. */
|
|
191
|
+
criticalOnSolid: [234, 117, 111],
|
|
192
|
+
/**
|
|
193
|
+
* Ambient backdrop: harbor-blue and gold-olive glows over the ink canvas.
|
|
194
|
+
* The brightest centre is the lightest glow colour at its alpha over #0B1626 (currently the gold glow, #4C452E).
|
|
195
|
+
* The blue alpha is capped at 0.38: at the 0.45 the fixture uses, gold (4.47) and olive (4.44) on .g2 fall
|
|
196
|
+
* under 4.5:1 over that centre (glassContrast.spec.ts).
|
|
197
|
+
*/
|
|
198
|
+
glows: [
|
|
199
|
+
{ alpha: 0.38, at: '18% 12%', reach: '38%', rgb: [91, 155, 216] },
|
|
200
|
+
{ alpha: 0.3, at: '85% 80%', reach: '42%', rgb: [227, 179, 65] },
|
|
201
|
+
{ alpha: 0.18, at: '70% 20%', reach: '36%', rgb: [163, 176, 78] },
|
|
202
|
+
],
|
|
203
|
+
serif: "'Source Serif 4', Georgia, serif",
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
type Harbor = HarborGlass;
|
|
207
|
+
|
|
208
|
+
const rgba = ([r, g, b]: Rgb, alpha: number): string => `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
209
|
+
const channels = ([r, g, b]: Rgb): string => `${r} ${g} ${b}`;
|
|
210
|
+
const hex = ([r, g, b]: Rgb): string => `#${[r, g, b].map((c) => c.toString(16).padStart(2, '0')).join('')}`;
|
|
211
|
+
const critical = (alpha: number): string => `rgb(var(--color-border-critical) / ${Math.round(alpha * 100)}%)`;
|
|
212
|
+
const WHITE: Rgb = [255, 255, 255];
|
|
213
|
+
|
|
214
|
+
const highlight = (alpha: number): string => `inset 0 1px 0 ${rgba(WHITE, alpha)}`;
|
|
215
|
+
const drop = (d: { alpha: number; blur: number; y: number }): string =>
|
|
216
|
+
`0 ${d.y}px ${d.blur}px rgba(0, 0, 0, ${d.alpha})`;
|
|
217
|
+
const ring = (colour: string): string => `0 0 0 1px ${colour}`;
|
|
218
|
+
const bloom = (blur: number, colour: string): string => `0 0 ${blur}px ${colour}`;
|
|
219
|
+
|
|
220
|
+
export const renderGlassFill = (glass: Harbor, tier: GlassTier): string =>
|
|
221
|
+
`linear-gradient(160deg, ${rgba(WHITE, tier.sheen)}, rgba(255, 255, 255, 0) 45%), ${rgba(glass.tint, tier.alpha)}`;
|
|
222
|
+
|
|
223
|
+
export const renderGlassFilter = (glass: Harbor, tier: GlassTier): string =>
|
|
224
|
+
`blur(${tier.blur}px) saturate(${glass.saturate}%)`;
|
|
225
|
+
|
|
226
|
+
export const renderEdge = (glass: Harbor): string =>
|
|
227
|
+
`linear-gradient(${glass.edge.angle}deg, ${glass.edge.stops
|
|
228
|
+
.map((stop) => `${rgba(stop.rgb, stop.alpha)}${stop.at === null ? '' : ` ${stop.at}%`}`)
|
|
229
|
+
.join(', ')})`;
|
|
230
|
+
|
|
231
|
+
export const renderCta = (glass: Harbor, angle: number, mid: number): string => {
|
|
232
|
+
const [light, base, deep] = glass.cta.stops;
|
|
233
|
+
return `linear-gradient(${angle}deg, ${light}, ${base} ${mid}%, ${deep})`;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
export const renderBackdrop = (glass: Harbor): string =>
|
|
237
|
+
[
|
|
238
|
+
...glass.glows.map(
|
|
239
|
+
(glow) => `radial-gradient(circle at ${glow.at}, ${rgba(glow.rgb, glow.alpha)}, transparent ${glow.reach})`
|
|
240
|
+
),
|
|
241
|
+
hex(glass.ink),
|
|
242
|
+
].join(', ');
|
|
243
|
+
|
|
244
|
+
export const renderHarborTokens = (glass: Harbor): EffectTokenValues => ({
|
|
245
|
+
'accept-glow': `${bloom(glass.accept.blur, rgba(glass.accept.rgb, glass.accept.glowAlpha))}, inset 0 0 ${glass.accept.blur}px ${rgba(glass.accept.rgb, glass.accept.insetAlpha)}`,
|
|
246
|
+
backdrop: renderBackdrop(glass),
|
|
247
|
+
'cta-fill': renderCta(glass, glass.cta.angle, glass.cta.mid),
|
|
248
|
+
'cta-fill-hover': renderCta(glass, glass.cta.hoverAngle, glass.cta.hoverMid),
|
|
249
|
+
'cta-ink-rgb': channels(glass.cta.ink),
|
|
250
|
+
dash: rgba(glass.dash.rgb, glass.dash.rest),
|
|
251
|
+
'dash-accept': rgba(glass.accept.rgb, glass.accept.dashAlpha),
|
|
252
|
+
'dash-disabled': rgba(glass.dash.disabledRgb, glass.dash.disabled),
|
|
253
|
+
'dash-reject': critical(glass.reject.dashAlpha),
|
|
254
|
+
'dash-strong': rgba(glass.dash.rgb, glass.dash.strong),
|
|
255
|
+
edge: renderEdge(glass),
|
|
256
|
+
error: `${highlight(glass.shine.surface)}, ${ring(critical(glass.error.ringAlpha))}, ${drop(glass.drop.state)}`,
|
|
257
|
+
featured: `${highlight(glass.shine.surface)}, ${ring(rgba(glass.featured.rgb, glass.featured.ringAlpha))}, ${bloom(glass.featured.glowBlur, rgba(glass.featured.rgb, glass.featured.glowAlpha))}, ${drop(glass.drop.surface)}`,
|
|
258
|
+
'font-serif': glass.serif,
|
|
259
|
+
glow: bloom(glass.glow.blur, rgba(glass.glow.rgb, glass.glow.alpha)),
|
|
260
|
+
halo: `${highlight(glass.shine.surface)}, ${bloom(glass.halo.blur, rgba(glass.halo.rgb, glass.halo.alpha))}, ${drop(glass.drop.surface)}`,
|
|
261
|
+
hover: `${highlight(glass.hover.shine)}, ${bloom(glass.hover.blur, rgba(glass.hover.rgb, glass.hover.alpha))}, ${drop(glass.drop.state)}`,
|
|
262
|
+
'hover-fill': `linear-gradient(160deg, ${rgba(WHITE, glass.hoverTint.sheen)}, rgba(255, 255, 255, 0) 45%), ${rgba(glass.hoverTint.rgb, glass.hoverTint.alpha)}`,
|
|
263
|
+
'raised-fill': renderGlassFill(glass, glass.raised),
|
|
264
|
+
'raised-filter': renderGlassFilter(glass, glass.raised),
|
|
265
|
+
'raised-shadow': `${highlight(glass.shine.raised)}, ${bloom(glass.raisedHalo.blur, rgba(glass.raisedHalo.rgb, glass.raisedHalo.alpha))}, ${drop(glass.drop.raised)}`,
|
|
266
|
+
'reject-glow': `${bloom(glass.reject.blur, critical(glass.reject.glowAlpha))}, inset 0 0 ${glass.reject.blur}px ${critical(glass.reject.insetAlpha)}`,
|
|
267
|
+
'scrim-fill': rgba(glass.ink, glass.scrim.alpha),
|
|
268
|
+
'scrim-filter': `blur(${glass.scrim.blur}px) saturate(${glass.saturate}%)`,
|
|
269
|
+
selected: `${highlight(glass.shine.surface)}, ${ring(rgba(glass.selected.rgb, glass.selected.ringAlpha))}, ${bloom(glass.selected.glowBlur, rgba(glass.selected.rgb, glass.selected.glowAlpha))}, ${drop(glass.drop.state)}`,
|
|
270
|
+
'solid-fill': hex(glass.solid),
|
|
271
|
+
success: `${highlight(glass.shine.surface)}, ${ring(rgba(glass.success.rgb, glass.success.ringAlpha))}, ${drop(glass.drop.state)}`,
|
|
272
|
+
'surface-fill': renderGlassFill(glass, glass.surface),
|
|
273
|
+
'surface-filter': renderGlassFilter(glass, glass.surface),
|
|
274
|
+
'surface-radius': `${glass.surfaceRadius}px`,
|
|
275
|
+
'surface-shadow': `${highlight(glass.shine.surface)}, inset 0 0 0 1px ${rgba(WHITE, glass.rim)}, ${drop(glass.drop.surface)}`,
|
|
276
|
+
'text-critical-glass-rgb': channels(glass.criticalOnGlass),
|
|
277
|
+
'text-critical-solid-rgb': channels(glass.criticalOnSolid),
|
|
278
|
+
'text-muted-rgb': channels(glass.mutedOnGlass),
|
|
279
|
+
'text-muted-solid-rgb': channels(glass.mutedOnSolid),
|
|
280
|
+
'text-success-rgb': channels(glass.successOnGlass),
|
|
281
|
+
'text-success-solid-rgb': channels(glass.successOnSolid),
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
/** Inside glass, muted and success text move to their glass values. The fallback blocks remap them back to the solid values. */
|
|
285
|
+
const glassText = { '--color-text-secondary': fx.textMutedRgb, '--color-text-success': fx.textSuccessRgb } as const;
|
|
286
|
+
|
|
287
|
+
/** Errored cards sit on bright glass: critical text moves to the glass value (the fallbacks remap it back). */
|
|
288
|
+
const criticalText = { '--color-text-critical': fx.textCriticalGlassRgb } as const;
|
|
289
|
+
|
|
290
|
+
const raisedHooks = [
|
|
291
|
+
'modal',
|
|
292
|
+
'drawer',
|
|
293
|
+
'bottomSheet',
|
|
294
|
+
'menu',
|
|
295
|
+
'tooltip',
|
|
296
|
+
'datePicker.popover',
|
|
297
|
+
'timePicker.popover',
|
|
298
|
+
] as const;
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Order matters: state rules come after their root, and error comes last so it wins over selected.
|
|
302
|
+
* A card's hover fill is covered by the `when: 'hover'` rule; the grid card's focus-within is acknowledged
|
|
303
|
+
* because the component paints that state itself.
|
|
304
|
+
*/
|
|
305
|
+
export const harborRules: readonly BrandEffectRule[] = [
|
|
306
|
+
...raisedHooks.map((on): BrandEffectRule => ({ on, material: 'raised', vars: glassText })),
|
|
307
|
+
// react-tooltip paints its own text colour (#222 light, #fff dark) and core sets only the background, so the
|
|
308
|
+
// hooks carry the text colour: pale on the glass tooltip, navy on the pale inverse tooltip.
|
|
309
|
+
{ on: 'tooltip', paint: { color: fx.tooltipText } },
|
|
310
|
+
{ on: 'tooltip.inverse', paint: { color: fx.tooltipTextInverse } },
|
|
311
|
+
|
|
312
|
+
{ on: 'select.menu', material: 'raised', vars: glassText },
|
|
313
|
+
{ on: 'toast', material: 'surface', vars: glassText },
|
|
314
|
+
|
|
315
|
+
{
|
|
316
|
+
on: 'dropzone',
|
|
317
|
+
material: 'surface',
|
|
318
|
+
vars: glassText,
|
|
319
|
+
after: {
|
|
320
|
+
borderColor: fx.dash,
|
|
321
|
+
borderRadius: 'inherit',
|
|
322
|
+
borderStyle: 'dashed',
|
|
323
|
+
borderWidth: '1px',
|
|
324
|
+
inset: '6px',
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
on: 'dropzone',
|
|
329
|
+
when: 'hover',
|
|
330
|
+
unless: '[aria-disabled="true"]',
|
|
331
|
+
paint: { background: fx.hoverFill },
|
|
332
|
+
after: { borderColor: fx.dashStrong },
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
on: 'dropzone',
|
|
336
|
+
when: 'active',
|
|
337
|
+
unless: '[aria-disabled="true"]',
|
|
338
|
+
paint: { background: fx.hoverFill },
|
|
339
|
+
},
|
|
340
|
+
{ on: 'dropzone.disabled', after: { borderColor: fx.dashDisabled } },
|
|
341
|
+
{ on: 'dropzone.dragging', after: { borderColor: fx.dashAccept, boxShadow: fx.acceptGlow } },
|
|
342
|
+
{ on: 'dropzone.dragging', when: 'hover', after: { borderColor: fx.dashAccept, boxShadow: fx.acceptGlow } },
|
|
343
|
+
{ on: 'dropzone.error', shadow: 'error', after: { borderColor: fx.dashReject, boxShadow: fx.rejectGlow } },
|
|
344
|
+
{ on: 'dropzone.error', when: 'hover', after: { borderColor: fx.dashReject, boxShadow: fx.rejectGlow } },
|
|
345
|
+
|
|
346
|
+
{
|
|
347
|
+
on: 'dropzoneOverlay.accept',
|
|
348
|
+
material: 'scrim',
|
|
349
|
+
after: {
|
|
350
|
+
borderColor: fx.dashAccept,
|
|
351
|
+
borderRadius: 'inherit',
|
|
352
|
+
borderStyle: 'dashed',
|
|
353
|
+
borderWidth: '2px',
|
|
354
|
+
boxShadow: fx.acceptGlow,
|
|
355
|
+
inset: '8px',
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
on: 'dropzoneOverlay.reject',
|
|
360
|
+
material: 'scrim',
|
|
361
|
+
after: {
|
|
362
|
+
borderColor: fx.dashReject,
|
|
363
|
+
borderRadius: 'inherit',
|
|
364
|
+
borderStyle: 'dashed',
|
|
365
|
+
borderWidth: '2px',
|
|
366
|
+
boxShadow: fx.rejectGlow,
|
|
367
|
+
inset: '8px',
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
// core hard-codes text-blue-600 for the valid-drag message (near-black on the ink scrim).
|
|
372
|
+
{ on: 'overlayDragText.valid', paint: { color: fx.overlayText } },
|
|
373
|
+
|
|
374
|
+
{ on: 'listFileCard', material: 'surface', vars: glassText },
|
|
375
|
+
{ on: 'listFileCard', when: 'hover', paint: { background: fx.hoverFill } },
|
|
376
|
+
{ on: 'listFileCard.uploading', shadow: 'glow' },
|
|
377
|
+
{ on: 'listFileCard.complete', shadow: 'success' },
|
|
378
|
+
{ on: 'listFileCard.selected', shadow: 'selected' },
|
|
379
|
+
{ on: 'listFileCard.error', shadow: 'error', vars: criticalText },
|
|
380
|
+
|
|
381
|
+
{ on: 'gridFileCard', material: 'surface', vars: glassText, acknowledge: ['focus-within'] },
|
|
382
|
+
{ on: 'gridFileCard', when: 'hover', paint: { background: fx.hoverFill } },
|
|
383
|
+
{ on: 'gridFileCard.uploading', shadow: 'glow' },
|
|
384
|
+
{ on: 'gridFileCard.complete', shadow: 'success' },
|
|
385
|
+
{ on: 'gridFileCard.selected', shadow: 'selected' },
|
|
386
|
+
{ on: 'gridFileCard.error', shadow: 'error', vars: criticalText },
|
|
387
|
+
];
|
|
388
|
+
|
|
389
|
+
export const everydayHarborEffects: EffectLayer = {
|
|
390
|
+
rules: harborRules,
|
|
391
|
+
tokens: renderHarborTokens(harborGlass),
|
|
392
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in effect classes. They do nothing unless an effect brand sheet scopes them.
|
|
3
|
+
* `none` removes one element from every effect selector.
|
|
4
|
+
* An opt-in edge takes `::before` with `!important`, so `before:` utilities on the same element are overridden.
|
|
5
|
+
*/
|
|
6
|
+
export const effectClasses = {
|
|
7
|
+
glass: 'hui-fx-glass',
|
|
8
|
+
glassRaised: 'hui-fx-glass-raised',
|
|
9
|
+
featured: 'hui-fx-featured',
|
|
10
|
+
halo: 'hui-fx-halo',
|
|
11
|
+
glow: 'hui-fx-glow',
|
|
12
|
+
cta: 'hui-fx-cta',
|
|
13
|
+
accentFont: 'hui-fx-accent-font',
|
|
14
|
+
serif: 'hui-fx-serif',
|
|
15
|
+
backdrop: 'hui-fx-backdrop',
|
|
16
|
+
none: 'hui-fx-none',
|
|
17
|
+
} as const;
|
|
18
|
+
|
|
19
|
+
export type EffectClass = (typeof effectClasses)[keyof typeof effectClasses];
|