@multiplatform.one/theme 7.2.2 → 7.4.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.
@@ -13,7 +13,8 @@
13
13
  * reserve it for non-enumerable values only. Do NOT put recipe tables on a
14
14
  * `createStyledContext` primitive (`neverFlatten`).
15
15
  *
16
- * Reference md ($4 / $true): height 32, padX 12, font 14, icon 16, gap 6.
16
+ * Reference md ($4 / $true): height 44, padX 18, font 14, icon 16, gap 9
17
+ * (the measured tamagui.dev $4 control — MPO-13).
17
18
  * `sizeToken` remains the recipe KEY (string "$4") so styled() variants
18
19
  * can switch; it is not a height scalar.
19
20
  *
@@ -110,11 +111,27 @@ export const SIZE_RECIPE_COMPACT_RATIOS = recipeFamilies.controlCompact.ratios;
110
111
 
111
112
  export const SIZE_RECIPE_FAMILY_NAMES = ["control", "controlCompact"] as const;
112
113
 
114
+ /**
115
+ * Live tile-size ladder (MPO-31) — generated from `inputs.tile` over the
116
+ * measured defaults, adopted in place on `setSizeRecipeInputs` like every
117
+ * other live table. Never handwrite rungs; restyle through inputs.
118
+ */
119
+ export const tileSizeLadder = initial.tiles;
120
+
121
+ export function getTileSizeLadder(): typeof tileSizeLadder {
122
+ return tileSizeLadder;
123
+ }
124
+
113
125
  function adoptGenerated(next: GeneratedSizeRecipes): GeneratedSizeRecipes {
114
126
  adoptFamily(recipeFamilies.control, next.families.control);
115
127
  adoptFamily(recipeFamilies.controlCompact, next.families.controlCompact);
128
+ replaceRecord(
129
+ tileSizeLadder as unknown as Record<string, unknown>,
130
+ next.tiles as unknown as Record<string, unknown>,
131
+ );
116
132
  current = {
117
133
  ...next,
134
+ tiles: tileSizeLadder,
118
135
  heights: SIZE_RECIPE_HEIGHTS,
119
136
  heightsTouch: SIZE_RECIPE_HEIGHTS_TOUCH,
120
137
  recipes: sizeRecipes,
@@ -167,10 +184,7 @@ export function applyTouchFloor(
167
184
  (key) => tables.heights[key] === recipe.height,
168
185
  );
169
186
  if (token && tables.recipesTouch[token]) return tables.recipesTouch[token];
170
- return recipeFromHeight(
171
- Math.max(recipe.height, tables.inputs.touchFloor),
172
- tables.ratios,
173
- );
187
+ return recipeFromHeight(Math.max(recipe.height, tables.inputs.touchFloor), tables.ratios);
174
188
  }
175
189
 
176
190
  export function sizeRecipeForToken(
@@ -0,0 +1,139 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+ import { generateSizeRecipes } from "./recipeInputs";
3
+ import { getSizeRecipeInputs, setSizeRecipeInputs, tileSizeLadder } from "./sizeRecipes";
4
+ import {
5
+ defaultTileSizeInputs,
6
+ generateTileSizeLadder,
7
+ mediaAspectRatios,
8
+ resolveMediaAspect,
9
+ resolveTileSizeInputs,
10
+ tileColumnsForWidth,
11
+ tileRungFlexProps,
12
+ tileSizeRungs,
13
+ } from "./tileSizes";
14
+
15
+ afterEach(() => {
16
+ setSizeRecipeInputs();
17
+ });
18
+
19
+ describe("tile-size ladder (MPO-31 H1)", () => {
20
+ it("defaults to the measured consumer ladders, not invented numbers", () => {
21
+ const ladder = generateTileSizeLadder(resolveTileSizeInputs());
22
+ // sm..xl are Lookout's TILE_SIZES basis/max (identical to the SPA's
23
+ // TILE_MIN); xs is the launcher icon-plate minmax(104px,1fr) track.
24
+ expect(ladder.sm).toEqual({ minWidth: 220, maxWidth: 300 });
25
+ expect(ladder.md).toEqual({ minWidth: 320, maxWidth: 440 });
26
+ expect(ladder.lg).toEqual({ minWidth: 480, maxWidth: 720 });
27
+ expect(ladder.xl).toEqual({ minWidth: 720, maxWidth: 1400 });
28
+ expect(ladder.xs.minWidth).toBe(104);
29
+ // xs has no measured max; it derives from maxRatio (440/320).
30
+ expect(ladder.xs.maxWidth).toBe(Math.round(104 * defaultTileSizeInputs.maxRatio));
31
+ });
32
+
33
+ it("is a generated table: rungs are monotonic and max >= min on every rung", () => {
34
+ const ladder = generateTileSizeLadder(resolveTileSizeInputs());
35
+ for (let i = 0; i < tileSizeRungs.length; i++) {
36
+ const stop = ladder[tileSizeRungs[i]];
37
+ expect(stop.maxWidth).toBeGreaterThanOrEqual(stop.minWidth);
38
+ if (i > 0) {
39
+ expect(stop.minWidth).toBeGreaterThan(ladder[tileSizeRungs[i - 1]].minWidth);
40
+ }
41
+ }
42
+ });
43
+
44
+ it("a bare baseWidth override regenerates the whole ladder geometrically", () => {
45
+ const inputs = resolveTileSizeInputs({ baseWidth: 280 });
46
+ // Scaling override drops the measured pins (the resolveSizeRecipeInputs contract).
47
+ expect(inputs.rungWidths).toBeUndefined();
48
+ expect(inputs.widthFactors).toBeUndefined();
49
+ expect(inputs.rungMaxWidths).toBeUndefined();
50
+ const ladder = generateTileSizeLadder(inputs);
51
+ expect(ladder.md.minWidth).toBe(280);
52
+ expect(ladder.lg.minWidth).toBe(Math.round(280 * 1.5));
53
+ expect(ladder.xl.minWidth).toBe(Math.round(280 * 1.5 ** 2));
54
+ expect(ladder.sm.minWidth).toBe(Math.round(280 / 1.5));
55
+ expect(ladder.xs.minWidth).toBe(Math.round(280 / 1.5 ** 2));
56
+ for (const rung of tileSizeRungs) {
57
+ expect(ladder[rung].maxWidth).toBe(
58
+ Math.round(ladder[rung].minWidth * defaultTileSizeInputs.maxRatio),
59
+ );
60
+ }
61
+ });
62
+
63
+ it("explicit rungMaxWidths pin maxes; maxRatio covers the rest", () => {
64
+ const ladder = generateTileSizeLadder(
65
+ resolveTileSizeInputs({ baseWidth: 300, maxRatio: 2, rungMaxWidths: { md: 555 } }),
66
+ );
67
+ expect(ladder.md).toEqual({ minWidth: 300, maxWidth: 555 });
68
+ expect(ladder.lg.maxWidth).toBe(ladder.lg.minWidth * 2);
69
+ });
70
+
71
+ it("tileRungFlexProps is the wrapping-flex tile fragment (the Lookout idiom)", () => {
72
+ const ladder = generateTileSizeLadder(resolveTileSizeInputs());
73
+ expect(tileRungFlexProps(ladder.md)).toEqual({
74
+ flexGrow: 1,
75
+ flexBasis: 320,
76
+ minWidth: 320,
77
+ maxWidth: 440,
78
+ });
79
+ });
80
+
81
+ it("tileColumnsForWidth derives fixed-column counts from the rung and the gutter", () => {
82
+ const ladder = generateTileSizeLadder(resolveTileSizeInputs());
83
+ // A 1024 window at md with a 12px gutter fits 3 columns.
84
+ expect(tileColumnsForWidth(1024, ladder.md, 12)).toBe(3);
85
+ // xs at the launcher's own 8px gutter reproduces the springboard split:
86
+ // 6 columns at the 700px wide-breakpoint, 4 in the phone band.
87
+ expect(tileColumnsForWidth(700, ladder.xs, 8)).toBe(6);
88
+ expect(tileColumnsForWidth(500, ladder.xs, 8)).toBe(4);
89
+ // Never fewer than one column.
90
+ expect(tileColumnsForWidth(80, ladder.md)).toBe(1);
91
+ expect(tileColumnsForWidth(0, ladder.md)).toBe(1);
92
+ expect(tileColumnsForWidth(Number.NaN, ladder.md)).toBe(1);
93
+ });
94
+
95
+ it("rides recipeInputs: generateSizeRecipes carries tiles and setSizeRecipeInputs rewrites the live ladder in place", () => {
96
+ expect(generateSizeRecipes().tiles.md).toEqual({ minWidth: 320, maxWidth: 440 });
97
+
98
+ const live = tileSizeLadder;
99
+ expect(live.md.minWidth).toBe(320);
100
+ setSizeRecipeInputs({ tile: { baseWidth: 280 } });
101
+ // Same object identity (styled()-spread safety), new generated rows.
102
+ expect(tileSizeLadder).toBe(live);
103
+ expect(tileSizeLadder.md.minWidth).toBe(280);
104
+ expect(tileSizeLadder.lg.minWidth).toBe(420);
105
+ // The stored inputs round-trip through getSizeRecipeInputs.
106
+ expect(getSizeRecipeInputs().tile?.baseWidth).toBe(280);
107
+
108
+ setSizeRecipeInputs();
109
+ expect(tileSizeLadder.md).toEqual({ minWidth: 320, maxWidth: 440 });
110
+ expect(tileSizeLadder.sm).toEqual({ minWidth: 220, maxWidth: 300 });
111
+ });
112
+ });
113
+
114
+ describe("media aspect vocabulary (MPO-31 H2)", () => {
115
+ it("names resolve to their measured ratios", () => {
116
+ expect(resolveMediaAspect("square")).toBe(1);
117
+ expect(resolveMediaAspect("photo")).toBeCloseTo(4 / 3);
118
+ // The Lookout frame shape — desktop screen captures at 16:10.
119
+ expect(resolveMediaAspect("screen")).toBeCloseTo(1.6);
120
+ expect(resolveMediaAspect("video")).toBeCloseTo(16 / 9);
121
+ expect(resolveMediaAspect("portrait")).toBeCloseTo(3 / 4);
122
+ expect(Object.keys(mediaAspectRatios)).toEqual([
123
+ "square",
124
+ "photo",
125
+ "screen",
126
+ "video",
127
+ "portrait",
128
+ ]);
129
+ });
130
+
131
+ it("numbers pass through; nullish and junk fall back to square", () => {
132
+ expect(resolveMediaAspect(1.6)).toBe(1.6);
133
+ expect(resolveMediaAspect()).toBe(1);
134
+ expect(resolveMediaAspect(null)).toBe(1);
135
+ expect(resolveMediaAspect(0)).toBe(1);
136
+ expect(resolveMediaAspect(-2)).toBe(1);
137
+ expect(resolveMediaAspect(Number.POSITIVE_INFINITY)).toBe(1);
138
+ });
139
+ });
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Tile-size ladder + media-aspect vocabulary (MPO-31, design-system/12 D.3
3
+ * holes H1/H2).
4
+ *
5
+ * Every tile wall needs the same two decisions: HOW BIG is a tile (a small
6
+ * named ladder a person can pick from — the user-facing S/M/L/XL control)
7
+ * and WHAT SHAPE is its media (so the loading skeleton is the same shape as
8
+ * the image that replaces it). Before this module both were app-invented:
9
+ * the console's `launcherGeometry.ts` (03-our-surfaces.md DL-9) and
10
+ * Lookout's private `TILE_SIZES` were the same gap twice.
11
+ *
12
+ * Like the control-size recipes (LC-70 RECIPE-INPUTS), STORED INPUTS are the
13
+ * source of truth and the ladder table is DERIVED — never handwritten at a
14
+ * call site. The default rung values are the MEASURED ladders from the two
15
+ * real consumers, not invented numbers:
16
+ *
17
+ * | rung | minWidth | maxWidth | measured from |
18
+ * |------|----------|----------|------------------------------------------------|
19
+ * | xs | 104 | 143 | launcher icon-plate track `minmax(104px,1fr)` |
20
+ * | sm | 220 | 300 | Lookout `TILE_SIZES.s` / SPA `TILE_MIN.s` |
21
+ * | md | 320 | 440 | Lookout `TILE_SIZES.m` / SPA `TILE_MIN.m` |
22
+ * | lg | 480 | 720 | Lookout `TILE_SIZES.l` / SPA `TILE_MIN.l` |
23
+ * | xl | 720 | 1400 | Lookout `TILE_SIZES.xl` / SPA `TILE_MIN.xl` |
24
+ *
25
+ * `{ baseWidth: 280 }` (no rungWidths / widthFactors) regenerates the whole
26
+ * ladder geometrically from `baseWidth * stepRatio ** step`, the same
27
+ * override contract `resolveSizeRecipeInputs` gives `baseHeight`.
28
+ *
29
+ * OWNERSHIP (12-one-design.md Part B): the RUNGS are universal — generated
30
+ * here, restyled only through recipe inputs
31
+ * (`setSizeRecipeInputs({ tile })` / `createDefaultThemeConfig({ recipeInputs:
32
+ * { tile } })`). WHICH rung a surface shows is per-app (a prop, often
33
+ * persisted per user). Tile gutters stay on the space knob and tile rounding
34
+ * on the borderRadius knob — this module never emits a gap or a radius.
35
+ *
36
+ * DENSITY (LC-76): density moves SPACE only. A compact density steps the
37
+ * gutters between tiles (via the space knob) and must never re-rung the
38
+ * ladder — tile magnitude is content scale, not chrome scale, so nothing
39
+ * here reads the density knob.
40
+ */
41
+
42
+ // ── Rungs ─────────────────────────────────────────────────────
43
+
44
+ export const tileSizeRungs = ["xs", "sm", "md", "lg", "xl"] as const;
45
+
46
+ export type TileSizeRung = (typeof tileSizeRungs)[number];
47
+
48
+ /** One rung: the narrowest a tile may render, and the widest. */
49
+ export type TileSizeStop = {
50
+ minWidth: number;
51
+ maxWidth: number;
52
+ };
53
+
54
+ export type TileSizeLadder = Record<TileSizeRung, TileSizeStop>;
55
+
56
+ // ── Inputs ────────────────────────────────────────────────────
57
+
58
+ export type TileSizeInputs = {
59
+ /** Desktop px min-width of the `md` rung. */
60
+ baseWidth: number;
61
+ /** Rung-to-rung ratio when regenerating geometrically. */
62
+ stepRatio: number;
63
+ /** Exponent of each rung relative to `md` (0). */
64
+ rungSteps: Record<TileSizeRung, number>;
65
+ /**
66
+ * Absolute desktop px min-widths. When omitted, min-widths come from
67
+ * `baseWidth * stepRatio ** step` (or `widthFactors`).
68
+ */
69
+ rungWidths?: Partial<Record<TileSizeRung, number>>;
70
+ /**
71
+ * Optional per-rung scale relative to `baseWidth`. Used only when
72
+ * `rungWidths` is omitted. Factors are `stepRatio ** exponent` otherwise.
73
+ */
74
+ widthFactors?: Partial<Record<TileSizeRung, number>>;
75
+ /** maxWidth = minWidth * maxRatio for any rung `rungMaxWidths` does not pin. */
76
+ maxRatio: number;
77
+ /** Absolute desktop px max-widths (measured pins win over `maxRatio`). */
78
+ rungMaxWidths?: Partial<Record<TileSizeRung, number>>;
79
+ };
80
+
81
+ export const tileSizeStepExponents: Record<TileSizeRung, number> = {
82
+ xs: -2,
83
+ sm: -1,
84
+ md: 0,
85
+ lg: 1,
86
+ xl: 2,
87
+ };
88
+
89
+ // Measured min-widths: xs is the launcher/springboard icon-plate grid track
90
+ // (`minmax(104px,1fr)` — launcherGeometry.ts / traefik menu.go); sm..xl are
91
+ // Lookout's `TILE_SIZES` basis values, identical in the production SPA's
92
+ // `TILE_MIN` map. Kept explicit so adopting the ladder is a rendered no-op
93
+ // for both consumers.
94
+ const measuredRungWidths: Record<TileSizeRung, number> = {
95
+ xs: 104,
96
+ sm: 220,
97
+ md: 320,
98
+ lg: 480,
99
+ xl: 720,
100
+ };
101
+
102
+ /** Lossless relative table: the measured 104/220/320/480/720 ramp at baseWidth 320. */
103
+ export const defaultWidthFactors: Record<TileSizeRung, number> = {
104
+ xs: 104 / 320,
105
+ sm: 220 / 320,
106
+ md: 1,
107
+ lg: 480 / 320,
108
+ xl: 720 / 320,
109
+ };
110
+
111
+ // Measured max-widths (Lookout `TILE_SIZES` max). xl's 1.94x is the
112
+ // deliberate "one huge tile" rung; xs has no measured max (the launcher caps
113
+ // tiles by column count), so it derives from `maxRatio`.
114
+ const measuredRungMaxWidths: Partial<Record<TileSizeRung, number>> = {
115
+ sm: 300,
116
+ md: 440,
117
+ lg: 720,
118
+ xl: 1400,
119
+ };
120
+
121
+ export const defaultTileSizeInputs: TileSizeInputs = {
122
+ baseWidth: 320,
123
+ stepRatio: 1.5,
124
+ rungSteps: { ...tileSizeStepExponents },
125
+ rungWidths: { ...measuredRungWidths },
126
+ widthFactors: { ...defaultWidthFactors },
127
+ // Lookout md: 440/320. The measured sm (1.36) and lg (1.5) bracket it.
128
+ maxRatio: 440 / 320,
129
+ rungMaxWidths: { ...measuredRungMaxWidths },
130
+ };
131
+
132
+ /**
133
+ * Deep copy that PRESERVES partiality: keys the caller never set stay
134
+ * unset, so a later `resolveTileSizeInputs` still sees them as defaults
135
+ * rather than explicit undefineds.
136
+ */
137
+ export function cloneTileSizeInputs<T extends Partial<TileSizeInputs>>(inputs: T): T {
138
+ const clone = { ...inputs };
139
+ if (inputs.rungSteps) clone.rungSteps = { ...inputs.rungSteps };
140
+ if (inputs.rungWidths) clone.rungWidths = { ...inputs.rungWidths };
141
+ if (inputs.widthFactors) clone.widthFactors = { ...inputs.widthFactors };
142
+ if (inputs.rungMaxWidths) clone.rungMaxWidths = { ...inputs.rungMaxWidths };
143
+ return clone;
144
+ }
145
+
146
+ /**
147
+ * Fill defaults. `{ baseWidth: 280 }` (any scaling override without explicit
148
+ * tables) drops the measured rungWidths/widthFactors/rungMaxWidths so every
149
+ * rung regenerates from `stepRatio` — the `resolveSizeRecipeInputs` contract.
150
+ */
151
+ export function resolveTileSizeInputs(partial: Partial<TileSizeInputs> = {}): TileSizeInputs {
152
+ const scalingOverride =
153
+ partial.baseWidth != null || partial.stepRatio != null || partial.rungSteps != null;
154
+ const hasRungWidths = Object.prototype.hasOwnProperty.call(partial, "rungWidths");
155
+ const hasWidthFactors = Object.prototype.hasOwnProperty.call(partial, "widthFactors");
156
+ const hasRungMaxWidths = Object.prototype.hasOwnProperty.call(partial, "rungMaxWidths");
157
+
158
+ const resolved: TileSizeInputs = {
159
+ baseWidth: partial.baseWidth ?? defaultTileSizeInputs.baseWidth,
160
+ stepRatio: partial.stepRatio ?? defaultTileSizeInputs.stepRatio,
161
+ rungSteps: { ...defaultTileSizeInputs.rungSteps, ...partial.rungSteps },
162
+ maxRatio: partial.maxRatio ?? defaultTileSizeInputs.maxRatio,
163
+ };
164
+
165
+ if (hasRungWidths) {
166
+ resolved.rungWidths = partial.rungWidths ? { ...partial.rungWidths } : undefined;
167
+ if (hasWidthFactors) {
168
+ resolved.widthFactors = partial.widthFactors ? { ...partial.widthFactors } : undefined;
169
+ }
170
+ } else if (hasWidthFactors) {
171
+ resolved.widthFactors = partial.widthFactors ? { ...partial.widthFactors } : undefined;
172
+ } else if (!scalingOverride) {
173
+ resolved.rungWidths = { ...defaultTileSizeInputs.rungWidths };
174
+ resolved.widthFactors = { ...defaultTileSizeInputs.widthFactors };
175
+ }
176
+
177
+ if (hasRungMaxWidths) {
178
+ resolved.rungMaxWidths = partial.rungMaxWidths ? { ...partial.rungMaxWidths } : undefined;
179
+ } else if (!scalingOverride && !hasRungWidths && !hasWidthFactors) {
180
+ resolved.rungMaxWidths = { ...defaultTileSizeInputs.rungMaxWidths };
181
+ }
182
+
183
+ return resolved;
184
+ }
185
+
186
+ // ── Generation ────────────────────────────────────────────────
187
+
188
+ function clampWidth(value: number): number {
189
+ return Math.max(1, Math.round(value));
190
+ }
191
+
192
+ function minWidthForRung(rung: TileSizeRung, inputs: TileSizeInputs): number {
193
+ const explicitPx = inputs.rungWidths?.[rung];
194
+ if (explicitPx != null) return clampWidth(explicitPx);
195
+ const factor = inputs.widthFactors?.[rung];
196
+ if (factor != null) return clampWidth(inputs.baseWidth * factor);
197
+ const step = inputs.rungSteps[rung] ?? 0;
198
+ return clampWidth(inputs.baseWidth * inputs.stepRatio ** step);
199
+ }
200
+
201
+ /** Derive the full ladder from resolved inputs. Tables are generated, never handwritten. */
202
+ export function generateTileSizeLadder(inputs: TileSizeInputs): TileSizeLadder {
203
+ const ladder = {} as TileSizeLadder;
204
+ for (const rung of tileSizeRungs) {
205
+ const minWidth = minWidthForRung(rung, inputs);
206
+ const pinnedMax = inputs.rungMaxWidths?.[rung];
207
+ const maxWidth = Math.max(
208
+ minWidth,
209
+ pinnedMax != null ? clampWidth(pinnedMax) : clampWidth(minWidth * inputs.maxRatio),
210
+ );
211
+ ladder[rung] = { minWidth, maxWidth };
212
+ }
213
+ return ladder;
214
+ }
215
+
216
+ // ── Consumption helpers ───────────────────────────────────────
217
+
218
+ export type TileRungFlexProps = {
219
+ flexGrow: number;
220
+ flexBasis: number;
221
+ minWidth: number;
222
+ maxWidth: number;
223
+ };
224
+
225
+ /**
226
+ * Style fragment for a tile in a wrapping flex wall (the Lookout idiom:
227
+ * tiles grow from `minWidth` and stop at `maxWidth`). Spread it on the tile
228
+ * surface; gutters stay on the container's space-knob gap.
229
+ */
230
+ export function tileRungFlexProps(stop: TileSizeStop): TileRungFlexProps {
231
+ return {
232
+ flexGrow: 1,
233
+ flexBasis: stop.minWidth,
234
+ minWidth: stop.minWidth,
235
+ maxWidth: stop.maxWidth,
236
+ };
237
+ }
238
+
239
+ /**
240
+ * Column count for a fixed-column tile grid (the ImageGrid / springboard
241
+ * idiom): as many columns as fit at the rung's minWidth. `gutter` is the
242
+ * inter-column gap (the space knob's value), never baked into the rung.
243
+ *
244
+ * The count is min-driven on purpose: adding a column to honor `maxWidth`
245
+ * would, by construction of the fit, drop every cell below `minWidth`
246
+ * (`(fit+1)` cells at minWidth already overflow the width). `maxWidth`
247
+ * binds in the wrapping-flex idiom (`tileRungFlexProps`), where a tile can
248
+ * stop growing without forcing a new column.
249
+ *
250
+ * At the springboard's own numbers this reproduces the launcher's split:
251
+ * `xs` (104) with the 8px launcher gutter yields 6 columns at the 700px
252
+ * wide-breakpoint and 4 in the phone band.
253
+ */
254
+ export function tileColumnsForWidth(width: number, stop: TileSizeStop, gutter = 0): number {
255
+ if (!Number.isFinite(width) || width <= 0) return 1;
256
+ return Math.max(1, Math.floor((width + gutter) / (stop.minWidth + gutter)));
257
+ }
258
+
259
+ // ── Media aspect (H2) ─────────────────────────────────────────
260
+
261
+ /**
262
+ * Named media aspect ratios (width / height). A skeleton twin drawn at the
263
+ * wrong aspect produces the exact layout jump it exists to prevent
264
+ * (ImageGrid hardcoded 1; Lookout frames are 16:10).
265
+ */
266
+ export const mediaAspectRatios = {
267
+ /** Album art, avatars, icon plates. */
268
+ square: 1,
269
+ /** Stills cameras, 4:3. */
270
+ photo: 4 / 3,
271
+ /** Desktop screen captures, 16:10 — the Lookout frame shape. */
272
+ screen: 16 / 10,
273
+ /** Video stills, 16:9. */
274
+ video: 16 / 9,
275
+ /** Portrait media, 3:4. */
276
+ portrait: 3 / 4,
277
+ } as const;
278
+
279
+ export type MediaAspectName = keyof typeof mediaAspectRatios;
280
+
281
+ /** A named aspect or an explicit width/height number. */
282
+ export type MediaAspect = MediaAspectName | number;
283
+
284
+ /** Resolve a name or number to a positive width/height ratio (default `square`). */
285
+ export function resolveMediaAspect(aspect?: MediaAspect | null): number {
286
+ if (typeof aspect === "number") {
287
+ return Number.isFinite(aspect) && aspect > 0 ? aspect : mediaAspectRatios.square;
288
+ }
289
+ if (aspect != null && aspect in mediaAspectRatios) return mediaAspectRatios[aspect];
290
+ return mediaAspectRatios.square;
291
+ }
@@ -26,6 +26,7 @@ export * from "./resolveKnobs";
26
26
  export * from "./shared";
27
27
  export * from "./sizeRecipes";
28
28
  export * from "./Surface";
29
+ export * from "./tileSizes";
29
30
  export * from "./theme";
30
31
  export * from "./Tint";
31
32
  export * from "./useResolvedKnobs";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,YAAY,EAAE,MAAM,IAAI,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACjF,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAE1B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,YAAY,EAAE,MAAM,IAAI,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACjF,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAE5B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC"}
@@ -10,8 +10,10 @@
10
10
  * Hybrid of shipped MP1 behavior + Material window-size naming:
11
11
  * - **Names** follow Material (compact → xl).
12
12
  * - **Numbers** prefer already-shipped thresholds:
13
- * - `medium` @ 640 = Tamagui `$sm` / DeskShell narrow (Fluent sm).
14
- * - `expanded` @ 860 = FloatingPanel sheet↔float pivot (~Material 840).
13
+ * - `medium` @ 640 = Tamagui `$sm` / DeskShell narrow (Fluent sm) — and,
14
+ * since MPO-13, the overlay sheet↔float pivot (see OVERLAY_BREAKPOINT).
15
+ * - `expanded` @ 860 = pane-budget class only (~Material 840). No Tamagui
16
+ * counterpart; Tamagui has no pane system, so this stays additive.
15
17
  * - `large` / `xl` = Tamagui `$lg` / `$xl`.
16
18
  *
17
19
  * Fluent (480/640/1024/1366/1920) and Material (600/840/1200/1600) stay in the
@@ -19,9 +21,9 @@
19
21
  *
20
22
  * | Class | Min px | Max panes (default) | Tamagui media | Notes |
21
23
  * |-----------|--------|---------------------|---------------|--------------------------------|
22
- * | compact | 0 | 1 | < `$sm` | phones; drawer nav |
23
- * | medium | 640 | 1 | `$sm` | DeskShell rail; still 1 pane |
24
- * | expanded | 860 | 2 | — (custom) | FloatingPanel float; 2 panes |
24
+ * | compact | 0 | 1 | < `$sm` | phones; drawer nav; sheets |
25
+ * | medium | 640 | 1 | `$sm` | DeskShell rail; overlays float |
26
+ * | expanded | 860 | 2 | — (custom) | 2 panes |
25
27
  * | large | 1024 | 2 | `$lg` | |
26
28
  * | xl | 1280 | 2 | `$xl` | |
27
29
  *
@@ -31,9 +33,13 @@
31
33
  export type LayoutSizeClass = "compact" | "medium" | "expanded" | "large" | "xl";
32
34
  /** Named min-widths. Prefer these over literals in layout code. */
33
35
  export declare const layoutBreakpoints: {
34
- /** End of compact / DeskShell narrow drawer threshold (Tamagui `$sm`). */
36
+ /**
37
+ * End of compact / DeskShell narrow drawer threshold (Tamagui `$sm`).
38
+ * Also the overlay sheet↔float pivot and the DataTable card↔grid pivot —
39
+ * one idiom flip per viewport (MPO-13 C5).
40
+ */
35
41
  readonly medium: 640;
36
- /** Two-pane + FloatingPanel float threshold (~M3 expanded 840). */
42
+ /** Two-pane budget threshold (~M3 expanded 840). Not an overlay pivot. */
37
43
  readonly expanded: 860;
38
44
  /** Tamagui `$lg`. */
39
45
  readonly large: 1024;
@@ -42,10 +48,16 @@ export declare const layoutBreakpoints: {
42
48
  };
43
49
  export type LayoutBreakpointName = keyof typeof layoutBreakpoints;
44
50
  /**
45
- * Alias kept for FloatingPanel / overlay consumers — same value as
46
- * `layoutBreakpoints.expanded`.
51
+ * Overlay sheet↔float pivot for FloatingPanel / AdaptivePopup consumers.
52
+ *
53
+ * MPO-13 C5: was `layoutBreakpoints.expanded` (860, a custom threshold with
54
+ * no Tamagui counterpart), which split the system in two — at 700px a
55
+ * DataTable rendered its desktop grid while a Select in one of its cells
56
+ * opened as a bottom sheet. Overlays now pivot where the table does:
57
+ * `layoutBreakpoints.medium` (640, Tamagui v5 `$sm` — the same `media.sm`
58
+ * key DataTableRoot reads), so every surface flips idiom at one width.
47
59
  */
48
- export declare const OVERLAY_BREAKPOINT: 860;
60
+ export declare const OVERLAY_BREAKPOINT: 640;
49
61
  /** DeskShell sidebar collapse — same value as `layoutBreakpoints.medium`. */
50
62
  export declare const NARROW_BREAKPOINT: 640;
51
63
  export declare const LAYOUT_SIZE_CLASS_ORDER: readonly LayoutSizeClass[];
@@ -1 +1 @@
1
- {"version":3,"file":"layoutTokens.d.ts","sourceRoot":"","sources":["../../src/theme/layoutTokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AASH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,IAAI,CAAC;AAEjF,mEAAmE;AACnE,eAAO,MAAM,iBAAiB;IAC5B,0EAA0E;;IAE1E,mEAAmE;;IAEnE,qBAAqB;;IAErB,qBAAqB;;CAEb,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,iBAAiB,CAAC;AAElE;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KAA6B,CAAC;AAE7D,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,KAA2B,CAAC;AAE1D,eAAO,MAAM,uBAAuB,EAAE,SAAS,eAAe,EAMpD,CAAC;AAEX,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAMjE;AAED,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,SAAS,EAAE,eAAe,GAAG,CAAC,GAAG,CAAC,CAEjE;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,eAAe,GACvB,OAAO,CAET;AAQD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CAiCpD;AAED,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,OAAO,CAItD;AAID,2DAA2D;AAC3D,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,gFAAgF;AAChF,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,MAAM,GAAG,MAAM,GAAG,IAAuB,GACjD,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAI3C;AAID,wFAAwF;AACxF,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC,kEAAkE;AAClE,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,IAAI,GAAE,MAAyB,GAAG,gBAAgB,CAElF;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,yEAAyE;AACzE,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,GAAG,GAAE,MAAyB,GAC7B,aAAa,CAGf;AAID;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAIpC;;;GAGG;AACH,wBAAgB,eAAe,IAAI;IACjC,WAAW,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7B,aAAa,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CAChC,CAMA"}
1
+ {"version":3,"file":"layoutTokens.d.ts","sourceRoot":"","sources":["../../src/theme/layoutTokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AASH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,IAAI,CAAC;AAEjF,mEAAmE;AACnE,eAAO,MAAM,iBAAiB;IAC5B;;;;OAIG;;IAEH,0EAA0E;;IAE1E,qBAAqB;;IAErB,qBAAqB;;CAEb,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,iBAAiB,CAAC;AAElE;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,KAA2B,CAAC;AAE3D,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,KAA2B,CAAC;AAE1D,eAAO,MAAM,uBAAuB,EAAE,SAAS,eAAe,EAMpD,CAAC;AAEX,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAMjE;AAED,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,SAAS,EAAE,eAAe,GAAG,CAAC,GAAG,CAAC,CAEjE;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,eAAe,GACvB,OAAO,CAET;AAQD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CAiCpD;AAED,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,OAAO,CAItD;AAID,2DAA2D;AAC3D,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,gFAAgF;AAChF,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,MAAM,GAAG,MAAM,GAAG,IAAuB,GACjD,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAI3C;AAID,wFAAwF;AACxF,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC,kEAAkE;AAClE,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,IAAI,GAAE,MAAyB,GAAG,gBAAgB,CAElF;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,yEAAyE;AACzE,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,GAAG,GAAE,MAAyB,GAC7B,aAAa,CAGf;AAID;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAIpC;;;GAGG;AACH,wBAAgB,eAAe,IAAI;IACjC,WAAW,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7B,aAAa,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CAChC,CAMA"}
@@ -9,10 +9,13 @@
9
9
  * Tamagui `createThemes` / theme-builder still emits COLOR palettes only.
10
10
  * Do not stuff recipe keys into `getTheme()` / `themes`.
11
11
  *
12
- * DEFAULT uses `tokenHeights` so published px match today's lossless table
13
- * ($4=32, $3=28, $5=40, $6=48). `{ baseHeight: 40 }` (no tokenHeights /
14
- * sizeFactors) regenerates geometrically from `baseHeight * stepRatio ** step`.
12
+ * DEFAULT uses `tokenHeights` pinned to the stock Tamagui `$size` scale as
13
+ * measured on tamagui.dev ($4=44, $3=36, $5=52, $6=64 — MPO-13 C1/C2: the
14
+ * recipe key and the raw token now agree). `{ baseHeight: 40 }` (no
15
+ * tokenHeights / sizeFactors) regenerates geometrically from
16
+ * `baseHeight * stepRatio ** step`.
15
17
  */
18
+ import { type TileSizeInputs, type TileSizeLadder } from "./tileSizes";
16
19
  export declare const SIZE_RECIPE_TOKENS: readonly ["$1", "$1.5", "$2", "$2.5", "$3", "$3.5", "$4", "$true", "$5", "$6"];
17
20
  export type SizeRecipeToken = (typeof SIZE_RECIPE_TOKENS)[number];
18
21
  export type SizeRecipeFamilyName = "control" | "controlCompact";
@@ -39,6 +42,11 @@ export type SizeRecipeInputs = {
39
42
  compactRatios: SizeRecipeRatios;
40
43
  radiusPx: number;
41
44
  touchFloor: number;
45
+ /**
46
+ * Tile-size ladder inputs (MPO-31). Merged over `defaultTileSizeInputs`;
47
+ * the ladder table derives from these the way every other table does.
48
+ */
49
+ tile?: Partial<TileSizeInputs>;
42
50
  };
43
51
  /** @deprecated Use `SizeRecipeInputs`. */
44
52
  export type RecipeInputs = SizeRecipeInputs;
@@ -89,9 +97,11 @@ export type GeneratedSizeRecipes = {
89
97
  typeVariants: Record<string, SizeRecipeTypeVariant>;
90
98
  iconVariants: Record<string, SizeRecipeIconVariant>;
91
99
  families: RecipeFamilies;
100
+ /** Tile-size ladder (MPO-31) — derived from `inputs.tile` over the measured defaults. */
101
+ tiles: TileSizeLadder;
92
102
  };
93
103
  export declare const SIZE_TOKEN_EXPONENTS: Record<SizeRecipeToken, number>;
94
- /** Lossless relative table: today's 20/24/28/32/40/48 ramp at baseHeight 32. */
104
+ /** Lossless relative table: the stock Tamagui 20/24/28/32/36/40/44/52/64 ramp at baseHeight 44. */
95
105
  export declare const defaultSizeFactors: Record<SizeRecipeToken, number>;
96
106
  export declare const DEFAULT_SIZE_RECIPE_INPUTS: SizeRecipeInputs;
97
107
  export declare const defaultRecipeInputs: SizeRecipeInputs;
@@ -1 +1 @@
1
- {"version":3,"file":"recipeInputs.d.ts","sourceRoot":"","sources":["../../src/theme/recipeInputs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"recipeInputs.d.ts","sourceRoot":"","sources":["../../src/theme/recipeInputs.ts"],"names":[],"mappings":"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"}
@@ -13,7 +13,8 @@
13
13
  * reserve it for non-enumerable values only. Do NOT put recipe tables on a
14
14
  * `createStyledContext` primitive (`neverFlatten`).
15
15
  *
16
- * Reference md ($4 / $true): height 32, padX 12, font 14, icon 16, gap 6.
16
+ * Reference md ($4 / $true): height 44, padX 18, font 14, icon 16, gap 9
17
+ * (the measured tamagui.dev $4 control — MPO-13).
17
18
  * `sizeToken` remains the recipe KEY (string "$4") so styled() variants
18
19
  * can switch; it is not a height scalar.
19
20
  *
@@ -43,6 +44,13 @@ export declare const SIZE_RECIPE_HEIGHTS_TOUCH: Record<string, number>;
43
44
  export declare const SIZE_RECIPE_RATIOS: import("./recipeInputs").SizeRecipeRatios;
44
45
  export declare const SIZE_RECIPE_COMPACT_RATIOS: import("./recipeInputs").SizeRecipeRatios;
45
46
  export declare const SIZE_RECIPE_FAMILY_NAMES: readonly ["control", "controlCompact"];
47
+ /**
48
+ * Live tile-size ladder (MPO-31) — generated from `inputs.tile` over the
49
+ * measured defaults, adopted in place on `setSizeRecipeInputs` like every
50
+ * other live table. Never handwrite rungs; restyle through inputs.
51
+ */
52
+ export declare const tileSizeLadder: import("./tileSizes").TileSizeLadder;
53
+ export declare function getTileSizeLadder(): typeof tileSizeLadder;
46
54
  /** Merge `partial` over defaultRecipeInputs (not the previous override) and rewrite live tables. */
47
55
  export declare function setSizeRecipeInputs(partial?: Partial<RecipeInputs>): GeneratedSizeRecipes;
48
56
  export declare function getSizeRecipeInputs(): RecipeInputs;