@multiplatform.one/theme 7.38.0 → 7.40.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplatform.one/theme",
3
- "version": "7.38.0",
3
+ "version": "7.40.0",
4
4
  "description": "Tamagui theme system for multiplatform.one",
5
5
  "keywords": [
6
6
  "multiplatform",
@@ -59,8 +59,8 @@
59
59
  "access": "public"
60
60
  },
61
61
  "dependencies": {
62
- "@multiplatform.one/platform": "^7.38.0",
63
- "@multiplatform.one/store": "^7.38.0",
62
+ "@multiplatform.one/platform": "^7.40.0",
63
+ "@multiplatform.one/store": "^7.40.0",
64
64
  "@tamagui/animations-react-native": "2.7.6",
65
65
  "@tamagui/colors": "2.7.6",
66
66
  "@tamagui/config": "2.7.6",
@@ -73,8 +73,8 @@
73
73
  "react-cookie": "^8.1.2"
74
74
  },
75
75
  "devDependencies": {
76
- "@multiplatform.one/config": "7.38.0",
77
- "@multiplatform.one/test-utils": "7.38.0",
76
+ "@multiplatform.one/config": "7.40.0",
77
+ "@multiplatform.one/test-utils": "7.40.0",
78
78
  "@tamagui/animations-css": "2.7.6",
79
79
  "@tamagui/animations-reanimated": "2.7.6",
80
80
  "@tamagui/font-inter": "2.7.6",
@@ -0,0 +1,88 @@
1
+ import { cleanup, render, screen } from "@testing-library/react";
2
+ import { forwardRef, memo } from "react";
3
+ import { getVariableValue, TamaguiProvider, useTheme } from "tamagui";
4
+ import { afterEach, describe, expect, it, vi } from "vitest";
5
+ import { createDefaultThemeConfig } from "./createDefaultThemeConfig";
6
+ import { IconSlot } from "./IconSlot";
7
+
8
+ const platform = vi.hoisted(() => ({ web: false }));
9
+ vi.mock("tamagui", async (original) => ({
10
+ ...(await original<typeof import("tamagui")>()),
11
+ get isWeb() {
12
+ return platform.web;
13
+ },
14
+ }));
15
+ const config = createDefaultThemeConfig();
16
+ function Probe({ color, size }: { color?: string; size?: number }) {
17
+ const theme = useTheme();
18
+ return (
19
+ <span
20
+ data-testid="icon"
21
+ data-color={color ?? "black"}
22
+ data-size={size}
23
+ data-ink={String(getVariableValue(theme.color))}
24
+ data-muted={String(getVariableValue(theme.color11))}
25
+ />
26
+ );
27
+ }
28
+ const Memo = memo(Probe);
29
+ const Forward = forwardRef<HTMLSpanElement, { color?: string; size?: number }>((props, _ref) => (
30
+ <Probe {...props} />
31
+ ));
32
+ afterEach(() => {
33
+ cleanup();
34
+ platform.web = false;
35
+ });
36
+ describe("IconSlot", () => {
37
+ it.each(["light", "dark"] as const)(
38
+ "%s resolves functions, memo, forwardRef, elements and fragments",
39
+ (scheme) => {
40
+ render(
41
+ <TamaguiProvider config={config.tamagui} defaultTheme={scheme} disableInjectCSS>
42
+ <IconSlot icon={Probe} />
43
+ <IconSlot icon={Memo} />
44
+ <IconSlot icon={Forward} />
45
+ <IconSlot icon={<Probe color="currentColor" />} />
46
+ <IconSlot
47
+ icon={
48
+ <>
49
+ <Probe />
50
+ </>
51
+ }
52
+ />
53
+ </TamaguiProvider>,
54
+ );
55
+ for (const icon of screen.getAllByTestId("icon"))
56
+ expect(icon.dataset.color).toBe(icon.dataset.ink);
57
+ },
58
+ );
59
+ it("resolves an explicit token and preserves literal ink", () => {
60
+ render(
61
+ <TamaguiProvider config={config.tamagui} defaultTheme="dark" disableInjectCSS>
62
+ <IconSlot icon={<Probe color="$color11" />} size={20} />
63
+ <IconSlot icon={<Probe color="rebeccapurple" />} />
64
+ </TamaguiProvider>,
65
+ );
66
+ const [token, literal] = screen.getAllByTestId("icon");
67
+ expect(token.dataset.color).toBe(token.dataset.muted);
68
+ expect(token.dataset.size).toBe("20");
69
+ expect(literal.dataset.color).toBe("rebeccapurple");
70
+ });
71
+ it("preserves explicit element size", () => {
72
+ render(
73
+ <TamaguiProvider config={config.tamagui} defaultTheme="dark" disableInjectCSS>
74
+ <IconSlot icon={<Probe size={12} />} size={20} />
75
+ </TamaguiProvider>,
76
+ );
77
+ expect(screen.getByTestId("icon").dataset.size).toBe("12");
78
+ });
79
+ it("keeps web inheritance", () => {
80
+ platform.web = true;
81
+ render(
82
+ <TamaguiProvider config={config.tamagui} defaultTheme="dark" disableInjectCSS>
83
+ <IconSlot icon={<Probe color="currentColor" />} />
84
+ </TamaguiProvider>,
85
+ );
86
+ expect(screen.getByTestId("icon").dataset.color).toBe("currentColor");
87
+ });
88
+ });
@@ -0,0 +1,51 @@
1
+ import {
2
+ Children,
3
+ Fragment,
4
+ cloneElement,
5
+ createElement,
6
+ isValidElement,
7
+ type ComponentType,
8
+ type ReactNode,
9
+ } from "react";
10
+ import { isWeb, useTheme } from "tamagui";
11
+ import { resolveGlyphPaint } from "./glyphPaint";
12
+
13
+ export type IconSlotValue = ReactNode | ComponentType<{ size?: number; color?: string }>;
14
+
15
+ export function IconSlot({
16
+ icon,
17
+ color,
18
+ size,
19
+ }: {
20
+ icon: IconSlotValue;
21
+ color?: string;
22
+ size?: number;
23
+ }) {
24
+ const theme = useTheme();
25
+ const ink = (value?: string) =>
26
+ resolveGlyphPaint(
27
+ theme,
28
+ value && value !== "currentColor" ? value : color === "currentColor" ? undefined : color,
29
+ );
30
+ const render = (value: IconSlotValue): ReactNode => {
31
+ if (isValidElement<{ color?: string; size?: number; children?: ReactNode }>(value)) {
32
+ if (value.type === Fragment)
33
+ return cloneElement(value, {}, Children.map(value.props.children, render));
34
+ return cloneElement(value, {
35
+ ...(size == null || value.props.size != null ? {} : { size }),
36
+ ...(!isWeb ? { color: ink(value.props.color) } : {}),
37
+ });
38
+ }
39
+ if (
40
+ typeof value === "function" ||
41
+ (value && typeof value === "object" && "$$typeof" in value)
42
+ ) {
43
+ return createElement(value as ComponentType<{ size?: number; color?: string }>, {
44
+ size,
45
+ ...(!isWeb ? { color: ink() } : {}),
46
+ });
47
+ }
48
+ return value as ReactNode;
49
+ };
50
+ return render(icon);
51
+ }
@@ -6,10 +6,24 @@ import { defaultKnobs, type Knobs } from "./knobs";
6
6
  import type { Preset as PresetType } from "./preset.types";
7
7
  import { getPreset } from "./shared";
8
8
 
9
+ /**
10
+ * FZ-7715. A preset owns shape, density, type and its default accent. A colour
11
+ * theme the user picks is the accent, so it replaces the preset's `theme` and
12
+ * every decorative tint; the preset's own colour shows only with no pick.
13
+ */
14
+ export function applyColorTheme(preset: PresetType, color?: ThemeName | ""): PresetType {
15
+ if (!color) return preset;
16
+ return { ...preset, theme: color, tints: [color] };
17
+ }
18
+
9
19
  export interface PresetProps extends PropsWithChildren {
10
20
  /** Named preset to apply. Looked up from the preset registry. */
11
21
  preset?: string;
12
- /** Tamagui theme name (e.g. "blue", "red"). Wraps children in <Theme />. */
22
+ /**
23
+ * Colour theme (e.g. "blue", "red"). Wraps children in <Theme /> and beats
24
+ * the preset's tint, through nested named presets too. `""` drops an
25
+ * inherited pick and returns to the preset's own colour.
26
+ */
13
27
  theme?: ThemeName;
14
28
  /** Partial knob overrides merged on top of the preset's knobs. */
15
29
  overrides?: Partial<Knobs>;
@@ -25,8 +39,9 @@ export function Preset({
25
39
  cascade = true,
26
40
  }: PresetProps) {
27
41
  const parentCtx = useContext(PresetContext);
42
+ const color = theme === undefined ? (cascade ? parentCtx?.color : undefined) : theme || undefined;
28
43
 
29
- const ctxValue = useMemo((): PresetContextValue => {
44
+ const composed = useMemo((): PresetContextValue => {
30
45
  // 1. Determine base preset
31
46
  let base: PresetType;
32
47
 
@@ -87,11 +102,21 @@ export function Preset({
87
102
  return { preset: base, ...scope };
88
103
  }, [cascade, parentCtx, presetName, overrides]);
89
104
 
105
+ const ctxValue = useMemo((): PresetContextValue => {
106
+ if (color) return { ...composed, preset: applyColorTheme(composed.preset, color), color };
107
+ if (theme !== "") return composed;
108
+ // The cascaded preset already carries the parent's colour: take theme and
109
+ // tints back from the named (or the provider's) preset.
110
+ const own = getPreset(presetName ?? parentCtx?.presetName ?? "") ?? composed.preset;
111
+ return { ...composed, preset: { ...composed.preset, theme: own.theme, tints: own.tints } };
112
+ }, [composed, color, theme, presetName, parentCtx]);
113
+
90
114
  let element = <PresetContext.Provider value={ctxValue}>{children}</PresetContext.Provider>;
91
115
 
92
- // Wrap with Tamagui <Theme /> when theme is specified
93
- if (theme) {
94
- element = <TamaguiTheme name={theme}>{element}</TamaguiTheme>;
116
+ // An explicit colour wraps in its Tamagui theme; `""` re-applies the preset's own.
117
+ const wrap = theme ? theme : theme === "" ? ctxValue.preset.theme : undefined;
118
+ if (wrap) {
119
+ element = <TamaguiTheme name={wrap}>{element}</TamaguiTheme>;
95
120
  }
96
121
 
97
122
  return element;
@@ -10,6 +10,11 @@ export interface PresetContextValue {
10
10
  presetName?: string;
11
11
  /** Id of the installed token scale; changes after a switch re-render every knob reader. */
12
12
  scaleId?: string;
13
+ /**
14
+ * The colour theme a user picked, when one was. It is the accent: it beats
15
+ * the preset's own `theme` and `tints`, and survives a nested named preset.
16
+ */
17
+ color?: ThemeName;
13
18
  }
14
19
 
15
20
  export const PresetContext = createContext<PresetContextValue | null>(null);
@@ -33,6 +33,9 @@ describe("DF-06 / WCAG 1.4.11: control boundaries hold 3:1", () => {
33
33
  expect(theme.boundaryColor).toBeTruthy();
34
34
  const surfaces = [theme.background, theme.color3, theme.color5];
35
35
  const minimum = (ink: string) => Math.min(...surfaces.map((surface) => ratio(ink, surface)));
36
+ expect(minimum(theme.boundaryColor)).toBeGreaterThanOrEqual(minContrastRatio);
37
+ // FZ-7715's hue solids are built after getTheme: the floor is all they owe.
38
+ if (!inputThemes[name]) return;
36
39
  // Tint text is neutralized after getBaseTheme; selection uses the input ramp.
37
40
  const candidates = [8, 9, 10, 11, 12].map((step) => inputThemes[name][`color${step}`]);
38
41
  const firstHolding = candidates.find((ink) => minimum(ink) >= minContrastRatio);
@@ -0,0 +1,54 @@
1
+ import { configWithoutAnimations } from "@tamagui/config";
2
+ import { createTamagui } from "tamagui";
3
+ import { describe, expect, it } from "vitest";
4
+ import { contrastRatio, normalizeToHex, relativeLuminance } from "./colorRules";
5
+ import { createThemesBuilder, houseComponentThemes } from "./createThemes";
6
+ import { defaultAccentTheme } from "./defaults/accent";
7
+ import { defaultBaseTheme } from "./defaults/base";
8
+ import { defaultBuilderOptions } from "./defaults/builderOptions";
9
+
10
+ const themes = createThemesBuilder(
11
+ defaultBaseTheme,
12
+ defaultAccentTheme,
13
+ defaultBuilderOptions,
14
+ ).themes();
15
+ const config = createTamagui({ ...configWithoutAnimations, themes });
16
+ const hues = ["blue", "red", "green", "yellow", "orange", "pink", "purple", "gray"];
17
+ const ratio = (a: string, b: string) =>
18
+ contrastRatio(relativeLuminance(normalizeToHex(a)!), relativeLuminance(normalizeToHex(b)!));
19
+
20
+ describe("FZ-7715: a colour theme owns its accent", () => {
21
+ for (const scheme of ["light", "dark"]) {
22
+ for (const hue of hues) {
23
+ it(`${scheme} ${hue} uses its own ramp for accent tokens and selected controls`, () => {
24
+ const scope = themes[`${scheme}_${hue}`];
25
+ const ramp = Array.from({ length: 12 }, (_, i) => themes[scheme][`${hue}${i + 1}`]);
26
+ expect(ramp).toContain(scope.accentBackground);
27
+ expect(ramp).toContain(scope.accentColor);
28
+ expect(scope.accentBackground).not.toBe(themes[scheme].accentBackground);
29
+ expect(ratio(scope.accentBackground, scope.color1)).toBeGreaterThanOrEqual(3);
30
+ for (const surface of [scope.color1, scope.color2, scope.color3]) {
31
+ expect(ratio(scope.accentColor, surface)).toBeGreaterThanOrEqual(4.5);
32
+ }
33
+ const selected = config.themes[`${scheme}_${hue}_accent`] as unknown as Record<
34
+ string,
35
+ { val: string }
36
+ >;
37
+ expect(selected.color5.val).toBe(scope.color5);
38
+ expect(selected.background.val).toBe(scope.accentBackground);
39
+ expect(ratio(selected.background.val, selected.color.val)).toBeGreaterThanOrEqual(4.5);
40
+ expect(ratio(selected.background.val, selected.outlineColor.val)).toBeGreaterThanOrEqual(3);
41
+ for (const ink of ["color11", "color12", "textMuted", "accentBackground"]) {
42
+ expect(ratio(selected.background.val, selected[ink].val)).toBeGreaterThanOrEqual(4.5);
43
+ }
44
+ for (const component of Object.keys(houseComponentThemes)) {
45
+ const child = config.themes[`${scheme}_${hue}_${component}`] as unknown as Record<
46
+ string,
47
+ { val: string }
48
+ >;
49
+ expect(child.accentBackground.val).toBe(scope.accentBackground);
50
+ }
51
+ });
52
+ }
53
+ }
54
+ });
@@ -411,10 +411,12 @@ describe("the v5 theme matrix", () => {
411
411
  expect(Object.keys(custom).some((name) => /_[A-Z]/.test(name))).toBe(false);
412
412
  });
413
413
 
414
- it("ships 462 themes and the heading-scope category fonts in under 260 KB of CSS (the v4 matrix and stock spread shipped 913 KB)", async () => {
414
+ // FZ-7715 added one accent solid per hue and scheme (16 themes) so
415
+ // `theme="accent"` follows a picked colour; the budget moved 260 -> 280 KB.
416
+ it("ships 478 themes and the heading-scope category fonts in under 280 KB of CSS (the v4 matrix and stock spread shipped 913 KB)", async () => {
415
417
  const { createTamagui: build } = await vi.importActual<typeof import("tamagui")>("tamagui");
416
418
  const css = build(config.tamagui).getCSS();
417
- expect(Object.keys(themes)).toHaveLength(462);
418
- expect(Buffer.byteLength(css, "utf8")).toBeLessThan(260_000);
419
+ expect(Object.keys(themes)).toHaveLength(478);
420
+ expect(Buffer.byteLength(css, "utf8")).toBeLessThan(280_000);
419
421
  });
420
422
  });
@@ -4,8 +4,10 @@ import { defaultChildrenThemes, v5Templates } from "@tamagui/themes/v5";
4
4
  import {
5
5
  aaTextContrastRatio,
6
6
  contrastRatio,
7
+ holdInkFloor,
7
8
  minContrastRatio,
8
9
  normalizeToHex,
10
+ pickReadableForeground,
9
11
  relativeLuminance,
10
12
  } from "./colorRules";
11
13
 
@@ -329,33 +331,45 @@ function holdSwitchTrack(
329
331
  for (const scheme of ["light", "dark"] as const) {
330
332
  const base = themes[scheme];
331
333
  if (!base) continue;
332
- const thumb = luminanceOf(base.switchThumb ?? "#ffffff");
333
- const on = luminanceOf(base.accentBackground);
334
- const surfaces = [base.background, base.color3].map(luminanceOf);
335
- if (thumb === null || on === null || surfaces.some((lum) => lum === null)) continue;
336
- const holds = (step: number, onPage: boolean) => {
337
- const lum = luminanceOf(base[`gray${step}`]);
338
- return (
339
- lum !== null &&
340
- contrastRatio(lum, thumb) >= minContrastRatio &&
341
- contrastRatio(lum, on) >= switchStateContrastRatio &&
342
- (!onPage || surfaces.every((surface) => contrastRatio(lum, surface!) >= minContrastRatio))
343
- );
344
- };
345
- const off =
346
- switchOffSteps.find((step) => holds(step, true)) ??
347
- switchOffSteps.find((step) => holds(step, false));
348
- if (off === undefined) continue;
349
- const paint = {
350
- switchTrackOff: base[`gray${off}`],
351
- switchTrackOffHover: base[`gray${off - 1}`],
352
- switchTrackOffPress: base[`gray${off + 1}`],
353
- };
354
- out[scheme] = { ...base, ...paint };
334
+ const paint = switchTrackPaint(base, base.accentBackground, [base.background, base.color3]);
335
+ if (paint) out[scheme] = { ...base, ...paint };
355
336
  }
356
337
  return out;
357
338
  }
358
339
 
340
+ /**
341
+ * The off track for one on-fill: the first grey step in `steps` that holds
342
+ * under the thumb and apart from `onFill`, on the page too when one can.
343
+ * Hover is one step toward the page and press one away, clamped to the ramp.
344
+ */
345
+ function switchTrackPaint(
346
+ base: Record<string, string>,
347
+ onFill: string | undefined,
348
+ pageSurfaces: (string | undefined)[],
349
+ steps: readonly number[] = switchOffSteps,
350
+ ): Record<string, string> | undefined {
351
+ const thumb = luminanceOf(base.switchThumb ?? "#ffffff");
352
+ const on = luminanceOf(onFill);
353
+ const surfaces = pageSurfaces.map(luminanceOf);
354
+ if (thumb === null || on === null || surfaces.some((lum) => lum === null)) return undefined;
355
+ const holds = (step: number, onPage: boolean) => {
356
+ const lum = luminanceOf(base[`gray${step}`]);
357
+ return (
358
+ lum !== null &&
359
+ contrastRatio(lum, thumb) >= minContrastRatio &&
360
+ contrastRatio(lum, on) >= switchStateContrastRatio &&
361
+ (!onPage || surfaces.every((surface) => contrastRatio(lum, surface!) >= minContrastRatio))
362
+ );
363
+ };
364
+ const off = steps.find((step) => holds(step, true)) ?? steps.find((step) => holds(step, false));
365
+ if (off === undefined) return undefined;
366
+ return {
367
+ switchTrackOff: base[`gray${off}`],
368
+ switchTrackOffHover: base[`gray${off - 1}`],
369
+ switchTrackOffPress: base[`gray${Math.min(off + 1, 12)}`],
370
+ };
371
+ }
372
+
359
373
  /** The hues the code syntax palette paints (MPO-468). */
360
374
  const syntaxInkHues = ["purple", "green", "blue", "orange", "red"] as const;
361
375
 
@@ -441,6 +455,107 @@ function holdNamedInkFloor(
441
455
  return out;
442
456
  }
443
457
 
458
+ /**
459
+ * FZ-7715. A colour theme is the accent. The house accent is the neutral
460
+ * inverse, and the hue sub-themes (light_green, dark_pink, ...) inherited it,
461
+ * so `theme="accent"` and `$accentBackground` stayed black under any colour a
462
+ * user or a preset picked. Each hue scope gets an accent fill from its own
463
+ * ramp, held to 3:1 on its page with a 4.5:1 label, accent ink held to 4.5:1
464
+ * on color1..color3, and a `<scheme>_<hue>_accent` solid that Tamagui resolves
465
+ * for `theme="accent"` (and every component under it) inside the hue.
466
+ */
467
+ function holdColorThemeAccent(
468
+ themes: Record<string, Record<string, string>>,
469
+ ): Record<string, Record<string, string>> {
470
+ const out: Record<string, Record<string, string>> = { ...themes };
471
+ for (const scheme of ["light", "dark"] as const) {
472
+ const base = themes[scheme];
473
+ if (!base) continue;
474
+ for (const hue of childrenHues) {
475
+ const scopeName = `${scheme}_${hue}`;
476
+ const scope = themes[scopeName];
477
+ const ramp = Array.from({ length: 12 }, (_, index) => base[`${hue}${index + 1}`]);
478
+ if (!scope || ramp.some((step) => !step)) continue;
479
+ const paper = luminanceOf(scope.color1);
480
+ const ink = luminanceOf(scope.color12);
481
+ const surfaces = [scope.color1, scope.color2, scope.color3].map(luminanceOf);
482
+ if (paper === null || ink === null || surfaces.some((lum) => lum === null)) continue;
483
+ const fill = nearestHoldingStep(
484
+ ramp[8],
485
+ ramp.slice(0, 11),
486
+ (lum) =>
487
+ Math.max(contrastRatio(lum, paper), contrastRatio(lum, ink)) >= aaTextContrastRatio &&
488
+ contrastRatio(lum, paper) >= minContrastRatio,
489
+ );
490
+ const accentInk = nearestHoldingStep(ramp[10], ramp, (lum) =>
491
+ surfaces.every((surface) => contrastRatio(lum, surface as number) >= aaTextContrastRatio),
492
+ );
493
+ const fillStep = ramp.indexOf(fill);
494
+ const fillHover = ramp[Math.min(fillStep + 1, ramp.length - 1)];
495
+ const onFill = holdInkFloor(fill, pickReadableForeground(fill, scope.color1, scope.color12));
496
+ const accent: Record<string, string> = { accentBackground: fill, accentColor: accentInk };
497
+ // A hue fill can sit on the root's off grey (light pink on gray10): walk
498
+ // outward from the root's step to the nearest one that holds again.
499
+ const rootOff = Number(
500
+ Object.keys(base)
501
+ .find((key) => /^gray\d+$/.test(key) && base[key] === base.switchTrackOff)
502
+ ?.slice(4),
503
+ );
504
+ const rootOn = luminanceOf(base.switchTrackOff);
505
+ const fillLum = luminanceOf(fill);
506
+ const rootHolds =
507
+ rootOn !== null &&
508
+ fillLum !== null &&
509
+ contrastRatio(rootOn, fillLum) >= switchStateContrastRatio;
510
+ const outward = Array.from({ length: 10 }, (_, index) => index + 3).sort(
511
+ (a, b) => Math.abs(a - rootOff) - Math.abs(b - rootOff) || b - a,
512
+ );
513
+ const track =
514
+ rootHolds || Number.isNaN(rootOff)
515
+ ? undefined
516
+ : switchTrackPaint(base, fill, [scope.background, scope.color3], outward);
517
+ for (const [name, theme] of Object.entries(themes)) {
518
+ if (name === scopeName) {
519
+ out[name] = { ...theme, ...accent, ...track };
520
+ } else if (name.startsWith(`${scopeName}_`)) {
521
+ const owned = Object.keys(accent).filter((key) => key in theme);
522
+ if (owned.length > 0) {
523
+ out[name] = { ...theme, ...Object.fromEntries(owned.map((key) => [key, accent[key]])) };
524
+ }
525
+ }
526
+ }
527
+ // Only what the solid changes: the rest resolves through the hue scope,
528
+ // which keeps the CSS sheet under its budget. Ink, focus ring and any
529
+ // nested accent mark take the fill's label so none vanishes into it.
530
+ out[`${scopeName}_accent`] = {
531
+ background: fill,
532
+ backgroundHover: fillHover,
533
+ backgroundPress: fillHover,
534
+ backgroundFocus: fillHover,
535
+ backgroundActive: fill,
536
+ borderColor: fill,
537
+ borderColorHover: fillHover,
538
+ borderColorFocus: fillHover,
539
+ borderColorPress: fillHover,
540
+ color: onFill,
541
+ colorHover: onFill,
542
+ colorPress: onFill,
543
+ colorFocus: onFill,
544
+ placeholderColor: onFill,
545
+ boundaryColor: onFill,
546
+ outlineColor: onFill,
547
+ color10: onFill,
548
+ color11: onFill,
549
+ color12: onFill,
550
+ textMuted: onFill,
551
+ accentBackground: onFill,
552
+ accentColor: onFill,
553
+ };
554
+ }
555
+ }
556
+ return out;
557
+ }
558
+
444
559
  const defaultLightShadows: Shadows = {
445
560
  shadow1: "rgba(0,0,0,0.04)",
446
561
  shadow2: "rgba(0,0,0,0.08)",
@@ -539,10 +654,12 @@ export function createThemesBuilder(
539
654
  const initialTheme = tamaguiCreateThemes(
540
655
  createThemesProps as Parameters<typeof tamaguiCreateThemes>[0],
541
656
  );
542
- const themes = holdSwitchTrack(
543
- holdSyntaxFloor(
544
- holdNamedInkFloor(
545
- holdAccentFloor(neutralizeSemanticEntryText(neutralizeTintText(initialTheme))),
657
+ const themes = holdColorThemeAccent(
658
+ holdSwitchTrack(
659
+ holdSyntaxFloor(
660
+ holdNamedInkFloor(
661
+ holdAccentFloor(neutralizeSemanticEntryText(neutralizeTintText(initialTheme))),
662
+ ),
546
663
  ),
547
664
  ),
548
665
  );
@@ -58,3 +58,5 @@ export {
58
58
  type MotionStop,
59
59
  } from "./animations/css";
60
60
  export { animationConfig as nativeAnimationConfig } from "./animations/reactNative";
61
+
62
+ export * from "./IconSlot";
@@ -10,7 +10,7 @@ vi.mock("tamagui", () => ({
10
10
 
11
11
  import type { ThemeName } from "@tamagui/web";
12
12
  import { Preset } from "./Preset";
13
- import { type PresetContextValue, usePresetContext } from "./PresetContext";
13
+ import { PresetContext, type PresetContextValue, usePresetContext } from "./PresetContext";
14
14
  import { defaultKnobs } from "./knobs";
15
15
  import type { Preset as PresetType } from "./preset.types";
16
16
  import { clearPresets, registerPreset } from "./shared";
@@ -177,6 +177,62 @@ describe("Preset component", () => {
177
177
  expect(html).toContain('data-tamagui-theme="blue"');
178
178
  });
179
179
 
180
+ it("uses an explicit colour for the preset metadata and every decorative tint", () => {
181
+ registerPreset("bold", boldPreset);
182
+ const capture = createPresetContextCapture();
183
+ renderToString(
184
+ <Preset preset="bold" theme={"green" as ThemeName}>
185
+ <capture.Capture />
186
+ </Preset>,
187
+ );
188
+ expect(capture.result?.preset.theme).toBe("green");
189
+ expect(capture.result?.preset.tints).toEqual(["green"]);
190
+ expect(capture.result?.preset.knobs).toEqual(boldPreset.knobs);
191
+ });
192
+
193
+ it("keeps the explicit colour through a nested named preset", () => {
194
+ registerPreset("bold", boldPreset);
195
+ const capture = createPresetContextCapture();
196
+ renderToString(
197
+ <Preset theme={"green" as ThemeName}>
198
+ <Preset preset="bold">
199
+ <capture.Capture />
200
+ </Preset>
201
+ </Preset>,
202
+ );
203
+ expect(capture.result?.preset.theme).toBe("green");
204
+ });
205
+
206
+ it("returns to the preset tint when the colour is reset to default", () => {
207
+ registerPreset("bold", boldPreset);
208
+ const capture = createPresetContextCapture();
209
+ const html = renderToString(
210
+ <Preset theme={"green" as ThemeName}>
211
+ <Preset preset="bold" theme={"" as ThemeName}>
212
+ <capture.Capture />
213
+ </Preset>
214
+ </Preset>,
215
+ );
216
+ expect(capture.result?.preset.theme).toBe("blue");
217
+ expect(capture.result?.preset.tints).toEqual(boldPreset.tints);
218
+ expect(html).toContain('data-tamagui-theme="blue"');
219
+ });
220
+
221
+ it("drops an inherited colour on an unnamed reset", () => {
222
+ registerPreset("bold", boldPreset);
223
+ const capture = createPresetContextCapture();
224
+ renderToString(
225
+ <PresetContext.Provider value={{ preset: boldPreset, presetName: "bold" }}>
226
+ <Preset theme={"green" as ThemeName}>
227
+ <Preset theme={"" as ThemeName}>
228
+ <capture.Capture />
229
+ </Preset>
230
+ </Preset>
231
+ </PresetContext.Provider>,
232
+ );
233
+ expect(capture.result?.preset.tints).toEqual(boldPreset.tints);
234
+ });
235
+
180
236
  it("does not wrap with Tamagui <Theme /> when theme prop is absent", () => {
181
237
  const capture = createPresetContextCapture();
182
238
 
@@ -79,7 +79,7 @@ describe("subsetThemes over the house matrix", () => {
79
79
  components: ["Button"],
80
80
  tints: ["accent", "active", "alt1", "alt2", "error", "success", "warning"],
81
81
  });
82
- expect(Object.keys(built)).toHaveLength(462);
82
+ expect(Object.keys(built)).toHaveLength(478);
83
83
  expect(Object.keys(kept)).toHaveLength(44);
84
84
  for (const name of ["light", "dark", "light_accent", "light_Button", "dark_error_Button"]) {
85
85
  expect(kept).toHaveProperty(name);
@@ -46,11 +46,13 @@ describe("switch thumb", () => {
46
46
  const theme = config.themes[name] as Record<string, { val: string }>;
47
47
  const scheme = name.split("_")[0] as "light" | "dark";
48
48
  const off = theme.switchTrackOff.val;
49
- expect(off).toBe(theme[scheme === "light" ? "gray10" : "gray7"].val);
50
- expect(theme.switchTrackOffHover.val).toBe(theme[scheme === "light" ? "gray9" : "gray6"].val);
51
- expect(theme.switchTrackOffPress.val).toBe(
52
- theme[scheme === "light" ? "gray11" : "gray8"].val,
53
- );
49
+ // FZ-7715: a hue scope's own fill can move its off track off the root's step.
50
+ const step = Array.from({ length: 10 }, (_, i) => i + 3).find(
51
+ (n) => theme[`gray${n}`].val === off,
52
+ )!;
53
+ if (name === scheme) expect(step).toBe(scheme === "light" ? 10 : 7);
54
+ expect(theme.switchTrackOffHover.val).toBe(theme[`gray${step - 1}`].val);
55
+ expect(theme.switchTrackOffPress.val).toBe(theme[`gray${Math.min(step + 1, 12)}`].val);
54
56
  expect(ratio(off, theme.switchThumb.val)).toBeGreaterThanOrEqual(minContrastRatio);
55
57
  expect(ratio(off, theme.accentBackground.val)).toBeGreaterThanOrEqual(1.5);
56
58
  if (scheme === "light") {
@@ -0,0 +1,11 @@
1
+ import { type ComponentType, type ReactNode } from "react";
2
+ export type IconSlotValue = ReactNode | ComponentType<{
3
+ size?: number;
4
+ color?: string;
5
+ }>;
6
+ export declare function IconSlot({ icon, color, size, }: {
7
+ icon: IconSlotValue;
8
+ color?: string;
9
+ size?: number;
10
+ }): ReactNode;
11
+ //# sourceMappingURL=IconSlot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IconSlot.d.ts","sourceRoot":"","sources":["../../src/theme/IconSlot.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,aAAa,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEzF,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,KAAK,EACL,IAAI,GACL,EAAE;IACD,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,aA4BA"}
@@ -1,10 +1,21 @@
1
1
  import { type PropsWithChildren } from "react";
2
2
  import type { ThemeName } from "@tamagui/web";
3
3
  import { type Knobs } from "./knobs";
4
+ import type { Preset as PresetType } from "./preset.types";
5
+ /**
6
+ * FZ-7715. A preset owns shape, density, type and its default accent. A colour
7
+ * theme the user picks is the accent, so it replaces the preset's `theme` and
8
+ * every decorative tint; the preset's own colour shows only with no pick.
9
+ */
10
+ export declare function applyColorTheme(preset: PresetType, color?: ThemeName | ""): PresetType;
4
11
  export interface PresetProps extends PropsWithChildren {
5
12
  /** Named preset to apply. Looked up from the preset registry. */
6
13
  preset?: string;
7
- /** Tamagui theme name (e.g. "blue", "red"). Wraps children in <Theme />. */
14
+ /**
15
+ * Colour theme (e.g. "blue", "red"). Wraps children in <Theme /> and beats
16
+ * the preset's tint, through nested named presets too. `""` drops an
17
+ * inherited pick and returns to the preset's own colour.
18
+ */
8
19
  theme?: ThemeName;
9
20
  /** Partial knob overrides merged on top of the preset's knobs. */
10
21
  overrides?: Partial<Knobs>;
@@ -1 +1 @@
1
- {"version":3,"file":"Preset.d.ts","sourceRoot":"","sources":["../../src/theme/Preset.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAuB,MAAM,OAAO,CAAC;AAEpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAgB,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAInD,MAAM,WAAW,WAAY,SAAQ,iBAAiB;IACpD,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,kEAAkE;IAClE,SAAS,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3B,uFAAuF;IACvF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,MAAM,CAAC,EACrB,QAAQ,EACR,MAAM,EAAE,UAAU,EAClB,KAAK,EACL,SAAS,EACT,OAAc,GACf,EAAE,WAAW,2CAwEb"}
1
+ {"version":3,"file":"Preset.d.ts","sourceRoot":"","sources":["../../src/theme/Preset.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAuB,MAAM,OAAO,CAAC;AAEpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAgB,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAG3D;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,SAAS,GAAG,EAAE,GAAG,UAAU,CAGtF;AAED,MAAM,WAAW,WAAY,SAAQ,iBAAiB;IACpD,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,kEAAkE;IAClE,SAAS,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3B,uFAAuF;IACvF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,MAAM,CAAC,EACrB,QAAQ,EACR,MAAM,EAAE,UAAU,EAClB,KAAK,EACL,SAAS,EACT,OAAc,GACf,EAAE,WAAW,2CAmFb"}
@@ -8,6 +8,11 @@ export interface PresetContextValue {
8
8
  presetName?: string;
9
9
  /** Id of the installed token scale; changes after a switch re-render every knob reader. */
10
10
  scaleId?: string;
11
+ /**
12
+ * The colour theme a user picked, when one was. It is the accent: it beats
13
+ * the preset's own `theme` and `tints`, and survives a nested named preset.
14
+ */
15
+ color?: ThemeName;
11
16
  }
12
17
  export declare const PresetContext: import("react").Context<PresetContextValue | null>;
13
18
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"PresetContext.d.ts","sourceRoot":"","sources":["../../src/theme/PresetContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3B,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,aAAa,oDAAiD,CAAC;AAE5E;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,kBAAkB,GAAG,IAAI,CAE5D;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,SAAS,EAAE,CAG5C"}
1
+ {"version":3,"file":"PresetContext.d.ts","sourceRoot":"","sources":["../../src/theme/PresetContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3B,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,eAAO,MAAM,aAAa,oDAAiD,CAAC;AAE5E;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,kBAAkB,GAAG,IAAI,CAE5D;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,SAAS,EAAE,CAG5C"}
@@ -1 +1 @@
1
- {"version":3,"file":"createThemes.d.ts","sourceRoot":"","sources":["../../src/theme/createThemes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,IAAI,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAsC7E,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAevB,CAAC;AAEX,eAAO,MAAM,wBAAwB;;;;;;;;;;CAI3B,CAAC;AAEX,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB,CACrC,WAAW,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,EACvC,YAAY,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE;IAExC,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,WAAW,0BAA0B,CACzC,WAAW,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,EACvC,YAAY,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,EACxC,WAAW,SAAS,OAAO,GAAG,OAAO,EACrC,YAAY,SAAS,OAAO,GAAG,OAAO,CACtC,SAAQ,sBAAsB,CAAC,WAAW,EAAE,YAAY,CAAC;IACzD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,eAAe,CAAC,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAC/E,mBAAmB,CAAC,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC;IACvF,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CACzF;AA8BD,eAAO,MAAM,YAAY,aA6BvB,CAAC;AAsTH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,0BAA0B,EACrC,WAAW,EAAE,sBAAsB,EACnC,OAAO,CAAC,EAAE,0BAA0B;;aA6EzB,CAAC,SAAS,MAAM,QAAQ,CAAC,SAAS,sBAAsB,GAuB1D,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE;;+BAAkC;EAG7E"}
1
+ {"version":3,"file":"createThemes.d.ts","sourceRoot":"","sources":["../../src/theme/createThemes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,IAAI,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAwC7E,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAevB,CAAC;AAEX,eAAO,MAAM,wBAAwB;;;;;;;;;;CAI3B,CAAC;AAEX,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB,CACrC,WAAW,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,EACvC,YAAY,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE;IAExC,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,WAAW,0BAA0B,CACzC,WAAW,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,EACvC,YAAY,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,EACxC,WAAW,SAAS,OAAO,GAAG,OAAO,EACrC,YAAY,SAAS,OAAO,GAAG,OAAO,CACtC,SAAQ,sBAAsB,CAAC,WAAW,EAAE,YAAY,CAAC;IACzD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,eAAe,CAAC,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAC/E,mBAAmB,CAAC,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC;IACvF,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CACzF;AA8BD,eAAO,MAAM,YAAY,aA6BvB,CAAC;AAuaH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,0BAA0B,EACrC,WAAW,EAAE,sBAAsB,EACnC,OAAO,CAAC,EAAE,0BAA0B;;aA+EzB,CAAC,SAAS,MAAM,QAAQ,CAAC,SAAS,sBAAsB,GAuB1D,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE;;+BAAkC;EAG7E"}
@@ -51,4 +51,5 @@ export * from "./overlaySurfaces";
51
51
  export * from "./motion";
52
52
  export { animationConfig as cssAnimationConfig, animationDurations, motionStops, type MotionStop, } from "./animations/css";
53
53
  export { animationConfig as nativeAnimationConfig } from "./animations/reactNative";
54
+ export * from "./IconSlot";
54
55
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,YAAY,EAAE,MAAM,IAAI,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACjF,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAElC,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAElC,cAAc,UAAU,CAAC;AACzB,OAAO,EACL,eAAe,IAAI,kBAAkB,EACrC,kBAAkB,EAClB,WAAW,EACX,KAAK,UAAU,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,IAAI,qBAAqB,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,YAAY,EAAE,MAAM,IAAI,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACjF,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAElC,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAElC,cAAc,UAAU,CAAC;AACzB,OAAO,EACL,eAAe,IAAI,kBAAkB,EACrC,kBAAkB,EAClB,WAAW,EACX,KAAK,UAAU,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,IAAI,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEpF,cAAc,YAAY,CAAC"}