@multiplatform.one/theme 7.69.1 → 7.69.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplatform.one/theme",
3
- "version": "7.69.1",
3
+ "version": "7.69.2",
4
4
  "description": "Tamagui theme system for multiplatform.one",
5
5
  "keywords": [
6
6
  "multiplatform",
@@ -72,8 +72,8 @@
72
72
  "access": "public"
73
73
  },
74
74
  "dependencies": {
75
- "@multiplatform.one/platform": "^7.69.1",
76
- "@multiplatform.one/store": "^7.69.1",
75
+ "@multiplatform.one/platform": "^7.69.2",
76
+ "@multiplatform.one/store": "^7.69.2",
77
77
  "@tamagui/animations-react-native": "2.7.6",
78
78
  "@tamagui/colors": "2.7.6",
79
79
  "@tamagui/config": "2.7.6",
@@ -89,8 +89,8 @@
89
89
  },
90
90
  "devDependencies": {
91
91
  "@expo/prebuild-config": "55.0.18",
92
- "@multiplatform.one/config": "7.69.1",
93
- "@multiplatform.one/test-utils": "7.69.1",
92
+ "@multiplatform.one/config": "7.69.2",
93
+ "@multiplatform.one/test-utils": "7.69.2",
94
94
  "@tamagui/animations-css": "2.7.6",
95
95
  "@tamagui/animations-reanimated": "2.7.6",
96
96
  "@tamagui/font-inter": "2.7.6",
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { menuRowChecked, menuRowFrame } from "./menuRow";
2
+ import { menuRowCheckInk, menuRowChecked, menuRowFrame, resolveMenuRowCheckInk } from "./menuRow";
3
3
 
4
4
  describe("menuRowFrame (LC-16 shared menu-row geometry)", () => {
5
5
  it("spans the overlay edge-to-edge with its own horizontal padding (SP-EDGE)", () => {
@@ -31,8 +31,48 @@ describe("menuRowFrame (LC-16 shared menu-row geometry)", () => {
31
31
  });
32
32
  });
33
33
 
34
- describe("menuRowChecked (LC-262 / LC-172 checked row)", () => {
35
- it("draws no fill: the check mark carries the selection (FZ-8355)", () => {
36
- expect(menuRowChecked).toEqual({ backgroundColor: "transparent" });
34
+ describe("menuRowChecked (LC-262 checked row, FZ-8355 r6)", () => {
35
+ it("rests light rows on the $color4 selection surface, one step past the $color3 hover", () => {
36
+ expect(menuRowChecked("light").backgroundColor).toBe("$color4");
37
+ });
38
+
39
+ it("recesses dark rows to $color2, off the $color4 dark overlay", () => {
40
+ expect(menuRowChecked("dark").backgroundColor).toBe("$color2");
41
+ });
42
+
43
+ it("keeps the fill through hover and press, and leaves focus rings to the consumer", () => {
44
+ for (const scheme of ["light", "dark"] as const) {
45
+ const checked = menuRowChecked(scheme);
46
+ expect(checked.hoverStyle).toEqual({ backgroundColor: checked.backgroundColor });
47
+ expect(checked.pressStyle).toEqual({ backgroundColor: checked.backgroundColor });
48
+ expect(checked).not.toHaveProperty("focusVisibleStyle");
49
+ expect(checked.backgroundColor).not.toBe(menuRowFrame.backgroundColor);
50
+ }
51
+ });
52
+
53
+ it("marks the check in the theme's selected ink", () => {
54
+ expect(menuRowCheckInk).toBe("$selectedColor");
55
+ });
56
+ });
57
+
58
+ describe("resolveMenuRowCheckInk (LC-176 bold mono check on hue-less themes)", () => {
59
+ it("keeps a colour theme's selected ink", () => {
60
+ expect(
61
+ resolveMenuRowCheckInk({ selectedColor: { val: "#113264" }, color: { val: "#050505" } }),
62
+ ).toBe("#113264");
63
+ });
64
+
65
+ it("paints the primary ink when the selected ink is grey", () => {
66
+ expect(
67
+ resolveMenuRowCheckInk({
68
+ selectedColor: { val: "hsla(0, 0%, 27%, 1)" },
69
+ color: { val: "hsla(0, 0%, 2%, 1)" },
70
+ }),
71
+ ).toBe("hsla(0, 0%, 2%, 1)");
72
+ });
73
+
74
+ it("falls back to the primary ink, then currentColor, when a key is missing", () => {
75
+ expect(resolveMenuRowCheckInk({ color: { val: "#fff" } })).toBe("#fff");
76
+ expect(resolveMenuRowCheckInk({})).toBe("currentColor");
37
77
  });
38
78
  });
package/src/menuRow.ts CHANGED
@@ -1,3 +1,7 @@
1
+ import { useTheme } from "tamagui";
2
+ import { normalizeToHex } from "./theme/colorRules";
3
+ import type { ColorScheme } from "./theme/resolveKnobs";
4
+
1
5
  /**
2
6
  * LC-16 ONE-IMPLEMENTATION — the shared menu-row frame geometry.
3
7
  *
@@ -35,11 +39,52 @@ export const menuRowFrame = {
35
39
  } as const;
36
40
 
37
41
  /**
38
- * LC-262 / LC-172: a checked or selected menu or picker row draws no fill in
39
- * either scheme; the accent check alone marks it. A per-row `$color2` plate
40
- * read as nothing in light and near-black in dark (FZ-8355). Hover, press and
41
- * keyboard focus stay transient feedback, owned by each consumer.
42
+ * LC-262: a checked or selected menu or picker row is highlighted, so it reads
43
+ * as chosen without hunting for the check (FZ-8355 r6, rv_9244d1b063a3a4de).
44
+ * Light rests on the LC-05 selection surface `$color4`, one step past the
45
+ * `$color3` hover. A dark overlay is itself `$color4` (`surface2`), so dark
46
+ * recesses to `$color2`, the band Clay rated good. In a colour theme both
47
+ * steps carry its hue. Hover and press keep the fill, so a checked row never
48
+ * drops to the transient hover fill. Spread it last on the row so it wins
49
+ * over the consumer's hover and keyboard-cursor fills; a consumer that paints
50
+ * focus as a fill paints `backgroundColor` there too. Focus rings stay the
51
+ * consumer's.
42
52
  */
43
- export const menuRowChecked = {
44
- backgroundColor: "transparent",
45
- } as const;
53
+ export function menuRowChecked(scheme: ColorScheme) {
54
+ const fill = scheme === "dark" ? "$color2" : "$color4";
55
+ return {
56
+ backgroundColor: fill,
57
+ hoverStyle: { backgroundColor: fill },
58
+ pressStyle: { backgroundColor: fill },
59
+ } as const;
60
+ }
61
+
62
+ /** The check mark on a `menuRowChecked` row: the theme's selected ink. */
63
+ export const menuRowCheckInk = "$selectedColor" as const;
64
+
65
+ type ThemeValues = Record<string, { val?: unknown } | undefined>;
66
+
67
+ function isHueless(paint: string): boolean {
68
+ const hex = normalizeToHex(paint);
69
+ if (!hex) return false;
70
+ const rgb = [1, 3, 5].map((i) => Number.parseInt(hex.slice(i, i + 2), 16));
71
+ return Math.max(...rgb) - Math.min(...rgb) <= 8;
72
+ }
73
+
74
+ /**
75
+ * The paint for a checked row's check (LC-176, FZ-8355 r6). A colour theme
76
+ * marks it in its selected ink; a hue-less theme's selected ink is a muted
77
+ * grey that reads as a thin grey tick on the fill, so it takes the primary
78
+ * ink instead, the bold mono glyph. Never undefined.
79
+ */
80
+ export function resolveMenuRowCheckInk(theme: ThemeValues): string {
81
+ const tint = theme[menuRowCheckInk.slice(1)]?.val;
82
+ const ink = theme.color?.val;
83
+ if (typeof tint === "string" && !(isHueless(tint) && typeof ink === "string")) return tint;
84
+ return typeof ink === "string" ? ink : "currentColor";
85
+ }
86
+
87
+ /** Live `resolveMenuRowCheckInk` for the theme in scope. */
88
+ export function useMenuRowCheckInk(): string {
89
+ return resolveMenuRowCheckInk(useTheme() as unknown as ThemeValues);
90
+ }
@@ -144,6 +144,11 @@ export function ThemeProvider<T extends CreateTamaguiProps>({
144
144
  ":root { --mp-transition: 200ms; }",
145
145
  // Reserve space for scrollbar to prevent gap when modal Sheet hides it
146
146
  "html { scrollbar-gutter: stable; }",
147
+ // A scroll lock (Tamagui's sheet lock writes an inline stable gutter plus
148
+ // overflow hidden) must not keep the gutter: it shrinks the initial
149
+ // containing block, so a fixed scrim and sheet stop a scrollbar's width
150
+ // short of the right edge with a page-coloured strip beside them (FZ-8355).
151
+ 'html[style*="overflow: hidden"] { scrollbar-gutter: auto !important; }',
147
152
  // Disable browser-native focus ring on form controls;
148
153
  // components provide their own focus-visible affordance.
149
154
  "input:focus, input:focus-visible,",
@@ -1,3 +1,4 @@
1
+ import type { ColorScheme } from "./theme/resolveKnobs";
1
2
  /**
2
3
  * LC-16 ONE-IMPLEMENTATION — the shared menu-row frame geometry.
3
4
  *
@@ -34,12 +35,39 @@ export declare const menuRowFrame: {
34
35
  readonly backgroundColor: "transparent";
35
36
  };
36
37
  /**
37
- * LC-262 / LC-172: a checked or selected menu or picker row draws no fill in
38
- * either scheme; the accent check alone marks it. A per-row `$color2` plate
39
- * read as nothing in light and near-black in dark (FZ-8355). Hover, press and
40
- * keyboard focus stay transient feedback, owned by each consumer.
38
+ * LC-262: a checked or selected menu or picker row is highlighted, so it reads
39
+ * as chosen without hunting for the check (FZ-8355 r6, rv_9244d1b063a3a4de).
40
+ * Light rests on the LC-05 selection surface `$color4`, one step past the
41
+ * `$color3` hover. A dark overlay is itself `$color4` (`surface2`), so dark
42
+ * recesses to `$color2`, the band Clay rated good. In a colour theme both
43
+ * steps carry its hue. Hover and press keep the fill, so a checked row never
44
+ * drops to the transient hover fill. Spread it last on the row so it wins
45
+ * over the consumer's hover and keyboard-cursor fills; a consumer that paints
46
+ * focus as a fill paints `backgroundColor` there too. Focus rings stay the
47
+ * consumer's.
41
48
  */
42
- export declare const menuRowChecked: {
43
- readonly backgroundColor: "transparent";
49
+ export declare function menuRowChecked(scheme: ColorScheme): {
50
+ readonly backgroundColor: "$color2" | "$color4";
51
+ readonly hoverStyle: {
52
+ readonly backgroundColor: "$color2" | "$color4";
53
+ };
54
+ readonly pressStyle: {
55
+ readonly backgroundColor: "$color2" | "$color4";
56
+ };
44
57
  };
58
+ /** The check mark on a `menuRowChecked` row: the theme's selected ink. */
59
+ export declare const menuRowCheckInk: "$selectedColor";
60
+ type ThemeValues = Record<string, {
61
+ val?: unknown;
62
+ } | undefined>;
63
+ /**
64
+ * The paint for a checked row's check (LC-176, FZ-8355 r6). A colour theme
65
+ * marks it in its selected ink; a hue-less theme's selected ink is a muted
66
+ * grey that reads as a thin grey tick on the fill, so it takes the primary
67
+ * ink instead, the bold mono glyph. Never undefined.
68
+ */
69
+ export declare function resolveMenuRowCheckInk(theme: ThemeValues): string;
70
+ /** Live `resolveMenuRowCheckInk` for the theme in scope. */
71
+ export declare function useMenuRowCheckInk(): string;
72
+ export {};
45
73
  //# sourceMappingURL=menuRow.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"menuRow.d.ts","sourceRoot":"","sources":["../src/menuRow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,YAAY;;;;;;;;CAQf,CAAC;AAEX;;;;;GAKG;AACH,eAAO,MAAM,cAAc;;CAEjB,CAAC"}
1
+ {"version":3,"file":"menuRow.d.ts","sourceRoot":"","sources":["../src/menuRow.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,YAAY;;;;;;;;CAQf,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW;;;;;;;;EAOjD;AAED,0EAA0E;AAC1E,eAAO,MAAM,eAAe,EAAG,gBAAyB,CAAC;AAEzD,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAAC,CAAC;AASjE;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAKjE;AAED,4DAA4D;AAC5D,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C"}
@@ -1 +1 @@
1
- {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/theme/theme.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,kBAAkB,EAEvB,KAAK,oBAAoB,EAG1B,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAU/C,OAAO,EAAE,KAAK,WAAW,EAAa,MAAM,UAAU,CAAC;AAGvD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,IAAI,GAAG,IAAI,CAkB5D;AAMD,MAAM,WAAW,kBAAkB,CACjC,CAAC,SAAS,kBAAkB,GAAG,kBAAkB,CACjD,SAAQ,iBAAiB,CAAC,IAAI,CAAC,oBAAoB,EAAE,QAAQ,GAAG,cAAc,CAAC,CAAC;IAChF,MAAM,CAAC,EAAE;QACP,OAAO,EAAE,UAAU,CAAC,cAAc,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;KAChE,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,GAAG,eAAe,CAAC,CAAC,CAAC;IAC3D,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAChC;AAkBD,wBAAgB,aAAa,CAAC,CAAC,SAAS,kBAAkB,EAAE,EAC1D,QAAQ,EACR,MAAM,EACN,WAAW,EACX,GAAG,YAAY,EAChB,EAAE,kBAAkB,CAAC,CAAC,CAAC,2CAsGvB"}
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/theme/theme.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,kBAAkB,EAEvB,KAAK,oBAAoB,EAG1B,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAU/C,OAAO,EAAE,KAAK,WAAW,EAAa,MAAM,UAAU,CAAC;AAGvD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,IAAI,GAAG,IAAI,CAkB5D;AAMD,MAAM,WAAW,kBAAkB,CACjC,CAAC,SAAS,kBAAkB,GAAG,kBAAkB,CACjD,SAAQ,iBAAiB,CAAC,IAAI,CAAC,oBAAoB,EAAE,QAAQ,GAAG,cAAc,CAAC,CAAC;IAChF,MAAM,CAAC,EAAE;QACP,OAAO,EAAE,UAAU,CAAC,cAAc,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;KAChE,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,GAAG,eAAe,CAAC,CAAC,CAAC;IAC3D,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAChC;AAkBD,wBAAgB,aAAa,CAAC,CAAC,SAAS,kBAAkB,EAAE,EAC1D,QAAQ,EACR,MAAM,EACN,WAAW,EACX,GAAG,YAAY,EAChB,EAAE,kBAAkB,CAAC,CAAC,CAAC,2CA2GvB"}