@welcome-back/brand 0.6.2 → 0.7.1

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/README.md CHANGED
@@ -324,6 +324,23 @@ POS_TOKENS['--wb-accent']; // '#171717'
324
324
  payment terminal, from it on is tablet. It is a number because media queries
325
325
  cannot read a CSS variable; pos-ui uses it as the `wide` screen in Tailwind.
326
326
 
327
+ ### Dark
328
+
329
+ `POS_DARK_TOKENS` holds a dark value for every colour token (Paper F4, direction A
330
+ «Warm night», owner 2026-10-21); sizes, radius and type do not change. The
331
+ generated `pos-tokens.css` also carries the rule `.theme-dark { … }`, so an app
332
+ turns dark by putting `POS_DARK_CLASS` (`theme-dark`) on `<body>`. Dark is a
333
+ per-device setting, never automatic. `CATEGORY_PALETTE_DARK` has the same 8
334
+ colours, slot for slot, and passes the same contrast test on the dark card.
335
+
336
+ ### On media
337
+
338
+ Square tiles show white text on a photo or on a category colour.
339
+ `--wb-on-media` / `--wb-on-media-muted` (text), `--wb-media-ink` (badge fill),
340
+ `--wb-media-veil` (sold-out wash) and `--wb-media-scrim` (the bottom gradient)
341
+ are the same in light and dark. The scrim keeps white text at 4.5:1 or more on
342
+ a pure white photo up to half the tile height.
343
+
327
344
  ### Category colours
328
345
 
329
346
  `CATEGORY_PALETTE` (`src/categoryPalette.ts`) is the 8-colour set of the POS
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CATEGORY_PALETTE_TOKENS = exports.DEFAULT_CATEGORY_SLOT = exports.CATEGORY_PALETTE = void 0;
3
+ exports.CATEGORY_PALETTE_DARK_TOKENS = exports.CATEGORY_PALETTE_TOKENS = exports.DEFAULT_CATEGORY_SLOT = exports.CATEGORY_PALETTE_DARK = exports.CATEGORY_PALETTE = void 0;
4
4
  exports.categorySlotForIndex = categorySlotForIndex;
5
5
  exports.CATEGORY_PALETTE = [
6
6
  { name: "Dorado", swatch: "#a07c2b", tint: "#f3ecdd", slot: "#eae2d0", ink: "#785d20" },
@@ -12,16 +12,35 @@ exports.CATEGORY_PALETTE = [
12
12
  { name: "Pizarra", swatch: "#5e7a80", tint: "#eaeeee", slot: "#dce2e3", ink: "#465c60" },
13
13
  { name: "Piedra", swatch: "#8a7f6e", tint: "#f0eeec", slot: "#e5e3df", ink: "#685f52" },
14
14
  ];
15
+ /**
16
+ * The same 8 colours for the dark look (F4 A «Warm night», owner 2026-10-21),
17
+ * slot for slot. Tint and slot are the swatch mixed into the dark card
18
+ * (#1a1917) at 20 % and 32 %; ink is the swatch mixed with white at 55 %;
19
+ * the swatch is 18 % lighter. Text on a dark tint is #f2f0ea and #b8b3a7.
20
+ */
21
+ exports.CATEGORY_PALETTE_DARK = [
22
+ { name: "Dorado", swatch: "#b19451", tint: "#352d1b", slot: "#45391d", ink: "#d4c4a0" },
23
+ { name: "Azul mar", swatch: "#4798b6", tint: "#1b2e34", slot: "#1c3a45", ink: "#9ac6d7" },
24
+ { name: "Terracota", swatch: "#c2785f", tint: "#39261e", slot: "#4c2e23", ink: "#deb5a7" },
25
+ { name: "Verde", swatch: "#66a279", tint: "#233025", slot: "#283e2d", ink: "#abccb6" },
26
+ { name: "Ciruela", swatch: "#ae729e", tint: "#34252e", slot: "#442c3b", ink: "#d2b2ca" },
27
+ { name: "Azul", swatch: "#6289c2", tint: "#212a37", slot: "#26354a", ink: "#a9bede" },
28
+ { name: "Pizarra", swatch: "#7b9297", tint: "#282c2c", slot: "#303839", ink: "#b7c3c6" },
29
+ { name: "Piedra", swatch: "#9f9688", tint: "#302d28", slot: "#3e3a33", ink: "#cac5be" },
30
+ ];
15
31
  /** The neutral slot a category gets when nothing else applies. */
16
32
  exports.DEFAULT_CATEGORY_SLOT = 8;
17
33
  /** Slot for the n-th category in tree order (0-based): 1…8, then 1… again. */
18
34
  function categorySlotForIndex(index) {
19
35
  return ((((index % 8) + 8) % 8) + 1);
20
36
  }
21
- /** CSS variables of the palette: --wb-cat-N (swatch), -tint, -slot, -ink. */
22
- exports.CATEGORY_PALETTE_TOKENS = Object.fromEntries(exports.CATEGORY_PALETTE.flatMap((c, i) => [
37
+ const paletteTokens = (palette) => Object.fromEntries(palette.flatMap((c, i) => [
23
38
  [`--wb-cat-${i + 1}`, c.swatch],
24
39
  [`--wb-cat-${i + 1}-tint`, c.tint],
25
40
  [`--wb-cat-${i + 1}-slot`, c.slot],
26
41
  [`--wb-cat-${i + 1}-ink`, c.ink],
27
42
  ]));
43
+ /** CSS variables of the palette: --wb-cat-N (swatch), -tint, -slot, -ink. */
44
+ exports.CATEGORY_PALETTE_TOKENS = paletteTokens(exports.CATEGORY_PALETTE);
45
+ /** The same variables with the dark values. */
46
+ exports.CATEGORY_PALETTE_DARK_TOKENS = paletteTokens(exports.CATEGORY_PALETTE_DARK);
package/dist/cjs/index.js CHANGED
@@ -1,14 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.productImageKeptArea = exports.productImageAspectRatio = exports.PRODUCT_IMAGE_FRAMES = exports.categorySlotForIndex = exports.DEFAULT_CATEGORY_SLOT = exports.CATEGORY_PALETTE_TOKENS = exports.CATEGORY_PALETTE = exports.posTokensCss = exports.POS_WIDE_MIN_WIDTH = exports.POS_TOKENS = exports.RunnerLoader = void 0;
3
+ exports.productImageKeptArea = exports.productImageAspectRatio = exports.PRODUCT_IMAGE_FRAMES = exports.categorySlotForIndex = exports.DEFAULT_CATEGORY_SLOT = exports.CATEGORY_PALETTE_TOKENS = exports.CATEGORY_PALETTE_DARK_TOKENS = exports.CATEGORY_PALETTE_DARK = exports.CATEGORY_PALETTE = exports.posTokensCss = exports.posDarkTokensCss = exports.POS_WIDE_MIN_WIDTH = exports.POS_TOKENS = exports.POS_DARK_TOKENS = exports.POS_DARK_CLASS = exports.RunnerLoader = void 0;
4
4
  var RunnerLoader_js_1 = require("./RunnerLoader.js");
5
5
  Object.defineProperty(exports, "RunnerLoader", { enumerable: true, get: function () { return RunnerLoader_js_1.RunnerLoader; } });
6
6
  var posTokens_js_1 = require("./posTokens.js");
7
+ Object.defineProperty(exports, "POS_DARK_CLASS", { enumerable: true, get: function () { return posTokens_js_1.POS_DARK_CLASS; } });
8
+ Object.defineProperty(exports, "POS_DARK_TOKENS", { enumerable: true, get: function () { return posTokens_js_1.POS_DARK_TOKENS; } });
7
9
  Object.defineProperty(exports, "POS_TOKENS", { enumerable: true, get: function () { return posTokens_js_1.POS_TOKENS; } });
8
10
  Object.defineProperty(exports, "POS_WIDE_MIN_WIDTH", { enumerable: true, get: function () { return posTokens_js_1.POS_WIDE_MIN_WIDTH; } });
11
+ Object.defineProperty(exports, "posDarkTokensCss", { enumerable: true, get: function () { return posTokens_js_1.posDarkTokensCss; } });
9
12
  Object.defineProperty(exports, "posTokensCss", { enumerable: true, get: function () { return posTokens_js_1.posTokensCss; } });
10
13
  var categoryPalette_js_1 = require("./categoryPalette.js");
11
14
  Object.defineProperty(exports, "CATEGORY_PALETTE", { enumerable: true, get: function () { return categoryPalette_js_1.CATEGORY_PALETTE; } });
15
+ Object.defineProperty(exports, "CATEGORY_PALETTE_DARK", { enumerable: true, get: function () { return categoryPalette_js_1.CATEGORY_PALETTE_DARK; } });
16
+ Object.defineProperty(exports, "CATEGORY_PALETTE_DARK_TOKENS", { enumerable: true, get: function () { return categoryPalette_js_1.CATEGORY_PALETTE_DARK_TOKENS; } });
12
17
  Object.defineProperty(exports, "CATEGORY_PALETTE_TOKENS", { enumerable: true, get: function () { return categoryPalette_js_1.CATEGORY_PALETTE_TOKENS; } });
13
18
  Object.defineProperty(exports, "DEFAULT_CATEGORY_SLOT", { enumerable: true, get: function () { return categoryPalette_js_1.DEFAULT_CATEGORY_SLOT; } });
14
19
  Object.defineProperty(exports, "categorySlotForIndex", { enumerable: true, get: function () { return categoryPalette_js_1.categorySlotForIndex; } });
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.POS_WIDE_MIN_WIDTH = exports.POS_TOKENS = void 0;
3
+ exports.POS_DARK_CLASS = exports.POS_DARK_TOKENS = exports.POS_WIDE_MIN_WIDTH = exports.POS_TOKENS = void 0;
4
4
  exports.posTokensCss = posTokensCss;
5
+ exports.posDarkTokensCss = posDarkTokensCss;
5
6
  /**
6
7
  * The WelcomeBack POS look: the CSS custom properties pos-ui paints with, and
7
8
  * the ones the dashboard's POS / KDS previews copy. One source, so a preview
@@ -56,6 +57,15 @@ exports.POS_TOKENS = {
56
57
  "--wb-primary-soft": "rgba(23, 23, 23, 0.15)",
57
58
  // The keyboard focus ring (3 px), pos-ui `.focus-ring`. Paper C1.
58
59
  "--wb-ring-soft": "rgba(23, 23, 23, 0.5)",
60
+ // Text and marks on a photo or a category colour (square tiles, owner
61
+ // 2026-10-21). The same in light and dark: a photo does not change. The
62
+ // scrim keeps white text >= 4.5:1 even on a pure white photo, up to half
63
+ // the tile height (two-line name).
64
+ "--wb-on-media": "#ffffff",
65
+ "--wb-on-media-muted": "rgba(255, 255, 255, 0.85)",
66
+ "--wb-media-ink": "#000000",
67
+ "--wb-media-veil": "rgba(255, 255, 255, 0.35)",
68
+ "--wb-media-scrim": "linear-gradient(to top, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0.55) 50%, rgba(0, 0, 0, 0) 85%)",
59
69
  // The brand tone. Same as the primary: the POS is monochrome.
60
70
  "--wb-brand": "#171717",
61
71
  // 12 px everywhere like the dashboard, 6 px for items inside a control
@@ -94,8 +104,57 @@ exports.POS_TOKENS = {
94
104
  * read a CSS variable, so it is a number for the app's breakpoint config.
95
105
  */
96
106
  exports.POS_WIDE_MIN_WIDTH = 600;
107
+ /**
108
+ * The dark look (Paper F4, direction A «Warm night», owner 2026-10-21): a
109
+ * value for every colour token; sizes, radius and type do not change. Dark
110
+ * is a per-device setting, never automatic. pos-ui puts `.theme-dark` on
111
+ * <body>; the generated stylesheet carries that rule.
112
+ */
113
+ exports.POS_DARK_TOKENS = {
114
+ "--wb-primary": "#f2f0ea",
115
+ "--wb-accent": "#f2f0ea",
116
+ "--wb-on-accent": "#121110",
117
+ "--wb-on-accent-soft": "rgba(18, 17, 16, 0.12)",
118
+ "--wb-accent-soft": "rgba(242, 240, 234, 0.08)",
119
+ "--wb-bg": "#121110",
120
+ "--wb-surface": "#1a1917",
121
+ "--wb-surface2": "#24221f",
122
+ "--wb-ticket-bg": "#1f1d1a",
123
+ "--wb-border": "#3b3832",
124
+ "--wb-veil": "rgba(18, 17, 16, 0.72)",
125
+ "--wb-text": "#f2f0ea",
126
+ "--wb-muted": "#b8b3a7",
127
+ "--wb-success": "#22c55e",
128
+ "--wb-success-soft": "rgba(34, 197, 94, 0.15)",
129
+ "--wb-success-strong": "#4ade80",
130
+ "--wb-amber": "#f59e0b",
131
+ "--wb-amber-soft": "rgba(245, 158, 11, 0.15)",
132
+ "--wb-error": "#ef4444",
133
+ "--wb-error-soft": "rgba(239, 68, 68, 0.15)",
134
+ "--wb-amber-strong": "#fbbf24",
135
+ "--wb-error-strong": "#f87171",
136
+ "--wb-soldout": "#413115",
137
+ "--wb-soldout-ink": "#fcd34d",
138
+ "--wb-faint": "#8f8a7e",
139
+ "--wb-primary-soft": "rgba(242, 240, 234, 0.15)",
140
+ "--wb-ring-soft": "rgba(242, 240, 234, 0.5)",
141
+ "--wb-brand": "#f2f0ea",
142
+ // A photo looks the same in both looks, so the marks on it do too.
143
+ "--wb-on-media": "#ffffff",
144
+ "--wb-on-media-muted": "rgba(255, 255, 255, 0.85)",
145
+ "--wb-media-ink": "#000000",
146
+ "--wb-media-veil": "rgba(255, 255, 255, 0.35)",
147
+ ...categoryPalette_js_1.CATEGORY_PALETTE_DARK_TOKENS,
148
+ };
149
+ /** The class pos-ui puts on <body> for the dark look. */
150
+ exports.POS_DARK_CLASS = "theme-dark";
97
151
  /** The tokens as one CSS rule, for apps that load them as a stylesheet. */
98
152
  function posTokensCss(selector = ":root") {
99
153
  const lines = Object.entries(exports.POS_TOKENS).map(([name, value]) => ` ${name}: ${value};`);
100
154
  return `${selector} {\n${lines.join("\n")}\n}\n`;
101
155
  }
156
+ /** The dark values as one CSS rule (default selector `.theme-dark`). */
157
+ function posDarkTokensCss(selector = `.${exports.POS_DARK_CLASS}`) {
158
+ const lines = Object.entries(exports.POS_DARK_TOKENS).map(([name, value]) => ` ${name}: ${value};`);
159
+ return `${selector} {\n${lines.join("\n")}\n}\n`;
160
+ }
@@ -65,12 +65,67 @@ export declare const CATEGORY_PALETTE: readonly [{
65
65
  readonly slot: "#e5e3df";
66
66
  readonly ink: "#685f52";
67
67
  }];
68
+ /**
69
+ * The same 8 colours for the dark look (F4 A «Warm night», owner 2026-10-21),
70
+ * slot for slot. Tint and slot are the swatch mixed into the dark card
71
+ * (#1a1917) at 20 % and 32 %; ink is the swatch mixed with white at 55 %;
72
+ * the swatch is 18 % lighter. Text on a dark tint is #f2f0ea and #b8b3a7.
73
+ */
74
+ export declare const CATEGORY_PALETTE_DARK: readonly [{
75
+ readonly name: "Dorado";
76
+ readonly swatch: "#b19451";
77
+ readonly tint: "#352d1b";
78
+ readonly slot: "#45391d";
79
+ readonly ink: "#d4c4a0";
80
+ }, {
81
+ readonly name: "Azul mar";
82
+ readonly swatch: "#4798b6";
83
+ readonly tint: "#1b2e34";
84
+ readonly slot: "#1c3a45";
85
+ readonly ink: "#9ac6d7";
86
+ }, {
87
+ readonly name: "Terracota";
88
+ readonly swatch: "#c2785f";
89
+ readonly tint: "#39261e";
90
+ readonly slot: "#4c2e23";
91
+ readonly ink: "#deb5a7";
92
+ }, {
93
+ readonly name: "Verde";
94
+ readonly swatch: "#66a279";
95
+ readonly tint: "#233025";
96
+ readonly slot: "#283e2d";
97
+ readonly ink: "#abccb6";
98
+ }, {
99
+ readonly name: "Ciruela";
100
+ readonly swatch: "#ae729e";
101
+ readonly tint: "#34252e";
102
+ readonly slot: "#442c3b";
103
+ readonly ink: "#d2b2ca";
104
+ }, {
105
+ readonly name: "Azul";
106
+ readonly swatch: "#6289c2";
107
+ readonly tint: "#212a37";
108
+ readonly slot: "#26354a";
109
+ readonly ink: "#a9bede";
110
+ }, {
111
+ readonly name: "Pizarra";
112
+ readonly swatch: "#7b9297";
113
+ readonly tint: "#282c2c";
114
+ readonly slot: "#303839";
115
+ readonly ink: "#b7c3c6";
116
+ }, {
117
+ readonly name: "Piedra";
118
+ readonly swatch: "#9f9688";
119
+ readonly tint: "#302d28";
120
+ readonly slot: "#3e3a33";
121
+ readonly ink: "#cac5be";
122
+ }];
68
123
  export type CategorySlot = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
69
124
  /** The neutral slot a category gets when nothing else applies. */
70
125
  export declare const DEFAULT_CATEGORY_SLOT: CategorySlot;
71
126
  /** Slot for the n-th category in tree order (0-based): 1…8, then 1… again. */
72
127
  export declare function categorySlotForIndex(index: number): CategorySlot;
73
- type CategoryVar = `--wb-cat-${CategorySlot}${"" | "-tint" | "-slot" | "-ink"}`;
74
128
  /** CSS variables of the palette: --wb-cat-N (swatch), -tint, -slot, -ink. */
75
- export declare const CATEGORY_PALETTE_TOKENS: Record<CategoryVar, string>;
76
- export {};
129
+ export declare const CATEGORY_PALETTE_TOKENS: Record<"--wb-cat-1" | "--wb-cat-1-tint" | "--wb-cat-1-slot" | "--wb-cat-1-ink" | "--wb-cat-8" | "--wb-cat-8-tint" | "--wb-cat-8-slot" | "--wb-cat-8-ink" | "--wb-cat-2" | "--wb-cat-2-tint" | "--wb-cat-2-slot" | "--wb-cat-2-ink" | "--wb-cat-3" | "--wb-cat-3-tint" | "--wb-cat-3-slot" | "--wb-cat-3-ink" | "--wb-cat-4" | "--wb-cat-4-tint" | "--wb-cat-4-slot" | "--wb-cat-4-ink" | "--wb-cat-5" | "--wb-cat-5-tint" | "--wb-cat-5-slot" | "--wb-cat-5-ink" | "--wb-cat-6" | "--wb-cat-6-tint" | "--wb-cat-6-slot" | "--wb-cat-6-ink" | "--wb-cat-7" | "--wb-cat-7-tint" | "--wb-cat-7-slot" | "--wb-cat-7-ink", string>;
130
+ /** The same variables with the dark values. */
131
+ export declare const CATEGORY_PALETTE_DARK_TOKENS: Record<"--wb-cat-1" | "--wb-cat-1-tint" | "--wb-cat-1-slot" | "--wb-cat-1-ink" | "--wb-cat-8" | "--wb-cat-8-tint" | "--wb-cat-8-slot" | "--wb-cat-8-ink" | "--wb-cat-2" | "--wb-cat-2-tint" | "--wb-cat-2-slot" | "--wb-cat-2-ink" | "--wb-cat-3" | "--wb-cat-3-tint" | "--wb-cat-3-slot" | "--wb-cat-3-ink" | "--wb-cat-4" | "--wb-cat-4-tint" | "--wb-cat-4-slot" | "--wb-cat-4-ink" | "--wb-cat-5" | "--wb-cat-5-tint" | "--wb-cat-5-slot" | "--wb-cat-5-ink" | "--wb-cat-6" | "--wb-cat-6-tint" | "--wb-cat-6-slot" | "--wb-cat-6-ink" | "--wb-cat-7" | "--wb-cat-7-tint" | "--wb-cat-7-slot" | "--wb-cat-7-ink", string>;
@@ -8,16 +8,35 @@ export const CATEGORY_PALETTE = [
8
8
  { name: "Pizarra", swatch: "#5e7a80", tint: "#eaeeee", slot: "#dce2e3", ink: "#465c60" },
9
9
  { name: "Piedra", swatch: "#8a7f6e", tint: "#f0eeec", slot: "#e5e3df", ink: "#685f52" },
10
10
  ];
11
+ /**
12
+ * The same 8 colours for the dark look (F4 A «Warm night», owner 2026-10-21),
13
+ * slot for slot. Tint and slot are the swatch mixed into the dark card
14
+ * (#1a1917) at 20 % and 32 %; ink is the swatch mixed with white at 55 %;
15
+ * the swatch is 18 % lighter. Text on a dark tint is #f2f0ea and #b8b3a7.
16
+ */
17
+ export const CATEGORY_PALETTE_DARK = [
18
+ { name: "Dorado", swatch: "#b19451", tint: "#352d1b", slot: "#45391d", ink: "#d4c4a0" },
19
+ { name: "Azul mar", swatch: "#4798b6", tint: "#1b2e34", slot: "#1c3a45", ink: "#9ac6d7" },
20
+ { name: "Terracota", swatch: "#c2785f", tint: "#39261e", slot: "#4c2e23", ink: "#deb5a7" },
21
+ { name: "Verde", swatch: "#66a279", tint: "#233025", slot: "#283e2d", ink: "#abccb6" },
22
+ { name: "Ciruela", swatch: "#ae729e", tint: "#34252e", slot: "#442c3b", ink: "#d2b2ca" },
23
+ { name: "Azul", swatch: "#6289c2", tint: "#212a37", slot: "#26354a", ink: "#a9bede" },
24
+ { name: "Pizarra", swatch: "#7b9297", tint: "#282c2c", slot: "#303839", ink: "#b7c3c6" },
25
+ { name: "Piedra", swatch: "#9f9688", tint: "#302d28", slot: "#3e3a33", ink: "#cac5be" },
26
+ ];
11
27
  /** The neutral slot a category gets when nothing else applies. */
12
28
  export const DEFAULT_CATEGORY_SLOT = 8;
13
29
  /** Slot for the n-th category in tree order (0-based): 1…8, then 1… again. */
14
30
  export function categorySlotForIndex(index) {
15
31
  return ((((index % 8) + 8) % 8) + 1);
16
32
  }
17
- /** CSS variables of the palette: --wb-cat-N (swatch), -tint, -slot, -ink. */
18
- export const CATEGORY_PALETTE_TOKENS = Object.fromEntries(CATEGORY_PALETTE.flatMap((c, i) => [
33
+ const paletteTokens = (palette) => Object.fromEntries(palette.flatMap((c, i) => [
19
34
  [`--wb-cat-${i + 1}`, c.swatch],
20
35
  [`--wb-cat-${i + 1}-tint`, c.tint],
21
36
  [`--wb-cat-${i + 1}-slot`, c.slot],
22
37
  [`--wb-cat-${i + 1}-ink`, c.ink],
23
38
  ]));
39
+ /** CSS variables of the palette: --wb-cat-N (swatch), -tint, -slot, -ink. */
40
+ export const CATEGORY_PALETTE_TOKENS = paletteTokens(CATEGORY_PALETTE);
41
+ /** The same variables with the dark values. */
42
+ export const CATEGORY_PALETTE_DARK_TOKENS = paletteTokens(CATEGORY_PALETTE_DARK);
@@ -1,8 +1,8 @@
1
1
  export { RunnerLoader } from "./RunnerLoader.js";
2
2
  export type { RunnerLoaderProps } from "./RunnerLoader.js";
3
- export { POS_TOKENS, POS_WIDE_MIN_WIDTH, posTokensCss } from "./posTokens.js";
3
+ export { POS_DARK_CLASS, POS_DARK_TOKENS, POS_TOKENS, POS_WIDE_MIN_WIDTH, posDarkTokensCss, posTokensCss, } from "./posTokens.js";
4
4
  export type { PosToken } from "./posTokens.js";
5
- export { CATEGORY_PALETTE, CATEGORY_PALETTE_TOKENS, DEFAULT_CATEGORY_SLOT, categorySlotForIndex, } from "./categoryPalette.js";
5
+ export { CATEGORY_PALETTE, CATEGORY_PALETTE_DARK, CATEGORY_PALETTE_DARK_TOKENS, CATEGORY_PALETTE_TOKENS, DEFAULT_CATEGORY_SLOT, categorySlotForIndex, } from "./categoryPalette.js";
6
6
  export type { CategoryColour, CategorySlot } from "./categoryPalette.js";
7
7
  export { PRODUCT_IMAGE_FRAMES, productImageAspectRatio, productImageKeptArea, } from "./productImageFrames.js";
8
8
  export type { ProductImageFrame } from "./productImageFrames.js";
package/dist/esm/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { RunnerLoader } from "./RunnerLoader.js";
2
- export { POS_TOKENS, POS_WIDE_MIN_WIDTH, posTokensCss } from "./posTokens.js";
3
- export { CATEGORY_PALETTE, CATEGORY_PALETTE_TOKENS, DEFAULT_CATEGORY_SLOT, categorySlotForIndex, } from "./categoryPalette.js";
2
+ export { POS_DARK_CLASS, POS_DARK_TOKENS, POS_TOKENS, POS_WIDE_MIN_WIDTH, posDarkTokensCss, posTokensCss, } from "./posTokens.js";
3
+ export { CATEGORY_PALETTE, CATEGORY_PALETTE_DARK, CATEGORY_PALETTE_DARK_TOKENS, CATEGORY_PALETTE_TOKENS, DEFAULT_CATEGORY_SLOT, categorySlotForIndex, } from "./categoryPalette.js";
4
4
  export { PRODUCT_IMAGE_FRAMES, productImageAspectRatio, productImageKeptArea, } from "./productImageFrames.js";
@@ -58,6 +58,11 @@ export declare const POS_TOKENS: {
58
58
  readonly "--wb-faint": "#6e6a5e";
59
59
  readonly "--wb-primary-soft": "rgba(23, 23, 23, 0.15)";
60
60
  readonly "--wb-ring-soft": "rgba(23, 23, 23, 0.5)";
61
+ readonly "--wb-on-media": "#ffffff";
62
+ readonly "--wb-on-media-muted": "rgba(255, 255, 255, 0.85)";
63
+ readonly "--wb-media-ink": "#000000";
64
+ readonly "--wb-media-veil": "rgba(255, 255, 255, 0.35)";
65
+ readonly "--wb-media-scrim": "linear-gradient(to top, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0.55) 50%, rgba(0, 0, 0, 0) 85%)";
61
66
  readonly "--wb-brand": "#171717";
62
67
  readonly "--wb-radius": "12px";
63
68
  readonly "--wb-radius-lg": "12px";
@@ -86,5 +91,81 @@ export type PosToken = keyof typeof POS_TOKENS;
86
91
  * read a CSS variable, so it is a number for the app's breakpoint config.
87
92
  */
88
93
  export declare const POS_WIDE_MIN_WIDTH = 600;
94
+ /**
95
+ * The dark look (Paper F4, direction A «Warm night», owner 2026-10-21): a
96
+ * value for every colour token; sizes, radius and type do not change. Dark
97
+ * is a per-device setting, never automatic. pos-ui puts `.theme-dark` on
98
+ * <body>; the generated stylesheet carries that rule.
99
+ */
100
+ export declare const POS_DARK_TOKENS: {
101
+ readonly "--wb-cat-1": string;
102
+ readonly "--wb-cat-1-tint": string;
103
+ readonly "--wb-cat-1-slot": string;
104
+ readonly "--wb-cat-1-ink": string;
105
+ readonly "--wb-cat-8": string;
106
+ readonly "--wb-cat-8-tint": string;
107
+ readonly "--wb-cat-8-slot": string;
108
+ readonly "--wb-cat-8-ink": string;
109
+ readonly "--wb-cat-2": string;
110
+ readonly "--wb-cat-2-tint": string;
111
+ readonly "--wb-cat-2-slot": string;
112
+ readonly "--wb-cat-2-ink": string;
113
+ readonly "--wb-cat-3": string;
114
+ readonly "--wb-cat-3-tint": string;
115
+ readonly "--wb-cat-3-slot": string;
116
+ readonly "--wb-cat-3-ink": string;
117
+ readonly "--wb-cat-4": string;
118
+ readonly "--wb-cat-4-tint": string;
119
+ readonly "--wb-cat-4-slot": string;
120
+ readonly "--wb-cat-4-ink": string;
121
+ readonly "--wb-cat-5": string;
122
+ readonly "--wb-cat-5-tint": string;
123
+ readonly "--wb-cat-5-slot": string;
124
+ readonly "--wb-cat-5-ink": string;
125
+ readonly "--wb-cat-6": string;
126
+ readonly "--wb-cat-6-tint": string;
127
+ readonly "--wb-cat-6-slot": string;
128
+ readonly "--wb-cat-6-ink": string;
129
+ readonly "--wb-cat-7": string;
130
+ readonly "--wb-cat-7-tint": string;
131
+ readonly "--wb-cat-7-slot": string;
132
+ readonly "--wb-cat-7-ink": string;
133
+ readonly "--wb-primary": "#f2f0ea";
134
+ readonly "--wb-accent": "#f2f0ea";
135
+ readonly "--wb-on-accent": "#121110";
136
+ readonly "--wb-on-accent-soft": "rgba(18, 17, 16, 0.12)";
137
+ readonly "--wb-accent-soft": "rgba(242, 240, 234, 0.08)";
138
+ readonly "--wb-bg": "#121110";
139
+ readonly "--wb-surface": "#1a1917";
140
+ readonly "--wb-surface2": "#24221f";
141
+ readonly "--wb-ticket-bg": "#1f1d1a";
142
+ readonly "--wb-border": "#3b3832";
143
+ readonly "--wb-veil": "rgba(18, 17, 16, 0.72)";
144
+ readonly "--wb-text": "#f2f0ea";
145
+ readonly "--wb-muted": "#b8b3a7";
146
+ readonly "--wb-success": "#22c55e";
147
+ readonly "--wb-success-soft": "rgba(34, 197, 94, 0.15)";
148
+ readonly "--wb-success-strong": "#4ade80";
149
+ readonly "--wb-amber": "#f59e0b";
150
+ readonly "--wb-amber-soft": "rgba(245, 158, 11, 0.15)";
151
+ readonly "--wb-error": "#ef4444";
152
+ readonly "--wb-error-soft": "rgba(239, 68, 68, 0.15)";
153
+ readonly "--wb-amber-strong": "#fbbf24";
154
+ readonly "--wb-error-strong": "#f87171";
155
+ readonly "--wb-soldout": "#413115";
156
+ readonly "--wb-soldout-ink": "#fcd34d";
157
+ readonly "--wb-faint": "#8f8a7e";
158
+ readonly "--wb-primary-soft": "rgba(242, 240, 234, 0.15)";
159
+ readonly "--wb-ring-soft": "rgba(242, 240, 234, 0.5)";
160
+ readonly "--wb-brand": "#f2f0ea";
161
+ readonly "--wb-on-media": "#ffffff";
162
+ readonly "--wb-on-media-muted": "rgba(255, 255, 255, 0.85)";
163
+ readonly "--wb-media-ink": "#000000";
164
+ readonly "--wb-media-veil": "rgba(255, 255, 255, 0.35)";
165
+ };
166
+ /** The class pos-ui puts on <body> for the dark look. */
167
+ export declare const POS_DARK_CLASS = "theme-dark";
89
168
  /** The tokens as one CSS rule, for apps that load them as a stylesheet. */
90
169
  export declare function posTokensCss(selector?: string): string;
170
+ /** The dark values as one CSS rule (default selector `.theme-dark`). */
171
+ export declare function posDarkTokensCss(selector?: string): string;
@@ -9,7 +9,7 @@
9
9
  * pos-ui imports the generated `@welcome-back/brand/pos-tokens.css`; React code
10
10
  * reads `POS_TOKENS`.
11
11
  */
12
- import { CATEGORY_PALETTE_TOKENS } from "./categoryPalette.js";
12
+ import { CATEGORY_PALETTE_DARK_TOKENS, CATEGORY_PALETTE_TOKENS } from "./categoryPalette.js";
13
13
  export const POS_TOKENS = {
14
14
  // Monochrome chrome on a warm ivory canvas (pos-ui DESIGN.md §3).
15
15
  "--wb-primary": "#171717",
@@ -52,6 +52,15 @@ export const POS_TOKENS = {
52
52
  "--wb-primary-soft": "rgba(23, 23, 23, 0.15)",
53
53
  // The keyboard focus ring (3 px), pos-ui `.focus-ring`. Paper C1.
54
54
  "--wb-ring-soft": "rgba(23, 23, 23, 0.5)",
55
+ // Text and marks on a photo or a category colour (square tiles, owner
56
+ // 2026-10-21). The same in light and dark: a photo does not change. The
57
+ // scrim keeps white text >= 4.5:1 even on a pure white photo, up to half
58
+ // the tile height (two-line name).
59
+ "--wb-on-media": "#ffffff",
60
+ "--wb-on-media-muted": "rgba(255, 255, 255, 0.85)",
61
+ "--wb-media-ink": "#000000",
62
+ "--wb-media-veil": "rgba(255, 255, 255, 0.35)",
63
+ "--wb-media-scrim": "linear-gradient(to top, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0.55) 50%, rgba(0, 0, 0, 0) 85%)",
55
64
  // The brand tone. Same as the primary: the POS is monochrome.
56
65
  "--wb-brand": "#171717",
57
66
  // 12 px everywhere like the dashboard, 6 px for items inside a control
@@ -90,8 +99,57 @@ export const POS_TOKENS = {
90
99
  * read a CSS variable, so it is a number for the app's breakpoint config.
91
100
  */
92
101
  export const POS_WIDE_MIN_WIDTH = 600;
102
+ /**
103
+ * The dark look (Paper F4, direction A «Warm night», owner 2026-10-21): a
104
+ * value for every colour token; sizes, radius and type do not change. Dark
105
+ * is a per-device setting, never automatic. pos-ui puts `.theme-dark` on
106
+ * <body>; the generated stylesheet carries that rule.
107
+ */
108
+ export const POS_DARK_TOKENS = {
109
+ "--wb-primary": "#f2f0ea",
110
+ "--wb-accent": "#f2f0ea",
111
+ "--wb-on-accent": "#121110",
112
+ "--wb-on-accent-soft": "rgba(18, 17, 16, 0.12)",
113
+ "--wb-accent-soft": "rgba(242, 240, 234, 0.08)",
114
+ "--wb-bg": "#121110",
115
+ "--wb-surface": "#1a1917",
116
+ "--wb-surface2": "#24221f",
117
+ "--wb-ticket-bg": "#1f1d1a",
118
+ "--wb-border": "#3b3832",
119
+ "--wb-veil": "rgba(18, 17, 16, 0.72)",
120
+ "--wb-text": "#f2f0ea",
121
+ "--wb-muted": "#b8b3a7",
122
+ "--wb-success": "#22c55e",
123
+ "--wb-success-soft": "rgba(34, 197, 94, 0.15)",
124
+ "--wb-success-strong": "#4ade80",
125
+ "--wb-amber": "#f59e0b",
126
+ "--wb-amber-soft": "rgba(245, 158, 11, 0.15)",
127
+ "--wb-error": "#ef4444",
128
+ "--wb-error-soft": "rgba(239, 68, 68, 0.15)",
129
+ "--wb-amber-strong": "#fbbf24",
130
+ "--wb-error-strong": "#f87171",
131
+ "--wb-soldout": "#413115",
132
+ "--wb-soldout-ink": "#fcd34d",
133
+ "--wb-faint": "#8f8a7e",
134
+ "--wb-primary-soft": "rgba(242, 240, 234, 0.15)",
135
+ "--wb-ring-soft": "rgba(242, 240, 234, 0.5)",
136
+ "--wb-brand": "#f2f0ea",
137
+ // A photo looks the same in both looks, so the marks on it do too.
138
+ "--wb-on-media": "#ffffff",
139
+ "--wb-on-media-muted": "rgba(255, 255, 255, 0.85)",
140
+ "--wb-media-ink": "#000000",
141
+ "--wb-media-veil": "rgba(255, 255, 255, 0.35)",
142
+ ...CATEGORY_PALETTE_DARK_TOKENS,
143
+ };
144
+ /** The class pos-ui puts on <body> for the dark look. */
145
+ export const POS_DARK_CLASS = "theme-dark";
93
146
  /** The tokens as one CSS rule, for apps that load them as a stylesheet. */
94
147
  export function posTokensCss(selector = ":root") {
95
148
  const lines = Object.entries(POS_TOKENS).map(([name, value]) => ` ${name}: ${value};`);
96
149
  return `${selector} {\n${lines.join("\n")}\n}\n`;
97
150
  }
151
+ /** The dark values as one CSS rule (default selector `.theme-dark`). */
152
+ export function posDarkTokensCss(selector = `.${POS_DARK_CLASS}`) {
153
+ const lines = Object.entries(POS_DARK_TOKENS).map(([name, value]) => ` ${name}: ${value};`);
154
+ return `${selector} {\n${lines.join("\n")}\n}\n`;
155
+ }
@@ -27,6 +27,11 @@
27
27
  --wb-faint: #6e6a5e;
28
28
  --wb-primary-soft: rgba(23, 23, 23, 0.15);
29
29
  --wb-ring-soft: rgba(23, 23, 23, 0.5);
30
+ --wb-on-media: #ffffff;
31
+ --wb-on-media-muted: rgba(255, 255, 255, 0.85);
32
+ --wb-media-ink: #000000;
33
+ --wb-media-veil: rgba(255, 255, 255, 0.35);
34
+ --wb-media-scrim: linear-gradient(to top, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0.55) 50%, rgba(0, 0, 0, 0) 85%);
30
35
  --wb-brand: #171717;
31
36
  --wb-radius: 12px;
32
37
  --wb-radius-lg: 12px;
@@ -80,3 +85,69 @@
80
85
  --wb-cat-8-slot: #e5e3df;
81
86
  --wb-cat-8-ink: #685f52;
82
87
  }
88
+ .theme-dark {
89
+ --wb-primary: #f2f0ea;
90
+ --wb-accent: #f2f0ea;
91
+ --wb-on-accent: #121110;
92
+ --wb-on-accent-soft: rgba(18, 17, 16, 0.12);
93
+ --wb-accent-soft: rgba(242, 240, 234, 0.08);
94
+ --wb-bg: #121110;
95
+ --wb-surface: #1a1917;
96
+ --wb-surface2: #24221f;
97
+ --wb-ticket-bg: #1f1d1a;
98
+ --wb-border: #3b3832;
99
+ --wb-veil: rgba(18, 17, 16, 0.72);
100
+ --wb-text: #f2f0ea;
101
+ --wb-muted: #b8b3a7;
102
+ --wb-success: #22c55e;
103
+ --wb-success-soft: rgba(34, 197, 94, 0.15);
104
+ --wb-success-strong: #4ade80;
105
+ --wb-amber: #f59e0b;
106
+ --wb-amber-soft: rgba(245, 158, 11, 0.15);
107
+ --wb-error: #ef4444;
108
+ --wb-error-soft: rgba(239, 68, 68, 0.15);
109
+ --wb-amber-strong: #fbbf24;
110
+ --wb-error-strong: #f87171;
111
+ --wb-soldout: #413115;
112
+ --wb-soldout-ink: #fcd34d;
113
+ --wb-faint: #8f8a7e;
114
+ --wb-primary-soft: rgba(242, 240, 234, 0.15);
115
+ --wb-ring-soft: rgba(242, 240, 234, 0.5);
116
+ --wb-brand: #f2f0ea;
117
+ --wb-on-media: #ffffff;
118
+ --wb-on-media-muted: rgba(255, 255, 255, 0.85);
119
+ --wb-media-ink: #000000;
120
+ --wb-media-veil: rgba(255, 255, 255, 0.35);
121
+ --wb-cat-1: #b19451;
122
+ --wb-cat-1-tint: #352d1b;
123
+ --wb-cat-1-slot: #45391d;
124
+ --wb-cat-1-ink: #d4c4a0;
125
+ --wb-cat-2: #4798b6;
126
+ --wb-cat-2-tint: #1b2e34;
127
+ --wb-cat-2-slot: #1c3a45;
128
+ --wb-cat-2-ink: #9ac6d7;
129
+ --wb-cat-3: #c2785f;
130
+ --wb-cat-3-tint: #39261e;
131
+ --wb-cat-3-slot: #4c2e23;
132
+ --wb-cat-3-ink: #deb5a7;
133
+ --wb-cat-4: #66a279;
134
+ --wb-cat-4-tint: #233025;
135
+ --wb-cat-4-slot: #283e2d;
136
+ --wb-cat-4-ink: #abccb6;
137
+ --wb-cat-5: #ae729e;
138
+ --wb-cat-5-tint: #34252e;
139
+ --wb-cat-5-slot: #442c3b;
140
+ --wb-cat-5-ink: #d2b2ca;
141
+ --wb-cat-6: #6289c2;
142
+ --wb-cat-6-tint: #212a37;
143
+ --wb-cat-6-slot: #26354a;
144
+ --wb-cat-6-ink: #a9bede;
145
+ --wb-cat-7: #7b9297;
146
+ --wb-cat-7-tint: #282c2c;
147
+ --wb-cat-7-slot: #303839;
148
+ --wb-cat-7-ink: #b7c3c6;
149
+ --wb-cat-8: #9f9688;
150
+ --wb-cat-8-tint: #302d28;
151
+ --wb-cat-8-slot: #3e3a33;
152
+ --wb-cat-8-ink: #cac5be;
153
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@welcome-back/brand",
3
- "version": "0.6.2",
3
+ "version": "0.7.1",
4
4
  "description": "WelcomeBack brand assets for React apps: the running-W loader and the POS design tokens.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {