@oxyhq/bloom 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/react-native-css.d.js +2 -0
- package/lib/commonjs/react-native-css.d.js.map +1 -0
- package/lib/commonjs/theme/BloomThemeProvider.js +39 -7
- package/lib/commonjs/theme/BloomThemeProvider.js.map +1 -1
- package/lib/commonjs/theme/apply-dark-class.js +6 -3
- package/lib/commonjs/theme/apply-dark-class.js.map +1 -1
- package/lib/commonjs/theme/color-presets.js +118 -118
- package/lib/commonjs/theme/color-presets.js.map +1 -1
- package/lib/commonjs/theme/color-scope/index.js +28 -6
- package/lib/commonjs/theme/color-scope/index.js.map +1 -1
- package/lib/commonjs/theme/color-scope/style-builder.js +35 -0
- package/lib/commonjs/theme/color-scope/style-builder.js.map +1 -1
- package/lib/commonjs/theme/native-root-vars.js +110 -0
- package/lib/commonjs/theme/native-root-vars.js.map +1 -0
- package/lib/commonjs/theme/native-root-vars.web.js +27 -0
- package/lib/commonjs/theme/native-root-vars.web.js.map +1 -0
- package/lib/commonjs/theme/preset-vars.js +71 -2
- package/lib/commonjs/theme/preset-vars.js.map +1 -1
- package/lib/module/react-native-css.d.js +2 -0
- package/lib/module/react-native-css.d.js.map +1 -0
- package/lib/module/theme/BloomThemeProvider.js +40 -8
- package/lib/module/theme/BloomThemeProvider.js.map +1 -1
- package/lib/module/theme/apply-dark-class.js +6 -3
- package/lib/module/theme/apply-dark-class.js.map +1 -1
- package/lib/module/theme/color-presets.js +118 -118
- package/lib/module/theme/color-presets.js.map +1 -1
- package/lib/module/theme/color-scope/index.js +29 -7
- package/lib/module/theme/color-scope/index.js.map +1 -1
- package/lib/module/theme/color-scope/style-builder.js +35 -0
- package/lib/module/theme/color-scope/style-builder.js.map +1 -1
- package/lib/module/theme/native-root-vars.js +106 -0
- package/lib/module/theme/native-root-vars.js.map +1 -0
- package/lib/module/theme/native-root-vars.web.js +23 -0
- package/lib/module/theme/native-root-vars.web.js.map +1 -0
- package/lib/module/theme/preset-vars.js +70 -2
- package/lib/module/theme/preset-vars.js.map +1 -1
- package/lib/typescript/commonjs/icons/common.d.ts +8 -8
- package/lib/typescript/commonjs/theme/BloomThemeProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/apply-dark-class.d.ts +6 -3
- package/lib/typescript/commonjs/theme/apply-dark-class.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-scope/index.d.ts +9 -2
- package/lib/typescript/commonjs/theme/color-scope/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-scope/style-builder.d.ts +32 -0
- package/lib/typescript/commonjs/theme/color-scope/style-builder.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/native-root-vars.d.ts +94 -0
- package/lib/typescript/commonjs/theme/native-root-vars.d.ts.map +1 -0
- package/lib/typescript/commonjs/theme/native-root-vars.web.d.ts +20 -0
- package/lib/typescript/commonjs/theme/native-root-vars.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/theme/preset-vars.d.ts +35 -2
- package/lib/typescript/commonjs/theme/preset-vars.d.ts.map +1 -1
- package/lib/typescript/module/icons/common.d.ts +8 -8
- package/lib/typescript/module/theme/BloomThemeProvider.d.ts.map +1 -1
- package/lib/typescript/module/theme/apply-dark-class.d.ts +6 -3
- package/lib/typescript/module/theme/apply-dark-class.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-scope/index.d.ts +9 -2
- package/lib/typescript/module/theme/color-scope/index.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-scope/style-builder.d.ts +32 -0
- package/lib/typescript/module/theme/color-scope/style-builder.d.ts.map +1 -1
- package/lib/typescript/module/theme/native-root-vars.d.ts +94 -0
- package/lib/typescript/module/theme/native-root-vars.d.ts.map +1 -0
- package/lib/typescript/module/theme/native-root-vars.web.d.ts +20 -0
- package/lib/typescript/module/theme/native-root-vars.web.d.ts.map +1 -0
- package/lib/typescript/module/theme/preset-vars.d.ts +35 -2
- package/lib/typescript/module/theme/preset-vars.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/native-root-vars.test.ts +127 -0
- package/src/__tests__/preset-vars.test.ts +45 -4
- package/src/react-native-css.d.ts +70 -0
- package/src/theme/BloomThemeProvider.tsx +32 -6
- package/src/theme/apply-dark-class.ts +6 -3
- package/src/theme/color-presets.ts +118 -118
- package/src/theme/color-scope/index.tsx +30 -8
- package/src/theme/color-scope/style-builder.ts +40 -0
- package/src/theme/native-root-vars.ts +107 -0
- package/src/theme/native-root-vars.web.ts +22 -0
- package/src/theme/preset-vars.ts +88 -4
|
@@ -1,11 +1,44 @@
|
|
|
1
1
|
import { type AppColorName, type PresetTokens } from './color-presets';
|
|
2
|
+
/**
|
|
3
|
+
* Convert a shadcn-style HSL triple (`'H S% L%'`, optionally with an alpha tail
|
|
4
|
+
* `'H S% L% / A'`) into an sRGB `rgb(...)` string.
|
|
5
|
+
*
|
|
6
|
+
* Why rgb and not hsl (the native alpha-utility bug this fixes)
|
|
7
|
+
* -------------------------------------------------------------
|
|
8
|
+
* Tailwind v4 compiles an alpha-on-theme-color utility (`bg-primary/10`) into a
|
|
9
|
+
* `color-mix(... var(--color-primary) 10%, transparent)` declaration. On native,
|
|
10
|
+
* react-native-css resolves that `color-mix` at runtime via `colorjs.io/fn`, but
|
|
11
|
+
* its `color-mix` implementation registers ONLY the `sRGB`, `P3`, and `OKLab`
|
|
12
|
+
* color spaces — never `HSL`. So if `--color-primary` resolves to an `hsl(...)`
|
|
13
|
+
* string, `parse('hsl(...)')` throws, the mix is swallowed, and the utility
|
|
14
|
+
* silently produces no color. Emitting the resolved var as `rgb(...)` (a space
|
|
15
|
+
* colorjs.io always has registered) makes the alpha utilities resolve on native,
|
|
16
|
+
* matching web. Full (non-alpha) utilities already worked because React Native's
|
|
17
|
+
* native color parser handles `hsl()` directly; only `color-mix` was affected.
|
|
18
|
+
*
|
|
19
|
+
* Output is space-separated channels (`rgb(31 153 239)`), the modern CSS syntax
|
|
20
|
+
* parsed by both `colorjs.io/fn` (with only sRGB/P3/OKLab registered) and
|
|
21
|
+
* react-native-web / browsers. An alpha tail emits `rgb(r g b / a)`, likewise
|
|
22
|
+
* parsed by both.
|
|
23
|
+
*
|
|
24
|
+
* Pure function — standard HSL→sRGB conversion, channels rounded to integers.
|
|
25
|
+
* Tolerates a `deg` suffix on the hue and `%` suffixes on saturation/lightness.
|
|
26
|
+
*/
|
|
27
|
+
export declare function hslTripletToRgb(triplet: string): string;
|
|
2
28
|
export interface PresetVarsOptions {
|
|
3
29
|
/**
|
|
4
|
-
* Also emit Tailwind v4 resolved `--color-*` vars (
|
|
30
|
+
* Also emit Tailwind v4 resolved `--color-*` vars (as sRGB `rgb(...)` strings)
|
|
5
31
|
* alongside the raw HSL triples. Needed when scoping a subtree where
|
|
6
32
|
* Tailwind's `@theme` block has already precomputed `--color-*` at `:root`,
|
|
7
33
|
* so overriding `--background` alone wouldn't cascade to `bg-background`.
|
|
8
|
-
*
|
|
34
|
+
*
|
|
35
|
+
* The resolved vars are emitted as `rgb(...)` rather than `hsl(...)` so the
|
|
36
|
+
* alpha-on-theme-color utilities (`bg-primary/10`, `text-foreground/50`)
|
|
37
|
+
* resolve on native: Tailwind compiles those to a runtime `color-mix(...)` that
|
|
38
|
+
* react-native-css evaluates via `colorjs.io/fn`, which only registers the
|
|
39
|
+
* sRGB/P3/OKLab spaces (not HSL) — so an `hsl(...)` var would throw and the
|
|
40
|
+
* utility would silently render no color. See `hslTripletToRgb`. Default
|
|
41
|
+
* `false`.
|
|
9
42
|
*/
|
|
10
43
|
includeResolvedColorVars?: boolean;
|
|
11
44
|
}
|
|
@@ -1 +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;
|
|
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;AAU1F;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA+CvD;AAyBD,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;;;;;;OAaG;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,CAwCd;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,YAAY,EACvB,IAAI,EAAE,OAAO,GAAG,MAAM,EACtB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,IAAI,CAQN"}
|
|
@@ -37,14 +37,6 @@ export declare function useCommonSVGProps(props: Props): {
|
|
|
37
37
|
title?: string | undefined;
|
|
38
38
|
children?: React.ReactNode;
|
|
39
39
|
filter?: string | undefined;
|
|
40
|
-
color?: ColorValue | undefined;
|
|
41
|
-
scale?: import("react-native-svg").NumberArray | undefined;
|
|
42
|
-
scaleX?: import("react-native-svg").NumberProp | undefined;
|
|
43
|
-
scaleY?: import("react-native-svg").NumberProp | undefined;
|
|
44
|
-
translateX?: import("react-native-svg").NumberProp | undefined;
|
|
45
|
-
translateY?: import("react-native-svg").NumberProp | undefined;
|
|
46
|
-
skewX?: import("react-native-svg").NumberProp | undefined;
|
|
47
|
-
skewY?: import("react-native-svg").NumberProp | undefined;
|
|
48
40
|
hitSlop?: number | import("react-native").Insets | undefined;
|
|
49
41
|
id?: string | undefined;
|
|
50
42
|
needsOffscreenAlphaCompositing?: boolean | undefined | undefined;
|
|
@@ -136,6 +128,14 @@ export declare function useCommonSVGProps(props: Props): {
|
|
|
136
128
|
accessibilityShowsLargeContentViewer?: boolean | undefined | undefined;
|
|
137
129
|
accessibilityLargeContentTitle?: string | undefined | undefined;
|
|
138
130
|
accessibilityRespondsToUserInteraction?: boolean | undefined | undefined;
|
|
131
|
+
color?: ColorValue | undefined;
|
|
132
|
+
scale?: import("react-native-svg").NumberArray | undefined;
|
|
133
|
+
scaleX?: import("react-native-svg").NumberProp | undefined;
|
|
134
|
+
scaleY?: import("react-native-svg").NumberProp | undefined;
|
|
135
|
+
translateX?: import("react-native-svg").NumberProp | undefined;
|
|
136
|
+
translateY?: import("react-native-svg").NumberProp | undefined;
|
|
137
|
+
skewX?: import("react-native-svg").NumberProp | undefined;
|
|
138
|
+
skewY?: import("react-native-svg").NumberProp | undefined;
|
|
139
139
|
fontSize?: import("react-native-svg").NumberProp | undefined;
|
|
140
140
|
fontWeight?: import("react-native-svg").FontWeight | undefined;
|
|
141
141
|
fontStyle?: import("react-native-svg").FontStyle | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BloomThemeProvider.d.ts","sourceRoot":"","sources":["../../../../src/theme/BloomThemeProvider.tsx"],"names":[],"mappings":"AAIA,OAAO,oBAAoB,CAAC;AAE5B,OAAO,KAQN,MAAM,OAAO,CAAC;AACf,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAOtB,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"BloomThemeProvider.d.ts","sourceRoot":"","sources":["../../../../src/theme/BloomThemeProvider.tsx"],"names":[],"mappings":"AAIA,OAAO,oBAAoB,CAAC;AAE5B,OAAO,KAQN,MAAM,OAAO,CAAC;AACf,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAOtB,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,EAKL,KAAK,iBAAiB,EAEvB,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAKhD,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,EAAE,YAAY,CAAC;IAC1B,OAAO,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACnC,cAAc,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAC/C;;;;OAIG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,eAAO,MAAM,iBAAiB,8CAAqD,CAAC;AAEpF,MAAM,WAAW,uBAAuB;IACtC,uFAAuF;IACvF,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,mEAAmE;IACnE,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,mEAAmE;IACnE,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,YAAY,CAAC;IAElC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACzC,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAErD;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iDAAiD;IACjD,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE9B,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wDAAwD;IACxD,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAEjC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAE1C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAkJD,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EAAE,cAAc,EACpB,WAAW,EAAE,gBAAgB,EAC7B,WAA0B,EAC1B,kBAAmC,EACnC,YAAY,EACZ,mBAAmB,EACnB,UAAU,EACV,OAAO,EACP,cAAc,EACd,WAAW,EACX,KAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,QAAQ,GACT,EAAE,uBAAuB,2CAwFzB"}
|
|
@@ -5,10 +5,13 @@ export declare function applyDarkClass(resolved: 'light' | 'dark'): void;
|
|
|
5
5
|
* No-op on native — only affects web.
|
|
6
6
|
*
|
|
7
7
|
* Writes both the raw HSL triples (e.g. `--primary: 185 100% 20%`) and the
|
|
8
|
-
* resolved `--color-*` vars (`--color-primary:
|
|
8
|
+
* resolved `--color-*` vars (`--color-primary: rgb(0 94 102)`) so both
|
|
9
9
|
* shadcn-style `hsl(var(--primary))` plumbing and Tailwind v4 `@theme`
|
|
10
|
-
* utilities resolve consistently.
|
|
11
|
-
*
|
|
10
|
+
* utilities resolve consistently. The resolved vars are emitted as `rgb(...)`
|
|
11
|
+
* (not `hsl(...)`) so the alpha utilities (`bg-primary/10`) resolve on native —
|
|
12
|
+
* see `getPresetVars` / `hslTripletToRgb`. Includes extended tokens (card,
|
|
13
|
+
* chart-*, content-area, sidebar-*) so consumer apps don't need to synthesize
|
|
14
|
+
* them.
|
|
12
15
|
*/
|
|
13
16
|
export declare function applyColorPresetVars(preset: AppColorName, resolved: 'light' | 'dark'): void;
|
|
14
17
|
//# sourceMappingURL=apply-dark-class.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-dark-class.d.ts","sourceRoot":"","sources":["../../../../src/theme/apply-dark-class.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGvE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,QAIxD;AAED
|
|
1
|
+
{"version":3,"file":"apply-dark-class.d.ts","sourceRoot":"","sources":["../../../../src/theme/apply-dark-class.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGvE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,QAIxD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,QAUpF"}
|
|
@@ -19,8 +19,15 @@ export interface BloomColorScopeProps {
|
|
|
19
19
|
export declare function BloomColorScope({ colorPreset, asChild, style, children, }: BloomColorScopeProps): import("react/jsx-runtime").JSX.Element;
|
|
20
20
|
/**
|
|
21
21
|
* Escape hatch for advanced cases where the wrapping element is owned by the
|
|
22
|
-
* caller. Returns a stable native style object carrying the preset's CSS vars
|
|
23
|
-
* Returns `undefined` on web or when `nativewind`
|
|
22
|
+
* caller. Returns a stable native style object carrying the preset's CSS vars
|
|
23
|
+
* via NativeWind's `vars()`. Returns `undefined` on web or when `nativewind`
|
|
24
|
+
* is not installed.
|
|
25
|
+
*
|
|
26
|
+
* NOTE: subject to the react-native-css@3 limitation — the returned style only
|
|
27
|
+
* propagates vars to descendants when applied to an interop className component
|
|
28
|
+
* (the runtime gates propagation on matched className style rules). It is a
|
|
29
|
+
* no-op on a plain `<View>` with no `className`. For the common case prefer
|
|
30
|
+
* `<BloomColorScope>`, which wraps children in `VariableContextProvider`.
|
|
24
31
|
*/
|
|
25
32
|
export declare function useColorScopeStyle(colorPreset: AppColorName): StyleProp<ViewStyle>;
|
|
26
33
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/theme/color-scope/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsE,MAAM,OAAO,CAAC;AAC3F,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAIpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGrD,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,WAAW,EAAE,YAAY,GAAG,SAAS,CAAC;IACtC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0GAA0G;IAC1G,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAMD,wBAAgB,eAAe,CAAC,EAC9B,WAAW,EACX,OAAe,EACf,KAAK,EACL,QAAQ,GACT,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/theme/color-scope/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsE,MAAM,OAAO,CAAC;AAC3F,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAIpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGrD,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,WAAW,EAAE,YAAY,GAAG,SAAS,CAAC;IACtC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0GAA0G;IAC1G,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAMD,wBAAgB,eAAe,CAAC,EAC9B,WAAW,EACX,OAAe,EACf,KAAK,EACL,QAAQ,GACT,EAAE,oBAAoB,2CAmDtB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,YAAY,GAAG,SAAS,CAAC,SAAS,CAAC,CAUlF"}
|
|
@@ -1,16 +1,48 @@
|
|
|
1
|
+
import type React from 'react';
|
|
1
2
|
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
2
3
|
import type { AppColorName } from '../color-presets';
|
|
4
|
+
/**
|
|
5
|
+
* Component that provides inline CSS variables to a subtree via NativeWind's
|
|
6
|
+
* (react-native-css's) real `VariableContext`. The runtime strips the leading
|
|
7
|
+
* `--` from each key internally, so we feed it the full `--name -> value`
|
|
8
|
+
* record produced by `buildScopeVars`.
|
|
9
|
+
*/
|
|
10
|
+
export type VariableContextProviderComponent = React.ComponentType<{
|
|
11
|
+
value: Record<string, string>;
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}>;
|
|
3
14
|
/**
|
|
4
15
|
* Build the CSS custom-property map for a preset, ready to be applied to a
|
|
5
16
|
* subtree. Always includes the resolved `--color-*` vars so Tailwind v4
|
|
6
17
|
* `@theme` utilities (e.g. `bg-background`) honour the scope.
|
|
7
18
|
*/
|
|
8
19
|
export declare function buildScopeVars(colorPreset: AppColorName, mode: 'light' | 'dark'): Record<string, string>;
|
|
20
|
+
/**
|
|
21
|
+
* Resolve NativeWind's `VariableContextProvider` — the canonical, non-deprecated
|
|
22
|
+
* react-native-css v3 API for providing inline CSS variables to a subtree. It
|
|
23
|
+
* reads the same `VariableContext` that interop className components consume, so
|
|
24
|
+
* descendants resolve `var(--primary)` etc. correctly. Returns `null` on web or
|
|
25
|
+
* when `nativewind` is not installed.
|
|
26
|
+
*
|
|
27
|
+
* This replaces the deprecated `vars()`-on-a-plain-`<View>`-style mechanism:
|
|
28
|
+
* under react-native-css@3, `vars()` returns an inline-vars marker that is only
|
|
29
|
+
* propagated to children when the host element matches className style rules
|
|
30
|
+
* (`usesVariables`/`variables`). A plain `<View>` with no `className` drops the
|
|
31
|
+
* vars silently, so the subtree renders without the preset palette.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getVariableContextProvider(): VariableContextProviderComponent | null;
|
|
9
34
|
/**
|
|
10
35
|
* Build a native style object carrying every CSS var of the preset, using
|
|
11
36
|
* NativeWind's `vars()` when available. Returns `undefined` on web (where the
|
|
12
37
|
* provider writes vars to `documentElement` instead) or when `nativewind` is
|
|
13
38
|
* not installed.
|
|
39
|
+
*
|
|
40
|
+
* @deprecated Under react-native-css@3, `vars()` only propagates to a subtree
|
|
41
|
+
* when applied to an interop className component (the runtime gates propagation
|
|
42
|
+
* on matched className style rules). Applied to a plain `<View>` the vars are
|
|
43
|
+
* dropped silently. Prefer wrapping children in `getVariableContextProvider()`.
|
|
44
|
+
* Retained only as the escape-hatch contract of `useColorScopeStyle`, where the
|
|
45
|
+
* caller owns the host element and is expected to be a className component.
|
|
14
46
|
*/
|
|
15
47
|
export declare function buildNativePresetStyle(colorPreset: AppColorName, mode: 'light' | 'dark'): StyleProp<ViewStyle>;
|
|
16
48
|
//# sourceMappingURL=style-builder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style-builder.d.ts","sourceRoot":"","sources":["../../../../../src/theme/color-scope/style-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAGxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"style-builder.d.ts","sourceRoot":"","sources":["../../../../../src/theme/color-scope/style-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAY,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAGxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGrD;;;;;GAKG;AACH,MAAM,MAAM,gCAAgC,GAAG,KAAK,CAAC,aAAa,CAAC;IACjE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC,CAAC;AASH;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,YAAY,EACzB,IAAI,EAAE,OAAO,GAAG,MAAM,GACrB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAExB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,IAAI,gCAAgC,GAAG,IAAI,CAKpF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,YAAY,EACzB,IAAI,EAAE,OAAO,GAAG,MAAM,GACrB,SAAS,CAAC,SAAS,CAAC,CAKtB"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { type AppColorName } from './color-presets';
|
|
2
|
+
/**
|
|
3
|
+
* Publish the active color preset's CSS custom properties into react-native-css's
|
|
4
|
+
* GLOBAL root-variable family so they reach the ENTIRE native app tree — including
|
|
5
|
+
* content rendered outside the React subtree of `BloomThemeProvider`: expo-router
|
|
6
|
+
* navigator screens, `Portal`s, `Dialog`/`BottomSheet` overlays, and any other host
|
|
7
|
+
* that paints through a separate React root.
|
|
8
|
+
*
|
|
9
|
+
* Why a global write is required (native only)
|
|
10
|
+
* --------------------------------------------
|
|
11
|
+
* react-native-css resolves a `var(--x)` reference (its `varResolver`) in this order:
|
|
12
|
+
* 1. `name in inheritedVariables` — the React-context-inherited vars. This is what
|
|
13
|
+
* `VariableContextProvider` populates, but it is React-context-scoped, so it only
|
|
14
|
+
* reaches *direct descendants* of the provider in the React tree.
|
|
15
|
+
* 2. `inlineVariables[name]` — same-component inline vars.
|
|
16
|
+
* 3. `variables[name]` — resolved inherited vars.
|
|
17
|
+
* 4. `universalVariables(name)` — a GLOBAL observable family.
|
|
18
|
+
* 5. `rootVariables(name)` — a GLOBAL observable family.
|
|
19
|
+
* 6. the declared fallback.
|
|
20
|
+
*
|
|
21
|
+
* `BloomThemeProvider`'s `VariableContextProvider` only feeds path #1, so the navigator
|
|
22
|
+
* host and every portal/modal — which render outside that subtree — never see the
|
|
23
|
+
* preset vars and fall through to `rootVariables`/`universalVariables`. By default those
|
|
24
|
+
* families hold only react-native-css's own `__rn-css-rem`/`__rn-css-color` entries
|
|
25
|
+
* (Bloom's `global.css` defines the `hsl(var(--primary))` indirection but not the HSL
|
|
26
|
+
* triples themselves — those are injected at runtime), so the whole tree below the
|
|
27
|
+
* navigator renders monochrome. Writing the vars into `rootVariables` here is the
|
|
28
|
+
* exact same mechanism react-native-css uses for compiled `:root {}` / `@theme` vars
|
|
29
|
+
* (`StyleCollection.inject` does `rootVariables(name).set(valueArray)`), so portaled
|
|
30
|
+
* content resolves the palette identically regardless of React-tree position.
|
|
31
|
+
*
|
|
32
|
+
* The raw tokens are written as HSL triples (`primary: '205 87% 53%'`) and the
|
|
33
|
+
* resolved Tailwind v4 `--color-*` vars as sRGB `rgb(...)` (`color-primary:
|
|
34
|
+
* 'rgb(31 153 239)'`) — the latter so `color-mix`-based alpha utilities resolve
|
|
35
|
+
* on native (see `getPresetVars` / `hslTripletToRgb`).
|
|
36
|
+
*
|
|
37
|
+
* Why a STATIC `import`, not a runtime `require` (critical)
|
|
38
|
+
* ---------------------------------------------------------
|
|
39
|
+
* react-native-css@3.0.7 ships both a `dist/commonjs` and a `dist/module` build, and
|
|
40
|
+
* Metro bundles BOTH. In `native-internal/root.js` the variable families are plain
|
|
41
|
+
* module-local consts — NOT `globalThis`-guarded the way `StyleCollection` is — so the
|
|
42
|
+
* commonjs `root.js` and the module `root.js` each instantiate their OWN separate
|
|
43
|
+
* `rootVariables` family. The react-native-css renderer (`react-native-css/src/components/View.tsx`
|
|
44
|
+
* → `varResolver`) imports its `root.js` via ESM `import`, i.e. the MODULE build. A
|
|
45
|
+
* runtime `require('react-native-css/native-internal')` resolves the package's `require`
|
|
46
|
+
* export condition → the COMMONJS build → a DIFFERENT family the renderer never
|
|
47
|
+
* subscribes to, so the writes are a silent no-op on device. A static top-level
|
|
48
|
+
* `import` resolves the `import` condition → the MODULE build → the renderer's instance.
|
|
49
|
+
*
|
|
50
|
+
* This plain file is the NATIVE/default variant (Metro selects it on iOS/Android,
|
|
51
|
+
* which have no `.native` override here); the web variant lives in the sibling
|
|
52
|
+
* `native-root-vars.web.ts` no-op, which web bundlers pick over this one. So the
|
|
53
|
+
* static `react-native-css/native-internal` import below is never pulled into a web
|
|
54
|
+
* bundle — only into native. This mirrors Bloom's existing platform-split convention
|
|
55
|
+
* (`color-scope/index.tsx` + `color-scope/index.web.tsx`, `FontLoader.native.tsx`).
|
|
56
|
+
*
|
|
57
|
+
* Keying contract (verified against react-native-css@3.0.7)
|
|
58
|
+
* ---------------------------------------------------------
|
|
59
|
+
* The `rootVariables` family is keyed by the BARE variable name, WITHOUT the leading
|
|
60
|
+
* `--`. The compiler strips it uniformly: a `:root { --primary: ... }` declaration is
|
|
61
|
+
* stored via `rule.v.push([property.slice(2), value])` then routed into
|
|
62
|
+
* `shared.rootVariables[name]`, and `@property --x` goes through
|
|
63
|
+
* `name.startsWith("--") ? name.slice(2) : name`. The lookup side matches: a `var(--x)`
|
|
64
|
+
* reference compiles to `[{}, "var", ident.slice(2)]`, so `varResolver` looks up the
|
|
65
|
+
* bare name. `buildScopeVars` returns keys WITH the `--` prefix, so we strip it before
|
|
66
|
+
* writing each entry.
|
|
67
|
+
*
|
|
68
|
+
* Value shape
|
|
69
|
+
* -----------
|
|
70
|
+
* Each entry is a `VariableValue[]` — an array of `[value, mediaCondition?]` tuples.
|
|
71
|
+
* Bloom's preset vars are unconditional, so each is a single-element `[[value]]`, e.g.
|
|
72
|
+
* `rootVariables('primary').set([['205 87% 53%']])`. This is the same shape the compiler
|
|
73
|
+
* emits for unconditional `:root` vars.
|
|
74
|
+
*
|
|
75
|
+
* This is native-only and additive: the `VariableContextProvider` wrapper stays (it is
|
|
76
|
+
* the correct, scoped mechanism for `BloomColorScope` subtree overrides and harmlessly
|
|
77
|
+
* covers direct descendants). The web fork (`native-root-vars.web.ts`) no-ops — web
|
|
78
|
+
* writes the same vars to `document.documentElement` via `applyColorPresetVars`.
|
|
79
|
+
*/
|
|
80
|
+
/**
|
|
81
|
+
* Write the preset's CSS vars to react-native-css's global `rootVariables` family
|
|
82
|
+
* so the whole native tree (navigator screens + portals/modals/bottom-sheets)
|
|
83
|
+
* resolves the active palette.
|
|
84
|
+
*
|
|
85
|
+
* Each `.set` notifies its own observers synchronously (react-native-css only
|
|
86
|
+
* batches notifications inside its own `StyleCollection.inject`; outside it,
|
|
87
|
+
* `observableBatch.current` is unset so every `.set` runs observers immediately),
|
|
88
|
+
* so sequential writes correctly trigger a re-render when the preset/mode changes.
|
|
89
|
+
*
|
|
90
|
+
* Defensive guard: if a host ships a react-native-css build that doesn't expose
|
|
91
|
+
* `rootVariables` as a callable (it always does on @3), we bail rather than throw.
|
|
92
|
+
*/
|
|
93
|
+
export declare function applyNativeRootVars(colorPreset: AppColorName, mode: 'light' | 'dark'): void;
|
|
94
|
+
//# sourceMappingURL=native-root-vars.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-root-vars.d.ts","sourceRoot":"","sources":["../../../../src/theme/native-root-vars.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6EG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAS3F"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type AppColorName } from './color-presets';
|
|
2
|
+
/**
|
|
3
|
+
* Web fork of `applyNativeRootVars` — a no-op.
|
|
4
|
+
*
|
|
5
|
+
* On web, `BloomThemeProvider` already writes the active preset's CSS vars to
|
|
6
|
+
* `document.documentElement` via `applyColorPresetVars`, which the browser /
|
|
7
|
+
* react-native-web resolve natively. The native/default variant — the plain
|
|
8
|
+
* sibling `native-root-vars.ts` — instead publishes those vars into
|
|
9
|
+
* react-native-css's GLOBAL `rootVariables` family.
|
|
10
|
+
*
|
|
11
|
+
* Keeping the native variant's `react-native-css/native-internal` import out of
|
|
12
|
+
* the web bundle is the whole reason this is a platform-split module: web (and
|
|
13
|
+
* any non-NativeWind host) picks this `.web` fork over the plain file and so
|
|
14
|
+
* never statically pulls react-native-css internals.
|
|
15
|
+
*
|
|
16
|
+
* The signature is intentionally identical to the plain variant so the import
|
|
17
|
+
* site in `BloomThemeProvider` is platform-agnostic.
|
|
18
|
+
*/
|
|
19
|
+
export declare function applyNativeRootVars(_colorPreset: AppColorName, _mode: 'light' | 'dark'): void;
|
|
20
|
+
//# sourceMappingURL=native-root-vars.web.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-root-vars.web.d.ts","sourceRoot":"","sources":["../../../../src/theme/native-root-vars.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAE7F"}
|
|
@@ -1,11 +1,44 @@
|
|
|
1
1
|
import { type AppColorName, type PresetTokens } from './color-presets';
|
|
2
|
+
/**
|
|
3
|
+
* Convert a shadcn-style HSL triple (`'H S% L%'`, optionally with an alpha tail
|
|
4
|
+
* `'H S% L% / A'`) into an sRGB `rgb(...)` string.
|
|
5
|
+
*
|
|
6
|
+
* Why rgb and not hsl (the native alpha-utility bug this fixes)
|
|
7
|
+
* -------------------------------------------------------------
|
|
8
|
+
* Tailwind v4 compiles an alpha-on-theme-color utility (`bg-primary/10`) into a
|
|
9
|
+
* `color-mix(... var(--color-primary) 10%, transparent)` declaration. On native,
|
|
10
|
+
* react-native-css resolves that `color-mix` at runtime via `colorjs.io/fn`, but
|
|
11
|
+
* its `color-mix` implementation registers ONLY the `sRGB`, `P3`, and `OKLab`
|
|
12
|
+
* color spaces — never `HSL`. So if `--color-primary` resolves to an `hsl(...)`
|
|
13
|
+
* string, `parse('hsl(...)')` throws, the mix is swallowed, and the utility
|
|
14
|
+
* silently produces no color. Emitting the resolved var as `rgb(...)` (a space
|
|
15
|
+
* colorjs.io always has registered) makes the alpha utilities resolve on native,
|
|
16
|
+
* matching web. Full (non-alpha) utilities already worked because React Native's
|
|
17
|
+
* native color parser handles `hsl()` directly; only `color-mix` was affected.
|
|
18
|
+
*
|
|
19
|
+
* Output is space-separated channels (`rgb(31 153 239)`), the modern CSS syntax
|
|
20
|
+
* parsed by both `colorjs.io/fn` (with only sRGB/P3/OKLab registered) and
|
|
21
|
+
* react-native-web / browsers. An alpha tail emits `rgb(r g b / a)`, likewise
|
|
22
|
+
* parsed by both.
|
|
23
|
+
*
|
|
24
|
+
* Pure function — standard HSL→sRGB conversion, channels rounded to integers.
|
|
25
|
+
* Tolerates a `deg` suffix on the hue and `%` suffixes on saturation/lightness.
|
|
26
|
+
*/
|
|
27
|
+
export declare function hslTripletToRgb(triplet: string): string;
|
|
2
28
|
export interface PresetVarsOptions {
|
|
3
29
|
/**
|
|
4
|
-
* Also emit Tailwind v4 resolved `--color-*` vars (
|
|
30
|
+
* Also emit Tailwind v4 resolved `--color-*` vars (as sRGB `rgb(...)` strings)
|
|
5
31
|
* alongside the raw HSL triples. Needed when scoping a subtree where
|
|
6
32
|
* Tailwind's `@theme` block has already precomputed `--color-*` at `:root`,
|
|
7
33
|
* so overriding `--background` alone wouldn't cascade to `bg-background`.
|
|
8
|
-
*
|
|
34
|
+
*
|
|
35
|
+
* The resolved vars are emitted as `rgb(...)` rather than `hsl(...)` so the
|
|
36
|
+
* alpha-on-theme-color utilities (`bg-primary/10`, `text-foreground/50`)
|
|
37
|
+
* resolve on native: Tailwind compiles those to a runtime `color-mix(...)` that
|
|
38
|
+
* react-native-css evaluates via `colorjs.io/fn`, which only registers the
|
|
39
|
+
* sRGB/P3/OKLab spaces (not HSL) — so an `hsl(...)` var would throw and the
|
|
40
|
+
* utility would silently render no color. See `hslTripletToRgb`. Default
|
|
41
|
+
* `false`.
|
|
9
42
|
*/
|
|
10
43
|
includeResolvedColorVars?: boolean;
|
|
11
44
|
}
|
|
@@ -1 +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;
|
|
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;AAU1F;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA+CvD;AAyBD,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;;;;;;OAaG;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,CAwCd;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,127 @@
|
|
|
1
|
+
type SetCall = ReadonlyArray<readonly [string]>;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Verifies the native/default variant `theme/native-root-vars.ts` publishes the
|
|
5
|
+
* active preset into react-native-css's GLOBAL `rootVariables` family with the
|
|
6
|
+
* exact keying and value shape react-native-css itself uses for compiled `:root`
|
|
7
|
+
* vars, and that the web fork `theme/native-root-vars.web.ts` is an inert no-op.
|
|
8
|
+
*
|
|
9
|
+
* - keys are written WITHOUT the leading `--` (the family is keyed by the bare
|
|
10
|
+
* name — confirmed against react-native-css@3.0.7's compiler, which stores
|
|
11
|
+
* `:root { --x }` via `slice(2)` and looks up `var(--x)` by the bare name);
|
|
12
|
+
* - each value is a single-element value-array `[[value]]` (`VariableValue[]`);
|
|
13
|
+
* - the native variant reaches react-native-css via a STATIC ESM import (so
|
|
14
|
+
* Metro resolves the `module` build — the SAME family the renderer reads);
|
|
15
|
+
* - the web variant never imports react-native-css and writes nothing.
|
|
16
|
+
*
|
|
17
|
+
* react-native-css is NOT a Bloom dependency (it arrives transitively via the
|
|
18
|
+
* host's NativeWind install), so the subpath is mocked virtually here — both for
|
|
19
|
+
* the runtime `.set` capture AND so ts-jest can compile the static import. The
|
|
20
|
+
* ambient `src/react-native-css.d.ts` declares the subpath's types for `tsc`.
|
|
21
|
+
*/
|
|
22
|
+
describe('applyNativeRootVars (native variant)', () => {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
jest.resetModules();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('writes preset vars to rootVariables with the leading -- stripped', () => {
|
|
28
|
+
const sets: Record<string, SetCall> = {};
|
|
29
|
+
|
|
30
|
+
jest.isolateModules(() => {
|
|
31
|
+
jest.doMock(
|
|
32
|
+
'react-native-css/native-internal',
|
|
33
|
+
() => ({
|
|
34
|
+
rootVariables: (name: string) => ({
|
|
35
|
+
set: (value: SetCall) => {
|
|
36
|
+
sets[name] = value;
|
|
37
|
+
},
|
|
38
|
+
}),
|
|
39
|
+
}),
|
|
40
|
+
{ virtual: true },
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const { applyNativeRootVars } = require('../theme/native-root-vars') as {
|
|
44
|
+
applyNativeRootVars: (preset: string, mode: 'light' | 'dark') => void;
|
|
45
|
+
};
|
|
46
|
+
applyNativeRootVars('blue', 'light');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Keys are bare (no `--`), and the raw HSL triple is present unwrapped.
|
|
50
|
+
expect(sets).toHaveProperty('primary');
|
|
51
|
+
expect(sets).not.toHaveProperty('--primary');
|
|
52
|
+
expect(sets.primary).toEqual([['205 87% 53%']]);
|
|
53
|
+
|
|
54
|
+
// Resolved Tailwind v4 `--color-*` vars are written too, also bare-keyed,
|
|
55
|
+
// and as sRGB `rgb(...)` (not `hsl(...)`) so native `color-mix` alpha
|
|
56
|
+
// utilities resolve — `205 87% 53%` → `rgb(31 153 239)`.
|
|
57
|
+
expect(sets).toHaveProperty('color-primary');
|
|
58
|
+
expect(sets['color-primary']).toEqual([['rgb(31 153 239)']]);
|
|
59
|
+
|
|
60
|
+
// Every value is a single-element value-array `[[value]]` of strings.
|
|
61
|
+
for (const value of Object.values(sets)) {
|
|
62
|
+
expect(Array.isArray(value)).toBe(true);
|
|
63
|
+
expect(value).toHaveLength(1);
|
|
64
|
+
expect(value[0]).toHaveLength(1);
|
|
65
|
+
expect(typeof value[0]?.[0]).toBe('string');
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('reflects the resolved mode (dark preset differs from light)', () => {
|
|
70
|
+
const collect = (mode: 'light' | 'dark'): Record<string, SetCall> => {
|
|
71
|
+
const sets: Record<string, SetCall> = {};
|
|
72
|
+
jest.isolateModules(() => {
|
|
73
|
+
jest.doMock(
|
|
74
|
+
'react-native-css/native-internal',
|
|
75
|
+
() => ({
|
|
76
|
+
rootVariables: (name: string) => ({
|
|
77
|
+
set: (value: SetCall) => {
|
|
78
|
+
sets[name] = value;
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
}),
|
|
82
|
+
{ virtual: true },
|
|
83
|
+
);
|
|
84
|
+
const { applyNativeRootVars } = require('../theme/native-root-vars') as {
|
|
85
|
+
applyNativeRootVars: (preset: string, mode: 'light' | 'dark') => void;
|
|
86
|
+
};
|
|
87
|
+
applyNativeRootVars('teal', mode);
|
|
88
|
+
});
|
|
89
|
+
return sets;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
expect(collect('light').background).not.toEqual(collect('dark').background);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('guards when rootVariables is not callable (defensive, never throws)', () => {
|
|
96
|
+
jest.isolateModules(() => {
|
|
97
|
+
jest.doMock(
|
|
98
|
+
'react-native-css/native-internal',
|
|
99
|
+
() => ({ rootVariables: undefined }),
|
|
100
|
+
{ virtual: true },
|
|
101
|
+
);
|
|
102
|
+
const { applyNativeRootVars } = require('../theme/native-root-vars') as {
|
|
103
|
+
applyNativeRootVars: (preset: string, mode: 'light' | 'dark') => void;
|
|
104
|
+
};
|
|
105
|
+
expect(() => applyNativeRootVars('blue', 'light')).not.toThrow();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('applyNativeRootVars (web fork)', () => {
|
|
111
|
+
beforeEach(() => {
|
|
112
|
+
jest.resetModules();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('no-ops on web (vars are written to documentElement there instead)', () => {
|
|
116
|
+
// The web fork must never touch react-native-css. We assert it neither
|
|
117
|
+
// imports the subpath nor throws — it is a pure no-op with the same
|
|
118
|
+
// signature as the native variant.
|
|
119
|
+
jest.isolateModules(() => {
|
|
120
|
+
const { applyNativeRootVars } = require('../theme/native-root-vars.web') as {
|
|
121
|
+
applyNativeRootVars: (preset: string, mode: 'light' | 'dark') => void;
|
|
122
|
+
};
|
|
123
|
+
expect(() => applyNativeRootVars('blue', 'light')).not.toThrow();
|
|
124
|
+
expect(applyNativeRootVars('blue', 'light')).toBeUndefined();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { APP_COLOR_NAMES, APP_COLOR_PRESETS } from '../theme/color-presets';
|
|
2
|
-
import { getPresetVars } from '../theme/preset-vars';
|
|
2
|
+
import { getPresetVars, hslTripletToRgb } from '../theme/preset-vars';
|
|
3
3
|
|
|
4
4
|
const EXTENDED_KEYS = [
|
|
5
5
|
'--card',
|
|
@@ -50,9 +50,50 @@ describe('getPresetVars', () => {
|
|
|
50
50
|
expect(vars['--color-primary']).toBeUndefined();
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
it('emits resolved --color-* vars
|
|
53
|
+
it('emits resolved --color-* vars as rgb() when requested', () => {
|
|
54
54
|
const vars = getPresetVars('green', 'dark', { includeResolvedColorVars: true });
|
|
55
|
-
|
|
56
|
-
expect(vars['--color-
|
|
55
|
+
const primaryTriple = APP_COLOR_PRESETS.green.dark['--primary'] ?? '';
|
|
56
|
+
expect(vars['--color-primary']).toBe(hslTripletToRgb(primaryTriple));
|
|
57
|
+
expect(vars['--color-primary']).toMatch(/^rgb\(\d+ \d+ \d+\)$/);
|
|
58
|
+
expect(vars['--color-card']).toBe(hslTripletToRgb(vars['--card'] ?? ''));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('resolves --color-primary to rgb for the blue preset (matches web reference)', () => {
|
|
62
|
+
// `blue` preset light `--primary` is `205 87% 53%` → rgb(31 153 239).
|
|
63
|
+
const vars = getPresetVars('blue', 'light', { includeResolvedColorVars: true });
|
|
64
|
+
expect(vars['--color-primary']).toBe('rgb(31 153 239)');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('hslTripletToRgb', () => {
|
|
69
|
+
it('converts a plain HSL triple to a space-separated rgb() string', () => {
|
|
70
|
+
expect(hslTripletToRgb('205 87% 53%')).toBe('rgb(31 153 239)');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('converts the achromatic extremes', () => {
|
|
74
|
+
expect(hslTripletToRgb('0 0% 0%')).toBe('rgb(0 0 0)');
|
|
75
|
+
expect(hslTripletToRgb('0 0% 100%')).toBe('rgb(255 255 255)');
|
|
76
|
+
expect(hslTripletToRgb('0 0% 12%')).toBe('rgb(31 31 31)');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('converts a saturated triple within ±1 per channel of the reference', () => {
|
|
80
|
+
// teal `185 100% 20%` ≈ rgb(0 94 102).
|
|
81
|
+
const match = /^rgb\((\d+) (\d+) (\d+)\)$/.exec(hslTripletToRgb('185 100% 20%'));
|
|
82
|
+
expect(match).not.toBeNull();
|
|
83
|
+
const r = Number(match?.[1] ?? NaN);
|
|
84
|
+
const g = Number(match?.[2] ?? NaN);
|
|
85
|
+
const b = Number(match?.[3] ?? NaN);
|
|
86
|
+
expect(Math.abs(r - 0)).toBeLessThanOrEqual(1);
|
|
87
|
+
expect(Math.abs(g - 94)).toBeLessThanOrEqual(1);
|
|
88
|
+
expect(Math.abs(b - 102)).toBeLessThanOrEqual(1);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('tolerates a deg suffix on the hue', () => {
|
|
92
|
+
expect(hslTripletToRgb('205deg 87% 53%')).toBe('rgb(31 153 239)');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('emits rgb(r g b / a) for an alpha tail (decimal or percentage)', () => {
|
|
96
|
+
expect(hslTripletToRgb('205 87% 53% / 0.5')).toBe('rgb(31 153 239 / 0.5)');
|
|
97
|
+
expect(hslTripletToRgb('205 87% 53% / 50%')).toBe('rgb(31 153 239 / 0.5)');
|
|
57
98
|
});
|
|
58
99
|
});
|