@oxyhq/bloom 1.0.0 → 1.0.2
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 +16 -4
- package/docs/accordion.mdx +69 -0
- package/docs/activity-heatmap.mdx +71 -0
- package/docs/admonition.mdx +77 -0
- package/docs/alert-dialog.mdx +75 -0
- package/docs/alert.mdx +93 -0
- package/docs/animated-check.mdx +53 -0
- package/docs/aspect-ratio.mdx +45 -0
- package/docs/avatar-group.mdx +91 -0
- package/docs/avatar.mdx +126 -0
- package/docs/badge.mdx +74 -0
- package/docs/benefit-list.mdx +56 -0
- package/docs/bottom-sheet.mdx +103 -0
- package/docs/button.mdx +218 -0
- package/docs/card.mdx +106 -0
- package/docs/checkbox.mdx +59 -0
- package/docs/chip.mdx +53 -0
- package/docs/code.mdx +50 -0
- package/docs/combobox.mdx +64 -0
- package/docs/command.mdx +85 -0
- package/docs/composition-bar.mdx +63 -0
- package/docs/connection-dots.mdx +50 -0
- package/docs/connection-status.mdx +56 -0
- package/docs/content-panel.mdx +62 -0
- package/docs/context-menu.mdx +65 -0
- package/docs/design-tokens.mdx +254 -0
- package/docs/dialog.mdx +277 -0
- package/docs/divider.mdx +56 -0
- package/docs/dot-grid-meter.mdx +66 -0
- package/docs/dropdown-menu.mdx +161 -0
- package/docs/error-boundary.mdx +72 -0
- package/docs/fab.mdx +67 -0
- package/docs/field.mdx +70 -0
- package/docs/fill.mdx +58 -0
- package/docs/fonts.mdx +144 -0
- package/docs/frosted-icon-button.mdx +60 -0
- package/docs/getting-started.mdx +75 -0
- package/docs/grid.mdx +64 -0
- package/docs/hooks.mdx +81 -0
- package/docs/icon-circle.mdx +66 -0
- package/docs/icons.mdx +69 -0
- package/docs/image-aspect-ratio-cache.mdx +63 -0
- package/docs/image-resolver.mdx +66 -0
- package/docs/index.mdx +44 -0
- package/docs/input-group.mdx +58 -0
- package/docs/item.mdx +69 -0
- package/docs/kbd.mdx +57 -0
- package/docs/label.mdx +50 -0
- package/docs/link-preview.mdx +66 -0
- package/docs/list.mdx +72 -0
- package/docs/loading.mdx +129 -0
- package/docs/media-inset-border.mdx +52 -0
- package/docs/menubar.mdx +86 -0
- package/docs/migrating-to-1.0.mdx +434 -0
- package/docs/motion.mdx +83 -0
- package/docs/overlay.mdx +75 -0
- package/docs/popover.mdx +73 -0
- package/docs/portal.mdx +63 -0
- package/docs/pressable-scale.mdx +51 -0
- package/docs/profile-card.mdx +66 -0
- package/docs/progressive-blur.mdx +54 -0
- package/docs/prompt-input.mdx +183 -0
- package/docs/provider.mdx +64 -0
- package/docs/radio-indicator.mdx +60 -0
- package/docs/radio.mdx +65 -0
- package/docs/scroll.mdx +66 -0
- package/docs/search.mdx +67 -0
- package/docs/segmented-control.mdx +76 -0
- package/docs/select.mdx +93 -0
- package/docs/settings-list.mdx +186 -0
- package/docs/skeleton.mdx +66 -0
- package/docs/slider.mdx +73 -0
- package/docs/stat-bar.mdx +59 -0
- package/docs/stat-widgets.mdx +33 -0
- package/docs/styles.mdx +61 -0
- package/docs/subtle-hover.mdx +53 -0
- package/docs/surfaces.mdx +72 -0
- package/docs/switch.mdx +90 -0
- package/docs/tab-bar.mdx +386 -0
- package/docs/tabs.mdx +75 -0
- package/docs/text-field.mdx +187 -0
- package/docs/theme.mdx +67 -0
- package/docs/toast.mdx +285 -0
- package/docs/tooltip.mdx +67 -0
- package/docs/typography.mdx +88 -0
- package/docs/user-hover-card.mdx +140 -0
- package/docs/zoomable-image-gallery.mdx +66 -0
- package/lib/commonjs/design-tokens/color-roles.js +21 -1
- package/lib/commonjs/design-tokens/color-roles.js.map +1 -1
- package/lib/commonjs/design-tokens/index.js +6 -0
- package/lib/commonjs/design-tokens/index.js.map +1 -1
- package/lib/commonjs/design-tokens/tailwind-preset.js +3 -1
- package/lib/commonjs/design-tokens/tailwind-preset.js.map +1 -1
- package/lib/commonjs/design-tokens/theme-css.js +1 -0
- package/lib/commonjs/design-tokens/theme-css.js.map +1 -1
- package/lib/commonjs/design-tokens/theme.css +3 -0
- package/lib/commonjs/design-tokens/tokens-json.js +13 -4
- package/lib/commonjs/design-tokens/tokens-json.js.map +1 -1
- package/lib/commonjs/design-tokens/tokens.json +6227 -265
- package/lib/commonjs/index.js +7 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +7 -0
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/theme/ambient-store.js +16 -1
- package/lib/commonjs/theme/ambient-store.js.map +1 -1
- package/lib/commonjs/theme/color-policy.js +30 -33
- package/lib/commonjs/theme/color-policy.js.map +1 -1
- package/lib/commonjs/theme/color-presets.js +413 -166
- package/lib/commonjs/theme/color-presets.js.map +1 -1
- package/lib/commonjs/theme/color-scope/seed-scope.js +1 -0
- package/lib/commonjs/theme/color-scope/seed-scope.js.map +1 -1
- package/lib/commonjs/theme/glass-colors.js +18 -21
- package/lib/commonjs/theme/glass-colors.js.map +1 -1
- package/lib/commonjs/theme/index.js +30 -0
- package/lib/commonjs/theme/index.js.map +1 -1
- package/lib/commonjs/theme/index.web.js +30 -0
- package/lib/commonjs/theme/index.web.js.map +1 -1
- package/lib/module/design-tokens/color-roles.js +20 -0
- package/lib/module/design-tokens/color-roles.js.map +1 -1
- package/lib/module/design-tokens/index.js +2 -2
- package/lib/module/design-tokens/index.js.map +1 -1
- package/lib/module/design-tokens/tailwind-preset.js +4 -2
- package/lib/module/design-tokens/tailwind-preset.js.map +1 -1
- package/lib/module/design-tokens/theme-css.js +2 -1
- package/lib/module/design-tokens/theme-css.js.map +1 -1
- package/lib/module/design-tokens/theme.css +3 -0
- package/lib/module/design-tokens/tokens-json.js +14 -5
- package/lib/module/design-tokens/tokens-json.js.map +1 -1
- package/lib/module/design-tokens/tokens.json +6227 -265
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +1 -1
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/theme/ambient-store.js +16 -1
- package/lib/module/theme/ambient-store.js.map +1 -1
- package/lib/module/theme/color-policy.js +30 -33
- package/lib/module/theme/color-policy.js.map +1 -1
- package/lib/module/theme/color-presets.js +412 -165
- package/lib/module/theme/color-presets.js.map +1 -1
- package/lib/module/theme/color-scope/seed-scope.js +2 -1
- package/lib/module/theme/color-scope/seed-scope.js.map +1 -1
- package/lib/module/theme/glass-colors.js +18 -21
- package/lib/module/theme/glass-colors.js.map +1 -1
- package/lib/module/theme/index.js +1 -1
- package/lib/module/theme/index.js.map +1 -1
- package/lib/module/theme/index.web.js +1 -1
- package/lib/module/theme/index.web.js.map +1 -1
- package/lib/typescript/commonjs/design-tokens/color-roles.d.ts +20 -0
- package/lib/typescript/commonjs/design-tokens/color-roles.d.ts.map +1 -1
- package/lib/typescript/commonjs/design-tokens/index.d.ts +3 -3
- package/lib/typescript/commonjs/design-tokens/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/design-tokens/tailwind-preset.d.ts.map +1 -1
- package/lib/typescript/commonjs/design-tokens/theme-css.d.ts.map +1 -1
- package/lib/typescript/commonjs/design-tokens/tokens-json.d.ts +12 -3
- package/lib/typescript/commonjs/design-tokens/tokens-json.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -2
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +2 -2
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/ambient-store.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-policy.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-presets.d.ts +386 -62
- package/lib/typescript/commonjs/theme/color-presets.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-scope/seed-scope.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/glass-colors.d.ts +18 -21
- package/lib/typescript/commonjs/theme/glass-colors.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/index.d.ts +2 -2
- package/lib/typescript/commonjs/theme/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/index.web.d.ts +2 -2
- package/lib/typescript/commonjs/theme/index.web.d.ts.map +1 -1
- package/lib/typescript/module/design-tokens/color-roles.d.ts +20 -0
- package/lib/typescript/module/design-tokens/color-roles.d.ts.map +1 -1
- package/lib/typescript/module/design-tokens/index.d.ts +3 -3
- package/lib/typescript/module/design-tokens/index.d.ts.map +1 -1
- package/lib/typescript/module/design-tokens/tailwind-preset.d.ts.map +1 -1
- package/lib/typescript/module/design-tokens/theme-css.d.ts.map +1 -1
- package/lib/typescript/module/design-tokens/tokens-json.d.ts +12 -3
- package/lib/typescript/module/design-tokens/tokens-json.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +2 -2
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +2 -2
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/theme/ambient-store.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-policy.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-presets.d.ts +386 -62
- package/lib/typescript/module/theme/color-presets.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-scope/seed-scope.d.ts.map +1 -1
- package/lib/typescript/module/theme/glass-colors.d.ts +18 -21
- package/lib/typescript/module/theme/glass-colors.d.ts.map +1 -1
- package/lib/typescript/module/theme/index.d.ts +2 -2
- package/lib/typescript/module/theme/index.d.ts.map +1 -1
- package/lib/typescript/module/theme/index.web.d.ts +2 -2
- package/lib/typescript/module/theme/index.web.d.ts.map +1 -1
- package/package.json +5 -2
- package/src/__tests__/support/collision-fixture-barrel.ts +20 -0
- package/src/__tests__/support/constructed-style-sheets.ts +68 -0
- package/src/__tests__/support/press-host.ts +30 -0
- package/src/__tests__/support/rendered-style.ts +99 -0
- package/src/__tests__/support/unread-hook-fixture.ts +33 -0
- package/src/design-tokens/color-roles.ts +21 -0
- package/src/design-tokens/index.ts +3 -2
- package/src/design-tokens/tailwind-preset.ts +3 -2
- package/src/design-tokens/theme-css.ts +2 -1
- package/src/design-tokens/theme.css +3 -0
- package/src/design-tokens/tokens-json.ts +29 -9
- package/src/design-tokens/tokens.json +6227 -265
- package/src/glass/GlassSurface.tsx +2 -2
- package/src/index.ts +2 -0
- package/src/index.web.ts +2 -0
- package/src/theme/__tests__/__fixtures__/golden-resolved-tokens.json +4082 -0
- package/src/theme/__tests__/fixtures/color-engine-golden.json +1 -0
- package/src/theme/ambient-store.ts +21 -1
- package/src/theme/color-policy.ts +33 -38
- package/src/theme/color-presets.ts +473 -146
- package/src/theme/color-scope/seed-scope.ts +2 -1
- package/src/theme/glass-colors.ts +18 -21
- package/src/theme/index.ts +16 -1
- package/src/theme/index.web.ts +16 -1
|
@@ -1,177 +1,504 @@
|
|
|
1
1
|
import type { SchemeVariant } from './color-engine';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
| 'teal'
|
|
5
|
-
| 'blue'
|
|
6
|
-
| 'green'
|
|
7
|
-
| 'yellow'
|
|
8
|
-
| 'red'
|
|
9
|
-
| 'purple'
|
|
10
|
-
| 'pink'
|
|
11
|
-
| 'sky'
|
|
12
|
-
| 'orange'
|
|
13
|
-
| 'mint'
|
|
14
|
-
| 'oxy'
|
|
15
|
-
| 'faircoin'
|
|
16
|
-
| 'pumpkin'
|
|
17
|
-
| 'gray'
|
|
18
|
-
| 'brown'
|
|
19
|
-
| 'peach'
|
|
20
|
-
| 'rose'
|
|
21
|
-
| 'mono';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* A single colour preset: a brand SEED colour plus the tonal scheme variant to
|
|
25
|
-
* derive its full role set with.
|
|
26
|
-
*
|
|
27
|
-
* Every preset's palette is now GENERATED by Bloom's dependency-free colour
|
|
28
|
-
* engine (`color-engine/`) from this seed — no hand-authored HSL triples. The
|
|
29
|
-
* engine's `generateRoleColors({ seed, variant, isDark })` yields the full
|
|
30
|
-
* Material-3 role set for light and dark; `getPresetVars` (in `preset-vars.ts`)
|
|
31
|
-
* maps those roles onto Bloom's canonical token names. A user-picked colour
|
|
32
|
-
* flows through the identical path — a preset is just a fixed seed.
|
|
33
|
-
*/
|
|
34
|
-
/**
|
|
35
|
-
* Why a preset is not offered to every user. Absent means it is.
|
|
36
|
-
*
|
|
37
|
-
* The two are NOT interchangeable and must not collapse back into one list: a
|
|
38
|
-
* `handle` preset belongs to a specific account and cannot be bought, while a
|
|
39
|
-
* `premium` one is exactly what is bought. Conflating them is how the picker
|
|
40
|
-
* came to offer both of Oxy's reserved brand colours to everybody.
|
|
41
|
-
*/
|
|
3
|
+
/** Why a preset is not offered to every user. Absent means it is free. */
|
|
42
4
|
export type ColorPresetGate =
|
|
43
5
|
/** Reserved for the account whose brand it is — not purchasable. */
|
|
44
6
|
| 'handle'
|
|
45
7
|
/** Sold with a subscription. */
|
|
46
8
|
| 'premium';
|
|
47
9
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
10
|
+
/** Stable picker/playground categories, ordered from specific to general. */
|
|
11
|
+
const COLOR_PRESET_FAMILY_SOURCES = [
|
|
12
|
+
{ name: 'brand', displayName: 'Brand', description: 'Reserved ecosystem identities.' },
|
|
13
|
+
{ name: 'botanical', displayName: 'Botanical', description: 'Greens, jade and living hues.' },
|
|
14
|
+
{ name: 'ocean', displayName: 'Ocean', description: 'Blue, cyan and aquatic identities.' },
|
|
15
|
+
{ name: 'sunset', displayName: 'Sunset', description: 'Red, orange and golden identities.' },
|
|
16
|
+
{ name: 'earth', displayName: 'Earth', description: 'Warm mineral and grounded identities.' },
|
|
17
|
+
{ name: 'jewel', displayName: 'Jewel', description: 'Purple, rose and saturated gem hues.' },
|
|
18
|
+
{ name: 'neutral', displayName: 'Neutral', description: 'Slate, graphite and monochrome.' },
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
export type ColorPresetFamily = (typeof COLOR_PRESET_FAMILY_SOURCES)[number]['name'];
|
|
22
|
+
|
|
23
|
+
export interface ColorPresetFamilyDefinition {
|
|
24
|
+
name: ColorPresetFamily;
|
|
25
|
+
displayName: string;
|
|
26
|
+
description: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const COLOR_PRESET_FAMILY_REGISTRY: readonly ColorPresetFamilyDefinition[] =
|
|
30
|
+
COLOR_PRESET_FAMILY_SOURCES;
|
|
31
|
+
|
|
32
|
+
export const COLOR_PRESET_FAMILIES: readonly ColorPresetFamily[] =
|
|
33
|
+
COLOR_PRESET_FAMILY_REGISTRY.map(({ name }) => name);
|
|
34
|
+
export type ColorPresetPairing = 'derived' | 'curated';
|
|
35
|
+
|
|
36
|
+
interface ColorPresetSource {
|
|
37
|
+
name: string;
|
|
38
|
+
/** Human-readable label; consumers must not rebuild one from the identifier. */
|
|
39
|
+
displayName: string;
|
|
40
|
+
family: ColorPresetFamily;
|
|
41
|
+
description: string;
|
|
42
|
+
/** The identity seed, `#rrggbb`. */
|
|
51
43
|
hex: string;
|
|
52
|
-
/** The tonal scheme variant the engine derives roles with. */
|
|
53
44
|
variant: SchemeVariant;
|
|
54
|
-
/**
|
|
55
|
-
* Optional explicit secondary-accent seed, `#rrggbb`. When set, the secondary
|
|
56
|
-
* palette is PINNED to this brand colour (hue + chroma preserved; M3 role tones
|
|
57
|
-
* still apply) instead of the derived hue-rotation of {@link AppColorPreset.hex}.
|
|
58
|
-
* Bloom's built-in presets do NOT set this, so their generated output is
|
|
59
|
-
* unchanged; a consumer brand with a distinct accent (blue primary + yellow
|
|
60
|
-
* secondary) sets it to pin its real accent family.
|
|
61
|
-
*/
|
|
62
45
|
secondaryHex?: string;
|
|
63
|
-
/**
|
|
64
|
-
* Optional explicit tertiary-accent seed, `#rrggbb`. Same semantics as
|
|
65
|
-
* {@link AppColorPreset.secondaryHex} for the tertiary palette.
|
|
66
|
-
*/
|
|
46
|
+
/** The standout action seed. `tertiary` owns FABs and compose actions. */
|
|
67
47
|
tertiaryHex?: string;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
*
|
|
73
|
-
* It has to be declared, because it cannot be derived: `yellow` (seed tone 86)
|
|
74
|
-
* wants to come down to a deep gold with white on it, and `faircoin` (tone 90)
|
|
75
|
-
* wants to stay a bright lime with black on it. Two seeds four tones apart,
|
|
76
|
-
* opposite answers — nothing in the hex distinguishes them, only what the brand
|
|
77
|
-
* is for.
|
|
78
|
-
*/
|
|
48
|
+
pairing: ColorPresetPairing;
|
|
49
|
+
/** Included in Bloom's permanent Mention-shaped colour playground. */
|
|
50
|
+
featured?: boolean;
|
|
51
|
+
/** Force a white primary label even when preserving the seed would choose black. */
|
|
79
52
|
label?: 'white';
|
|
80
|
-
/**
|
|
81
|
-
* Who may pick this preset. Absent = everyone. Bloom only DECLARES the gate;
|
|
82
|
-
* whether a given viewer satisfies it is the consuming app's question, since
|
|
83
|
-
* only the app knows who is signed in and what they pay for.
|
|
84
|
-
*/
|
|
85
53
|
gate?: ColorPresetGate;
|
|
86
54
|
}
|
|
87
55
|
|
|
88
56
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
57
|
+
* The ONE authored preset source.
|
|
58
|
+
*
|
|
59
|
+
* Everything public below — the name union, ordered registry, lookup record,
|
|
60
|
+
* hex lookup, gate lists, family groups and playground list — is derived from
|
|
61
|
+
* this tuple. A preset is a seed recipe, never a frozen light/dark token table;
|
|
62
|
+
* Bloom's colour policy resolves both modes at runtime.
|
|
93
63
|
*/
|
|
94
|
-
|
|
64
|
+
const COLOR_PRESET_SOURCES = [
|
|
65
|
+
{
|
|
66
|
+
name: 'teal',
|
|
67
|
+
displayName: 'Teal',
|
|
68
|
+
family: 'ocean',
|
|
69
|
+
description: 'Deep teal identity with dynamically derived split-complementary accents.',
|
|
70
|
+
hex: '#005c67',
|
|
71
|
+
variant: 'vivid',
|
|
72
|
+
pairing: 'derived',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'blue',
|
|
76
|
+
displayName: 'Blue',
|
|
77
|
+
family: 'ocean',
|
|
78
|
+
description: 'Clear social blue with vivid generated supporting colours.',
|
|
79
|
+
hex: '#1d9bf0',
|
|
80
|
+
variant: 'vivid',
|
|
81
|
+
pairing: 'derived',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'green',
|
|
85
|
+
displayName: 'Green',
|
|
86
|
+
family: 'botanical',
|
|
87
|
+
description: 'Fresh green identity with dynamically generated contrast accents.',
|
|
88
|
+
hex: '#10b981',
|
|
89
|
+
variant: 'vivid',
|
|
90
|
+
pairing: 'derived',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'yellow',
|
|
94
|
+
displayName: 'Yellow',
|
|
95
|
+
family: 'sunset',
|
|
96
|
+
description: 'A signal-yellow identity deliberately brought deep enough for a white label.',
|
|
97
|
+
hex: '#ffd400',
|
|
98
|
+
variant: 'vivid',
|
|
99
|
+
pairing: 'derived',
|
|
100
|
+
label: 'white',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'red',
|
|
104
|
+
displayName: 'Red',
|
|
105
|
+
family: 'sunset',
|
|
106
|
+
description: 'Direct red identity with contrasting generated accent arms.',
|
|
107
|
+
hex: '#ef4444',
|
|
108
|
+
variant: 'vivid',
|
|
109
|
+
pairing: 'derived',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'purple',
|
|
113
|
+
displayName: 'Purple',
|
|
114
|
+
family: 'jewel',
|
|
115
|
+
description: 'Bright purple identity with dynamically derived action colours.',
|
|
116
|
+
hex: '#b866ff',
|
|
117
|
+
variant: 'vivid',
|
|
118
|
+
pairing: 'derived',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'pink',
|
|
122
|
+
displayName: 'Pink',
|
|
123
|
+
family: 'jewel',
|
|
124
|
+
description: 'Hot pink identity with a vivid generated complement.',
|
|
125
|
+
hex: '#f91880',
|
|
126
|
+
variant: 'vivid',
|
|
127
|
+
pairing: 'derived',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'sky',
|
|
131
|
+
displayName: 'Sky',
|
|
132
|
+
family: 'ocean',
|
|
133
|
+
description: 'Open sky blue with dynamically generated supporting accents.',
|
|
134
|
+
hex: '#03a9f4',
|
|
135
|
+
variant: 'vivid',
|
|
136
|
+
pairing: 'derived',
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: 'orange',
|
|
140
|
+
displayName: 'Orange',
|
|
141
|
+
family: 'sunset',
|
|
142
|
+
description: 'A gamut-aware orange that stays orange at white-label fill tones.',
|
|
143
|
+
hex: '#ff7a00',
|
|
144
|
+
variant: 'vivid',
|
|
145
|
+
pairing: 'derived',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: 'mint',
|
|
149
|
+
displayName: 'Mint',
|
|
150
|
+
family: 'botanical',
|
|
151
|
+
description: 'Cool mint identity with vivid generated supporting hues.',
|
|
152
|
+
hex: '#14b8a6',
|
|
153
|
+
variant: 'vivid',
|
|
154
|
+
pairing: 'derived',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: 'oxy',
|
|
158
|
+
displayName: 'Oxy',
|
|
159
|
+
family: 'brand',
|
|
160
|
+
description: 'Oxy purple, reserved for the Oxy account identity.',
|
|
161
|
+
hex: '#c46ede',
|
|
162
|
+
variant: 'vivid',
|
|
163
|
+
pairing: 'derived',
|
|
164
|
+
gate: 'handle',
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: 'faircoin',
|
|
168
|
+
displayName: 'FairCoin',
|
|
169
|
+
family: 'brand',
|
|
170
|
+
description: 'FairCoin lime, reserved for the FairCoin account identity.',
|
|
171
|
+
hex: '#9ffb50',
|
|
172
|
+
variant: 'vivid',
|
|
173
|
+
pairing: 'derived',
|
|
174
|
+
gate: 'handle',
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: 'pumpkin',
|
|
178
|
+
displayName: 'Pumpkin',
|
|
179
|
+
family: 'sunset',
|
|
180
|
+
description: 'Golden pumpkin orange with a generated split complement.',
|
|
181
|
+
hex: '#ff9800',
|
|
182
|
+
variant: 'vivid',
|
|
183
|
+
pairing: 'derived',
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: 'gray',
|
|
187
|
+
displayName: 'Slate',
|
|
188
|
+
family: 'neutral',
|
|
189
|
+
description: 'Cool slate identity with restrained chroma and generated accents.',
|
|
190
|
+
hex: '#607d8b',
|
|
191
|
+
variant: 'vivid',
|
|
192
|
+
pairing: 'derived',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'brown',
|
|
196
|
+
displayName: 'Brown',
|
|
197
|
+
family: 'earth',
|
|
198
|
+
description: 'Deep earthen brown with a contrasting generated action colour.',
|
|
199
|
+
hex: '#813519',
|
|
200
|
+
variant: 'vivid',
|
|
201
|
+
pairing: 'derived',
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: 'peach',
|
|
205
|
+
displayName: 'Peach',
|
|
206
|
+
family: 'earth',
|
|
207
|
+
description: 'Warm peach identity with dynamically derived contrast accents.',
|
|
208
|
+
hex: '#ffb28d',
|
|
209
|
+
variant: 'vivid',
|
|
210
|
+
pairing: 'derived',
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: 'rose',
|
|
214
|
+
displayName: 'Rose',
|
|
215
|
+
family: 'jewel',
|
|
216
|
+
description: 'Light rose identity with generated vivid supporting hues.',
|
|
217
|
+
hex: '#fcaffe',
|
|
218
|
+
variant: 'vivid',
|
|
219
|
+
pairing: 'derived',
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: 'mono',
|
|
223
|
+
displayName: 'Monochrome',
|
|
224
|
+
family: 'neutral',
|
|
225
|
+
description: 'Colourless identity and surfaces, derived as a true greyscale theme.',
|
|
226
|
+
hex: '#000000',
|
|
227
|
+
variant: 'monochrome',
|
|
228
|
+
pairing: 'derived',
|
|
229
|
+
gate: 'premium',
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
name: 'grove',
|
|
233
|
+
displayName: 'Grove + Marigold',
|
|
234
|
+
family: 'botanical',
|
|
235
|
+
description: 'Green identity with warm marigold reserved for the action that must win.',
|
|
236
|
+
hex: '#087a3e',
|
|
237
|
+
tertiaryHex: '#ffb000',
|
|
238
|
+
variant: 'vivid',
|
|
239
|
+
pairing: 'curated',
|
|
240
|
+
featured: true,
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: 'ember',
|
|
244
|
+
displayName: 'Terracotta + Saffron',
|
|
245
|
+
family: 'earth',
|
|
246
|
+
description: 'Terracotta identity with a high-energy saffron action.',
|
|
247
|
+
hex: '#b52f0c',
|
|
248
|
+
tertiaryHex: '#ffb000',
|
|
249
|
+
variant: 'vivid',
|
|
250
|
+
pairing: 'curated',
|
|
251
|
+
featured: true,
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
name: 'merlot',
|
|
255
|
+
displayName: 'Oxblood + Coral',
|
|
256
|
+
family: 'jewel',
|
|
257
|
+
description: 'Deep wine identity with coral action, expressive without reading destructive.',
|
|
258
|
+
hex: '#a5003c',
|
|
259
|
+
tertiaryHex: '#ff542e',
|
|
260
|
+
variant: 'vivid',
|
|
261
|
+
pairing: 'curated',
|
|
262
|
+
featured: true,
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: 'cobalt',
|
|
266
|
+
displayName: 'Cobalt + Signal Yellow',
|
|
267
|
+
family: 'ocean',
|
|
268
|
+
description: 'Cobalt structure cut by a saturated signal-yellow action.',
|
|
269
|
+
hex: '#154fd7',
|
|
270
|
+
tertiaryHex: '#ffd000',
|
|
271
|
+
variant: 'vivid',
|
|
272
|
+
pairing: 'curated',
|
|
273
|
+
featured: true,
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: 'jade',
|
|
277
|
+
displayName: 'Jade + Tangerine',
|
|
278
|
+
family: 'botanical',
|
|
279
|
+
description: 'Clean jade identity with a direct tangerine action.',
|
|
280
|
+
hex: '#007a4b',
|
|
281
|
+
tertiaryHex: '#ff5e00',
|
|
282
|
+
variant: 'vivid',
|
|
283
|
+
pairing: 'curated',
|
|
284
|
+
featured: true,
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: 'pine',
|
|
288
|
+
displayName: 'Pine + Rose',
|
|
289
|
+
family: 'botanical',
|
|
290
|
+
description: 'Sober pine identity with a vivid rose action.',
|
|
291
|
+
hex: '#135c2d',
|
|
292
|
+
tertiaryHex: '#f0005a',
|
|
293
|
+
variant: 'vivid',
|
|
294
|
+
pairing: 'curated',
|
|
295
|
+
featured: true,
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
name: 'olive',
|
|
299
|
+
displayName: 'Olive + Lilac',
|
|
300
|
+
family: 'earth',
|
|
301
|
+
description: 'Editorial olive identity with an unexpected clear lilac action.',
|
|
302
|
+
hex: '#5f6400',
|
|
303
|
+
tertiaryHex: '#7c25e8',
|
|
304
|
+
variant: 'vivid',
|
|
305
|
+
pairing: 'curated',
|
|
306
|
+
featured: true,
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
name: 'lagoon',
|
|
310
|
+
displayName: 'Teal + Coral',
|
|
311
|
+
family: 'ocean',
|
|
312
|
+
description: 'Aquatic teal identity with a warm coral action.',
|
|
313
|
+
hex: '#00706d',
|
|
314
|
+
tertiaryHex: '#ff4935',
|
|
315
|
+
variant: 'vivid',
|
|
316
|
+
pairing: 'curated',
|
|
317
|
+
featured: true,
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
name: 'navy',
|
|
321
|
+
displayName: 'Navy + Apricot',
|
|
322
|
+
family: 'ocean',
|
|
323
|
+
description: 'Serious navy identity softened by a human apricot action.',
|
|
324
|
+
hex: '#0b2c72',
|
|
325
|
+
tertiaryHex: '#ff6b20',
|
|
326
|
+
variant: 'vivid',
|
|
327
|
+
pairing: 'curated',
|
|
328
|
+
featured: true,
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
name: 'azure',
|
|
332
|
+
displayName: 'Sky + Vermilion',
|
|
333
|
+
family: 'ocean',
|
|
334
|
+
description: 'Airy sky identity with a decisive vermilion action.',
|
|
335
|
+
hex: '#006a94',
|
|
336
|
+
tertiaryHex: '#e82e12',
|
|
337
|
+
variant: 'vivid',
|
|
338
|
+
pairing: 'curated',
|
|
339
|
+
featured: true,
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: 'violet',
|
|
343
|
+
displayName: 'Violet + Lime',
|
|
344
|
+
family: 'jewel',
|
|
345
|
+
description: 'Deep violet identity with an electric lime action.',
|
|
346
|
+
hex: '#7227c4',
|
|
347
|
+
tertiaryHex: '#78c900',
|
|
348
|
+
variant: 'vivid',
|
|
349
|
+
pairing: 'curated',
|
|
350
|
+
featured: true,
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
name: 'lavender',
|
|
354
|
+
displayName: 'Lavender + Brass',
|
|
355
|
+
family: 'jewel',
|
|
356
|
+
description: 'Calm lavender identity with a warmer, more editorial brass action.',
|
|
357
|
+
hex: '#5c3db5',
|
|
358
|
+
tertiaryHex: '#d98900',
|
|
359
|
+
variant: 'vivid',
|
|
360
|
+
pairing: 'curated',
|
|
361
|
+
featured: true,
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: 'plum',
|
|
365
|
+
displayName: 'Plum + Aqua',
|
|
366
|
+
family: 'jewel',
|
|
367
|
+
description: 'Nocturnal plum identity with a bright aqua action.',
|
|
368
|
+
hex: '#8d075f',
|
|
369
|
+
tertiaryHex: '#00aeb8',
|
|
370
|
+
variant: 'vivid',
|
|
371
|
+
pairing: 'curated',
|
|
372
|
+
featured: true,
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
name: 'cherry',
|
|
376
|
+
displayName: 'Cherry + Powder Blue',
|
|
377
|
+
family: 'jewel',
|
|
378
|
+
description: 'Cherry identity with a cool powder-blue counterpoint.',
|
|
379
|
+
hex: '#b00038',
|
|
380
|
+
tertiaryHex: '#2979e8',
|
|
381
|
+
variant: 'vivid',
|
|
382
|
+
pairing: 'curated',
|
|
383
|
+
featured: true,
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
name: 'chocolate',
|
|
387
|
+
displayName: 'Chocolate + Cyan',
|
|
388
|
+
family: 'earth',
|
|
389
|
+
description: 'Material chocolate identity with a crisp technological cyan action.',
|
|
390
|
+
hex: '#7a2a00',
|
|
391
|
+
tertiaryHex: '#00aabe',
|
|
392
|
+
variant: 'vivid',
|
|
393
|
+
pairing: 'curated',
|
|
394
|
+
featured: true,
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
name: 'graphite',
|
|
398
|
+
displayName: 'Graphite + Acid',
|
|
399
|
+
family: 'neutral',
|
|
400
|
+
description: 'Near-neutral identity that gives the stage to an acid-green action.',
|
|
401
|
+
hex: '#272b28',
|
|
402
|
+
tertiaryHex: '#78c900',
|
|
403
|
+
variant: 'vivid',
|
|
404
|
+
pairing: 'curated',
|
|
405
|
+
featured: true,
|
|
406
|
+
},
|
|
407
|
+
] as const satisfies readonly ColorPresetSource[];
|
|
95
408
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
409
|
+
/** Preset identifiers are inferred from the authored registry, never repeated. */
|
|
410
|
+
export type AppColorName = (typeof COLOR_PRESET_SOURCES)[number]['name'];
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Backwards-compatible named-preset contract. Rich picker metadata lives on
|
|
414
|
+
* {@link ColorPresetRecipe} so existing consumers constructing this type do not
|
|
415
|
+
* acquire new required fields in a patch release.
|
|
416
|
+
*/
|
|
417
|
+
export interface AppColorPreset {
|
|
418
|
+
name: AppColorName;
|
|
419
|
+
hex: string;
|
|
420
|
+
variant: SchemeVariant;
|
|
421
|
+
secondaryHex?: string;
|
|
422
|
+
tertiaryHex?: string;
|
|
423
|
+
label?: 'white';
|
|
424
|
+
gate?: ColorPresetGate;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** Enriched registry entry for a picker or colour playground. */
|
|
428
|
+
export interface ColorPresetRecipe extends AppColorPreset {
|
|
429
|
+
displayName: string;
|
|
430
|
+
family: ColorPresetFamily;
|
|
431
|
+
description: string;
|
|
432
|
+
pairing: ColorPresetPairing;
|
|
433
|
+
featured: boolean;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/** Stable ordered entries for pickers, filters and the Storybook playground. */
|
|
437
|
+
export const COLOR_PRESET_REGISTRY: readonly ColorPresetRecipe[] = COLOR_PRESET_SOURCES.map(
|
|
438
|
+
(source) => ({ featured: false, ...source }),
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
export const APP_COLOR_NAMES: readonly AppColorName[] = COLOR_PRESET_REGISTRY.map(
|
|
442
|
+
({ name }) => name,
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
export const APP_COLOR_PRESETS: Record<AppColorName, AppColorPreset> =
|
|
446
|
+
COLOR_PRESET_REGISTRY.reduce<Record<AppColorName, AppColorPreset>>((presets, preset) => {
|
|
447
|
+
// Preserve the historical runtime shape as well as its TypeScript type.
|
|
448
|
+
// Picker metadata belongs to COLOR_PRESET_REGISTRY; adding it invisibly to
|
|
449
|
+
// this older record would change Object.keys()/serialization in consumers.
|
|
450
|
+
presets[preset.name] = {
|
|
451
|
+
name: preset.name,
|
|
452
|
+
hex: preset.hex,
|
|
453
|
+
variant: preset.variant,
|
|
454
|
+
...(preset.secondaryHex !== undefined ? { secondaryHex: preset.secondaryHex } : {}),
|
|
455
|
+
...(preset.tertiaryHex !== undefined ? { tertiaryHex: preset.tertiaryHex } : {}),
|
|
456
|
+
...(preset.label !== undefined ? { label: preset.label } : {}),
|
|
457
|
+
...(preset.gate !== undefined ? { gate: preset.gate } : {}),
|
|
458
|
+
};
|
|
459
|
+
return presets;
|
|
460
|
+
}, {} as Record<AppColorName, AppColorPreset>);
|
|
461
|
+
|
|
462
|
+
export const HEX_TO_APP_COLOR: Record<string, AppColorName> =
|
|
463
|
+
COLOR_PRESET_REGISTRY.reduce<Record<string, AppColorName>>((names, preset) => {
|
|
464
|
+
names[preset.hex.toLowerCase()] = preset.name;
|
|
465
|
+
return names;
|
|
466
|
+
}, {});
|
|
118
467
|
|
|
119
468
|
export function hexToAppColorName(hex: string): AppColorName {
|
|
120
469
|
return HEX_TO_APP_COLOR[hex.toLowerCase()] ?? 'teal';
|
|
121
470
|
}
|
|
122
471
|
|
|
123
|
-
/**
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
orange: { name: 'orange', hex: '#ff7a00', variant: 'vivid' },
|
|
145
|
-
mint: { name: 'mint', hex: '#14b8a6', variant: 'vivid' },
|
|
146
|
-
oxy: { name: 'oxy', hex: '#c46ede', variant: 'vivid', gate: 'handle' },
|
|
147
|
-
faircoin: { name: 'faircoin', hex: '#9ffb50', variant: 'vivid', gate: 'handle' },
|
|
148
|
-
pumpkin: { name: 'pumpkin', hex: '#ff9800', variant: 'vivid' },
|
|
149
|
-
gray: { name: 'gray', hex: '#607d8b', variant: 'vivid' },
|
|
150
|
-
brown: { name: 'brown', hex: '#813519', variant: 'vivid' },
|
|
151
|
-
peach: { name: 'peach', hex: '#ffb28d', variant: 'vivid' },
|
|
152
|
-
rose: { name: 'rose', hex: '#fcaffe', variant: 'vivid' },
|
|
153
|
-
/**
|
|
154
|
-
* No colour at all — the black-and-white theme. Its seed carries zero chroma,
|
|
155
|
-
* which is the whole mechanism: the policy derives a greyscale palette from any
|
|
156
|
-
* seed with none, so a user who picks a grey in the colour wheel lands here too
|
|
157
|
-
* rather than on a nearly-grey approximation.
|
|
158
|
-
*/
|
|
159
|
-
mono: { name: 'mono', hex: '#000000', variant: 'monochrome', gate: 'premium' },
|
|
160
|
-
};
|
|
472
|
+
/** The sixteen approved paired directions in the permanent Mention playground. */
|
|
473
|
+
export const COLOR_PLAYGROUND_NAMES: readonly AppColorName[] = COLOR_PRESET_REGISTRY
|
|
474
|
+
.filter(({ featured }) => featured)
|
|
475
|
+
.map(({ name }) => name);
|
|
476
|
+
|
|
477
|
+
export interface ColorPresetGroup extends ColorPresetFamilyDefinition {
|
|
478
|
+
presets: readonly ColorPresetRecipe[];
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/** Ordered family metadata and entries for consumer pickers. */
|
|
482
|
+
export const COLOR_PRESET_GROUPS: Readonly<Record<ColorPresetFamily, ColorPresetGroup>> =
|
|
483
|
+
COLOR_PRESET_FAMILY_REGISTRY.reduce<Record<ColorPresetFamily, ColorPresetGroup>>(
|
|
484
|
+
(groups, family) => {
|
|
485
|
+
groups[family.name] = {
|
|
486
|
+
...family,
|
|
487
|
+
presets: COLOR_PRESET_REGISTRY.filter((preset) => preset.family === family.name),
|
|
488
|
+
};
|
|
489
|
+
return groups;
|
|
490
|
+
},
|
|
491
|
+
{} as Record<ColorPresetFamily, ColorPresetGroup>,
|
|
492
|
+
);
|
|
161
493
|
|
|
162
494
|
/**
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
* partition {@link APP_COLOR_NAMES} exactly — asserted in
|
|
166
|
-
* `__tests__/color-preset-gates.test.ts`, since a preset silently missing from
|
|
167
|
-
* every group would simply vanish from a picker with no error anywhere.
|
|
168
|
-
*
|
|
169
|
-
* A consumer builds its visible list from these; it must NOT start from
|
|
170
|
-
* `APP_COLOR_NAMES` and add the unlocked ones, which yields the gated presets to
|
|
171
|
-
* everybody and duplicates whatever the viewer did unlock.
|
|
495
|
+
* Historically each preset carried a full light/dark map of raw HSL triples.
|
|
496
|
+
* The engine now derives every role from the registry's seeds.
|
|
172
497
|
*/
|
|
498
|
+
export type PresetTokens = Record<string, string>;
|
|
499
|
+
|
|
173
500
|
const namesGatedBy = (gate: ColorPresetGate | undefined): readonly AppColorName[] =>
|
|
174
|
-
|
|
501
|
+
COLOR_PRESET_REGISTRY.filter((preset) => preset.gate === gate).map(({ name }) => name);
|
|
175
502
|
|
|
176
503
|
/** Available to every user, signed in or not. */
|
|
177
504
|
export const FREE_COLOR_NAMES: readonly AppColorName[] = namesGatedBy(undefined);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { generateRoleColors, type RoleColors, type SchemeVariant } from '../color-engine';
|
|
2
2
|
import { buildPolicyTokens, isColourlessSeed } from '../color-policy';
|
|
3
|
-
import { BORDER_ROLES, FILL_ROLES, TEXT_ROLES } from '../../design-tokens/color-roles';
|
|
3
|
+
import { ACCENT_TEXT_ROLES, BORDER_ROLES, FILL_ROLES, TEXT_ROLES } from '../../design-tokens/color-roles';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Every alias `theme.css` declares at `:root` as a reference to a canonical
|
|
@@ -19,6 +19,7 @@ import { BORDER_ROLES, FILL_ROLES, TEXT_ROLES } from '../../design-tokens/color-
|
|
|
19
19
|
const SCOPED_ALIASES: Readonly<Record<string, string>> = {
|
|
20
20
|
...FILL_ROLES,
|
|
21
21
|
...TEXT_ROLES,
|
|
22
|
+
...ACCENT_TEXT_ROLES,
|
|
22
23
|
...BORDER_ROLES,
|
|
23
24
|
divider: 'var(--border)',
|
|
24
25
|
};
|