@oxyhq/bloom 0.6.15 → 0.6.17
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/lib/commonjs/theme/BloomThemeProvider.js +0 -28
- package/lib/commonjs/theme/BloomThemeProvider.js.map +1 -1
- package/lib/commonjs/theme/color-presets.js +4 -1
- package/lib/commonjs/theme/color-presets.js.map +1 -1
- package/lib/commonjs/theme/color-scope/index.js +66 -0
- package/lib/commonjs/theme/color-scope/index.js.map +1 -0
- package/lib/commonjs/theme/color-scope/index.web.js +56 -0
- package/lib/commonjs/theme/color-scope/index.web.js.map +1 -0
- package/lib/commonjs/theme/color-scope/style-builder.js +18 -0
- package/lib/commonjs/theme/color-scope/style-builder.js.map +1 -0
- package/lib/commonjs/theme/index.js +27 -1
- package/lib/commonjs/theme/index.js.map +1 -1
- package/lib/commonjs/theme/preset-vars.js +98 -0
- package/lib/commonjs/theme/preset-vars.js.map +1 -0
- package/lib/module/theme/BloomThemeProvider.js +1 -28
- package/lib/module/theme/BloomThemeProvider.js.map +1 -1
- package/lib/module/theme/color-presets.js +3 -0
- package/lib/module/theme/color-presets.js.map +1 -1
- package/lib/module/theme/color-scope/index.js +60 -0
- package/lib/module/theme/color-scope/index.js.map +1 -0
- package/lib/module/theme/color-scope/index.web.js +50 -0
- package/lib/module/theme/color-scope/index.web.js.map +1 -0
- package/lib/module/theme/color-scope/style-builder.js +14 -0
- package/lib/module/theme/color-scope/style-builder.js.map +1 -0
- package/lib/module/theme/index.js +4 -2
- package/lib/module/theme/index.js.map +1 -1
- package/lib/module/theme/preset-vars.js +93 -0
- package/lib/module/theme/preset-vars.js.map +1 -0
- package/lib/typescript/commonjs/icons/common.d.ts +12 -12
- package/lib/typescript/commonjs/theme/BloomThemeProvider.d.ts +0 -9
- package/lib/typescript/commonjs/theme/BloomThemeProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-presets.d.ts +2 -0
- package/lib/typescript/commonjs/theme/color-presets.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-scope/index.d.ts +23 -0
- package/lib/typescript/commonjs/theme/color-scope/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/theme/color-scope/index.web.d.ts +22 -0
- package/lib/typescript/commonjs/theme/color-scope/index.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/theme/color-scope/style-builder.d.ts +8 -0
- package/lib/typescript/commonjs/theme/color-scope/style-builder.d.ts.map +1 -0
- package/lib/typescript/commonjs/theme/index.d.ts +7 -3
- package/lib/typescript/commonjs/theme/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/preset-vars.d.ts +28 -0
- package/lib/typescript/commonjs/theme/preset-vars.d.ts.map +1 -0
- package/lib/typescript/module/icons/common.d.ts +12 -12
- package/lib/typescript/module/theme/BloomThemeProvider.d.ts +0 -9
- package/lib/typescript/module/theme/BloomThemeProvider.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-presets.d.ts +2 -0
- package/lib/typescript/module/theme/color-presets.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-scope/index.d.ts +23 -0
- package/lib/typescript/module/theme/color-scope/index.d.ts.map +1 -0
- package/lib/typescript/module/theme/color-scope/index.web.d.ts +22 -0
- package/lib/typescript/module/theme/color-scope/index.web.d.ts.map +1 -0
- package/lib/typescript/module/theme/color-scope/style-builder.d.ts +8 -0
- package/lib/typescript/module/theme/color-scope/style-builder.d.ts.map +1 -0
- package/lib/typescript/module/theme/index.d.ts +7 -3
- package/lib/typescript/module/theme/index.d.ts.map +1 -1
- package/lib/typescript/module/theme/preset-vars.d.ts +28 -0
- package/lib/typescript/module/theme/preset-vars.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/BloomColorScope.test.tsx +53 -0
- package/src/__tests__/preset-vars.test.ts +58 -0
- package/src/theme/BloomThemeProvider.tsx +0 -23
- package/src/theme/color-presets.ts +3 -0
- package/src/theme/color-scope/index.tsx +80 -0
- package/src/theme/color-scope/index.web.tsx +62 -0
- package/src/theme/color-scope/style-builder.ts +14 -0
- package/src/theme/index.ts +9 -2
- package/src/theme/preset-vars.ts +115 -0
|
@@ -21,6 +21,8 @@ export interface AppColorPreset {
|
|
|
21
21
|
dark: PresetTokens;
|
|
22
22
|
}
|
|
23
23
|
export declare const APP_COLOR_NAMES: readonly AppColorName[];
|
|
24
|
+
/** Premium-exclusive presets, hidden from the standard color picker. */
|
|
25
|
+
export declare const PREMIUM_COLOR_NAMES: readonly AppColorName[];
|
|
24
26
|
export declare const HEX_TO_APP_COLOR: Record<string, AppColorName>;
|
|
25
27
|
export declare function hexToAppColorName(hex: string): AppColorName;
|
|
26
28
|
export declare const APP_COLOR_PRESETS: Record<AppColorName, AppColorPreset>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"color-presets.d.ts","sourceRoot":"","sources":["../../../../src/theme/color-presets.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC;AAEvJ;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAElD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,YAAY,CAAC;IACpB,IAAI,EAAE,YAAY,CAAC;CACpB;AAED,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAAsH,CAAC;AAE1K,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAczD,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAE3D;AAED,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,YAAY,EAAE,cAAc,CAkmBlE,CAAC"}
|
|
1
|
+
{"version":3,"file":"color-presets.d.ts","sourceRoot":"","sources":["../../../../src/theme/color-presets.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC;AAEvJ;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAElD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,YAAY,CAAC;IACpB,IAAI,EAAE,YAAY,CAAC;CACpB;AAED,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAAsH,CAAC;AAE1K,wEAAwE;AACxE,eAAO,MAAM,mBAAmB,EAAE,SAAS,YAAY,EAAwB,CAAC;AAEhF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAczD,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAE3D;AAED,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,YAAY,EAAE,cAAc,CAkmBlE,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
import type { AppColorName } from '../color-presets';
|
|
4
|
+
export interface BloomColorScopeProps {
|
|
5
|
+
/** Preset to apply within this subtree. */
|
|
6
|
+
colorPreset: AppColorName;
|
|
7
|
+
/**
|
|
8
|
+
* When `true`, do not render a wrapping `<View>`. The caller owns the
|
|
9
|
+
* element that receives the CSS vars (via `useColorScopeStyle`).
|
|
10
|
+
*/
|
|
11
|
+
asChild?: boolean;
|
|
12
|
+
/** Additional style applied to the wrapping `<View>`. Ignored with `asChild`. */
|
|
13
|
+
style?: StyleProp<ViewStyle>;
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
}
|
|
16
|
+
export declare function BloomColorScope({ colorPreset, asChild, style, children, }: BloomColorScopeProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
/**
|
|
18
|
+
* Escape hatch for advanced cases where the wrapping element is owned by the
|
|
19
|
+
* caller. Returns a stable native style object carrying the preset's CSS vars.
|
|
20
|
+
* Returns `undefined` when nativewind is not installed.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useColorScopeStyle(colorPreset: AppColorName): StyleProp<ViewStyle>;
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/theme/color-scope/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AACnD,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAIpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAgBrD,MAAM,WAAW,oBAAoB;IACnC,2CAA2C;IAC3C,WAAW,EAAE,YAAY,CAAC;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iFAAiF;IACjF,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,eAAe,CAAC,EAC9B,WAAW,EACX,OAAe,EACf,KAAK,EACL,QAAQ,GACT,EAAE,oBAAoB,2CAuBtB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,YAAY,GAAG,SAAS,CAAC,SAAS,CAAC,CAUlF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AppColorName } from '../color-presets';
|
|
3
|
+
export interface BloomColorScopeProps {
|
|
4
|
+
/** Preset to apply within this subtree. */
|
|
5
|
+
colorPreset: AppColorName;
|
|
6
|
+
/**
|
|
7
|
+
* When `true`, do not render a wrapping element. The caller is responsible
|
|
8
|
+
* for placing the returned context provider over a DOM node that owns the
|
|
9
|
+
* CSS vars (via `useColorScopeStyle`).
|
|
10
|
+
*/
|
|
11
|
+
asChild?: boolean;
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}
|
|
14
|
+
export declare function BloomColorScope({ colorPreset, asChild, children }: BloomColorScopeProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
/**
|
|
16
|
+
* Escape hatch for advanced cases where the wrapping element is owned by the
|
|
17
|
+
* caller (e.g. a Pressable, a NativeWind-styled View that already has a style
|
|
18
|
+
* prop). Returns a stable React `style` object carrying every CSS custom
|
|
19
|
+
* property of the preset.
|
|
20
|
+
*/
|
|
21
|
+
export declare function useColorScopeStyle(colorPreset: AppColorName): React.CSSProperties;
|
|
22
|
+
//# sourceMappingURL=index.web.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../../src/theme/color-scope/index.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAIrD,MAAM,WAAW,oBAAoB;IACnC,2CAA2C;IAC3C,WAAW,EAAE,YAAY,CAAC;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,OAAe,EAAE,QAAQ,EAAE,EAAE,oBAAoB,2CAuB/F;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,YAAY,GAAG,KAAK,CAAC,aAAa,CAUjF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AppColorName } from '../color-presets';
|
|
2
|
+
/**
|
|
3
|
+
* Build the CSS custom-property map for a preset, ready to be applied to a
|
|
4
|
+
* subtree. Always includes the resolved `--color-*` vars so Tailwind v4
|
|
5
|
+
* `@theme` utilities (e.g. `bg-background`) honour the scope.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildScopeVars(colorPreset: AppColorName, mode: 'light' | 'dark'): Record<string, string>;
|
|
8
|
+
//# sourceMappingURL=style-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style-builder.d.ts","sourceRoot":"","sources":["../../../../../src/theme/color-scope/style-builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,YAAY,EACzB,IAAI,EAAE,OAAO,GAAG,MAAM,GACrB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAExB"}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
export { BloomThemeProvider
|
|
2
|
-
export type { BloomThemeProviderProps, BloomThemeContextValue,
|
|
1
|
+
export { BloomThemeProvider } from './BloomThemeProvider';
|
|
2
|
+
export type { BloomThemeProviderProps, BloomThemeContextValue, } from './BloomThemeProvider';
|
|
3
|
+
export { BloomColorScope, useColorScopeStyle } from './color-scope';
|
|
4
|
+
export type { BloomColorScopeProps } from './color-scope';
|
|
3
5
|
export { buildTheme, STATUS_COLORS } from './build-theme';
|
|
4
6
|
export { useTheme, useThemeColor, useBloomTheme } from './use-theme';
|
|
5
7
|
export type { Theme, ThemeColors, ThemeMode } from './types';
|
|
6
8
|
export type { AppColorName, AppColorPreset, PresetTokens } from './color-presets';
|
|
7
|
-
export { APP_COLOR_NAMES, APP_COLOR_PRESETS, HEX_TO_APP_COLOR, hexToAppColorName, } from './color-presets';
|
|
9
|
+
export { APP_COLOR_NAMES, PREMIUM_COLOR_NAMES, APP_COLOR_PRESETS, HEX_TO_APP_COLOR, hexToAppColorName, } from './color-presets';
|
|
10
|
+
export { getPresetVars, applyPresetVarsToDocument, } from './preset-vars';
|
|
11
|
+
export type { PresetVarsOptions } from './preset-vars';
|
|
8
12
|
export { applyDarkClass } from './apply-dark-class';
|
|
9
13
|
export { setColorSchemeSafe } from './set-color-scheme-safe';
|
|
10
14
|
export { initCssInteropDarkMode } from './init-css-interop';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EACV,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACpE,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAClF,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,aAAa,EACb,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type AppColorName, type PresetTokens } from './color-presets';
|
|
2
|
+
export interface PresetVarsOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Also emit Tailwind v4 resolved `--color-*` vars (wrapped in `hsl(...)`)
|
|
5
|
+
* alongside the raw HSL triples. Needed when scoping a subtree where
|
|
6
|
+
* Tailwind's `@theme` block has already precomputed `--color-*` at `:root`,
|
|
7
|
+
* so overriding `--background` alone wouldn't cascade to `bg-background`.
|
|
8
|
+
* Default `false`.
|
|
9
|
+
*/
|
|
10
|
+
includeResolvedColorVars?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Bloom's base preset tokens extended with the surface/card/chart/sidebar
|
|
14
|
+
* tokens that apps layer on top of the core shadcn palette. Synthesized from
|
|
15
|
+
* the preset's primary hue so every preset stays in sync automatically.
|
|
16
|
+
*
|
|
17
|
+
* This is the single source of truth for extended theming vars — consumer apps
|
|
18
|
+
* must not redefine these per-app.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getPresetVars(colorName: AppColorName, mode: 'light' | 'dark', options?: PresetVarsOptions): PresetTokens;
|
|
21
|
+
/**
|
|
22
|
+
* Apply a preset's extended vars to `document.documentElement`. Web-only no-op
|
|
23
|
+
* on native. `BloomThemeProvider` already writes the base preset vars on web;
|
|
24
|
+
* call this only when an app needs the extended (card/chart/sidebar) tokens on
|
|
25
|
+
* the document root.
|
|
26
|
+
*/
|
|
27
|
+
export declare function applyPresetVarsToDocument(colorName: AppColorName, mode: 'light' | 'dark', options?: PresetVarsOptions): void;
|
|
28
|
+
//# sourceMappingURL=preset-vars.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-vars.d.ts","sourceRoot":"","sources":["../../../../src/theme/preset-vars.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAiC1F,MAAM,WAAW,iBAAiB;IAChC;;;;;;OAMG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,YAAY,EACvB,IAAI,EAAE,OAAO,GAAG,MAAM,EACtB,OAAO,GAAE,iBAAsB,GAC9B,YAAY,CAqCd;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,YAAY,EACvB,IAAI,EAAE,OAAO,GAAG,MAAM,EACtB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,IAAI,CAQN"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text } from 'react-native';
|
|
3
|
+
import { render } from '@testing-library/react-native';
|
|
4
|
+
|
|
5
|
+
import { BloomThemeProvider } from '../theme/BloomThemeProvider';
|
|
6
|
+
import { BloomColorScope } from '../theme/color-scope';
|
|
7
|
+
import { useBloomTheme } from '../theme/use-theme';
|
|
8
|
+
|
|
9
|
+
function CurrentPreset() {
|
|
10
|
+
const { colorPreset } = useBloomTheme();
|
|
11
|
+
return <Text testID="preset">{colorPreset}</Text>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('BloomColorScope', () => {
|
|
15
|
+
it('overrides the colorPreset of its subtree without affecting the parent', () => {
|
|
16
|
+
const { getAllByTestId } = render(
|
|
17
|
+
<BloomThemeProvider defaultColorPreset="blue" fonts={false}>
|
|
18
|
+
<CurrentPreset />
|
|
19
|
+
<BloomColorScope colorPreset="green">
|
|
20
|
+
<CurrentPreset />
|
|
21
|
+
</BloomColorScope>
|
|
22
|
+
</BloomThemeProvider>,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const presets = getAllByTestId('preset').map((node) => node.props.children);
|
|
26
|
+
expect(presets[0]).toBe('blue');
|
|
27
|
+
expect(presets[1]).toBe('green');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('renders children directly with asChild (no wrapper element)', () => {
|
|
31
|
+
const { getByTestId } = render(
|
|
32
|
+
<BloomThemeProvider defaultColorPreset="blue" fonts={false}>
|
|
33
|
+
<BloomColorScope colorPreset="purple" asChild>
|
|
34
|
+
<CurrentPreset />
|
|
35
|
+
</BloomColorScope>
|
|
36
|
+
</BloomThemeProvider>,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
expect(getByTestId('preset').props.children).toBe('purple');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('throws when used outside BloomThemeProvider', () => {
|
|
43
|
+
const consoleError = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
44
|
+
expect(() =>
|
|
45
|
+
render(
|
|
46
|
+
<BloomColorScope colorPreset="red">
|
|
47
|
+
<CurrentPreset />
|
|
48
|
+
</BloomColorScope>,
|
|
49
|
+
),
|
|
50
|
+
).toThrow('BloomColorScope must be used within a <BloomThemeProvider>');
|
|
51
|
+
consoleError.mockRestore();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { APP_COLOR_NAMES, APP_COLOR_PRESETS } from '../theme/color-presets';
|
|
2
|
+
import { getPresetVars } from '../theme/preset-vars';
|
|
3
|
+
|
|
4
|
+
const EXTENDED_KEYS = [
|
|
5
|
+
'--card',
|
|
6
|
+
'--card-foreground',
|
|
7
|
+
'--chart-1',
|
|
8
|
+
'--chart-2',
|
|
9
|
+
'--chart-3',
|
|
10
|
+
'--chart-4',
|
|
11
|
+
'--chart-5',
|
|
12
|
+
'--content-area',
|
|
13
|
+
'--sidebar-foreground',
|
|
14
|
+
'--sidebar-primary',
|
|
15
|
+
'--sidebar-primary-foreground',
|
|
16
|
+
'--sidebar-accent',
|
|
17
|
+
'--sidebar-accent-foreground',
|
|
18
|
+
'--sidebar-border',
|
|
19
|
+
'--sidebar-ring',
|
|
20
|
+
] as const;
|
|
21
|
+
|
|
22
|
+
describe('getPresetVars', () => {
|
|
23
|
+
it('includes every base preset token plus extended tokens for all presets and modes', () => {
|
|
24
|
+
for (const name of APP_COLOR_NAMES) {
|
|
25
|
+
for (const mode of ['light', 'dark'] as const) {
|
|
26
|
+
const vars = getPresetVars(name, mode);
|
|
27
|
+
const base = mode === 'light' ? APP_COLOR_PRESETS[name].light : APP_COLOR_PRESETS[name].dark;
|
|
28
|
+
|
|
29
|
+
for (const key of Object.keys(base)) {
|
|
30
|
+
expect(vars[key]).toBe(base[key]);
|
|
31
|
+
}
|
|
32
|
+
for (const key of EXTENDED_KEYS) {
|
|
33
|
+
expect(vars[key]).toBeDefined();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('derives chart/sidebar hue from the preset primary', () => {
|
|
40
|
+
const vars = getPresetVars('blue', 'light');
|
|
41
|
+
const primary = APP_COLOR_PRESETS.blue.light['--primary'] ?? '';
|
|
42
|
+
const primaryHue = primary.split(' ')[0] ?? '';
|
|
43
|
+
const chart1 = vars['--chart-1'] ?? '';
|
|
44
|
+
expect(chart1.startsWith(`${primaryHue} `)).toBe(true);
|
|
45
|
+
expect(vars['--sidebar-primary']).toBe(primary);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('does not emit resolved --color-* vars by default', () => {
|
|
49
|
+
const vars = getPresetVars('green', 'dark');
|
|
50
|
+
expect(vars['--color-primary']).toBeUndefined();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('emits resolved --color-* vars wrapped in hsl() when requested', () => {
|
|
54
|
+
const vars = getPresetVars('green', 'dark', { includeResolvedColorVars: true });
|
|
55
|
+
expect(vars['--color-primary']).toBe(`hsl(${APP_COLOR_PRESETS.green.dark['--primary']})`);
|
|
56
|
+
expect(vars['--color-card']).toBe(`hsl(${vars['--card']})`);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -304,27 +304,4 @@ export function BloomThemeProvider({
|
|
|
304
304
|
);
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
-
/**
|
|
308
|
-
* Scoped color override for a subtree. Inherits the resolved mode from the
|
|
309
|
-
* parent `BloomThemeProvider` but renders descendants with a different preset.
|
|
310
|
-
*/
|
|
311
|
-
export interface BloomColorScopeProps {
|
|
312
|
-
colorPreset: AppColorName;
|
|
313
|
-
children: React.ReactNode;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
export function BloomColorScope({ colorPreset, children }: BloomColorScopeProps) {
|
|
317
|
-
const parent = useContext(BloomThemeContext);
|
|
318
|
-
if (!parent) {
|
|
319
|
-
throw new Error('BloomColorScope must be used within a <BloomThemeProvider>');
|
|
320
|
-
}
|
|
321
307
|
|
|
322
|
-
const contextValue = useMemo<BloomThemeContextValue>(() => {
|
|
323
|
-
const theme = buildTheme(colorPreset, parent.theme.mode);
|
|
324
|
-
return { ...parent, theme, colorPreset };
|
|
325
|
-
}, [colorPreset, parent]);
|
|
326
|
-
|
|
327
|
-
return (
|
|
328
|
-
<BloomThemeContext.Provider value={contextValue}>{children}</BloomThemeContext.Provider>
|
|
329
|
-
);
|
|
330
|
-
}
|
|
@@ -25,6 +25,9 @@ export interface AppColorPreset {
|
|
|
25
25
|
|
|
26
26
|
export const APP_COLOR_NAMES: readonly AppColorName[] = ['teal', 'blue', 'green', 'amber', 'yellow', 'red', 'purple', 'pink', 'sky', 'orange', 'mint', 'oxy', 'faircoin'];
|
|
27
27
|
|
|
28
|
+
/** Premium-exclusive presets, hidden from the standard color picker. */
|
|
29
|
+
export const PREMIUM_COLOR_NAMES: readonly AppColorName[] = ['oxy', 'faircoin'];
|
|
30
|
+
|
|
28
31
|
export const HEX_TO_APP_COLOR: Record<string, AppColorName> = {
|
|
29
32
|
'#005c67': 'teal',
|
|
30
33
|
'#1d9bf0': 'blue',
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React, { useContext, useMemo } from 'react';
|
|
2
|
+
import { View, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { BloomThemeContext, type BloomThemeContextValue } from '../BloomThemeProvider';
|
|
5
|
+
import { buildTheme } from '../build-theme';
|
|
6
|
+
import type { AppColorName } from '../color-presets';
|
|
7
|
+
import { lazyRequire } from '../../utils/lazy-require';
|
|
8
|
+
import { buildScopeVars } from './style-builder';
|
|
9
|
+
|
|
10
|
+
interface NativeWindVarsModule {
|
|
11
|
+
vars: (record: Record<string, string>) => StyleProp<ViewStyle>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const getNativeWindVars = lazyRequire<NativeWindVarsModule>('nativewind');
|
|
15
|
+
|
|
16
|
+
function presetStyle(colorPreset: AppColorName, mode: 'light' | 'dark'): StyleProp<ViewStyle> {
|
|
17
|
+
const module = getNativeWindVars();
|
|
18
|
+
if (!module || typeof module.vars !== 'function') return undefined;
|
|
19
|
+
return module.vars(buildScopeVars(colorPreset, mode));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface BloomColorScopeProps {
|
|
23
|
+
/** Preset to apply within this subtree. */
|
|
24
|
+
colorPreset: AppColorName;
|
|
25
|
+
/**
|
|
26
|
+
* When `true`, do not render a wrapping `<View>`. The caller owns the
|
|
27
|
+
* element that receives the CSS vars (via `useColorScopeStyle`).
|
|
28
|
+
*/
|
|
29
|
+
asChild?: boolean;
|
|
30
|
+
/** Additional style applied to the wrapping `<View>`. Ignored with `asChild`. */
|
|
31
|
+
style?: StyleProp<ViewStyle>;
|
|
32
|
+
children: React.ReactNode;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function BloomColorScope({
|
|
36
|
+
colorPreset,
|
|
37
|
+
asChild = false,
|
|
38
|
+
style,
|
|
39
|
+
children,
|
|
40
|
+
}: BloomColorScopeProps) {
|
|
41
|
+
const parent = useContext(BloomThemeContext);
|
|
42
|
+
if (!parent) {
|
|
43
|
+
throw new Error('BloomColorScope must be used within a <BloomThemeProvider>');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const resolvedMode = parent.theme.mode;
|
|
47
|
+
|
|
48
|
+
const contextValue = useMemo<BloomThemeContextValue>(() => {
|
|
49
|
+
const theme = buildTheme(colorPreset, resolvedMode);
|
|
50
|
+
return { ...parent, theme, colorPreset };
|
|
51
|
+
}, [colorPreset, resolvedMode, parent]);
|
|
52
|
+
|
|
53
|
+
const varsStyle = useMemo(
|
|
54
|
+
() => presetStyle(colorPreset, resolvedMode),
|
|
55
|
+
[colorPreset, resolvedMode],
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<BloomThemeContext.Provider value={contextValue}>
|
|
60
|
+
{asChild ? children : <View style={[{ flex: 1 }, varsStyle, style]}>{children}</View>}
|
|
61
|
+
</BloomThemeContext.Provider>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Escape hatch for advanced cases where the wrapping element is owned by the
|
|
67
|
+
* caller. Returns a stable native style object carrying the preset's CSS vars.
|
|
68
|
+
* Returns `undefined` when nativewind is not installed.
|
|
69
|
+
*/
|
|
70
|
+
export function useColorScopeStyle(colorPreset: AppColorName): StyleProp<ViewStyle> {
|
|
71
|
+
const parent = useContext(BloomThemeContext);
|
|
72
|
+
if (!parent) {
|
|
73
|
+
throw new Error('useColorScopeStyle must be used within a <BloomThemeProvider>');
|
|
74
|
+
}
|
|
75
|
+
const resolvedMode = parent.theme.mode;
|
|
76
|
+
return useMemo(
|
|
77
|
+
() => presetStyle(colorPreset, resolvedMode),
|
|
78
|
+
[colorPreset, resolvedMode],
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React, { useContext, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import { BloomThemeContext } from '../BloomThemeProvider';
|
|
4
|
+
import { buildTheme } from '../build-theme';
|
|
5
|
+
import type { AppColorName } from '../color-presets';
|
|
6
|
+
import { BloomThemeContextValue } from '../BloomThemeProvider';
|
|
7
|
+
import { buildScopeVars } from './style-builder';
|
|
8
|
+
|
|
9
|
+
export interface BloomColorScopeProps {
|
|
10
|
+
/** Preset to apply within this subtree. */
|
|
11
|
+
colorPreset: AppColorName;
|
|
12
|
+
/**
|
|
13
|
+
* When `true`, do not render a wrapping element. The caller is responsible
|
|
14
|
+
* for placing the returned context provider over a DOM node that owns the
|
|
15
|
+
* CSS vars (via `useColorScopeStyle`).
|
|
16
|
+
*/
|
|
17
|
+
asChild?: boolean;
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function BloomColorScope({ colorPreset, asChild = false, children }: BloomColorScopeProps) {
|
|
22
|
+
const parent = useContext(BloomThemeContext);
|
|
23
|
+
if (!parent) {
|
|
24
|
+
throw new Error('BloomColorScope must be used within a <BloomThemeProvider>');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const resolvedMode = parent.theme.mode;
|
|
28
|
+
|
|
29
|
+
const contextValue = useMemo<BloomThemeContextValue>(() => {
|
|
30
|
+
const theme = buildTheme(colorPreset, resolvedMode);
|
|
31
|
+
return { ...parent, theme, colorPreset };
|
|
32
|
+
}, [colorPreset, resolvedMode, parent]);
|
|
33
|
+
|
|
34
|
+
const style = useMemo(
|
|
35
|
+
() => buildScopeVars(colorPreset, resolvedMode) as React.CSSProperties,
|
|
36
|
+
[colorPreset, resolvedMode],
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<BloomThemeContext.Provider value={contextValue}>
|
|
41
|
+
{asChild ? children : <div style={style}>{children}</div>}
|
|
42
|
+
</BloomThemeContext.Provider>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Escape hatch for advanced cases where the wrapping element is owned by the
|
|
48
|
+
* caller (e.g. a Pressable, a NativeWind-styled View that already has a style
|
|
49
|
+
* prop). Returns a stable React `style` object carrying every CSS custom
|
|
50
|
+
* property of the preset.
|
|
51
|
+
*/
|
|
52
|
+
export function useColorScopeStyle(colorPreset: AppColorName): React.CSSProperties {
|
|
53
|
+
const parent = useContext(BloomThemeContext);
|
|
54
|
+
if (!parent) {
|
|
55
|
+
throw new Error('useColorScopeStyle must be used within a <BloomThemeProvider>');
|
|
56
|
+
}
|
|
57
|
+
const resolvedMode = parent.theme.mode;
|
|
58
|
+
return useMemo(
|
|
59
|
+
() => buildScopeVars(colorPreset, resolvedMode) as React.CSSProperties,
|
|
60
|
+
[colorPreset, resolvedMode],
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getPresetVars } from '../preset-vars';
|
|
2
|
+
import type { AppColorName } from '../color-presets';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Build the CSS custom-property map for a preset, ready to be applied to a
|
|
6
|
+
* subtree. Always includes the resolved `--color-*` vars so Tailwind v4
|
|
7
|
+
* `@theme` utilities (e.g. `bg-background`) honour the scope.
|
|
8
|
+
*/
|
|
9
|
+
export function buildScopeVars(
|
|
10
|
+
colorPreset: AppColorName,
|
|
11
|
+
mode: 'light' | 'dark',
|
|
12
|
+
): Record<string, string> {
|
|
13
|
+
return getPresetVars(colorPreset, mode, { includeResolvedColorVars: true });
|
|
14
|
+
}
|
package/src/theme/index.ts
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
export { BloomThemeProvider
|
|
1
|
+
export { BloomThemeProvider } from './BloomThemeProvider';
|
|
2
2
|
export type {
|
|
3
3
|
BloomThemeProviderProps,
|
|
4
4
|
BloomThemeContextValue,
|
|
5
|
-
BloomColorScopeProps,
|
|
6
5
|
} from './BloomThemeProvider';
|
|
6
|
+
export { BloomColorScope, useColorScopeStyle } from './color-scope';
|
|
7
|
+
export type { BloomColorScopeProps } from './color-scope';
|
|
7
8
|
export { buildTheme, STATUS_COLORS } from './build-theme';
|
|
8
9
|
export { useTheme, useThemeColor, useBloomTheme } from './use-theme';
|
|
9
10
|
export type { Theme, ThemeColors, ThemeMode } from './types';
|
|
10
11
|
export type { AppColorName, AppColorPreset, PresetTokens } from './color-presets';
|
|
11
12
|
export {
|
|
12
13
|
APP_COLOR_NAMES,
|
|
14
|
+
PREMIUM_COLOR_NAMES,
|
|
13
15
|
APP_COLOR_PRESETS,
|
|
14
16
|
HEX_TO_APP_COLOR,
|
|
15
17
|
hexToAppColorName,
|
|
16
18
|
} from './color-presets';
|
|
19
|
+
export {
|
|
20
|
+
getPresetVars,
|
|
21
|
+
applyPresetVarsToDocument,
|
|
22
|
+
} from './preset-vars';
|
|
23
|
+
export type { PresetVarsOptions } from './preset-vars';
|
|
17
24
|
export { applyDarkClass } from './apply-dark-class';
|
|
18
25
|
export { setColorSchemeSafe } from './set-color-scheme-safe';
|
|
19
26
|
export { initCssInteropDarkMode } from './init-css-interop';
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import { APP_COLOR_PRESETS, type AppColorName, type PresetTokens } from './color-presets';
|
|
3
|
+
|
|
4
|
+
function extractHue(hslVar: string): number {
|
|
5
|
+
return parseInt(hslVar.split(' ')[0] ?? '0', 10);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function extractSat(hslVar: string): number {
|
|
9
|
+
return parseInt(hslVar.split(' ')[1] ?? '0', 10);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const RESOLVED_COLOR_MAP: Record<string, string> = {
|
|
13
|
+
'--background': '--color-background',
|
|
14
|
+
'--foreground': '--color-foreground',
|
|
15
|
+
'--primary': '--color-primary',
|
|
16
|
+
'--primary-foreground': '--color-primary-foreground',
|
|
17
|
+
'--secondary': '--color-secondary',
|
|
18
|
+
'--secondary-foreground': '--color-secondary-foreground',
|
|
19
|
+
'--muted': '--color-muted',
|
|
20
|
+
'--muted-foreground': '--color-muted-foreground',
|
|
21
|
+
'--accent': '--color-accent',
|
|
22
|
+
'--accent-foreground': '--color-accent-foreground',
|
|
23
|
+
'--destructive': '--color-destructive',
|
|
24
|
+
'--border': '--color-border',
|
|
25
|
+
'--input': '--color-input',
|
|
26
|
+
'--ring': '--color-ring',
|
|
27
|
+
'--popover': '--color-popover',
|
|
28
|
+
'--popover-foreground': '--color-popover-foreground',
|
|
29
|
+
'--surface': '--color-surface',
|
|
30
|
+
'--surface-foreground': '--color-surface-foreground',
|
|
31
|
+
'--card': '--color-card',
|
|
32
|
+
'--card-foreground': '--color-card-foreground',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export interface PresetVarsOptions {
|
|
36
|
+
/**
|
|
37
|
+
* Also emit Tailwind v4 resolved `--color-*` vars (wrapped in `hsl(...)`)
|
|
38
|
+
* alongside the raw HSL triples. Needed when scoping a subtree where
|
|
39
|
+
* Tailwind's `@theme` block has already precomputed `--color-*` at `:root`,
|
|
40
|
+
* so overriding `--background` alone wouldn't cascade to `bg-background`.
|
|
41
|
+
* Default `false`.
|
|
42
|
+
*/
|
|
43
|
+
includeResolvedColorVars?: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Bloom's base preset tokens extended with the surface/card/chart/sidebar
|
|
48
|
+
* tokens that apps layer on top of the core shadcn palette. Synthesized from
|
|
49
|
+
* the preset's primary hue so every preset stays in sync automatically.
|
|
50
|
+
*
|
|
51
|
+
* This is the single source of truth for extended theming vars — consumer apps
|
|
52
|
+
* must not redefine these per-app.
|
|
53
|
+
*/
|
|
54
|
+
export function getPresetVars(
|
|
55
|
+
colorName: AppColorName,
|
|
56
|
+
mode: 'light' | 'dark',
|
|
57
|
+
options: PresetVarsOptions = {},
|
|
58
|
+
): PresetTokens {
|
|
59
|
+
const preset = APP_COLOR_PRESETS[colorName];
|
|
60
|
+
const base = mode === 'light' ? preset.light : preset.dark;
|
|
61
|
+
const get = (key: string): string => base[key] ?? '0 0% 0%';
|
|
62
|
+
const primary = get('--primary');
|
|
63
|
+
const foreground = get('--foreground');
|
|
64
|
+
const hue = extractHue(primary);
|
|
65
|
+
const sat = Math.min(extractSat(primary), 80);
|
|
66
|
+
const isDark = mode === 'dark';
|
|
67
|
+
|
|
68
|
+
const extended: PresetTokens = {
|
|
69
|
+
...base,
|
|
70
|
+
'--card': isDark ? `${hue} 30% 10%` : '0 0% 100%',
|
|
71
|
+
'--card-foreground': foreground,
|
|
72
|
+
'--chart-1': `${hue} ${sat}% 85%`,
|
|
73
|
+
'--chart-2': `${hue} ${sat}% 75%`,
|
|
74
|
+
'--chart-3': `${hue} ${sat}% 65%`,
|
|
75
|
+
'--chart-4': `${hue} ${sat}% ${isDark ? 55 : 75}%`,
|
|
76
|
+
'--chart-5': `${hue} ${sat}% ${isDark ? 45 : 65}%`,
|
|
77
|
+
'--content-area': isDark ? `${hue} 30% 8%` : get('--surface'),
|
|
78
|
+
'--sidebar-foreground': foreground,
|
|
79
|
+
'--sidebar-primary': primary,
|
|
80
|
+
'--sidebar-primary-foreground': get('--primary-foreground'),
|
|
81
|
+
'--sidebar-accent': isDark ? get('--sidebar') : get('--accent'),
|
|
82
|
+
'--sidebar-accent-foreground': isDark ? foreground : get('--accent-foreground'),
|
|
83
|
+
'--sidebar-border': get('--border'),
|
|
84
|
+
'--sidebar-ring': get('--ring'),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
if (!options.includeResolvedColorVars) return extended;
|
|
88
|
+
|
|
89
|
+
const resolved: PresetTokens = { ...extended };
|
|
90
|
+
for (const [rawKey, colorKey] of Object.entries(RESOLVED_COLOR_MAP)) {
|
|
91
|
+
const value = extended[rawKey];
|
|
92
|
+
if (value) resolved[colorKey] = `hsl(${value})`;
|
|
93
|
+
}
|
|
94
|
+
return resolved;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Apply a preset's extended vars to `document.documentElement`. Web-only no-op
|
|
99
|
+
* on native. `BloomThemeProvider` already writes the base preset vars on web;
|
|
100
|
+
* call this only when an app needs the extended (card/chart/sidebar) tokens on
|
|
101
|
+
* the document root.
|
|
102
|
+
*/
|
|
103
|
+
export function applyPresetVarsToDocument(
|
|
104
|
+
colorName: AppColorName,
|
|
105
|
+
mode: 'light' | 'dark',
|
|
106
|
+
options?: PresetVarsOptions,
|
|
107
|
+
): void {
|
|
108
|
+
if (Platform.OS !== 'web' || typeof document === 'undefined') return;
|
|
109
|
+
|
|
110
|
+
const vars = getPresetVars(colorName, mode, options);
|
|
111
|
+
const root = document.documentElement.style;
|
|
112
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
113
|
+
root.setProperty(key, value);
|
|
114
|
+
}
|
|
115
|
+
}
|