@evolve.ui/theme-system 1.0.1 → 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.
Files changed (119) hide show
  1. package/{allThemes-CFS_9-tk.cjs → allThemes-3d8nhymo.cjs} +12 -2
  2. package/allThemes-3d8nhymo.cjs.map +1 -0
  3. package/{allThemes-OV-ayn4C.mjs → allThemes-Abn6KDm7.mjs} +7 -3
  4. package/allThemes-Abn6KDm7.mjs.map +1 -0
  5. package/allThemes.cjs +1 -1
  6. package/allThemes.d.cts +1 -0
  7. package/allThemes.d.cts.map +1 -1
  8. package/allThemes.d.mts +1 -0
  9. package/allThemes.d.mts.map +1 -1
  10. package/allThemes.mjs +1 -1
  11. package/brands-CE0wIkKX.d.cts +58 -0
  12. package/brands-CE0wIkKX.d.cts.map +1 -0
  13. package/brands-CE0wIkKX.d.mts +58 -0
  14. package/brands-CE0wIkKX.d.mts.map +1 -0
  15. package/brands.cjs +16 -13
  16. package/brands.cjs.map +1 -1
  17. package/brands.d.cts +2 -2
  18. package/brands.d.mts +2 -2
  19. package/brands.mjs +16 -13
  20. package/brands.mjs.map +1 -1
  21. package/css/EverydayHarbor.css +377 -0
  22. package/css/EverydayHarbor.effects.css +650 -0
  23. package/{default-b-tgfyxR.cjs → default-B6VWF0Js.cjs} +237 -36
  24. package/default-B6VWF0Js.cjs.map +1 -0
  25. package/{default-B2n4PnsJ.mjs → default-BogJDhKE.mjs} +238 -37
  26. package/default-BogJDhKE.mjs.map +1 -0
  27. package/effects.cjs +26 -0
  28. package/effects.cjs.map +1 -0
  29. package/effects.d.cts +22 -0
  30. package/effects.d.cts.map +1 -0
  31. package/effects.d.mts +22 -0
  32. package/effects.d.mts.map +1 -0
  33. package/effects.mjs +22 -0
  34. package/effects.mjs.map +1 -0
  35. package/evolve-ui.entries.json +51 -0
  36. package/index.cjs +12 -5
  37. package/index.cjs.map +1 -1
  38. package/index.d.cts +108 -4
  39. package/index.d.cts.map +1 -1
  40. package/index.d.mts +108 -4
  41. package/index.d.mts.map +1 -1
  42. package/index.mjs +9 -6
  43. package/index.mjs.map +1 -1
  44. package/package.json +5 -1
  45. package/{plugin-DPtvp4lp.cjs → plugin-B3PF4RMI.cjs} +299 -110
  46. package/plugin-B3PF4RMI.cjs.map +1 -0
  47. package/{plugin-D4eCyUJe.mjs → plugin-D6aLCeNB.mjs} +258 -111
  48. package/plugin-D6aLCeNB.mjs.map +1 -0
  49. package/src/effects/__fixtures__/fixtureLayer.ts +88 -0
  50. package/src/effects/brands/EverydayHarbor.ts +392 -0
  51. package/src/effects/classes.ts +19 -0
  52. package/src/effects/emit.ts +314 -0
  53. package/src/effects/hooks.ts +493 -0
  54. package/src/effects/index.ts +2 -0
  55. package/src/effects/layers.ts +18 -0
  56. package/src/effects/rules.ts +90 -0
  57. package/src/effects/rules.typecheck.ts +50 -0
  58. package/src/effects/tokens.ts +148 -0
  59. package/src/effects/validate.ts +683 -0
  60. package/src/effects/vocabulary.ts +32 -0
  61. package/src/index.ts +40 -0
  62. package/src/tailwind/animation.ts +30 -0
  63. package/src/tailwind/borderRadius.ts +32 -0
  64. package/src/tailwind/borderWidth.ts +14 -0
  65. package/src/tailwind/boxShadow.ts +19 -0
  66. package/src/tailwind/colors.ts +332 -0
  67. package/src/tailwind/cssVars.ts +301 -0
  68. package/src/tailwind/fontFamily.ts +14 -0
  69. package/src/tailwind/plugin.ts +299 -0
  70. package/src/tailwind/reducedMotion.ts +164 -0
  71. package/src/tailwind/screens.ts +22 -0
  72. package/src/tailwind/spacing.ts +20 -0
  73. package/src/tailwind/text.ts +172 -0
  74. package/src/tailwind/width.ts +14 -0
  75. package/src/themeValues.snapshot.ts +6735 -0
  76. package/src/themes/BasketballTraining.ts +166 -0
  77. package/src/themes/Default.ts +165 -0
  78. package/src/themes/EverydayHarbor.ts +147 -0
  79. package/src/themes/ProfessionalBrand.ts +166 -0
  80. package/src/themes/allThemes.ts +14 -0
  81. package/src/themes/brandSources.ts +37 -0
  82. package/src/themes/brands.ts +63 -0
  83. package/src/themes/common.ts +65 -0
  84. package/src/themes/contrastFloors.ts +26 -0
  85. package/src/themes/generateCSSVars.ts +123 -0
  86. package/src/themes/inkRamps.ts +43 -0
  87. package/src/themes/runtime/basketballTraining.ts +3 -0
  88. package/src/themes/runtime/buildTheme.ts +411 -0
  89. package/src/themes/runtime/default.ts +3 -0
  90. package/src/themes/runtime/everydayHarbor.ts +3 -0
  91. package/src/themes/runtime/professionalBrand.ts +3 -0
  92. package/src/themes/semanticColorTokenSteps.ts +23 -0
  93. package/src/themes/themeType.ts +169 -0
  94. package/src/themes/themeValues.ts +374 -0
  95. package/src/themes/wcag.ts +48 -0
  96. package/src/themes/writeToCSSFile.ts +11 -0
  97. package/src/utils/cn.ts +35 -0
  98. package/src/utils/color.ts +82 -0
  99. package/src/utils/wrapVar.ts +24 -0
  100. package/tailwind/plugin.cjs +5 -1
  101. package/tailwind/plugin.d.cts +458 -1
  102. package/tailwind/plugin.d.cts.map +1 -1
  103. package/tailwind/plugin.d.mts +458 -1
  104. package/tailwind/plugin.d.mts.map +1 -1
  105. package/tailwind/plugin.mjs +2 -2
  106. package/themes/runtime/default.cjs +1 -1
  107. package/themes/runtime/default.mjs +1 -1
  108. package/themes/themeType.d.cts +1 -1
  109. package/themes/themeType.d.mts +1 -1
  110. package/allThemes-CFS_9-tk.cjs.map +0 -1
  111. package/allThemes-OV-ayn4C.mjs.map +0 -1
  112. package/brands-n30rolHQ.d.cts +0 -33
  113. package/brands-n30rolHQ.d.cts.map +0 -1
  114. package/brands-n30rolHQ.d.mts +0 -33
  115. package/brands-n30rolHQ.d.mts.map +0 -1
  116. package/default-B2n4PnsJ.mjs.map +0 -1
  117. package/default-b-tgfyxR.cjs.map +0 -1
  118. package/plugin-D4eCyUJe.mjs.map +0 -1
  119. package/plugin-DPtvp4lp.cjs.map +0 -1
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The one build-time table of brand palette modules. Internal: not a tsdown entry and not
3
+ * re-exported from index.ts (brands.ts stays pure data for the browser). Consumers read this instead
4
+ * of hand-listing brands: tsdown.config.ts onSuccess, runtime/buildTheme.ts, themeValues.ts,
5
+ * contrast.spec.ts and tailwind/text.spec.ts. allThemes.ts keeps its explicit map because index.ts
6
+ * re-exports the per-brand runtime themes; `satisfies Record<Brands, …>` makes a missing brand a compile error.
7
+ *
8
+ * Key order is load-bearing: the themeValues snapshot is an ORDERED entry list built by iterating
9
+ * this object, so keep BasketballTraining, Default, ProfessionalBrand and append new brands (EverydayHarbor).
10
+ */
11
+ import type { StepObject } from '../utils/color';
12
+ import type { Brands } from './brands';
13
+
14
+ import * as BasketballTraining from './BasketballTraining';
15
+ import * as Default from './Default';
16
+ import * as EverydayHarbor from './EverydayHarbor';
17
+ import { type ColorAliasesObject, type NonColorCSSVariables, makeColorCSSVarsFromObject } from './generateCSSVars';
18
+ import * as ProfessionalBrand from './ProfessionalBrand';
19
+
20
+ export type BrandModule = {
21
+ readonly coreColors: Readonly<Record<string, StepObject>>;
22
+ readonly colorAliases: ColorAliasesObject;
23
+ readonly nonColorVariables: Readonly<Record<NonColorCSSVariables, string>>;
24
+ };
25
+
26
+ export const brandSources = {
27
+ BasketballTraining,
28
+ Default,
29
+ ProfessionalBrand,
30
+ EverydayHarbor,
31
+ } as const satisfies Record<Brands, BrandModule>;
32
+
33
+ /** `{ ...nonColorVariables, ...palette vars }` in that order: exactly what the CSS generator receives. */
34
+ export const brandCssVariables = (brand: Brands): Record<string, string> => ({
35
+ ...brandSources[brand].nonColorVariables,
36
+ ...makeColorCSSVarsFromObject(brandSources[brand].colorAliases),
37
+ });
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The `Brands` union is the ONE authored list of brand names. Every other per-brand table is derived
3
+ * from `brandRegistry` or checked against this union with `satisfies Record<Brands, …>`, so a missing
4
+ * entry is a compile error rather than a silent omission.
5
+ *
6
+ * Stays pure data: this module is the published `@evolve.ui/theme-system/brands` subpath and is
7
+ * imported by Storybook in the browser, so it must not import brand palette modules.
8
+ */
9
+ export type Brands = 'BasketballTraining' | 'Default' | 'EverydayHarbor' | 'ProfessionalBrand';
10
+
11
+ /**
12
+ * `paper`: light canvas; the brand renders on `bg-background-primary`.
13
+ * `ink`: inverted canvas; Storybook frames it separately instead of in the white "All together" grid.
14
+ */
15
+ export type BrandCanvas = 'ink' | 'paper';
16
+
17
+ export type BrandRegistryEntry = {
18
+ readonly canvas: BrandCanvas;
19
+ readonly cssClass: `brand-${string}`;
20
+ readonly label: string;
21
+ /** Kept for API compatibility; always equal to the registry key. */
22
+ readonly storybookKey: Brands;
23
+ readonly themeKey: Brands;
24
+ };
25
+
26
+ export const brandRegistry = {
27
+ BasketballTraining: {
28
+ canvas: 'paper',
29
+ cssClass: 'brand-basketball',
30
+ label: 'Basketball Training',
31
+ storybookKey: 'BasketballTraining',
32
+ themeKey: 'BasketballTraining',
33
+ },
34
+ Default: {
35
+ canvas: 'paper',
36
+ cssClass: 'brand-default',
37
+ label: 'Default (Neutral)',
38
+ storybookKey: 'Default',
39
+ themeKey: 'Default',
40
+ },
41
+ ProfessionalBrand: {
42
+ canvas: 'paper',
43
+ cssClass: 'brand-professional',
44
+ label: 'Professional Brand',
45
+ storybookKey: 'ProfessionalBrand',
46
+ themeKey: 'ProfessionalBrand',
47
+ },
48
+ // Appended after the paper brands: the themeValues snapshot and `brands` order follow key order.
49
+ EverydayHarbor: {
50
+ canvas: 'ink',
51
+ cssClass: 'brand-harbor',
52
+ label: 'Everyday Harbor',
53
+ storybookKey: 'EverydayHarbor',
54
+ themeKey: 'EverydayHarbor',
55
+ },
56
+ } as const satisfies Record<Brands, BrandRegistryEntry>;
57
+
58
+ /** Registry key order (previously a hand list ordered Basketball, Professional, Default). */
59
+ export const brands: Brands[] = Object.keys(brandRegistry) as Brands[];
60
+
61
+ export const brandsToCSS: Record<Brands, string> = Object.fromEntries(
62
+ brands.map((brand) => [brand, brandRegistry[brand].cssClass])
63
+ ) as Record<Brands, string>;
@@ -0,0 +1,65 @@
1
+ import type { Theme } from './themeType';
2
+
3
+ import { borderRadius } from '../tailwind/borderRadius';
4
+ import { screens } from '../tailwind/screens';
5
+ import { spacing as tailwindSpacing } from '../tailwind/spacing';
6
+
7
+ const parsePx = (value: string): number => Number(value.replace('px', ''));
8
+
9
+ export const spacing = {
10
+ lg: tailwindSpacing.lg,
11
+ lgValue: parsePx(tailwindSpacing.lg),
12
+ md: tailwindSpacing.md,
13
+ mdValue: parsePx(tailwindSpacing.md),
14
+ sm: tailwindSpacing.sm,
15
+ smValue: parsePx(tailwindSpacing.sm),
16
+ xl: tailwindSpacing.xl,
17
+ xlValue: parsePx(tailwindSpacing.xl),
18
+ xs: tailwindSpacing.xs,
19
+ xsValue: parsePx(tailwindSpacing.xs),
20
+ xxl: tailwindSpacing['2xl'],
21
+ xxlValue: parsePx(tailwindSpacing['2xl']),
22
+ xxs: tailwindSpacing['2xs'],
23
+ xxsValue: parsePx(tailwindSpacing['2xs']),
24
+ xxxl: tailwindSpacing['3xl'],
25
+ xxxlValue: parsePx(tailwindSpacing['3xl']),
26
+ xxxs: tailwindSpacing['3xs'],
27
+ xxxsValue: parsePx(tailwindSpacing['3xs']),
28
+ } as const satisfies Theme['spacing'];
29
+
30
+ export const breakpoints = {
31
+ lgMax: screens.lgMax.max,
32
+ lgMaxValue: parsePx(screens.lgMax.max),
33
+ lgMin: screens.lgMin.min,
34
+ lgMinValue: parsePx(screens.lgMin.min),
35
+ mdMax: screens.mdMax.max,
36
+ mdMaxValue: parsePx(screens.mdMax.max),
37
+ mdMin: screens.mdMin.min,
38
+ mdMinValue: parsePx(screens.mdMin.min),
39
+ smMax: screens.smMax.max,
40
+ smMaxValue: parsePx(screens.smMax.max),
41
+ smMin: screens.smMin.min,
42
+ smMinValue: parsePx(screens.smMin.min),
43
+ xlMin: screens.xlMin.min,
44
+ xlMinValue: parsePx(screens.xlMin.min),
45
+ xsMax: screens.xsMax.max,
46
+ xsMaxValue: parsePx(screens.xsMax.max),
47
+ } as const satisfies Theme['breakpoints'];
48
+
49
+ export const radius = {
50
+ circle: '50%',
51
+ lg: borderRadius.lg,
52
+ md: borderRadius.md,
53
+ sm: borderRadius.sm,
54
+ xl: borderRadius.xl,
55
+ xs: borderRadius.xs,
56
+ zero: borderRadius.none,
57
+ } as const satisfies Theme['border']['radius'];
58
+
59
+ export const width = {
60
+ lg: '8px',
61
+ md: '4px',
62
+ sm: '2px',
63
+ xs: '1px',
64
+ xxs: '0.5px',
65
+ } as const satisfies Theme['border']['width'];
@@ -0,0 +1,26 @@
1
+ export const TEXT_AA = 4.5;
2
+
3
+ export const TEXT_AAA_BODY = 7;
4
+
5
+ export const UI_AA = 3;
6
+
7
+ export const TEXT_CONTRAST_TOKENS = [
8
+ ['--color-link-primary', TEXT_AA],
9
+ ['--color-text-critical', TEXT_AA],
10
+ ['--color-text-secondary', TEXT_AA],
11
+ ['--color-text-info', TEXT_AA],
12
+ ['--color-text-warning', TEXT_AA],
13
+ ['--color-text-success', TEXT_AA],
14
+ ] as const;
15
+
16
+ export const UI_CONTRAST_TOKENS = [
17
+ '--color-focus-primary',
18
+ '--color-icon-critical',
19
+ '--color-icon-info',
20
+ '--color-icon-warning',
21
+ '--color-icon-success',
22
+ ] as const;
23
+
24
+ export const FILLED_BUTTON_FAMILIES = ['primary', 'critical', 'surface'] as const;
25
+
26
+ export const FILLED_BUTTON_STEPS = ['500', '700', '900'] as const;
@@ -0,0 +1,123 @@
1
+ import type * as CSSVars from '../tailwind/cssVars';
2
+
3
+ import { StepObject, steps } from '../utils/color';
4
+
5
+ export type CSSVariables = (typeof CSSVars)[keyof typeof CSSVars];
6
+ export type NonColorCSSVariables = Exclude<
7
+ CSSVariables,
8
+ | '--color-primary-50'
9
+ | '--color-primary-100'
10
+ | '--color-primary-200'
11
+ | '--color-primary-300'
12
+ | '--color-primary-400'
13
+ | '--color-primary-500'
14
+ | '--color-primary-600'
15
+ | '--color-primary-700'
16
+ | '--color-primary-800'
17
+ | '--color-primary-900'
18
+ | '--color-secondary-50'
19
+ | '--color-secondary-100'
20
+ | '--color-secondary-200'
21
+ | '--color-secondary-300'
22
+ | '--color-secondary-400'
23
+ | '--color-secondary-500'
24
+ | '--color-secondary-600'
25
+ | '--color-secondary-700'
26
+ | '--color-secondary-800'
27
+ | '--color-secondary-900'
28
+ | '--color-tertiary-50'
29
+ | '--color-tertiary-100'
30
+ | '--color-tertiary-200'
31
+ | '--color-tertiary-300'
32
+ | '--color-tertiary-400'
33
+ | '--color-tertiary-500'
34
+ | '--color-tertiary-600'
35
+ | '--color-tertiary-700'
36
+ | '--color-tertiary-800'
37
+ | '--color-tertiary-900'
38
+ | '--color-info-50'
39
+ | '--color-info-100'
40
+ | '--color-info-200'
41
+ | '--color-info-300'
42
+ | '--color-info-400'
43
+ | '--color-info-500'
44
+ | '--color-info-600'
45
+ | '--color-info-700'
46
+ | '--color-info-800'
47
+ | '--color-info-900'
48
+ | '--color-success-50'
49
+ | '--color-success-100'
50
+ | '--color-success-200'
51
+ | '--color-success-300'
52
+ | '--color-success-400'
53
+ | '--color-success-500'
54
+ | '--color-success-600'
55
+ | '--color-success-700'
56
+ | '--color-success-800'
57
+ | '--color-success-900'
58
+ | '--color-critical-50'
59
+ | '--color-critical-100'
60
+ | '--color-critical-200'
61
+ | '--color-critical-300'
62
+ | '--color-critical-400'
63
+ | '--color-critical-500'
64
+ | '--color-critical-600'
65
+ | '--color-critical-700'
66
+ | '--color-critical-800'
67
+ | '--color-critical-900'
68
+ | '--color-warning-50'
69
+ | '--color-warning-100'
70
+ | '--color-warning-200'
71
+ | '--color-warning-300'
72
+ | '--color-warning-400'
73
+ | '--color-warning-500'
74
+ | '--color-warning-600'
75
+ | '--color-warning-700'
76
+ | '--color-warning-800'
77
+ | '--color-warning-900'
78
+ | '--color-surface-50'
79
+ | '--color-surface-100'
80
+ | '--color-surface-200'
81
+ | '--color-surface-300'
82
+ | '--color-surface-400'
83
+ | '--color-surface-500'
84
+ | '--color-surface-600'
85
+ | '--color-surface-700'
86
+ | '--color-surface-800'
87
+ | '--color-surface-900'
88
+ >;
89
+
90
+ export type ColorAliases =
91
+ | 'primary'
92
+ | 'secondary'
93
+ | 'tertiary'
94
+ | 'info'
95
+ | 'success'
96
+ | 'warning'
97
+ | 'critical'
98
+ | 'surface';
99
+ export type ColorAliasesObject = Record<ColorAliases, StepObject>;
100
+
101
+ export const makeColorCSSVarsFromObject = (colors: ColorAliasesObject): Record<string, string> => {
102
+ const colorVars: Record<string, string> = {};
103
+ for (const [key, value] of Object.entries(colors)) {
104
+ for (const shade of steps) {
105
+ const varName = `--color-${key}-${shade}`;
106
+ colorVars[varName] = `${value[shade]}`;
107
+ }
108
+ }
109
+
110
+ return colorVars;
111
+ };
112
+
113
+ export const generateCSSVars = (vars: Record<string, string>, className: string): string => {
114
+ const classBlock = `.${className} {\n${Object.entries(vars)
115
+ .map(([key, value]) => ` ${key}: ${value};`)
116
+ .join('\n')}\n}`;
117
+
118
+ const rootBlock = `:root {\n${Object.entries(vars)
119
+ .map(([key, value]) => ` ${key}: ${value};`)
120
+ .join('\n')}\n}`;
121
+
122
+ return `${classBlock}\n\n${rootBlock}`;
123
+ };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Ramp builders for canvas-dark ("ink") brands. Deliberately NOT in utils/color.ts: the Ground
3
+ * baseline records that file's export list, and the themeValues snapshot pins its generators.
4
+ * These builders only rearrange generator output or parse approved hexes; they never change
5
+ * what generateNormalColorSteps returns.
6
+ *
7
+ * Why invert: core-components hard-code palette steps relative to a light canvas
8
+ * (`bg-primary-500 text-text-inverse` filled, `text-primary-500` on `bg-background-primary`
9
+ * outlined, `hover:bg-primary-100 hover:text-primary-700`, ...). Those pairs only keep their
10
+ * contrast when step 50 sits next to the canvas, 900 sits far from it, and text-inverse equals the
11
+ * canvas. Reversing every ramp keeps that geometry on an ink canvas, so the brand can reuse the
12
+ * shared semantic map almost unchanged (see EverydayHarbor.ts).
13
+ */
14
+ import { type StepObject, type Steps, colorStepArrayToObject, generateNormalColorSteps, steps } from '../utils/color';
15
+ import { hexToChannels, luminance, parseChannels } from './wcag';
16
+
17
+ /** A `#RRGGBB` literal. Validated when the theme module loads; a bad literal throws there. */
18
+ export type Hex = `#${string}`;
19
+
20
+ /**
21
+ * generateNormalColorSteps(seed), reversed: 50 is the deepest shade, 900 the palest tint.
22
+ * The seed lands on step 400 (the generator's 500), and the generator's 400 tint lands on 500.
23
+ * Example: seed #8EC5E8 -> 400 '142 197 232' (#8EC5E8), 500 '165 209 237' (#A5D1ED).
24
+ */
25
+ export const inkNormalSteps = (seed: Hex): StepObject =>
26
+ colorStepArrayToObject([...generateNormalColorSteps(seed)].reverse());
27
+
28
+ /**
29
+ * A hand-authored ramp from approved hexes, for families no seed reproduces (the ink surface ramp
30
+ * has seven approved anchors). Every step is required; output is the generator's channel format.
31
+ * Throws at load when a literal is malformed or the ramp is not strictly lighter at each step, so a
32
+ * swapped anchor fails immediately instead of in review.
33
+ */
34
+ export const anchoredSteps = (hexes: Readonly<Record<Steps, Hex>>): StepObject => {
35
+ const ramp = Object.fromEntries(steps.map((step) => [step, hexToChannels(hexes[step])])) as StepObject;
36
+ steps.slice(1).forEach((step, index) => {
37
+ const previous = steps[index];
38
+ if (luminance(parseChannels(ramp[step])) <= luminance(parseChannels(ramp[previous]))) {
39
+ throw new Error(`anchoredSteps: step ${step} (${hexes[step]}) is not lighter than step ${previous}`);
40
+ }
41
+ });
42
+ return ramp;
43
+ };
@@ -0,0 +1,3 @@
1
+ import { buildRuntimeTheme } from './buildTheme';
2
+
3
+ export const basketballTrainingTheme = buildRuntimeTheme('BasketballTraining');