@welcome-back/brand 0.1.2 → 0.2.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.
package/README.md CHANGED
@@ -18,10 +18,12 @@ Use it for loading moments that mean "on the way to the next step".
18
18
  - [Quick start](#quick-start)
19
19
  - [API](#api)
20
20
  - [Examples](#examples)
21
+ - [POS design tokens](#pos-design-tokens)
21
22
  - [Accessibility](#accessibility)
22
23
  - [Where to use it, and where not](#where-to-use-it-and-where-not)
23
24
  - [Troubleshooting](#troubleshooting)
24
- - [Release](#release)
25
+ - [Develop](#develop)
26
+ - [Publish a change](#publish-a-change)
25
27
  - [Source art](#source-art)
26
28
 
27
29
  ## Install
@@ -288,6 +290,35 @@ render(<Loading />);
288
290
  expect(screen.getByRole("img", { name: "Loading" })).toHaveClass("wb-runner");
289
291
  ```
290
292
 
293
+ ## POS design tokens
294
+
295
+ The WelcomeBack POS look: the `--wb-*` CSS custom properties that pos-ui paints
296
+ with. **The POS is not themable** (owner decision, 2026-09-28): every business
297
+ gets this same look, so nothing overrides these per tenant. The dashboard's POS
298
+ and KDS previews read the same values, so a preview cannot drift from the
299
+ register.
300
+
301
+ The source is `src/posTokens.ts`. `npm run build` also writes
302
+ `dist/pos-tokens.css` from it (`scripts/write-pos-tokens-css.mjs`), so the
303
+ stylesheet and the TS export always agree. Change a value there, never in an app.
304
+
305
+ In a stylesheet (pos-ui), before any other rule:
306
+
307
+ ```css
308
+ @import '@welcome-back/brand/pos-tokens.css';
309
+ ```
310
+
311
+ In React (the dashboard previews):
312
+
313
+ ```tsx
314
+ import { POS_TOKENS } from '@welcome-back/brand';
315
+
316
+ <div style={POS_TOKENS as React.CSSProperties}>…</div>;
317
+ POS_TOKENS['--wb-accent']; // '#171717'
318
+ ```
319
+
320
+ `posTokensCss(selector)` returns the same rule for another selector.
321
+
291
322
  ## Accessibility
292
323
 
293
324
  - **Give exactly one accessible name per loading state.** Either put `label` on
@@ -320,17 +351,146 @@ the content will appear.
320
351
  | The runner fills the whole container | No width is set | Give it a width (`w-24`, or `width` in CSS) |
321
352
  | A Tailwind arbitrary property has no effect | Tailwind does not scan the file | Check the `content` globs in the Tailwind config |
322
353
 
323
- ## Release
354
+ ## Develop
355
+
356
+ ### Repo layout
357
+
358
+ | Path | What it holds |
359
+ |---|---|
360
+ | `src/index.ts` | Public exports. Only what is exported here is part of the API. |
361
+ | `src/RunnerLoader.tsx` | The component: SVG structure, clip paths, props |
362
+ | `src/runnerCss.ts` | The animation: keyframes, timing, swing angles, reduced motion |
363
+ | `src/runnerArt.ts` | Vector data: paths, pants fills, clip polygons |
364
+ | `src/*.test.tsx` | Vitest tests (server render, no browser) |
365
+ | `scripts/mark-cjs.mjs` | Marks `dist/cjs` as CommonJS after the build |
366
+ | `.github/workflows/ci.yml` | Runs on every PR and on `main` |
367
+ | `.github/workflows/publish.yml` | Runs on a `v*` tag and publishes to npm |
368
+
369
+ ### Commands
370
+
371
+ Node 22 or later.
372
+
373
+ ```bash
374
+ npm ci # install
375
+ npm run typecheck # tsc, no output
376
+ npm test # vitest
377
+ npm run build # dist/esm (ESM + types) and dist/cjs (CommonJS)
378
+ ```
379
+
380
+ CI runs all four, then loads both builds to prove that `import` and `require`
381
+ work.
382
+
383
+ ### Try a change in an app before you publish
384
+
385
+ Pack the package and install the tarball in the app:
386
+
387
+ ```bash
388
+ # in brand/
389
+ npm run build && npm pack # writes welcome-back-brand-<version>.tgz
390
+
391
+ # in the app, for example staff-ui/
392
+ npm i ../brand/welcome-back-brand-<version>.tgz
393
+ ```
394
+
395
+ Undo it in the app with `npm i @welcome-back/brand@latest`. Do not commit the
396
+ tarball path in the app's `package.json`.
397
+
398
+ Prefer `npm pack` to `npm link`. A linked package can load a second copy of
399
+ React, and then hooks fail with "Invalid hook call".
400
+
401
+ ### Common changes
402
+
403
+ | To change | Edit | Note |
404
+ |---|---|---|
405
+ | Speed | `--wb-runner-step-default` in `src/runnerCss.ts` | Apps can already override it per use with `--wb-runner-step` |
406
+ | Stride length (swing angles) | `@keyframes wb-runner-swing-front` / `-back` in `src/runnerCss.ts` | Angles are measured from each leg drawing's rest pose. The front leg is vertical at `-36deg`, the back leg at `39deg`. Keep forward and back swings equal around those values. |
407
+ | Where the legs join the body | `@keyframes wb-runner-hip-front` / `-back` | Values are in the art's coordinates (the 1500×1500 PDF page) |
408
+ | What is hidden behind the W | `LEGS_CLIP` / `BODY_CLIP` in `src/runnerArt.ts` | See [Source art](#source-art) |
409
+ | A new prop | `src/RunnerLoader.tsx`, plus a test and a row in the props table | |
410
+ | A new asset (logo, icon, token) | A new file in `src/`, exported from `src/index.ts`, plus a test and a README section | |
411
+
412
+ After a visual change, check it in a real app with `npm pack` (above) in light
413
+ mode, dark mode and with reduced motion on.
414
+
415
+ ## Publish a change
416
+
417
+ Every change reaches npm through a PR and a tag. Nobody publishes from a laptop,
418
+ and there is no npm token: GitHub Actions publishes with npm trusted publishing.
419
+
420
+ ### 1. Pick the version
421
+
422
+ The package follows [semver](https://semver.org). Apps install it with `^`, so
423
+ they get minor and patch releases on their next install.
424
+
425
+ | Change | Bump | Example |
426
+ |---|---|---|
427
+ | Docs, tests, or a fix with no visible change | patch | `0.1.2` → `0.1.3` |
428
+ | A new prop, asset or CSS variable; a visual tweak that keeps the API | minor | `0.1.3` → `0.2.0` |
429
+ | A removed or renamed prop, export or CSS variable | major | `0.9.0` → `1.0.0` |
430
+
431
+ While the version is `0.x`, npm treats a minor bump as breaking for `^`
432
+ ranges. Apps then need an explicit update to get it.
433
+
434
+ ### 2. Open a PR with the change and the new version
435
+
436
+ ```bash
437
+ git switch main && git pull
438
+ git switch -c <type>/<short-name>
439
+ # make the change
440
+ npm version <patch|minor|major> --no-git-tag-version # edits package.json and package-lock.json
441
+ git commit -am "<type>: <what changed> (v<new version>)"
442
+ git push -u origin HEAD
443
+ gh pr create --base main
444
+ ```
445
+
446
+ CI must pass before you merge. Squash-merge the PR.
447
+
448
+ ### 3. Tag the merge commit
449
+
450
+ ```bash
451
+ git switch main && git pull
452
+ git tag v<new version> # must equal "version" in package.json
453
+ git push origin v<new version>
454
+ ```
455
+
456
+ The tag starts the Publish workflow. It:
457
+
458
+ 1. checks that the tag equals `version` in `package.json`,
459
+ 2. runs typecheck, tests and build (`prepublishOnly`),
460
+ 3. publishes to npm through trusted publishing.
461
+
462
+ Watch it with `gh run watch`, or in the repo's **Actions** tab.
463
+
464
+ ### 4. Check npm and update the apps
465
+
466
+ ```bash
467
+ npm view @welcome-back/brand version # the new version, after a few minutes
468
+ ```
469
+
470
+ A new version can take up to about 3 minutes to show on the registry. Then, in
471
+ each app:
472
+
473
+ ```bash
474
+ npm i @welcome-back/brand@latest
475
+ ```
476
+
477
+ ### If the publish fails
478
+
479
+ | Error in the workflow log | Cause | Fix |
480
+ |---|---|---|
481
+ | `Tag must match package.json version` step fails | The tag and `version` differ | Delete the tag (`git push origin :v<x>` and `git tag -d v<x>`), fix the version in a PR, tag again |
482
+ | `403 … OIDC permission denied` | The trusted publisher on npmjs.com does not allow direct publish, or its fields do not match | npmjs.com → package → Settings → Trusted Publisher: `WelcomeBackLoyalty` / `brand` / `publish.yml`, no environment, **Allow npm publish** ticked |
483
+ | `403 … cannot publish over the previously published versions` | That version is already on npm | Bump the version in a new PR and tag again. npm never allows a republish of a version. |
484
+ | Typecheck or test fails | The tagged commit is broken | Fix it in a PR, bump the version, tag again |
485
+
486
+ To rerun a failed publish without a new tag: `gh run rerun <run-id> --failed`.
324
487
 
325
- 1. Bump `version` in `package.json` in a PR, and merge it to `main`.
326
- 2. Tag the merge commit: `git tag v0.2.0 && git push origin v0.2.0`.
327
- 3. The Publish workflow checks the tag against the version, runs typecheck,
328
- tests and build, and publishes to npm.
488
+ ### npm settings (for maintainers)
329
489
 
330
- Publishing uses npm trusted publishing (GitHub OIDC): no npm token is stored.
331
- The trusted publisher is set on npmjs.com → `@welcome-back/brand` → Settings:
332
- GitHub Actions, `WelcomeBackLoyalty/brand`, workflow `publish.yml`, with
333
- **Allow npm publish** ticked.
490
+ - Org: `welcome-back`. Package access: public.
491
+ - Trusted publisher: GitHub Actions, `WelcomeBackLoyalty/brand`, `publish.yml`,
492
+ no environment, **Allow npm publish** ticked.
493
+ - No provenance: npm cannot generate it for a private GitHub repo.
334
494
 
335
495
  ## Source art
336
496
 
package/dist/cjs/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RunnerLoader = void 0;
3
+ exports.posTokensCss = exports.POS_TOKENS = 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
+ var posTokens_js_1 = require("./posTokens.js");
7
+ Object.defineProperty(exports, "POS_TOKENS", { enumerable: true, get: function () { return posTokens_js_1.POS_TOKENS; } });
8
+ Object.defineProperty(exports, "posTokensCss", { enumerable: true, get: function () { return posTokens_js_1.posTokensCss; } });
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.POS_TOKENS = void 0;
4
+ exports.posTokensCss = posTokensCss;
5
+ /**
6
+ * The WelcomeBack POS look: the CSS custom properties pos-ui paints with, and
7
+ * the ones the dashboard's POS / KDS previews copy. One source, so a preview
8
+ * can never drift from the register.
9
+ *
10
+ * The POS is not themable (owner decision, 2026-09-28): every business gets
11
+ * this same look. Nothing overrides these per tenant.
12
+ *
13
+ * pos-ui imports the generated `@welcome-back/brand/pos-tokens.css`; React code
14
+ * reads `POS_TOKENS`.
15
+ */
16
+ exports.POS_TOKENS = {
17
+ // Monochrome chrome on a warm ivory canvas (pos-ui DESIGN.md §3).
18
+ "--wb-primary": "#171717",
19
+ "--wb-accent": "#171717",
20
+ "--wb-on-accent": "#ffffff",
21
+ // Tonal fill for hover/selected. A `-soft` token exists because the theme
22
+ // tokens are complete colors — Tailwind's `/NN` alpha modifier on them
23
+ // compiles to an invalid rgb() and renders transparent.
24
+ "--wb-accent-soft": "rgba(23, 23, 23, 0.06)",
25
+ // Warm ivory neutrals — the identity. Never cool gray.
26
+ "--wb-bg": "#f6f4e9",
27
+ "--wb-surface": "#ffffff",
28
+ "--wb-surface2": "#f2f1ec",
29
+ "--wb-ticket-bg": "#f2f1ec",
30
+ "--wb-border": "#e5e2d6",
31
+ // Translucent ivory veil for overlays (e.g. "Agotado").
32
+ "--wb-veil": "rgba(246, 244, 233, 0.72)",
33
+ "--wb-text": "#000000",
34
+ "--wb-muted": "#404040",
35
+ "--wb-success": "#22c55e",
36
+ "--wb-amber": "#f59e0b",
37
+ "--wb-amber-soft": "rgba(245, 158, 11, 0.15)",
38
+ "--wb-error": "#ef4444",
39
+ "--wb-error-soft": "rgba(239, 68, 68, 0.15)",
40
+ "--wb-primary-soft": "rgba(23, 23, 23, 0.15)",
41
+ // The brand tone. Same as the primary: the POS is monochrome.
42
+ "--wb-brand": "#171717",
43
+ "--wb-radius": "12px",
44
+ "--wb-radius-lg": "16px",
45
+ "--wb-font": "'Montserrat'",
46
+ };
47
+ /** The tokens as one CSS rule, for apps that load them as a stylesheet. */
48
+ function posTokensCss(selector = ":root") {
49
+ const lines = Object.entries(exports.POS_TOKENS).map(([name, value]) => ` ${name}: ${value};`);
50
+ return `${selector} {\n${lines.join("\n")}\n}\n`;
51
+ }
@@ -1,2 +1,4 @@
1
1
  export { RunnerLoader } from "./RunnerLoader.js";
2
2
  export type { RunnerLoaderProps } from "./RunnerLoader.js";
3
+ export { POS_TOKENS, posTokensCss } from "./posTokens.js";
4
+ export type { PosToken } from "./posTokens.js";
package/dist/esm/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export { RunnerLoader } from "./RunnerLoader.js";
2
+ export { POS_TOKENS, posTokensCss } from "./posTokens.js";
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The WelcomeBack POS look: the CSS custom properties pos-ui paints with, and
3
+ * the ones the dashboard's POS / KDS previews copy. One source, so a preview
4
+ * can never drift from the register.
5
+ *
6
+ * The POS is not themable (owner decision, 2026-09-28): every business gets
7
+ * this same look. Nothing overrides these per tenant.
8
+ *
9
+ * pos-ui imports the generated `@welcome-back/brand/pos-tokens.css`; React code
10
+ * reads `POS_TOKENS`.
11
+ */
12
+ export declare const POS_TOKENS: {
13
+ readonly "--wb-primary": "#171717";
14
+ readonly "--wb-accent": "#171717";
15
+ readonly "--wb-on-accent": "#ffffff";
16
+ readonly "--wb-accent-soft": "rgba(23, 23, 23, 0.06)";
17
+ readonly "--wb-bg": "#f6f4e9";
18
+ readonly "--wb-surface": "#ffffff";
19
+ readonly "--wb-surface2": "#f2f1ec";
20
+ readonly "--wb-ticket-bg": "#f2f1ec";
21
+ readonly "--wb-border": "#e5e2d6";
22
+ readonly "--wb-veil": "rgba(246, 244, 233, 0.72)";
23
+ readonly "--wb-text": "#000000";
24
+ readonly "--wb-muted": "#404040";
25
+ readonly "--wb-success": "#22c55e";
26
+ readonly "--wb-amber": "#f59e0b";
27
+ readonly "--wb-amber-soft": "rgba(245, 158, 11, 0.15)";
28
+ readonly "--wb-error": "#ef4444";
29
+ readonly "--wb-error-soft": "rgba(239, 68, 68, 0.15)";
30
+ readonly "--wb-primary-soft": "rgba(23, 23, 23, 0.15)";
31
+ readonly "--wb-brand": "#171717";
32
+ readonly "--wb-radius": "12px";
33
+ readonly "--wb-radius-lg": "16px";
34
+ readonly "--wb-font": "'Montserrat'";
35
+ };
36
+ export type PosToken = keyof typeof POS_TOKENS;
37
+ /** The tokens as one CSS rule, for apps that load them as a stylesheet. */
38
+ export declare function posTokensCss(selector?: string): string;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The WelcomeBack POS look: the CSS custom properties pos-ui paints with, and
3
+ * the ones the dashboard's POS / KDS previews copy. One source, so a preview
4
+ * can never drift from the register.
5
+ *
6
+ * The POS is not themable (owner decision, 2026-09-28): every business gets
7
+ * this same look. Nothing overrides these per tenant.
8
+ *
9
+ * pos-ui imports the generated `@welcome-back/brand/pos-tokens.css`; React code
10
+ * reads `POS_TOKENS`.
11
+ */
12
+ export const POS_TOKENS = {
13
+ // Monochrome chrome on a warm ivory canvas (pos-ui DESIGN.md §3).
14
+ "--wb-primary": "#171717",
15
+ "--wb-accent": "#171717",
16
+ "--wb-on-accent": "#ffffff",
17
+ // Tonal fill for hover/selected. A `-soft` token exists because the theme
18
+ // tokens are complete colors — Tailwind's `/NN` alpha modifier on them
19
+ // compiles to an invalid rgb() and renders transparent.
20
+ "--wb-accent-soft": "rgba(23, 23, 23, 0.06)",
21
+ // Warm ivory neutrals — the identity. Never cool gray.
22
+ "--wb-bg": "#f6f4e9",
23
+ "--wb-surface": "#ffffff",
24
+ "--wb-surface2": "#f2f1ec",
25
+ "--wb-ticket-bg": "#f2f1ec",
26
+ "--wb-border": "#e5e2d6",
27
+ // Translucent ivory veil for overlays (e.g. "Agotado").
28
+ "--wb-veil": "rgba(246, 244, 233, 0.72)",
29
+ "--wb-text": "#000000",
30
+ "--wb-muted": "#404040",
31
+ "--wb-success": "#22c55e",
32
+ "--wb-amber": "#f59e0b",
33
+ "--wb-amber-soft": "rgba(245, 158, 11, 0.15)",
34
+ "--wb-error": "#ef4444",
35
+ "--wb-error-soft": "rgba(239, 68, 68, 0.15)",
36
+ "--wb-primary-soft": "rgba(23, 23, 23, 0.15)",
37
+ // The brand tone. Same as the primary: the POS is monochrome.
38
+ "--wb-brand": "#171717",
39
+ "--wb-radius": "12px",
40
+ "--wb-radius-lg": "16px",
41
+ "--wb-font": "'Montserrat'",
42
+ };
43
+ /** The tokens as one CSS rule, for apps that load them as a stylesheet. */
44
+ export function posTokensCss(selector = ":root") {
45
+ const lines = Object.entries(POS_TOKENS).map(([name, value]) => ` ${name}: ${value};`);
46
+ return `${selector} {\n${lines.join("\n")}\n}\n`;
47
+ }
@@ -0,0 +1,25 @@
1
+ /* Generated from src/posTokens.ts by scripts/write-pos-tokens-css.mjs. Do not edit. */
2
+ :root {
3
+ --wb-primary: #171717;
4
+ --wb-accent: #171717;
5
+ --wb-on-accent: #ffffff;
6
+ --wb-accent-soft: rgba(23, 23, 23, 0.06);
7
+ --wb-bg: #f6f4e9;
8
+ --wb-surface: #ffffff;
9
+ --wb-surface2: #f2f1ec;
10
+ --wb-ticket-bg: #f2f1ec;
11
+ --wb-border: #e5e2d6;
12
+ --wb-veil: rgba(246, 244, 233, 0.72);
13
+ --wb-text: #000000;
14
+ --wb-muted: #404040;
15
+ --wb-success: #22c55e;
16
+ --wb-amber: #f59e0b;
17
+ --wb-amber-soft: rgba(245, 158, 11, 0.15);
18
+ --wb-error: #ef4444;
19
+ --wb-error-soft: rgba(239, 68, 68, 0.15);
20
+ --wb-primary-soft: rgba(23, 23, 23, 0.15);
21
+ --wb-brand: #171717;
22
+ --wb-radius: 12px;
23
+ --wb-radius-lg: 16px;
24
+ --wb-font: 'Montserrat';
25
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@welcome-back/brand",
3
- "version": "0.1.2",
4
- "description": "WelcomeBack brand assets for React apps: the running-W loader.",
3
+ "version": "0.2.0",
4
+ "description": "WelcomeBack brand assets for React apps: the running-W loader and the POS design tokens.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
7
7
  "type": "git",
@@ -17,13 +17,14 @@
17
17
  "types": "./dist/esm/index.d.ts",
18
18
  "import": "./dist/esm/index.js",
19
19
  "require": "./dist/cjs/index.js"
20
- }
20
+ },
21
+ "./pos-tokens.css": "./dist/pos-tokens.css"
21
22
  },
22
23
  "files": [
23
24
  "dist"
24
25
  ],
25
26
  "scripts": {
26
- "build": "rm -rf dist && tsc -p tsconfig.build.json && tsc -p tsconfig.cjs.json && node scripts/mark-cjs.mjs",
27
+ "build": "rm -rf dist && tsc -p tsconfig.build.json && tsc -p tsconfig.cjs.json && node scripts/mark-cjs.mjs && node scripts/write-pos-tokens-css.mjs",
27
28
  "typecheck": "tsc --noEmit",
28
29
  "test": "vitest run",
29
30
  "prepublishOnly": "npm run typecheck && npm test && npm run build"