@multiplatform.one/theme 7.63.3 → 7.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,9 +9,12 @@ export type MotionRoleName = "control" | "overlay" | "scrim" | "reveal" | "slide
9
9
 
10
10
  export interface MotionRole {
11
11
  transition: TransitionProp | undefined;
12
+ exit?: TransitionProp | undefined;
12
13
  animateOnly: readonly string[];
13
14
  }
14
15
 
16
+ export const motionExitFactor = 2 / 3;
17
+
15
18
  export const motionRoleProperties = {
16
19
  control: ["backgroundColor", "borderColor", "color", "opacity", "transform"],
17
20
  overlay: ["opacity", "transform"],
@@ -22,8 +25,8 @@ export const motionRoleProperties = {
22
25
 
23
26
  const nonBouncyStops = motionStops.filter((stop) => stop !== "superBouncy" && stop !== "bouncy");
24
27
 
25
- function nearestStop(duration: number): MotionStop {
26
- return nonBouncyStops.reduce((nearest, candidate) =>
28
+ function nearestStop(duration: number, stops: readonly MotionStop[] = nonBouncyStops): MotionStop {
29
+ return stops.reduce((nearest, candidate) =>
27
30
  Math.abs(animationDurations[candidate] - duration) <
28
31
  Math.abs(animationDurations[nearest] - duration)
29
32
  ? candidate
@@ -31,18 +34,28 @@ function nearestStop(duration: number): MotionStop {
31
34
  );
32
35
  }
33
36
 
37
+ function exitStop(enter: MotionStop): MotionStop {
38
+ const enterMs = animationDurations[enter];
39
+ const shorter = nonBouncyStops.filter((stop) => animationDurations[stop] < enterMs);
40
+ return shorter.length === 0 ? enter : nearestStop(enterMs * motionExitFactor, shorter);
41
+ }
42
+
34
43
  export function motionFor(stop: Animation): Record<MotionRoleName, MotionRole> {
35
44
  const transition = stop === "none" ? undefined : stop;
36
45
  const duration = motionDurationMs(transition);
37
- const role = (name: MotionRoleName, factor: number): MotionRole => ({
38
- transition:
46
+ const role = (name: MotionRoleName, factor: number): MotionRole => {
47
+ const enter =
39
48
  transition === undefined
40
49
  ? undefined
41
50
  : name === "control"
42
51
  ? transition
43
- : nearestStop(duration * factor),
44
- animateOnly: motionRoleProperties[name],
45
- });
52
+ : nearestStop(duration * factor);
53
+ return {
54
+ transition: enter,
55
+ exit: enter === undefined ? undefined : exitStop(enter),
56
+ animateOnly: motionRoleProperties[name],
57
+ };
58
+ };
46
59
  return {
47
60
  control: role("control", 1),
48
61
  overlay: role("overlay", 0.8),
@@ -208,9 +208,9 @@ describe("MPO-19 X5/X6 — surfaces address semantic tokens, so sub-themes work"
208
208
  });
209
209
 
210
210
  describe("MPO-19 X7 — the focus ring is inset only where a clip demands it", () => {
211
- it("the default sits on the border-box edge, as Tamagui paints it", () => {
212
- expect(FOCUS_VISIBLE_RING.outlineOffset).toBe(0);
213
- expect(ensureFocusVisibleRing().outlineOffset).toBe(0);
211
+ it("the default sits 2px clear of the border box (FZ-7944 round 2)", () => {
212
+ expect(FOCUS_VISIBLE_RING.outlineOffset).toBe(2);
213
+ expect(ensureFocusVisibleRing().outlineOffset).toBe(2);
214
214
  });
215
215
 
216
216
  it("the ring shape itself is unchanged — 2px solid, non-removable", () => {
@@ -1,3 +1,4 @@
1
+ import { focusOnOpen, wasKeyboardFocus } from "../keyboardFocusRing";
1
2
  import { transitionProps } from "./transitionProps";
2
3
  import { Surface } from "./Surface";
3
4
  import { useResolvedKnobs } from "./useResolvedKnobs";
@@ -40,7 +41,9 @@ const overlayFrameFocusStyle = {
40
41
  // tamagui's deferred path still runs. Dialog content sits in a native
41
42
  // <dialog> whose show() puts focus inside before the check in browsers,
42
43
  // so there FocusScope never calls the handler; useDialogShowFocus does
43
- // when show() left focus on something that is not a tab stop (MPO-470).
44
+ // when show() left focus on something that is not a tab stop (MPO-470), or
45
+ // on any control after a non-keyboard open, which Chromium would ring
46
+ // (LC-71, FZ-7944).
44
47
 
45
48
  function isTabbableWithin(node: HTMLElement, container: HTMLElement): boolean {
46
49
  if (node.tabIndex < 0 || node.hidden || node.tagName === "A") return false;
@@ -67,8 +70,10 @@ function focusFirstTabbableOnOpen(event: Event): void {
67
70
  );
68
71
  if (!first) return;
69
72
  event.preventDefault();
70
- first.focus({ preventScroll: true });
71
- if (typeof HTMLInputElement !== "undefined" && first instanceof HTMLInputElement) first.select();
73
+ const focused = focusOnOpen(first, container);
74
+ if (typeof HTMLInputElement !== "undefined" && focused instanceof HTMLInputElement) {
75
+ focused.select();
76
+ }
72
77
  }
73
78
 
74
79
  type OpenAutoFocus = (event: Event) => void;
@@ -100,7 +105,7 @@ function useDialogShowFocus(onOpenAutoFocus: OpenAutoFocus): void {
100
105
  const content = document.getElementById(contentId);
101
106
  const focused = document.activeElement;
102
107
  if (!content || typeof HTMLElement === "undefined" || !(focused instanceof HTMLElement)) return;
103
- if (!content.contains(focused) || focused.tabIndex >= 0) return;
108
+ if (!content.contains(focused) || (focused.tabIndex >= 0 && wasKeyboardFocus())) return;
104
109
  content.addEventListener(DIALOG_SHOW_FOCUS_EVENT, onOpenAutoFocus, { once: true });
105
110
  content.dispatchEvent(new CustomEvent(DIALOG_SHOW_FOCUS_EVENT, { cancelable: true }));
106
111
  }, [open, contentId]);
@@ -69,6 +69,15 @@ export interface ElevationChromeProps extends ElevationPaintProps {
69
69
  elevation?: number;
70
70
  }
71
71
 
72
+ /**
73
+ * A content card's lift (LC-151): an explicit shadow plus Android dp under
74
+ * `elevationAndroid`. Not `elevation`: on native that key is Tamagui's stack
75
+ * variant, which replaces the shadow with its own size-formula one.
76
+ */
77
+ export interface CardLiftProps extends ElevationPaintProps {
78
+ elevationAndroid?: number;
79
+ }
80
+
72
81
  // ── Disabled state recipe (LC-40 DISABLED-VISIBLE) ───────────────────────
73
82
 
74
83
  /** Text-bearing control chrome (Button, Chip, Input.Box): muted fill/border, no opacity. */
@@ -228,7 +237,7 @@ export interface KnobProps {
228
237
  borderWidth?: number;
229
238
  borderRadius: string | number;
230
239
  padding: string;
231
- } & ElevationChromeProps &
240
+ } & CardLiftProps &
232
241
  CornerSmoothingCarrier;
233
242
  /** Elevated/overlay tier (popovers, dropdowns): border + elevation. */
234
243
  elevatedSurface: {
@@ -30,6 +30,7 @@ import {
30
30
  resolveKnobs,
31
31
  resolvePageTitleScale,
32
32
  spacingRoles,
33
+ cardLift,
33
34
  sizedElevation,
34
35
  } from "./resolveKnobs";
35
36
  import { getActiveScaleTokens, setActiveScaleTokens } from "./scale/activeScale";
@@ -314,7 +315,7 @@ describe("resolveKnobs", () => {
314
315
  scheme === "dark" ? "$color4" : "$background",
315
316
  );
316
317
  expect(knobProps.elevatedSurface.borderColor).toBe(
317
- scheme === "dark" ? "$color6" : "$borderColor",
318
+ scheme === "dark" ? "transparent" : "$borderColor",
318
319
  );
319
320
  if (!key) {
320
321
  expect(knobProps.elevationChrome).toEqual({});
@@ -362,7 +363,7 @@ describe("resolveKnobs", () => {
362
363
  });
363
364
 
364
365
  describe("elevation knob (native)", () => {
365
- it("returns active scale numbers with the same paint in both schemes", async () => {
366
+ it("returns active scale numbers, and every shadow carries its alpha as shadowOpacity on an opaque ink", async () => {
366
367
  vi.resetModules();
367
368
  vi.doMock("@multiplatform.one/platform", () => ({
368
369
  isWeb: false,
@@ -407,15 +408,33 @@ describe("resolveKnobs", () => {
407
408
  if (key) {
408
409
  const metrics = sizedElevation(active.getActiveScaleTokens().size[key]);
409
410
  expect(knobProps.elevationChrome).toEqual({
410
- shadowColor: "$shadowColor",
411
- shadowOpacity: 1,
411
+ shadowColor: "rgb(0,0,0)",
412
+ shadowOpacity: 0.12,
412
413
  shadowOffset: { width: 0, height: metrics.offsetY },
413
414
  shadowRadius: metrics.radius,
414
415
  elevation: metrics.android,
415
416
  });
416
417
  } else expect(knobProps.elevationChrome).toEqual({});
417
418
  expect(knobProps.thumbShadow.style).toBeUndefined();
418
- expect(knobProps.thumbShadow.shadowColor).toBe("$shadowColor");
419
+ expect(knobProps.thumbShadow.shadowOpacity).toBe(0.12);
420
+ const card = knobProps.cardSurface;
421
+ if (scheme === "dark" || stop === "none") {
422
+ expect(card).not.toHaveProperty("shadowColor");
423
+ } else {
424
+ expect(card.shadowColor).toBe("rgb(0,0,0)");
425
+ expect(card.shadowOpacity).toBe(0.08);
426
+ expect(card.style).toBeUndefined();
427
+ }
428
+ expect(knobProps.featureSurface.shadowOpacity).toBeLessThanOrEqual(0.08);
429
+ for (const fragment of [
430
+ knobProps.elevationChrome,
431
+ knobProps.thumbShadow,
432
+ card,
433
+ knobProps.elevatedSurface,
434
+ knobProps.featureSurface,
435
+ ]) {
436
+ if ("shadowOpacity" in fragment) expect(fragment.shadowOpacity).toBeLessThan(0.5);
437
+ }
419
438
  }
420
439
  }
421
440
  }
@@ -668,23 +687,41 @@ describe("resolveKnobs", () => {
668
687
  // ── surface tiers ─────────────────────────────────────────
669
688
 
670
689
  describe("surface tier recipes", () => {
671
- it("cardSurface = grouped fill + hairline edge + a hair of lift (LC-151)", () => {
690
+ it("cardSurface = grouped fill + hairline edge + a natural lift (LC-151)", () => {
672
691
  const { knobProps } = resolveKnobs(defaultKnobs);
673
- const { offsetY, radius, android } = sizedElevation(getActiveScaleTokens().size["$0.25"]);
692
+ const { offsetY, radius, android } = cardLift(getActiveScaleTokens().size["$0.25"]);
674
693
  expect(knobProps.cardSurface).toEqual({
675
694
  backgroundColor: "$background",
676
695
  borderColor: edgeColorAt("$borderColor", "small"),
677
696
  borderWidth: hairlineBorderWidth,
678
697
  borderRadius: "$6",
679
698
  padding: "$4",
680
- shadowColor: "$shadowColor",
699
+ shadowColor: "$shadow2",
681
700
  shadowOffset: { width: 0, height: offsetY },
682
701
  shadowOpacity: 1,
683
702
  shadowRadius: radius,
684
- elevation: android,
685
- style: { boxShadow: `0px ${offsetY}px ${radius}px var(--shadowColor)` },
703
+ elevationAndroid: android,
704
+ style: {
705
+ boxShadow: `0px 1px 2px var(--shadow1), 0px ${offsetY}px ${radius * 2}px var(--shadow2)`,
706
+ },
686
707
  });
687
- expect(offsetY).toBeLessThanOrEqual(2);
708
+ });
709
+
710
+ it("card lift: the blur is three times the drop, and no `elevation` key for Tamagui's variant to replace it", () => {
711
+ for (const elevation of ["small", "medium", "large"] as const) {
712
+ const card = resolveKnobs(knobsWith({ elevation })).knobProps.cardSurface;
713
+ expect(card.shadowRadius! / card.shadowOffset!.height, elevation).toBeGreaterThanOrEqual(
714
+ 2.5,
715
+ );
716
+ expect(card.shadowRadius! / card.shadowOffset!.height, elevation).toBeLessThanOrEqual(3.5);
717
+ expect(card, elevation).not.toHaveProperty("elevation");
718
+ }
719
+ });
720
+
721
+ it("card lift is off in dark: the lighter card fill carries the step", () => {
722
+ const card = resolveKnobs(defaultKnobs, undefined, "dark").knobProps.cardSurface;
723
+ expect(card).not.toHaveProperty("shadowColor");
724
+ expect(card).not.toHaveProperty("elevationAndroid");
688
725
  });
689
726
 
690
727
  it("cardSurface lift is lower than every overlay stop and gone at elevation none", () => {
@@ -21,6 +21,7 @@ import { SIZE_KEYS, type SizeKey } from "./scale/scaleInputs";
21
21
  import type {
22
22
  ControlStateProps,
23
23
  DisabledRecipe,
24
+ CardLiftProps,
24
25
  ElevationChromeProps,
25
26
  ElevationPaintProps,
26
27
  ElevationStateProps,
@@ -395,6 +396,15 @@ export function sizedElevation(px: number): { offsetY: number; radius: number; a
395
396
  return { offsetY, radius: Math.round(px / 2 + 2), android: 2 * offsetY };
396
397
  }
397
398
 
399
+ // Native: Tamagui's normalizeShadow repaints shadowColor at full alpha and
400
+ // keeps only shadowOpacity, so a translucent shadow token at shadowOpacity 1
401
+ // draws solid black. Native gets an opaque ink and the token's alpha.
402
+ function shadowInk(token: string, alpha: number): { shadowColor: string; shadowOpacity: number } {
403
+ return isWeb
404
+ ? { shadowColor: token, shadowOpacity: 1 }
405
+ : { shadowColor: "rgb(0,0,0)", shadowOpacity: alpha };
406
+ }
407
+
398
408
  function platformElevation(key: SizeKey | undefined): string | number | undefined {
399
409
  return key && (isWeb ? key : getActiveScaleTokens().size[key]);
400
410
  }
@@ -403,14 +413,41 @@ function elevationPaint(key: SizeKey | undefined): ElevationPaintProps {
403
413
  if (!key) return {};
404
414
  const { offsetY, radius } = sizedElevation(getActiveScaleTokens().size[key]);
405
415
  return {
406
- shadowColor: "$shadowColor",
416
+ ...shadowInk("$shadowColor", 0.12),
407
417
  shadowOffset: { width: 0, height: offsetY },
408
- shadowOpacity: 1,
409
418
  shadowRadius: radius,
410
419
  ...(isWeb ? { style: { boxShadow: `0px ${offsetY}px ${radius}px var(--shadowColor)` } } : {}),
411
420
  };
412
421
  }
413
422
 
423
+ // LC-151 card lift, a natural shadow: the blur three times the drop, a faint
424
+ // ink, and on web a tight contact layer under the ambient one. Dark lets the
425
+ // card's lighter fill carry the step; a black shadow on a black page reads as
426
+ // a smudge.
427
+ export function cardLift(px: number): { offsetY: number; radius: number; android: number } {
428
+ const radius = Math.round(8 + px * 2);
429
+ const offsetY = Math.round(radius / 3);
430
+ return { offsetY, radius, android: Math.max(1, Math.round(offsetY / 2)) };
431
+ }
432
+
433
+ function cardLiftFor(key: SizeKey | undefined, scheme: ColorScheme): CardLiftProps {
434
+ if (!key || scheme === "dark") return {};
435
+ const { offsetY, radius, android } = cardLift(getActiveScaleTokens().size[key]);
436
+ return {
437
+ ...shadowInk("$shadow2", 0.08),
438
+ shadowOffset: { width: 0, height: offsetY },
439
+ shadowRadius: radius,
440
+ elevationAndroid: android,
441
+ ...(isWeb
442
+ ? {
443
+ style: {
444
+ boxShadow: `0px 1px 2px var(--shadow1), 0px ${offsetY}px ${radius * 2}px var(--shadow2)`,
445
+ },
446
+ }
447
+ : {}),
448
+ };
449
+ }
450
+
414
451
  function elevationChromeFor(key: SizeKey | undefined): ElevationChromeProps {
415
452
  if (!key) return {};
416
453
  return {
@@ -419,10 +456,10 @@ function elevationChromeFor(key: SizeKey | undefined): ElevationChromeProps {
419
456
  };
420
457
  }
421
458
 
422
- // v5's surface2 template steps: fill and edge two palette steps above the
423
- // base `$background`/`$borderColor` (color2/color4), one above Card's surface1.
424
- // The fill alone would land on the base edge step and erase the border.
425
- const surface2 = { fill: "$color4", edge: "$color6" } as const;
459
+ // v5's surface2 template step: the fill two palette steps above the base
460
+ // `$background` (color2), one above Card's surface1. A dark overlay separates
461
+ // by this lighter fill and its shadow, with no light edge (HIG, FZ-7944).
462
+ const surface2 = { fill: "$color4" } as const;
426
463
 
427
464
  // `$textMuted` is the theme's secondary ink (LC-152): the most muted ramp
428
465
  // step that clears 7:1 on the page and every card tier in its own scheme. A
@@ -750,7 +787,7 @@ export function resolveKnobs(
750
787
  borderWidth: knobs.borderWidth === "none" ? 0 : borderWidthMap.small,
751
788
  borderRadius: containerRadiusValue,
752
789
  padding: paddingToken,
753
- ...elevationChromeFor(cardElevationMap[knobs.elevation]),
790
+ ...cardLiftFor(cardElevationMap[knobs.elevation], scheme),
754
791
  ...containerSmoothing,
755
792
  },
756
793
  elevatedSurface: painted
@@ -764,7 +801,7 @@ export function resolveKnobs(
764
801
  }
765
802
  : {
766
803
  backgroundColor: scheme === "dark" ? surface2.fill : "$background",
767
- borderColor: edge(scheme === "dark" ? surface2.edge : "$borderColor", borderWidthValue),
804
+ borderColor: scheme === "dark" ? "transparent" : edge("$borderColor", borderWidthValue),
768
805
  borderWidth: borderWidthValue,
769
806
  borderRadius: radiusToken,
770
807
  elevation: platformElevation(overlayElevationToken),
@@ -777,9 +814,8 @@ export function resolveKnobs(
777
814
  padding: spacingRoles.featurePadding,
778
815
  // Bento marketing/auth card shadow: ~0px 9px 12px rgba(0,0,0,0.02) —
779
816
  // softer/wider than any elevationMap token, so explicit shadow props.
780
- shadowColor: "rgba(0,0,0,0.02)",
817
+ ...shadowInk("rgba(0,0,0,0.02)", 0.02),
781
818
  shadowOffset: { width: 0, height: 9 },
782
- shadowOpacity: 1,
783
819
  shadowRadius: 12,
784
820
  ...containerSmoothing,
785
821
  },
@@ -1,5 +1,6 @@
1
1
  import type { MotionRole } from "./motion";
2
2
  import type { TransitionProp } from "tamagui";
3
+ import { animationConfig as nativeAnimationConfig } from "./animations/reactNative";
3
4
 
4
5
  /**
5
6
  * Native twin of `transitionProps.ts`.
@@ -23,12 +24,26 @@ export function transitionProps(
23
24
  const properties = [...role.animateOnly, ...extra].flatMap(
24
25
  (property) => nativeLonghands[property] ?? [property],
25
26
  );
27
+ if (typeof transition !== "string") return { transition, animateOnly: properties };
28
+ const exit = typeof role.exit === "string" && role.exit !== transition ? role.exit : undefined;
29
+ const clamp = isSpring(transition) && !presetBounce.has(transition);
30
+ if (!exit && !clamp) return { transition, animateOnly: properties };
26
31
  return {
27
- transition,
32
+ transition: [
33
+ transition,
34
+ { ...(clamp ? { overshootClamping: true } : {}), ...(exit ? { exit } : {}) },
35
+ ],
28
36
  animateOnly: properties,
29
37
  };
30
38
  }
31
39
 
40
+ function isSpring(stop: string): boolean {
41
+ const config = nativeAnimationConfig[stop as keyof typeof nativeAnimationConfig] as
42
+ | { type?: string }
43
+ | undefined;
44
+ return config !== undefined && config.type !== "timing";
45
+ }
46
+
32
47
  /**
33
48
  * FZ-7919: tamagui expands `borderColor` into its four sides before the React
34
49
  * Native driver sees the style, and the driver matches `animateOnly` by exact
@@ -36,6 +51,8 @@ export function transitionProps(
36
51
  * (the bubbled focus of a field's text input) animated them. The animated
37
52
  * copy outranked the static one and held the previous focus or theme colour.
38
53
  */
54
+ const presetBounce = new Set(["bouncy", "superBouncy"]);
55
+
39
56
  const nativeLonghands: Record<string, readonly string[]> = {
40
57
  borderColor: [
41
58
  "borderColor",
@@ -24,7 +24,7 @@ describe("role property scopes", () => {
24
24
  });
25
25
  it("hyphenates the control scope and additional properties", () => {
26
26
  expect(transitionProps(motionFor("quick").control, ["maxHeight", "left"])).toEqual({
27
- transition: "quick",
27
+ transition: ["quick", { exit: "quickerLessBouncy" }],
28
28
  animateOnly: [
29
29
  "background-color",
30
30
  "border-color",
@@ -41,7 +41,7 @@ describe("role property scopes", () => {
41
41
  expect(nativeTransitionProps(undefined)).toEqual({});
42
42
  expect(nativeTransitionProps("quick")).toEqual({ transition: "quick" });
43
43
  expect(nativeTransitionProps(motionFor("quick").control, ["maxHeight"])).toEqual({
44
- transition: "quick",
44
+ transition: ["quick", { overshootClamping: true, exit: "quickerLessBouncy" }],
45
45
  animateOnly: [
46
46
  "backgroundColor",
47
47
  "borderColor",
@@ -22,13 +22,23 @@ export function transitionProps(
22
22
  if (!isRole) return { transition };
23
23
  const properties = [...role.animateOnly, ...extra];
24
24
  return {
25
- transition,
25
+ transition: withExit(transition, role.exit),
26
26
  animateOnly: properties.map((property) =>
27
27
  property.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`),
28
28
  ),
29
29
  };
30
30
  }
31
31
 
32
+ function withExit(
33
+ transition: TransitionProp | undefined,
34
+ exit: TransitionProp | undefined,
35
+ ): TransitionProp | undefined {
36
+ if (typeof transition !== "string" || typeof exit !== "string" || exit === transition) {
37
+ return transition;
38
+ }
39
+ return [transition, { exit }];
40
+ }
41
+
32
42
  function isMotionRole(value: MotionRole | TransitionProp | undefined): value is MotionRole {
33
43
  return (
34
44
  typeof value === "object" &&
@@ -22,6 +22,17 @@ export declare function ensureKeyboardModalityTracking(): void;
22
22
  * false) and during SSR.
23
23
  */
24
24
  export declare function wasKeyboardFocus(): boolean;
25
+ /**
26
+ * An overlay's open focus (LC-71, FZ-7944). Only a keyboard open lands on a
27
+ * control and rings it. Any other open (pointer, touch, mounted open) lands on
28
+ * the frame, which paints no ring: Chromium ignores `focusVisible: false` and
29
+ * rings a control focused before any input. A mouse open still puts the caret
30
+ * in a first text field, which shows LC-133's border; a touch open does not,
31
+ * so the keyboard does not rise over a sheet the user only looked at. A frame
32
+ * without a tabindex gets -1, or its focus() is a no-op and the browser's
33
+ * own pick stays. Returns the node that took focus.
34
+ */
35
+ export declare function focusOnOpen(target: HTMLElement | null, frame: HTMLElement): HTMLElement;
25
36
  /**
26
37
  * Standard manual ring props — the INSET ring geometry
27
38
  * (`FOCUS_VISIBLE_RING_INSET`: 2px solid `$outlineColor`, offset −2), spread
@@ -1 +1 @@
1
- {"version":3,"file":"keyboardFocusRing.d.ts","sourceRoot":"","sources":["../src/keyboardFocusRing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;;;GAIG;AACH,wBAAgB,8BAA8B,SAK7C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAG1C;AAID;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB;;;;;CAEzB,CAAC;AAEX;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,8uCA+BhC,CAAC;AAIH,uEAAuE;AACvE,wBAAgB,wBAAwB,IAAI,IAAI,CAO/C"}
1
+ {"version":3,"file":"keyboardFocusRing.d.ts","sourceRoot":"","sources":["../src/keyboardFocusRing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH;;;;GAIG;AACH,wBAAgB,8BAA8B,SAY7C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAG1C;AAWD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,EAAE,KAAK,EAAE,WAAW,GAAG,WAAW,CAQvF;AAID;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB;;;;;CAEzB,CAAC;AAEX;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,8uCA+BhC,CAAC;AAIH,uEAAuE;AACvE,wBAAgB,wBAAwB,IAAI,IAAI,CAO/C"}
@@ -1 +1 @@
1
- {"version":3,"file":"createThemes.d.ts","sourceRoot":"","sources":["../../src/theme/createThemes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"createThemes.d.ts","sourceRoot":"","sources":["../../src/theme/createThemes.ts"],"names":[],"mappings":"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"}
@@ -16,26 +16,21 @@ export declare const FOCUS_RING_MIN_CONTRAST = 3;
16
16
  export declare function focusRingColorMeetsContrast(ringColor: string, surfaceColor: string): boolean;
17
17
  /**
18
18
  * ONE ring geometry (DG-A11Y-01 / LC-71 §4): 2px solid `$outlineColor` at
19
- * offset **0** — the band hugs the border box and paints OUTSIDE it, on the
20
- * surface behind the control (reference §10; board F1 verify pass: offset 0,
21
- * not −2 and not +2). Offset 0 is what makes the ≥3:1 floor computable: the
22
- * ring contrasts against the page ground, not the control's own fill. The
23
- * old flat inset (−2) painted the band INSIDE solid intent fills and sank to
24
- * 1.17–1.31:1 (light accent 1.27, light error 1.17, dark error 1.31) where
25
- * the same ring on the page ground clears 5.78–6.50:1 (MPO-41 / MPO-19 X7).
19
+ * offset **+2**, a band clear of the border box with a 2px gap, painted on the
20
+ * surface behind the control. FZ-7944 round 2 moved the default from 0 to +2
21
+ * (the macOS and iPadOS keyboard ring); the ≥3:1 floor stays computable
22
+ * because the band still paints on the page ground, never inside a fill. The
23
+ * old flat inset (−2) painted INSIDE solid intent fills and sank to
24
+ * 1.17–1.31:1 (MPO-41 / MPO-19 X7).
26
25
  *
27
- * The inset did not die — it moved to `FOCUS_VISIBLE_RING_INSET`, an
28
- * explicit per-call-site carve-out for clipping ancestors. MPO-19 X7 names
29
- * the same offset `FOCUS_RING_CLIPPED_OFFSET` so call sites do not type `-2`.
30
- *
31
- * MPO-92 ratified a second, shape-scoped carve-out on the other side:
32
- * `FOCUS_VISIBLE_RING_HALO` (+2) for pill/circular thumbs and inline text
33
- * links. The F1 "not +2" reading was about the DEFAULT — a blanket +2 on
34
- * every control — and it stands. The halo is a named carve-out with a written
35
- * shape scope, exactly as the inset is; `SANCTIONED_FOCUS_RING_OFFSETS` below
36
- * is the closed set of all three.
26
+ * Two carve-outs stay named: `FOCUS_VISIBLE_RING_INSET` (−2) for clipping
27
+ * ancestors, and `FOCUS_RING_FLUSH_OFFSET` (0) for rows and cells that sit
28
+ * edge to edge inside a padded overlay, where a gapped band would collide
29
+ * with the neighbour. `SANCTIONED_FOCUS_RING_OFFSETS` is the closed set.
37
30
  */
38
31
  export declare const FOCUS_RING_CLIPPED_OFFSET = -2;
32
+ export declare const FOCUS_RING_FLUSH_OFFSET = 0;
33
+ export declare const FOCUS_RING_HALO_OFFSET = 2;
39
34
  export declare const FOCUS_VISIBLE_RING: {
40
35
  outlineWidth: number;
41
36
  outlineStyle: "solid";
@@ -62,29 +57,10 @@ export declare const FOCUS_VISIBLE_RING_INSET: {
62
57
  outlineColor: string;
63
58
  };
64
59
  /**
65
- * The LC-71 §4 halo carve-out (MPO-92, ratified 2026-09-02): the SAME ring,
66
- * pushed 2px clear of the border box, for the two shapes where a band hugging
67
- * the edge reads as part of the glyph instead of around it.
68
- *
69
- * Scope is by SHAPE, not by instance, and it is closed at two:
70
- * - **pill / circular thumbs** — a round handle the user drags (Slider
71
- * thumb, Rating star, TimeRangeScrubber handle). At offset 0 the band is
72
- * concentric with the glyph's own edge and reads as a thicker glyph.
73
- * - **inline text links** — a link inside a run of prose (markdown/mdx
74
- * `Link`, ErrorSummary's summary link, backoffice "View All"). At offset 0
75
- * the band crosses the line box and collides with ascenders/descenders of
76
- * the surrounding text.
77
- *
78
- * NOT for standalone rectangles on the page ground — cards, tiles, rows,
79
- * fields. Those are the default `FOCUS_VISIBLE_RING` (offset 0), which is
80
- * what makes the Axiom 12 ≥3:1 floor computable against the page ground.
81
- *
82
- * Contrast is unaffected: like offset 0 the halo paints OUTSIDE the border
83
- * box, on the surface behind the control, so it measures against the same
84
- * page ground the default does — the 1.17–1.31:1 failures MPO-41 retired were
85
- * the INSET painting inside solid intent fills, a defect the halo cannot have.
60
+ * The halo (MPO-92): pill and circular thumbs and inline text links. Since
61
+ * FZ-7944 round 2 it equals the default ring; the name stays so those call
62
+ * sites keep saying why they hold +2 if the default ever moves again.
86
63
  */
87
- export declare const FOCUS_RING_HALO_OFFSET = 2;
88
64
  export declare const FOCUS_VISIBLE_RING_HALO: {
89
65
  outlineOffset: number;
90
66
  outlineWidth: number;
@@ -93,13 +69,13 @@ export declare const FOCUS_VISIBLE_RING_HALO: {
93
69
  };
94
70
  /**
95
71
  * The CLOSED set of sanctioned focus-ring offsets (LC-71 §4). Three, and only
96
- * three: the default 0, the clipping inset −2, the shape halo +2. A call site
72
+ * three: the clipping inset −2, the flush 0, the default +2. A call site
97
73
  * that wants any other number is a rule gap, not a preference — file it and
98
74
  * amend §4, do not type the number. `focusState.spec.ts` asserts this set is
99
75
  * exactly what the repo paints, so a fourth offset fails a test instead of
100
76
  * shipping (MPO-92 AC-3).
101
77
  */
102
- export declare const SANCTIONED_FOCUS_RING_OFFSETS: readonly [-2, number, 2];
78
+ export declare const SANCTIONED_FOCUS_RING_OFFSETS: readonly [-2, 0, number];
103
79
  /** True when `offset` is one of the three offsets LC-71 §4 sanctions. */
104
80
  export declare function isSanctionedFocusRingOffset(offset: number): boolean;
105
81
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"focusState.d.ts","sourceRoot":"","sources":["../../src/theme/focusState.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAuBzC;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAO5F;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAC5C,eAAO,MAAM,kBAAkB;;;;;CAKF,CAAC;AAE9B;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,wBAAwB;;;;;CAGR,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAC;AACxC,eAAO,MAAM,uBAAuB;;;;;CAGP,CAAC;AAE9B;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,0BAIhC,CAAC;AAEX,yEAAyE;AACzE,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;CAED,CAAC;AAE9B,eAAO,MAAM,iBAAiB;;CAED,CAAC;AAE9B;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,iBAAiB,EAChE,QAAQ,CAAC,EAAE,CAAC,CAAC,GAAG,iBAAiB,CAAC,GAAG,IAAI,GACxC,CAAC,GAAG,iBAAiB,CAyBvB;AAED,mFAAmF;AACnF,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,iBAAiB,EAC1B,KAAK,EAAE,iBAAiB,GACvB,iBAAiB,CAKnB"}
1
+ {"version":3,"file":"focusState.d.ts","sourceRoot":"","sources":["../../src/theme/focusState.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAuBzC;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAO5F;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAC5C,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,sBAAsB,IAAI,CAAC;AACxC,eAAO,MAAM,kBAAkB;;;;;CAKF,CAAC;AAE9B;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,wBAAwB;;;;;CAGR,CAAC;AAE9B;;;;GAIG;AACH,eAAO,MAAM,uBAAuB;;;;;CAGP,CAAC;AAE9B;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,0BAIhC,CAAC;AAEX,yEAAyE;AACzE,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;CAED,CAAC;AAE9B,eAAO,MAAM,iBAAiB;;CAED,CAAC;AAE9B;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,iBAAiB,EAChE,QAAQ,CAAC,EAAE,CAAC,CAAC,GAAG,iBAAiB,CAAC,GAAG,IAAI,GACxC,CAAC,GAAG,iBAAiB,CAyBvB;AAED,mFAAmF;AACnF,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,iBAAiB,EAC1B,KAAK,EAAE,iBAAiB,GACvB,iBAAiB,CAKnB"}
@@ -4,8 +4,10 @@ export { motionDurationMs, motionHideDelayMs, motionSeconds, motionSettleMs } fr
4
4
  export type MotionRoleName = "control" | "overlay" | "scrim" | "reveal" | "slide";
5
5
  export interface MotionRole {
6
6
  transition: TransitionProp | undefined;
7
+ exit?: TransitionProp | undefined;
7
8
  animateOnly: readonly string[];
8
9
  }
10
+ export declare const motionExitFactor: number;
9
11
  export declare const motionRoleProperties: {
10
12
  readonly control: readonly ["backgroundColor", "borderColor", "color", "opacity", "transform"];
11
13
  readonly overlay: readonly ["opacity", "transform"];
@@ -1 +1 @@
1
- {"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../../src/theme/motion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAIzC,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEpG,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElF,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,cAAc,GAAG,SAAS,CAAC;IACvC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,eAAO,MAAM,oBAAoB;;;;;;CAM6B,CAAC;AAa/D,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC,cAAc,EAAE,UAAU,CAAC,CAmB7E"}
1
+ {"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../../src/theme/motion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAIzC,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEpG,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElF,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,cAAc,GAAG,SAAS,CAAC;IACvC,IAAI,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IAClC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,eAAO,MAAM,gBAAgB,QAAQ,CAAC;AAEtC,eAAO,MAAM,oBAAoB;;;;;;CAM6B,CAAC;AAmB/D,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC,cAAc,EAAE,UAAU,CAAC,CAuB7E"}
@@ -1 +1 @@
1
- {"version":3,"file":"overlaySurfaces.d.ts","sourceRoot":"","sources":["../../src/theme/overlaySurfaces.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAML,KAAK,kBAAkB,IAAI,yBAAyB,EAEpD,KAAK,UAAU,EACf,KAAK,cAAc,EAEpB,MAAM,SAAS,CAAC;AAyDjB,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;AAmC5C;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAAE,eAAe,EAAE,EAAE;IAAE,eAAe,CAAC,EAAE,aAAa,CAAA;CAAE,GAAG,IAAI,CAG9F;AAID,MAAM,WAAW,wBAAwB;IACvC,4EAA4E;IAC5E,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,mDAE9B,CAAC;AAEH,wBAAgB,sBAAsB,IAAI,wBAAwB,CAEjE;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAQD,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,yBAAyB,EAAE,MAAM,CAAC,GAAG;IACzE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,aAAa;eAJb,SAAS;WACb,UAAU;kDAcjB,CAAC;AA0CH;;;;;;GAMG;AACH,eAAO,MAAM,aAAa;;;;;;;;eAEiB,SAAS;kDAelD,CAAC;AAOH,eAAO,MAAM,kBAAkB;eAxFlB,SAAS;WACb,UAAU;kDAiGlB,CAAC;AAgCF,eAAO,MAAM,kBAAkB;;;;;;;;eAEY,SAAS;kDAelD,CAAC;AAiBH,eAAO,MAAM,cAAc;eALd,SAAS;IACpB,iEAAiE;sBAC/C,aAAa;kDA0BhC,CAAC"}
1
+ {"version":3,"file":"overlaySurfaces.d.ts","sourceRoot":"","sources":["../../src/theme/overlaySurfaces.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAML,KAAK,kBAAkB,IAAI,yBAAyB,EAEpD,KAAK,UAAU,EACf,KAAK,cAAc,EAEpB,MAAM,SAAS,CAAC;AA6DjB,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;AAmC5C;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAAE,eAAe,EAAE,EAAE;IAAE,eAAe,CAAC,EAAE,aAAa,CAAA;CAAE,GAAG,IAAI,CAG9F;AAID,MAAM,WAAW,wBAAwB;IACvC,4EAA4E;IAC5E,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,mDAE9B,CAAC;AAEH,wBAAgB,sBAAsB,IAAI,wBAAwB,CAEjE;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAQD,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,yBAAyB,EAAE,MAAM,CAAC,GAAG;IACzE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,aAAa;eAJb,SAAS;WACb,UAAU;kDAcjB,CAAC;AA0CH;;;;;;GAMG;AACH,eAAO,MAAM,aAAa;;;;;;;;eAEiB,SAAS;kDAelD,CAAC;AAOH,eAAO,MAAM,kBAAkB;eAxFlB,SAAS;WACb,UAAU;kDAiGlB,CAAC;AAgCF,eAAO,MAAM,kBAAkB;;;;;;;;eAEY,SAAS;kDAelD,CAAC;AAiBH,eAAO,MAAM,cAAc;eALd,SAAS;IACpB,iEAAiE;sBAC/C,aAAa;kDA0BhC,CAAC"}
@@ -59,6 +59,14 @@ export interface ElevationChromeProps extends ElevationPaintProps {
59
59
  /** Android dp is twice the size-formula offsetY. */
60
60
  elevation?: number;
61
61
  }
62
+ /**
63
+ * A content card's lift (LC-151): an explicit shadow plus Android dp under
64
+ * `elevationAndroid`. Not `elevation`: on native that key is Tamagui's stack
65
+ * variant, which replaces the shadow with its own size-formula one.
66
+ */
67
+ export interface CardLiftProps extends ElevationPaintProps {
68
+ elevationAndroid?: number;
69
+ }
62
70
  /** Text-bearing control chrome (Button, Chip, Input.Box): muted fill/border, no opacity. */
63
71
  export interface DisabledSurfaceProps {
64
72
  backgroundColor?: string;
@@ -224,7 +232,7 @@ export interface KnobProps {
224
232
  borderWidth?: number;
225
233
  borderRadius: string | number;
226
234
  padding: string;
227
- } & ElevationChromeProps & CornerSmoothingCarrier;
235
+ } & CardLiftProps & CornerSmoothingCarrier;
228
236
  /** Elevated/overlay tier (popovers, dropdowns): border + elevation. */
229
237
  elevatedSurface: {
230
238
  backgroundColor: string;