@vectoriox/iox-ui 5.44.0 → 5.45.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.
@@ -29,3 +29,4 @@ export * from './fonts';
29
29
  export * from './token-names';
30
30
  export * from './design-settings';
31
31
  export * from './design-apply';
32
+ export * from './style-presets';
@@ -0,0 +1,71 @@
1
+ import { SPEC_MOODS, SPEC_STYLE_PRESETS } from './flavor-spec.generated';
2
+ import { resolveDesignChoices } from './design-settings';
3
+ import {
4
+ DensityId, DesignChoices, DesignMode, DesignMotion, MotionId, ShapeId, TintId,
5
+ } from './design-token.model';
6
+
7
+ /**
8
+ * Style presets (AI plan rev 3 §3f, H1-1) — a style is a preset over what exists, never a stylesheet: a
9
+ * named `DesignChoices` + the site's motion character + the colour modes it passed the contrast audit in.
10
+ * Generated 1:1 from the contract (`stylePresets`, AA-gated per preset × mode in build-spec.mjs); Design ▸
11
+ * Look lists them, ranked by the harness (`@vectoriox/iox-ui/harness-core`).
12
+ */
13
+
14
+ /** The closed mood vocabulary a preset carries (≤ 3) and an analysis returns. */
15
+ export type MoodId = keyof typeof SPEC_MOODS;
16
+ export const MOOD_IDS = Object.keys(SPEC_MOODS) as ReadonlyArray<MoodId>;
17
+ export const MOOD_LABELS: Readonly<Record<MoodId, string>> = SPEC_MOODS;
18
+
19
+ /** A colour mode a preset was audited in (`auto` is offered on top when it passed both). */
20
+ export type PresetMode = Exclude<DesignMode, 'auto'>;
21
+
22
+ export interface StylePreset {
23
+ readonly id: string;
24
+ readonly name: string;
25
+ readonly blurb: string;
26
+ readonly flavorId: string;
27
+ readonly seed: string;
28
+ readonly tint: TintId;
29
+ readonly pairId: string;
30
+ readonly shape: ShapeId;
31
+ readonly density: DensityId;
32
+ readonly motion: MotionId;
33
+ readonly pillButtons: boolean;
34
+ readonly moves: Readonly<DesignMotion>;
35
+ /** The modes it PASSED the audit in; the first is its default. */
36
+ readonly modes: ReadonlyArray<PresetMode>;
37
+ readonly moods: ReadonlyArray<MoodId>;
38
+ /** The UI UX Pro Max styles it stands for (attribution and the mapping table). */
39
+ readonly uupm: ReadonlyArray<string>;
40
+ /** In the M1-alpha slice. */
41
+ readonly slice: boolean;
42
+ }
43
+
44
+ /** The 24, catalogue order (four per flavor). Assignment here proves the generated data fits the type. */
45
+ export const STYLE_PRESETS: ReadonlyArray<StylePreset> = SPEC_STYLE_PRESETS;
46
+
47
+ export function stylePresetById(id: string | null | undefined): StylePreset | undefined {
48
+ return id ? STYLE_PRESETS.find(p => p.id === id) : undefined;
49
+ }
50
+
51
+ const FOLLOW_VISITOR: DesignMode = 'auto';
52
+
53
+ /** The modes Design offers for a preset: the audited ones, then "Follow visitor" when it passed both (H-D3). */
54
+ export function presetModes(preset: StylePreset): DesignMode[] {
55
+ return preset.modes.length > 1 ? [...preset.modes, FOLLOW_VISITOR] : [...preset.modes];
56
+ }
57
+
58
+ /**
59
+ * What picking a preset means — the complete `DesignChoices`, its motion included — in `mode` (default:
60
+ * its first). A mode it was not audited in is REFUSED (null): a light-only preset is never worn dark.
61
+ */
62
+ export function presetChoices(id: string, mode?: DesignMode): DesignChoices | null {
63
+ const preset = stylePresetById(id);
64
+ if (!preset) return null;
65
+ const wear = mode ?? preset.modes[0];
66
+ if (!presetModes(preset).includes(wear)) return null;
67
+ return resolveDesignChoices({
68
+ flavorId: preset.flavorId, seed: preset.seed, pairId: preset.pairId, shape: preset.shape, density: preset.density,
69
+ motion: preset.motion, pillButtons: preset.pillButtons, mode: wear, tint: preset.tint, ...preset.moves,
70
+ });
71
+ }