@hotelfriendag/design-tokens 0.4.0 → 0.7.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
@@ -6,6 +6,15 @@ Cross-project design foundation: tokens, generated outputs for every stack (CSS
6
6
  >
7
7
  > **Status:** RFC-0001 Phase 1 complete (semantic three-tier model + collision-safe `hf-` prefix + drift CI). Published on npmjs.com via Trusted Publishing. See [`ROADMAP.md`](ROADMAP.md) for remaining items and [`CHANGELOG.md`](CHANGELOG.md) for phase history.
8
8
 
9
+ ## Contract
10
+
11
+ [`CONTRACT.md`](CONTRACT.md) is the **normative design-system contract** — the single home for
12
+ versioning & breaking-change policy, the governed variation model (density / theme / brand),
13
+ error-UX mapping, the accessibility responsibility split, i18n posture, performance budgets, the
14
+ consumer build pipeline, token/Figma governance, the supported-stack matrix, and the proposal
15
+ process. Read it before proposing changes or integrating; this README and the integration playbooks
16
+ are the *consumption* detail under it.
17
+
9
18
  ## Install
10
19
 
11
20
  ```bash
@@ -16,7 +25,7 @@ Public package — no `.npmrc`, no auth, no CI secrets. Works from any project,
16
25
 
17
26
  ## Wire it up (pick your stack)
18
27
 
19
- The package exposes each generated file via a subpath export, e.g. `@hotelfriendag/design-tokens/tailwind.css`, `/components.css`, `/status.css`, `/_tokens.scss`, `/tokens.css`, `/tokens.ts`, `/shadcn-tokens.css`.
28
+ The package exposes each generated file via a subpath export, e.g. `@hotelfriendag/design-tokens/tailwind.css`, `/components.css`, `/status.css`, `/_tokens.scss`, `/tokens.css`, `/tokens.ts`, `/shadcn-tokens.css`, `/dark.css`, `/web.css`, `/utilities.css`, `/_ionic.scss`, `/themes.json`.
20
29
 
21
30
  ### Tailwind v4 (recommended — e.g. `ui-hf`)
22
31
 
@@ -32,7 +41,7 @@ The package exposes each generated file via a subpath export, e.g. `@hotelfriend
32
41
  Put the DS imports **after** any project-local `@theme {}` block so DS values win `--color-hf-*` collisions. Import `tailwind.css` only — `tailwind.additive.css` is byte-identical (the `hf-` prefix made the additive filter unnecessary), importing both duplicates every declaration.
33
42
 
34
43
  ```html
35
- <button class="bg-hf-accent hover:bg-hf-accent-hover text-white h-10 px-5 rounded-hf-sm text-hf-base font-semibold">Save</button>
44
+ <button class="bg-hf-accent hover:bg-hf-accent-hover text-hf-on-accent h-10 px-5 rounded-hf-sm text-hf-base font-semibold">Save</button>
36
45
  <span class="hf-pill status-booking-confirmed">Confirmed</span>
37
46
  ```
38
47
 
@@ -46,7 +55,7 @@ The package ships `pre-built/_tokens.scss` with 135 compile-time variables — `
46
55
 
47
56
  .my-btn {
48
57
  background: ds.$colorAccent; // #24AFE8
49
- color: #fff;
58
+ color: ds.$colorOn-accent; // #0B2F46 — AA text-on-accent (decision D1)
50
59
  border-radius: ds.$radiusSm; // 6px
51
60
  box-shadow: ds.$shadowModal;
52
61
  }
@@ -87,6 +96,37 @@ export default defineNuxtConfig({
87
96
 
88
97
  Then use `var(--color-hf-accent)`, `var(--font-size-hf-base)`, or `.hf-modal` / `.hf-pill .status-booking-confirmed` in any template.
89
98
 
99
+ ### Dark theme
100
+
101
+ `dark.css` is a `[data-theme="dark"]` override of the ~15 semantic tokens (surfaces, text, borders, accent tints). Import it after `tokens.css` and flip `<html data-theme="dark">`:
102
+
103
+ ```css
104
+ @import "@hotelfriendag/design-tokens/tokens.css";
105
+ @import "@hotelfriendag/design-tokens/dark.css"; /* [data-theme="dark"] overrides */
106
+ ```
107
+
108
+ Then `<html data-theme="dark">` (or any container) switches the whole subtree. `pre-built/themes.json` is the machine-readable list of which semantic vars a theme overrides (the validation contract for custom themes, enforced by validator check #8).
109
+
110
+ ### Web / marketing theme
111
+
112
+ `web.css` is a `[data-theme="web"]` theme for the marketing surface (`web-hf`, hotelfriend.com). It is the **same brand** as the portal — **zero color overrides** (accent, neutrals, Roboto unchanged) — and forks only the **rhythm**: larger fluid type (`--font-size-hf-*`, with `clamp()` page/hero), rounder radius (`--radius-hf-*`), and softer layered elevation (`--shadow-hf-*`). It demonstrates the `[data-theme]` axis extended to non-color semantic tiers (founder decision D5). Import it after `tokens.css` and flip `<html data-theme="web">`:
113
+
114
+ ```css
115
+ @import "@hotelfriendag/design-tokens/tokens.css";
116
+ @import "@hotelfriendag/design-tokens/web.css"; /* [data-theme="web"] — non-color rhythm */
117
+ ```
118
+
119
+ Then `<html data-theme="web">` (or any region) re-rhythms the subtree. web-hf's pricing-tier sub-brand (gold/navy) stays local to web-hf (decision D6) and is intentionally not shipped here.
120
+
121
+ ### Utilities (non-Tailwind)
122
+
123
+ For Angular / plain-SCSS apps without Tailwind, `utilities.css` ships atomic helpers `.bg-hf-{name}`, `.text-hf-{name}`, `.border-hf-{name}`, `.shadow-hf-{name}` for the semantic tier (primitives via `tokens.css` vars directly). Requires `tokens.css` loaded:
124
+
125
+ ```css
126
+ @import "@hotelfriendag/design-tokens/tokens.css";
127
+ @import "@hotelfriendag/design-tokens/utilities.css"; /* .bg-hf-*, .text-hf-*, .border-hf-*, .shadow-hf-* */
128
+ ```
129
+
90
130
  ### TypeScript / CSS-in-JS
91
131
 
92
132
  ```ts
@@ -140,13 +180,15 @@ App code should reference **semantic** tokens (`accent`, `fg`, `bg-*`, `border`,
140
180
  | `.hf-switch` | Toggle — styled checkbox, 40×22 track, accent when on | components.html#inputs |
141
181
  | `.hf-card` + `__header/__title/__description/__body/__footer` (`--flat`) | Elevated card — 12px radius, card shadow | components.html#cards |
142
182
  | `.hf-drawer` + `__backdrop/__panel/__header/__body/__footer` (`--left`) | Side-panel — backdrop + sliding panel | components.html#layout |
183
+ | `.hf-table` (+ `__num`, `--static`) | Table — thead section bg, subtle row dividers, warm hover; style a plain `<table>` | components.html#table |
184
+ | `.hf-empty` + `__icon/__title/__text/__action` (`--compact`, `--row`) | Empty state — page-level, in-card, in-table-row | components.html#empty |
143
185
  | `.hf-pill` + `.status-{domain}-{state}` | Status badge — 6px radius, 15% bg + 100% text + 1px border | components.html#status |
144
186
  | `.hf-tab` / `.hf-tab--sm` / `.hf-pill-tabs` | Underline + segmented tabs | components.html#tabs |
145
187
  | `.hf-pagination` + `__item` / `__ellipsis` | Subtle gray active (NOT accent!) — 34×34, 8px radius | components.html#pagination |
146
188
  | `.hf-modal` + `__header/__title/__body/__footer/__close` | 6px radius, footer no top border | components.html#modal |
147
189
  | `.hf-alert` + `--success/--info/--warn/--error` | White bg + 3px top accent bar + 26×26 squared icon | components.html#alerts |
148
190
  | `.hf-alert--tinted` / `--banner` / `--compact` | Modifiers — bg tint / full-width strip / compact-in-card | components.html#alerts |
149
- | `.hf-toast` | Floating notification — 9px radius | components.html#alerts |
191
+ | `.hf-toast` + `--success/--error/--warn/--info` | Floating notification — 9px radius; variant colors the icon + 3px left edge | components.html#alerts |
150
192
  | `.hf-check` / `.hf-radio` | Custom checkbox+radio — 18×18, filled accent on check | components.html#inputs |
151
193
  | `.hf-dropdown-menu` + `__header / __item / __item-icon / __shortcut / __divider` (+ `__item--danger`) | 9px radius dropdown, portal shadow (legacy flat `.hf-dropdown-{header\|item\|divider}` kept as deprecated aliases) | components.html#dropdown |
152
194
  | `.skeleton` + `.hf-spin` | Loading-state primitives (shimmer + rotation keyframes) | components.html#empty |
@@ -223,11 +265,12 @@ Copy-paste markup for the main `.hf-*` components — framework-agnostic (works
223
265
  ### Toast
224
266
 
225
267
  ```html
226
- <div class="hf-toast">
268
+ <div class="hf-toast hf-toast--success">
227
269
  <span class="hf-toast__icon"><!-- icon svg --></span>
228
270
  <span class="hf-toast__text">Booking <strong>#2841</strong> confirmed</span>
229
271
  <button class="hf-toast__close" aria-label="Dismiss">✕</button>
230
272
  </div>
273
+ <!-- variants color the icon + 3px left edge: --success | --error | --warn | --info -->
231
274
  ```
232
275
 
233
276
  ### Dropdown menu
@@ -264,6 +307,21 @@ Copy-paste markup for the main `.hf-*` components — framework-agnostic (works
264
307
  <button class="hf-btn hf-btn--icon hf-btn--outline-default" aria-label="Edit">✎</button>
265
308
  ```
266
309
 
310
+ ### Density / touch mode
311
+
312
+ Buttons and inputs read their height from a density scope — switch a whole app (or one container) with `data-density`; no per-component change. Default = desktop (40px); `touch` (POS/mobile) = 48px controls + 44px tap.
313
+
314
+ ```html
315
+ <html data-density="touch"> <!-- 48px controls -->
316
+ <div data-density="comfortable">…</div> <!-- 44px, scoped -->
317
+ ```
318
+
319
+ | density | button | small | input |
320
+ |---|---|---|---|
321
+ | default | 40px | 32px | 39px |
322
+ | `comfortable` | 44px | 36px | 44px |
323
+ | `touch` | 48px | 40px | 48px |
324
+
267
325
  ### Form controls & field
268
326
 
269
327
  ```html
@@ -288,6 +346,11 @@ Copy-paste markup for the main `.hf-*` components — framework-agnostic (works
288
346
 
289
347
  <!-- toggle: a styled checkbox -->
290
348
  <input type="checkbox" class="hf-switch" checked />
349
+
350
+ <!-- standalone invalid (no .hf-form-field wrapper) — for framework form state, e.g. Angular.
351
+ Either trigger gives the red border + red focus ring: -->
352
+ <input class="hf-input" aria-invalid="true" />
353
+ <input class="hf-input hf-input--error" /> <!-- alias class, same effect -->
291
354
  ```
292
355
 
293
356
  ### Card
@@ -330,6 +393,42 @@ Copy-paste markup for the main `.hf-*` components — framework-agnostic (works
330
393
  <!-- slide from the left: add .hf-drawer--left on .hf-drawer -->
331
394
  ```
332
395
 
396
+ ### Table
397
+
398
+ ```html
399
+ <!-- style a plain <table>; wrap in a bordered container for the card look -->
400
+ <div class="rounded-lg border border-hf-border overflow-hidden">
401
+ <table class="hf-table">
402
+ <thead>
403
+ <tr><th>Guest</th><th>Status</th><th class="hf-table__num">Total</th></tr>
404
+ </thead>
405
+ <tbody>
406
+ <tr>
407
+ <td>Amanda Peterson</td>
408
+ <td><span class="hf-pill status-booking-confirmed">Confirmed</span></td>
409
+ <td class="hf-table__num">€ 1,240</td>
410
+ </tr>
411
+ </tbody>
412
+ </table>
413
+ </div>
414
+ <!-- disable the row hover (static summary tables): add .hf-table--static -->
415
+ ```
416
+
417
+ ### Empty state
418
+
419
+ ```html
420
+ <!-- empty when the request SUCCEEDS with no results; use .skeleton while it's IN FLIGHT -->
421
+ <div class="hf-empty">
422
+ <div class="hf-empty__icon"><!-- icon svg --></div>
423
+ <h3 class="hf-empty__title">No bookings found</h3>
424
+ <p class="hf-empty__text">Try adjusting your filters.</p>
425
+ <div class="hf-empty__action">
426
+ <button class="hf-btn hf-btn--primary">New booking</button>
427
+ </div>
428
+ </div>
429
+ <!-- --compact (in-card) · --row (inside an .hf-table colspan cell) -->
430
+ ```
431
+
333
432
  ## Cheat-sheet — common tokens & utilities
334
433
 
335
434
  Tailwind v4 derives utilities from the `@theme` tokens (`bg-hf-*`, `text-hf-*`, `rounded-hf-*`, `shadow-hf-*`). Vanilla CSS uses the `var(--color-hf-*)` form; SCSS uses `$colorAccent`, `$radiusSm`, … See [`UI_DESIGN.md`](UI_DESIGN.md) §9 for the full token list.
@@ -343,7 +442,7 @@ Tailwind v4 derives utilities from the `@theme` tokens (`bg-hf-*`, `text-hf-*`,
343
442
  | `bg-hf-accent-subtle` / `-subtler` | `--color-hf-accent-subtle` | light tints |
344
443
  | `text-hf-fg` | `--color-hf-fg` | body `#2B2B2B` |
345
444
  | `text-hf-fg-muted` / `-subtle` / `-faint` | `--color-hf-fg-*` | secondary → placeholder |
346
- | `text-hf-on-accent` | `--color-hf-on-accent` | `#FFFFFF` (text on accent) |
445
+ | `text-hf-on-accent` | `--color-hf-on-accent` | `#0B2F46` (AA text on accent — D1) |
347
446
 
348
447
  **Surfaces & borders**
349
448
 
@@ -366,7 +465,7 @@ Tailwind v4 derives utilities from the `@theme` tokens (`bg-hf-*`, `text-hf-*`,
366
465
 
367
466
  **→ https://unpkg.com/@hotelfriendag/design-tokens/components.html**
368
467
 
369
- Always serves the latest; pin a version with `…/design-tokens@0.4.0/components.html`. Or open the copy in your own project:
468
+ Always serves the latest; pin a version with `…/design-tokens@0.5.0/components.html`. Or open the copy in your own project:
370
469
 
371
470
  ```bash
372
471
  open node_modules/@hotelfriendag/design-tokens/components.html # macOS
package/UI_DESIGN.md CHANGED
@@ -248,7 +248,7 @@ Live observation: 25+ unique button variants across 13 sidebar sections + 7 sub-
248
248
 
249
249
  | Style | Class | Look |
250
250
  | ----------- | ------------------------------ | ------------------------------------------------------------ |
251
- | **Primary** | `.btn.btn-primary` | Solid `#24AFE8` bg, white text, 40px h, 15/600. Hover: `#149AD1` bg. |
251
+ | **Primary** | `.btn.btn-primary` | Solid `#24AFE8` bg, AA dark text `#0B2F46` (founder decision D1 — white failed WCAG AA at 2.51:1), 40px h, 15/600. Hover: `#149AD1` bg. |
252
252
  | **Primary sm** | `.btn.btn-primary.btn-sm` | Solid bg, 32px h, 15/600, padding `7px 14px`. |
253
253
  | **Primary search** | `.btn.btn-primary.pr13` | 38px h, padding `10px 13px` — used as inline search submit. |
254
254
  | **Outline** | `.btn.btn-hf-outline-primary` | White bg, `#24AFE8` border + text. 40px h, 15/600. |
@@ -433,6 +433,7 @@ These are the **canonical component classes** shipped in `pre-built/components.c
433
433
  - **Anatomy:** white bg, 9px radius, `0 6px 18px rgba(0,0,0,.10)` shadow, 1px border `rgba(72,91,120,.15)`
434
434
  - **Layout:** inline-flex with `__icon` (20×20) + `__text` (flex:1) + optional `__close` (20×20)
435
435
  - **Sizing:** min-width 280px, max-width 420px
436
+ - **Variants (v0.6):** `--success` / `--error` / `--warn` / `--info` — bound to the `--color-hf-status-*` families (same structure as `.hf-alert--*`). The variant sets the accent color via `currentColor` (which tints the `__icon`) and adds a 3px left edge; the message text stays `--color-hf-fg`. Use these instead of hand-coloring the icon.
436
437
  - **Positioning:** typically `position: fixed; bottom: 16px; right: 16px; z-index: 50`
437
438
  - **Reference:** `components.html#alerts` · **Portal source:** `.alert-box` (different DOM structure) · **Decision:** New modern primitive — portal `.alert-box` is positioned but uses heavier `.alert` chrome. Toast is lighter.
438
439
 
@@ -466,17 +467,19 @@ These are the **canonical component classes** shipped in `pre-built/components.c
466
467
  ### `.hf-btn` — Buttons (v0.4+)
467
468
  - **Anatomy:** `inline-flex` center, height 40px, padding `0 20px`, 6px radius, 15px/600, `border: 1px solid transparent`, 200ms transitions. `:focus-visible` → 2px accent outline (offset 2px). `:disabled` / `.is-disabled` → opacity .6, not-allowed.
468
469
  - **Variants:**
469
- - `--primary` — accent bg, white text; hover `--color-hf-accent-hover` (`#149AD1`)
470
+ - `--primary` — accent bg, AA dark text `--color-hf-on-accent` (`#0B2F46`, 5.55:1 — founder decision D1; white failed AA); hover `--color-hf-accent-hover` (`#149AD1`)
470
471
  - `--danger` — `--color-hf-status-error-strong` (`#D9534F`) bg; hover `--color-hf-status-error-strong-hover` (`#C9302C`)
471
472
  - `--outline-primary` — white bg, accent text + border; hover bg-muted
472
473
  - `--outline-default` — white bg, steel text, neutral border, `--shadow-hf-outline`; hover bg-muted
473
474
  - `--cancel` — transparent, muted text; hover bg-muted
474
475
  - **Sizes/shapes:** `--sm` (32px / 14px), `--icon` (square — width = height), `--block` (full width)
476
+ - **Density (v0.5):** height follows the `[data-density]` scope — desktop 40 / `comfortable` 44 / `touch` 48px (vars `--hf-btn-h` / `-sm`). Set `data-density="touch"` on a container/`<html>` for POS/mobile; see `ADOPTION.md`.
475
477
  - **Reference:** `components.html#buttons` · **Portal source:** `.btn-primary` / `.btn-hf-outline-*` / `.btn-cancel` / `.btn-delete` · **Decision:** consolidates 25+ portal button recipes into one base + modifiers. Compact portal-only families (reset-filters, icon-square) stay Tailwind-utility recipes until promoted.
476
478
 
477
479
  ### `.hf-input` / `.hf-textarea` / `.hf-select` — Form controls (v0.4+)
478
480
  - **Anatomy:** width 100%, height 39px, padding `0 12px`, 6px radius, 1px `--color-hf-input-border` (`#DBDFE9`), 14px text. Placeholder `--color-hf-fg-faint`.
479
481
  - **Focus:** accent border + 3px `--color-hf-accent-subtle` ring (accessible — portal had no ring).
482
+ - **Invalid (v0.6):** a red border + red focus ring fires from any of three equivalent triggers — inside an `.hf-form-field--error` wrapper, the standalone `[aria-invalid="true"]` attribute, or the alias class `.hf-input--error` / `.hf-textarea--error` / `.hf-select--error`. The standalone forms let framework form wrappers (Angular) flag a control without the `.hf-form-field` wrapper.
480
483
  - **Disabled:** bg-muted, faint text, not-allowed.
481
484
  - **Textarea:** auto height, min-height 80px, vertical resize.
482
485
  - **Select:** `appearance:none` + wrapper `.hf-select-wrap` draws a token-colored caret (clip-path triangle, same technique as `.hf-pill--dd` — no data-URI, so no bare hex).
@@ -500,9 +503,22 @@ These are the **canonical component classes** shipped in `pre-built/components.c
500
503
  - **Anatomy:** `position:fixed; inset:0; z-index:10051` (= `zIndex.modalDialog`), flex justify-end. `__backdrop` (`rgba(0,0,0,.4)`), `__panel` (max-width 480px, full height, `--shadow-hf-modal`, slide-in keyframe). `--left` slides from the left. `__header` / `__title` / `__close` / `__body` (scroll) / `__footer` mirror `.hf-modal`.
501
504
  - **Reference:** `components.html#layout` · **Decision:** new primitive — consolidates the repeated backdrop + sliding-panel pattern (reservation / room / guest drawers).
502
505
 
506
+ ### `.hf-table` — Table (v0.6+)
507
+ - **Anatomy:** style a plain `<table class="hf-table">` with element selectors — `thead` gets `--color-hf-bg-section` bg; `th` are uppercase 13px/500 `--color-hf-tab-fg-inactive` (steel) with `padding: 12px 20px`; `td` get a 1px `--color-hf-border-subtle` top divider (the first body row has none). Row hover = `--color-hf-menu-bg-hover` (the legacy portal `$table__hover`).
508
+ - **Helpers:** `.hf-table__num` on a th/td right-aligns (numeric / action columns). `--static` disables the row hover (summary tables).
509
+ - **Usage:** wrap in `bg-white rounded-lg border overflow-hidden` for the card look. An empty result renders `.hf-empty--row` inside a `<td colspan>` cell.
510
+ - **Reference:** `components.html#table` · **Decision:** new primitive — the thead-bg + divider + hover recipe was hand-rolled on every dashboard page; no new token (reuses existing semantic + component-tier tokens).
511
+
512
+ ### `.hf-empty` — Empty state (v0.6+)
513
+ - **Anatomy:** centered flex column — `__icon` (64px disc, `--color-hf-bg-muted` bg, faint icon) + `__title` (18px/600) + `__text` (14px muted, max-w 28rem) + `__action` (button row).
514
+ - **Modifiers:** `--compact` (in-card — 40px transparent icon, tighter padding, 14px title) · `--row` (inside an `.hf-table` `<td colspan>` — single centered line + optional secondary action).
515
+ - **Usage rule:** empty states are for when the request **succeeds with no results**; use `.skeleton` while the request is **in flight** — they are not interchangeable (documented in the CSS).
516
+ - **Reference:** `components.html#empty` · **Decision:** new primitive — promotes the three demo recipes (page / in-card / in-table) into shipped CSS; no new token.
517
+
503
518
  ### `.skeleton` / `.hf-spin` — Loading primitives
504
519
  - **Skeleton:** linear-gradient shimmer animation, 1.4s infinite, 4px radius. Use as block element with width/height utilities (e.g. `<div class="skeleton h-3 w-3/4">`)
505
520
  - **Spinner:** `@keyframes hf-spin` rotate animation. Apply `.hf-spin` to an SVG.
521
+ - **Reduced motion (v0.6):** a `@media (prefers-reduced-motion: reduce)` block stops the infinite shimmer + spin (skeleton keeps a flat muted block) and neutralizes the drawer slide-in.
506
522
  - **Reference:** `components.html#empty` · **Decision:** New primitives — portal has no equivalent.
507
523
 
508
524
  ---
@@ -4,29 +4,30 @@
4
4
 
5
5
  ## Project: {{PROJECT_NAME}}
6
6
 
7
- This project follows the **HotelFriend Design System v0.1.0**. The design system is the SSOT for all UI work — tokens, components, states, spacing, typography. Do NOT introduce off-system colors, sizes, or one-off shadows.
7
+ This project follows the **HotelFriend Design System** (@hotelfriendag/design-tokens — version per the installed package, see CHANGELOG.md). The design system is the SSOT for all UI work — tokens, components, states, spacing, typography. Do NOT introduce off-system colors, sizes, or one-off shadows.
8
8
 
9
9
  ## Where the design system lives in this repo
10
10
 
11
- - **`docs/portable-design/components.html`** **PRIMARY visual reference** (open in browser). Canonical rendering of every component. When in doubt, look here.
12
- - `docs/portable-design/UI_DESIGN.md` — narrative SSOT (read this first for context, anatomy, decision rationale)
13
- - `docs/portable-design/tokens.figma.json`atomic tokens (Tokens Studio format)
14
- - `docs/portable-design/states-canonical.json` — canonical interactive states for 24 components (modal, alert, pagination, dropdown items, checkboxes etc.)
15
- - `docs/portable-design/pre-built/`:
16
- - `tailwind.css` (Tailwind v4, recommended) — `@theme {}` token bindings
17
- - `tailwind.preset.js` (Tailwind v3 legacy)
18
- - `tokens.css` (vanilla CSS variables)
19
- - `_tokens.scss` (SCSS variables)
20
- - `tokens.ts` (TypeScript const)
21
- - `shadcn-tokens.css` (shadcn/ui contract)
22
- - **`components.css`** — `.hf-*` component primitives (extracted from components.html — import this in your CSS!)
23
- - `docs/portable-design/portal-audit.html` **archival** legacy portal snapshot. Do NOT copy patterns from here for new code.
11
+ Files are shipped in the `@hotelfriendag/design-tokens` npm package. After `pnpm add @hotelfriendag/design-tokens` they are available via subpath exports:
12
+
13
+ - **`node_modules/@hotelfriendag/design-tokens/components.html`****PRIMARY visual reference** (open in browser, or use the public URL https://unpkg.com/@hotelfriendag/design-tokens/components.html). Canonical rendering of every component. When in doubt, look here.
14
+ - `node_modules/@hotelfriendag/design-tokens/UI_DESIGN.md` — narrative SSOT (read this first for context, anatomy, decision rationale)
15
+ - `node_modules/@hotelfriendag/design-tokens/states-canonical.json` — canonical interactive states for 24 components (modal, alert, pagination, dropdown items, checkboxes etc.)
16
+ - Import paths (use `@hotelfriendag/design-tokens/<file>` in build tools):
17
+ - `@hotelfriendag/design-tokens/tailwind.css` (Tailwind v4, recommended) — `@theme {}` token bindings
18
+ - `@hotelfriendag/design-tokens/tokens.css` (vanilla CSS variables)
19
+ - `@hotelfriendag/design-tokens/components.css` `.hf-*` component primitives
20
+ - `@hotelfriendag/design-tokens/status.css` — `.status-{domain}-{state}` rules (generated from status-map.json)
21
+ - `@hotelfriendag/design-tokens/pre-built/_tokens.scss` (SCSS variables — Dart Sass: `@use '@hotelfriendag/design-tokens/pre-built/tokens' as ds`)
22
+ - `@hotelfriendag/design-tokens/tokens.ts` (TypeScript const)
23
+ - `@hotelfriendag/design-tokens/shadcn-tokens.css` (shadcn/ui contract)
24
+ - `portal-audit.html` — **archival** legacy portal snapshot. Do NOT copy patterns from here for new code.
24
25
 
25
26
  ## Hard rules — never violate these
26
27
 
27
- 1. **Colors:** Use only tokens defined in `pre-built/tailwind.css` / `pre-built/tokens.css`. Never hard-code hex codes. The primary brand color is `#24AFE8` (CSS var `--color-primary`, Tailwind `primary`). The four status colors are success `#59B59D`, warning `#FFBD5A`, error `#EA6565`, coral `#F87921`.
28
+ 1. **Colors:** Use only tokens defined in `@hotelfriendag/design-tokens/tailwind.css` / `@hotelfriendag/design-tokens/tokens.css`. Never hard-code hex codes. The primary brand color is `#24AFE8` (CSS var `--color-hf-accent`, SCSS `$colorAccent`, Tailwind `bg-hf-accent`). The four status colors are success `#59B59D`, warning `#FFBD5A`, error `#EA6565`, coral `#F87921`.
28
29
  2. **Status badges:** Use `.hf-pill .status-{domain}-{state}` (rules in `status.css`). Each class sets `color`/`background` from a semantic slot — `var(--color-hf-status-{role})` and `var(--color-hf-status-{role}-bg)` (15% alpha) — and the domain→role map lives in `status-map.json`. Cancellation reasons (`canceled-by-hotel/guest/hf/pos`) all share the `.status-cancel` alias. Do NOT invent new status colors.
29
- 3. **Component primitives — reuse, never rebuild:** Before writing a button, input, modal, alert, dropdown, pagination, tabs, checkbox, toggle, card, or drawer, **check `pre-built/components.css` for an existing `.hf-*` primitive**. Available: `.hf-btn` (`--primary` / `--danger` / `--outline-primary` / `--outline-default` / `--cancel`, sizes `--sm` / `--icon`); `.hf-input` / `.hf-textarea` / `.hf-select` (wrap `<select>` in `.hf-select-wrap`); `.hf-switch`; `.hf-form-field` (`__label` / `__hint` / `__error`, state `--error`); `.hf-card`; `.hf-drawer`; `.hf-modal`; `.hf-alert`; `.hf-pagination`; `.hf-dropdown-menu`; `.hf-check` / `.hf-radio`; `.hf-pill`; `.hf-tab`; `.hf-toast`. Use BEM-style children (`__header`, `__body`, `__footer`, `__title`, `__close`, `__item`, …) and modifiers (`--primary`, `--success`, `--tinted`, `--banner`, `--compact`, `--sm`). For dropdowns the canonical children are `.hf-dropdown-menu__header / __item / __item-icon / __shortcut / __divider` and modifier `.hf-dropdown-menu__item--danger` (the older flat `.hf-dropdown-{header|item|divider}` names still work as deprecated aliases). See `UI_DESIGN.md` §5.1 for the full list.
30
+ 3. **Component primitives — reuse, never rebuild:** Before writing a button, input, modal, alert, dropdown, pagination, tabs, checkbox, toggle, card, or drawer, **check `@hotelfriendag/design-tokens/components.css` for an existing `.hf-*` primitive**. Available: `.hf-btn` (`--primary` / `--danger` / `--outline-primary` / `--outline-default` / `--cancel`, sizes `--sm` / `--icon`); `.hf-input` / `.hf-textarea` / `.hf-select` (wrap `<select>` in `.hf-select-wrap`); `.hf-switch`; `.hf-form-field` (`__label` / `__hint` / `__error`, state `--error`); `.hf-card`; `.hf-drawer`; `.hf-modal`; `.hf-alert`; `.hf-pagination`; `.hf-dropdown-menu`; `.hf-check` / `.hf-radio`; `.hf-pill`; `.hf-tab`; `.hf-toast` (`--success` / `--error` / `--warn` / `--info`); `.hf-table`; `.hf-empty` (`--compact` / `--row`); invalid state `[aria-invalid="true"]` / `.hf-input--error`. Use BEM-style children (`__header`, `__body`, `__footer`, `__title`, `__close`, `__item`, …) and modifiers (`--primary`, `--success`, `--tinted`, `--banner`, `--compact`, `--sm`). For dropdowns the canonical children are `.hf-dropdown-menu__header / __item / __item-icon / __shortcut / __divider` and modifier `.hf-dropdown-menu__item--danger` (the older flat `.hf-dropdown-{header|item|divider}` names still work as deprecated aliases). See `UI_DESIGN.md` §5.1 for the full list.
30
31
  4. **Typography:** Family is **Roboto**. Sizes from canonical scale only: 11/13/14/15/16/18/20/24/26/30 px. Weights only 400 / 500 / 600. Line-heights 1.2 (headings), 1.5 (body), 1 (chips).
31
32
  5. **Spacing:** Multiples of 4 only. Default section gap is 20px. Never write 7px / 13px / 18px — round to the scale. (Exception: portal-exact padding `7px 10px 9px` for `.hf-alert` `help-block` is documented in components.html.)
32
33
  6. **Border radius:** `6px` (buttons/inputs/checkboxes/modals/alerts), `8px` (pagination items), `9px` (dropdowns/toasts/pill-tabs), `12px` (large cards), `99px` (status pills are 6px square — pills are NOT round). Never invent intermediate values.
@@ -35,17 +36,38 @@ This project follows the **HotelFriend Design System v0.1.0**. The design system
35
36
  9. **Pagination active state:** Pagination active is a **subtle gray** (`bg: #E4E8EF; color: #252F4A`) — NOT primary blue. This is portal-exact and easy to get wrong. Use `.hf-pagination__item.is-active`.
36
37
  10. **Components:** Before creating a new component, check `components.html` for an existing equivalent (open in browser, search section IDs `#buttons`, `#inputs`, `#cards`, `#status`, `#tabs`, `#dropdown`, `#modal`, `#alerts`, `#pagination`, `#empty`, `#layout`). Reuse beats duplicate.
37
38
 
39
+ ## Accessibility — required markup
40
+
41
+ The design system ships CSS only; the **JS behaviors and ARIA wiring below are YOUR responsibility**
42
+ (focus trap, arrow-key navigation, Escape/dismissal, focus return, `aria-live`). Always supply the
43
+ required markup for these components:
44
+
45
+ - **Modal** — `role="dialog"` + `aria-modal="true"` + a labelled title (`aria-labelledby`) + a focus trap + Escape-to-close.
46
+ - **Drawer** — same as modal (dialog semantics, focus trap, Escape).
47
+ - **Tabs** — `role="tablist"` / `role="tab"` / `role="tabpanel"` + `aria-selected` + arrow-key navigation.
48
+ - **Dropdown menu** — `role="menu"` / `role="menuitem"` + arrow keys + Escape + focus return to the trigger on close.
49
+ - **Switch** — a native `<input type="checkbox">` (or `role="switch"`) with an associated `<label>`.
50
+ - **Checkbox / radio** — native `<input type="checkbox|radio">` (`.hf-check` / `.hf-radio` style native inputs).
51
+ - **Icon-only buttons** — `aria-label` (the icon is not an accessible name).
52
+ - **Pagination** — wrap in `<nav aria-label="Pagination">`.
53
+ - **Toasts** — render inside an `aria-live="polite"` region.
54
+ - **Tables** — `<th scope="col">` / `<th scope="row">` on header cells.
55
+
56
+ WCAG 2.1 **AA is the floor.** Keep `:focus-visible` outlines (the design system ships them — never
57
+ remove without a visual replacement) and don't rely on placeholder text to convey required
58
+ information (placeholder contrast is intentionally below AA).
59
+
38
60
  ## When the user asks for UI work
39
61
 
40
- 1. **Read `UI_DESIGN.md`** — start here for new sessions.
41
- 2. **Open `components.html`** in a browser — see the canonical rendering.
42
- 3. **Pick existing `.hf-*` primitives** from `pre-built/components.css` first. Use Tailwind utility classes only for layout (flex/grid/spacing) — not for component chrome (colors, shadows, borders).
43
- 4. **States matter:** every interactive element needs default + hover + focus + disabled + loading. See `states-canonical.json` for the exact declarations to mirror.
62
+ 1. **Read `UI_DESIGN.md`** (at `node_modules/@hotelfriendag/design-tokens/UI_DESIGN.md`) — start here for new sessions.
63
+ 2. **Open `components.html`** in a browser (`node_modules/@hotelfriendag/design-tokens/components.html`, or https://unpkg.com/@hotelfriendag/design-tokens/components.html) — see the canonical rendering.
64
+ 3. **Pick existing `.hf-*` primitives** from `@hotelfriendag/design-tokens/components.css` first. Use Tailwind utility classes only for layout (flex/grid/spacing) — not for component chrome (colors, shadows, borders).
65
+ 4. **States matter:** every interactive element needs default + hover + focus + disabled + loading. See `states-canonical.json` (`node_modules/@hotelfriendag/design-tokens/states-canonical.json`) for the exact declarations to mirror.
44
66
  5. **No "raw HTML" answers** — wrap markup in the documented component class system. If the design system lacks something, propose a NEW token or primitive in a comment, do not silently introduce one.
45
67
 
46
68
  ## When the user asks for "the same look as the portal"
47
69
 
48
- The visual reference is `components.html`. If the user asks "make a settings page like the portal", do this in order:
70
+ The visual reference is `components.html` (`node_modules/@hotelfriendag/design-tokens/components.html` or https://unpkg.com/@hotelfriendag/design-tokens/components.html). If the user asks "make a settings page like the portal", do this in order:
49
71
 
50
72
  1. Open `components.html`, find Page Layouts section (`#layout`). Pick the matching pattern (List / Detail / Form / Dashboard).
51
73
  2. Replicate the structure (Title bar → Tabs → Filters → Data grid → Footer pagination).
@@ -67,7 +89,7 @@ The visual reference is `components.html`. If the user asks "make a settings pag
67
89
  - ❌ Pagination with `bg-hf-primary text-white` active — use `.hf-pagination__item.is-active` (subtle gray)
68
90
  - ❌ Using native `<input type="checkbox" class="accent-primary">` instead of `<input type="checkbox" class="hf-check">` — accent-color doesn't match portal's filled-blue-with-white-tick design
69
91
  - ❌ Mixing icon sets (Lucide + Font Awesome + Heroicons in the same screen)
70
- - ❌ Copying patterns from `portal-audit.html` — that's the legacy snapshot, NOT canonical reference
92
+ - ❌ Copying patterns from `portal-audit.html` (`node_modules/@hotelfriendag/design-tokens/portal-audit.html`) — that's the legacy snapshot, NOT canonical reference
71
93
 
72
94
  ## When in doubt
73
95
 
@@ -75,6 +97,6 @@ Ask the user. Better to pause for 5 seconds than ship a one-off color that drift
75
97
 
76
98
  ---
77
99
 
78
- **System version:** Design System v0.1.0
100
+ **System version:** @hotelfriendag/design-tokens version per the installed package (see CHANGELOG.md)
79
101
  **Maintained:** alongside `UI_DESIGN.md`
80
- **Refresh policy:** when `tokens.figma.json` changes, run `node generate-tokens.cjs --target=tailwind-v4 > pre-built/tailwind.css` (and repeat for other targets as needed). Script is in the same folder as `tokens.figma.json`. The `pre-built/components.css` file is hand-extracted from `components.html`'s `@layer components` block re-sync manually after component changes.
102
+ **Refresh policy:** when `tokens.figma.json` changes in the design-system repo, run `npm run build` (which runs `node generate-tokens.cjs` for all targets). The `pre-built/components.css` file is generated from `src/components.css` by `node generate-tokens.cjs --target=components-css`; hand-edits to `pre-built/` are wiped by the next build and CI fails on drift. Consumer repos update with `pnpm update @hotelfriendag/design-tokens`.
@@ -1,13 +1,13 @@
1
1
  # Drop this file at the project root as `.cursorrules` (or `.cursor/rules/00-design-system.md` in newer Cursor).
2
2
  # Cursor reads it automatically and prepends it to every AI request.
3
3
 
4
- You are working in a project that uses the **HotelFriend Design System v0.1.0**. The canonical visual reference is `docs/portable-design/components.html` (open in browser). Narrative SSOT in `docs/portable-design/UI_DESIGN.md`. Token bindings in `pre-built/tailwind.css` (v4, recommended) or `pre-built/tokens.css` (vanilla). Component primitives in `pre-built/components.css`.
4
+ You are working in a project that uses the **HotelFriend Design System** (@hotelfriendag/design-tokens — version per the installed package, see CHANGELOG.md). The canonical visual reference is `node_modules/@hotelfriendag/design-tokens/components.html` (open in browser, or https://unpkg.com/@hotelfriendag/design-tokens/components.html). Narrative SSOT in `node_modules/@hotelfriendag/design-tokens/UI_DESIGN.md`. Token bindings in `@hotelfriendag/design-tokens/tailwind.css` (v4, recommended) or `@hotelfriendag/design-tokens/tokens.css` (vanilla). Component primitives in `@hotelfriendag/design-tokens/components.css`.
5
5
 
6
6
  # Inviolable rules
7
7
 
8
- - **Colors:** only use tokens. Primary `#24AFE8` (`--color-primary` / Tailwind `primary`). Status colors success `#59B59D`, warning `#FFBD5A`, error `#EA6565`, coral `#F87921`. Cancellation reasons share `.status-cancel`. Never hard-code other hex codes.
8
+ - **Colors:** only use tokens. Primary `#24AFE8` (`--color-hf-accent` / `$colorAccent` / Tailwind `bg-hf-accent`). Status colors success `#59B59D`, warning `#FFBD5A`, error `#EA6565`, coral `#F87921`. Cancellation reasons share `.status-cancel`. Never hard-code other hex codes.
9
9
  - **Status badges:** use `.hf-pill .status-{domain}-{state}` (rules in `status.css`). Color/bg come from `var(--color-hf-status-{role})` / `-bg` (15% alpha); the domain→role map is in `status-map.json`. Never invent new status colors.
10
- - **Component primitives — reuse, never rebuild:** Use `.hf-btn`, `.hf-input` / `.hf-textarea` / `.hf-select`, `.hf-switch`, `.hf-form-field`, `.hf-card`, `.hf-drawer`, `.hf-modal`, `.hf-alert`, `.hf-pagination`, `.hf-dropdown-menu`, `.hf-tab`, `.hf-check`, `.hf-radio`, `.hf-toast` from `pre-built/components.css` BEFORE writing new component CSS. BEM children with `__` (e.g. `__header`, `__body`, `__footer`, `__item`, `__icon`). Variants with `--` (`--primary`, `--success`, `--banner`, `--compact`, `--sm`). State classes with `.is-*` (`.is-active`, `.is-disabled`). For dropdowns the canonical children are `.hf-dropdown-menu__header / __item / __item-icon / __shortcut / __divider` (the older flat `.hf-dropdown-{header|item|divider}` names still work as deprecated aliases).
10
+ - **Component primitives — reuse, never rebuild:** Use `.hf-btn`, `.hf-input` / `.hf-textarea` / `.hf-select`, `.hf-switch`, `.hf-form-field`, `.hf-card`, `.hf-drawer`, `.hf-modal`, `.hf-alert`, `.hf-pagination`, `.hf-dropdown-menu`, `.hf-tab`, `.hf-check`, `.hf-radio`, `.hf-toast` (`--success` / `--error` / `--warn` / `--info`), `.hf-table`, `.hf-empty` (`--compact` / `--row`), invalid state `[aria-invalid="true"]` / `.hf-input--error` from `@hotelfriendag/design-tokens/components.css` BEFORE writing new component CSS. BEM children with `__` (e.g. `__header`, `__body`, `__footer`, `__item`, `__icon`). Variants with `--` (`--primary`, `--success`, `--banner`, `--compact`, `--sm`). State classes with `.is-*` (`.is-active`, `.is-disabled`). For dropdowns the canonical children are `.hf-dropdown-menu__header / __item / __item-icon / __shortcut / __divider` (the older flat `.hf-dropdown-{header|item|divider}` names still work as deprecated aliases).
11
11
  - **Typography:** Roboto only. Sizes: 11 / 13 / 14 / 15 / 16 / 18 / 20 / 24 / 26 / 30 px. Weights: 400 / 500 / 600.
12
12
  - **Spacing:** multiples of 4. Default section gap 20px.
13
13
  - **Border radius:** 6 (buttons/inputs/modals/alerts), 8 (pagination items), 9 (dropdowns/toasts), 12 (cards), 99 (pills are square 6px, not round).
@@ -15,25 +15,26 @@ You are working in a project that uses the **HotelFriend Design System v0.1.0**.
15
15
  - **Shadows:** pick from the 7 documented (`default`, `subtle`, `wrapper`, `card`, `modal`, `outline`, `hover`). No bespoke shadows.
16
16
  - **Pagination active state:** subtle gray `bg: #E4E8EF; color: #252F4A` — NOT primary blue. Use `.hf-pagination__item.is-active`.
17
17
  - **Components:** check `components.html` (open in browser, search anchors `#buttons`, `#modal`, etc.) for an existing equivalent before writing new component code. Reuse beats duplicate.
18
+ - **Accessibility (required markup — the DS is CSS-only, so the ARIA + JS behavior is yours; WCAG 2.1 AA floor):** Modal/Drawer = `role="dialog"` + `aria-modal` + labelled title + focus trap + Escape. Tabs = `role="tablist/tab/tabpanel"` + `aria-selected` + arrow keys. Dropdown = `role="menu/menuitem"` + arrow keys + Escape + focus return. Switch = native checkbox (or `role="switch"`) + label. Checkbox/radio = native inputs. Icon-only buttons = `aria-label`. Pagination = `<nav aria-label="Pagination">`. Toasts = `aria-live="polite"` region. Tables = `<th scope>`. Never strip the shipped `:focus-visible` outlines.
18
19
 
19
20
  # Workflow
20
21
 
21
- 1. Open `docs/portable-design/UI_DESIGN.md` whenever starting a UI task.
22
- 2. Open `docs/portable-design/components.html` in a browser to verify visual decisions.
23
- 3. Reuse `.hf-*` primitives from `pre-built/components.css` before creating new ones.
24
- 4. For interactive elements, mirror states declared in `states-canonical.json` (default + hover + focus + disabled + loading).
25
- 5. If a token is missing for what the user asks, propose adding one in `tokens.figma.json` rather than hard-coding.
22
+ 1. Open `node_modules/@hotelfriendag/design-tokens/UI_DESIGN.md` whenever starting a UI task.
23
+ 2. Open `node_modules/@hotelfriendag/design-tokens/components.html` in a browser (or https://unpkg.com/@hotelfriendag/design-tokens/components.html) to verify visual decisions.
24
+ 3. Reuse `.hf-*` primitives from `@hotelfriendag/design-tokens/components.css` before creating new ones.
25
+ 4. For interactive elements, mirror states declared in `node_modules/@hotelfriendag/design-tokens/states-canonical.json` (default + hover + focus + disabled + loading).
26
+ 5. If a token is missing for what the user asks, propose adding one in `tokens.figma.json` (design-system repo) rather than hard-coding.
26
27
 
27
28
  # Anti-patterns to refuse
28
29
 
29
- - Hard-coded hex colors not in `tokens.css`.
30
+ - Hard-coded hex colors not in `@hotelfriendag/design-tokens/tokens.css`.
30
31
  - Tailwind utility-only components for things that have primitives (e.g. building a modal with raw `<div class="fixed inset-0 bg-black/40">` instead of using `.hf-modal`).
31
32
  - One-off shadows / radii / paddings outside the documented scale.
32
33
  - Mixing icon sets within a single screen (Lucide + FA + Heroicons).
33
34
  - Pagination with `bg-hf-primary text-white` active state (it's subtle gray — use `.hf-pagination__item.is-active`).
34
35
  - Native checkboxes with `accent-color` (use `.hf-check`).
35
36
  - Alerts with left-border accent (portal uses TOP 3px accent bar — use `.hf-alert`).
36
- - Copying patterns from `portal-audit.html` (legacy archival snapshot NOT canonical).
37
+ - Copying patterns from `portal-audit.html` (`node_modules/@hotelfriendag/design-tokens/portal-audit.html`) — legacy archival snapshot, NOT canonical.
37
38
  - Skipping component states (always provide hover / focus / disabled at minimum).
38
39
 
39
40
  When unsure, prefer asking the user over inventing.
@@ -2,13 +2,13 @@
2
2
 
3
3
  > Drop this file at `.github/copilot-instructions.md`. Copilot reads it for repository-wide context.
4
4
 
5
- This project uses the **HotelFriend Design System v0.1.0**. Canonical visual reference: `docs/portable-design/components.html` (open in browser). Token bindings: `pre-built/tailwind.css` (v4, recommended) or `pre-built/tokens.css` (vanilla). Component primitives: `pre-built/components.css`. Narrative SSOT: `docs/portable-design/UI_DESIGN.md`.
5
+ This project uses the **HotelFriend Design System** (@hotelfriendag/design-tokens — version per the installed package, see CHANGELOG.md). Canonical visual reference: `node_modules/@hotelfriendag/design-tokens/components.html` (open in browser, or https://unpkg.com/@hotelfriendag/design-tokens/components.html). Token bindings: `@hotelfriendag/design-tokens/tailwind.css` (v4, recommended) or `@hotelfriendag/design-tokens/tokens.css` (vanilla). Component primitives: `@hotelfriendag/design-tokens/components.css`. Narrative SSOT: `node_modules/@hotelfriendag/design-tokens/UI_DESIGN.md`.
6
6
 
7
7
  ## Hard rules — never violate
8
8
 
9
9
  1. **Colors:** use only tokens. Brand accent `#24AFE8` (`var(--color-hf-accent)` / Tailwind `bg-hf-accent`). Status colors via `var(--color-hf-status-{success|warning|error|info})`. Cancellation reasons share `.status-cancel`. Never hard-code other hex codes.
10
10
  2. **Status badges:** use `.hf-pill .status-{domain}-{state}` (rules in `status.css`). Color/background come from a semantic slot `var(--color-hf-status-{role})` / `-bg` (15% alpha); the domain→role map is in `status-map.json`. Never invent new status colors.
11
- 3. **Component primitives — reuse, never rebuild:** Use `.hf-btn`, `.hf-input` / `.hf-textarea` / `.hf-select`, `.hf-switch`, `.hf-form-field`, `.hf-card`, `.hf-drawer`, `.hf-modal`, `.hf-alert`, `.hf-pagination`, `.hf-dropdown-menu`, `.hf-tab`, `.hf-pill`, `.hf-check`, `.hf-radio`, `.hf-toast` from `pre-built/components.css` BEFORE writing new component CSS.
11
+ 3. **Component primitives — reuse, never rebuild:** Use `.hf-btn`, `.hf-input` / `.hf-textarea` / `.hf-select`, `.hf-switch`, `.hf-form-field`, `.hf-card`, `.hf-drawer`, `.hf-modal`, `.hf-alert`, `.hf-pagination`, `.hf-dropdown-menu`, `.hf-tab`, `.hf-pill`, `.hf-check`, `.hf-radio`, `.hf-toast` (`--success` / `--error` / `--warn` / `--info`), `.hf-table`, `.hf-empty` (`--compact` / `--row`), invalid state `[aria-invalid="true"]` / `.hf-input--error` from `@hotelfriendag/design-tokens/components.css` BEFORE writing new component CSS.
12
12
  4. **Typography:** Roboto only. Sizes from scale: 11 / 13 / 14 / 15 / 16 / 18 / 20 / 24 / 26 / 30 px. Weights: 400 / 500 / 600. Line-heights: 1.2 (headings), 1.5 (body), 1 (chips).
13
13
  5. **Spacing:** multiples of 4 only. Default section gap 20 px. Never write 7 / 13 / 18 px.
14
14
  6. **Border radius:** `6px` (buttons/inputs/modals/alerts), `8px` (pagination items), `9px` (dropdowns/toasts), `12px` (large cards), `99px` (pill-rounded). No intermediate values.
@@ -17,6 +17,17 @@ This project uses the **HotelFriend Design System v0.1.0**. Canonical visual ref
17
17
  9. **Pagination active state:** subtle gray (`bg: #E4E8EF; color: #252F4A`) — NOT primary blue. Use `.hf-pagination__item.is-active`.
18
18
  10. **Components:** check `components.html` (open in browser, search anchors `#buttons`, `#modal`, etc.) for an existing equivalent before writing new component code.
19
19
 
20
+ ## Accessibility — required markup
21
+
22
+ The design system is **CSS-only**, so the ARIA wiring and JS behavior (focus trap, arrow keys, Escape, focus return, `aria-live`) are **yours to supply**. WCAG 2.1 **AA is the floor**.
23
+
24
+ - **Modal / Drawer:** `role="dialog"` + `aria-modal="true"` + labelled title + focus trap + Escape-to-close.
25
+ - **Tabs:** `role="tablist"` / `tab` / `tabpanel` + `aria-selected` + arrow-key navigation.
26
+ - **Dropdown:** `role="menu"` / `menuitem` + arrow keys + Escape + focus return to the trigger.
27
+ - **Switch:** native `<input type="checkbox">` (or `role="switch"`) + `<label>`. **Checkbox / radio:** native inputs.
28
+ - **Icon-only buttons:** `aria-label`. **Pagination:** `<nav aria-label="Pagination">`. **Toasts:** `aria-live="polite"` region. **Tables:** `<th scope>`.
29
+ - Keep the shipped `:focus-visible` outlines; don't rely on placeholder text for required info (its contrast is intentionally below AA).
30
+
20
31
  ## When generating UI code
21
32
 
22
33
  - **Button classes:** `.hf-btn` + variant — `.hf-btn--primary`, `.hf-btn--outline-primary`, `.hf-btn--outline-default`, `.hf-btn--cancel`, `.hf-btn--danger`; sizes `.hf-btn--sm`, `.hf-btn--icon` (compact portal-only variants like reset-filters / icon-square can be layered with Tailwind utilities until promoted)
@@ -25,12 +36,12 @@ This project uses the **HotelFriend Design System v0.1.0**. Canonical visual ref
25
36
  - **Navigation:** `.hf-tab` + `--sm` + `__count`, `.hf-pill-tabs` + `.hf-pill-tab`, `.hf-pagination` + `__item/__ellipsis`, `.hf-dropdown-menu` + `__header/__item/__item-icon/__shortcut/__divider` (+ `__item--danger`)
26
37
  - **Status:** `.hf-pill .status-{domain}-{state}` + modifiers (`--striped`, `--dd`, `.is-active`, `min-w-*`)
27
38
  - **Loading:** `.skeleton` for shimmer, `.hf-spin` for spinner
28
- - Every interactive element needs: default + `:hover` + `:focus-visible` + `:disabled` states at minimum. See `states-canonical.json`.
39
+ - Every interactive element needs: default + `:hover` + `:focus-visible` + `:disabled` states at minimum. See `node_modules/@hotelfriendag/design-tokens/states-canonical.json`.
29
40
 
30
41
  ## Forbidden patterns
31
42
 
32
- - Hard-coded hex colors not in `pre-built/tokens.css`.
33
- - Building modals/alerts/dropdowns/pagination from scratch when `pre-built/components.css` has primitives.
43
+ - Hard-coded hex colors not in `@hotelfriendag/design-tokens/tokens.css`.
44
+ - Building modals/alerts/dropdowns/pagination from scratch when `@hotelfriendag/design-tokens/components.css` has primitives.
34
45
  - Tailwind utility classes for component chrome — only use them for layout (flex/grid/spacing).
35
46
  - Border-radius outside `6 / 8 / 9 / 12 / 99`.
36
47
  - Spacing values outside the 4-multiple scale.
@@ -39,7 +50,7 @@ This project uses the **HotelFriend Design System v0.1.0**. Canonical visual ref
39
50
  - Pagination active state with primary blue (use subtle gray).
40
51
  - Native checkboxes with `accent-color` (use `.hf-check`).
41
52
  - Left-border alerts (portal uses top accent bar — use `.hf-alert`).
42
- - Copying patterns from `portal-audit.html` (archival snapshot, NOT canonical).
53
+ - Copying patterns from `portal-audit.html` (`node_modules/@hotelfriendag/design-tokens/portal-audit.html`) — archival snapshot, NOT canonical.
43
54
  - Skipping component states.
44
55
 
45
- When unsure, mirror an example from `components.html`.
56
+ When unsure, mirror an example from `node_modules/@hotelfriendag/design-tokens/components.html` (or https://unpkg.com/@hotelfriendag/design-tokens/components.html).
@@ -27,17 +27,23 @@ Shadows: default `0 1px 8px rgba(0,0,0,.1)` · subtle `0 2px 4px rgba(0,0,0,.05)
27
27
  **Dropdown** — `.hf-dropdown-menu` 9 r shadow `0 1px 10px rgba(0,0,0,.1)` · `__header` (uppercase 11px) · `__item` 14/normal hover `#F5F5F5` · `__item-icon` · `__shortcut` right-aligned · `__divider` · `.is-active` primary-tint · `__item--danger` (legacy flat `.hf-dropdown-{header|item|divider}` still alias)
28
28
  **Modal** — `.hf-modal` 6 r border `1px rgba(72,91,120,.15)` shadow `0 2px 4px rgba(72,91,120,.18)` · `__header` bottom border / `__footer` NO top border · sizes sm 420 / md 500 / lg 720 / xl 960
29
29
  **Alerts** — `.hf-alert` white bg + 3px top accent bar + 26×26 squared icon · variants `--success/--info/--warn/--error` · modifiers `--tinted/--banner/--compact`
30
- **Toast** — `.hf-toast` 9 r modal-shadow · `__icon` 20×20 · floating bottom-right
30
+ **Toast** — `.hf-toast` 9 r modal-shadow · `__icon` 20×20 · floating bottom-right · variants `--success` / `--error` / `--warn` / `--info`
31
+ **Table** — `.hf-table` semantic token–driven table chrome
32
+ **Empty state** — `.hf-empty` centered empty-state block · `--compact` (inline within a card) · `--row` (inline row in a table)
33
+ **Invalid input** — `[aria-invalid="true"]` / `.hf-input--error` on `.hf-input` / `.hf-textarea` / `.hf-select` (red border + ring)
31
34
  **Pagination** — `.hf-pagination__item` 34×34 8 r · active SUBTLE GRAY `bg: #E4E8EF` `color: #252F4A` (NOT primary blue!) · hover `bg: #F1F3F6`
32
35
  **Skeleton/spinner** — `.skeleton` shimmer 1.4 s · `.hf-spin` rotate
33
36
  **Icons** — Lucide preferred (inline SVG `<use>`) · Font Awesome 4 legacy (avoid in new code)
34
37
 
38
+ ## Accessibility — required markup (DS is CSS-only; ARIA + JS behavior are YOUR job)
39
+ WCAG 2.1 AA floor. Modal/Drawer: `role="dialog"` + `aria-modal` + labelled title + focus trap + Escape. Tabs: `role="tablist/tab/tabpanel"` + `aria-selected` + arrow keys. Dropdown: `role="menu/menuitem"` + arrow keys + Escape + focus return. Switch: native checkbox (or `role="switch"`) + label. Checkbox/radio: native inputs. Icon-only buttons: `aria-label`. Pagination: `<nav aria-label="Pagination">`. Toasts: `aria-live="polite"` region. Tables: `<th scope>`. Keep the shipped `:focus-visible` outlines.
40
+
35
41
  ## Hard Rules
36
- 1. Never hard-code hex — use tokens (`--color-primary`, `$colorPrimaryDefault`, Tailwind class `primary`).
37
- 2. Use `.hf-*` primitives BEFORE writing new component CSS — check `pre-built/components.css` first.
42
+ 1. Never hard-code hex — use tokens (`--color-hf-accent`, `$colorAccent`, Tailwind class `bg-hf-accent`).
43
+ 2. Use `.hf-*` primitives BEFORE writing new component CSS — check `@hotelfriendag/design-tokens/components.css` first. Wave-1 additions: `.hf-table`, `.hf-empty` (`--compact` / `--row`), `.hf-toast--success/--error/--warn/--info`, invalid state `[aria-invalid="true"]` / `.hf-input--error`.
38
44
  3. Every interactive element must have hover, focus, and disabled states.
39
45
  4. No mixed icon sets in one component.
40
- 5. Status badge colors via `--color-badge-{domain}-{state}-color` / `--color-badge-{domain}-{state}-bg` never raw hex.
46
+ 5. Status badge colors via `.hf-pill .status-{domain}-{state}` (rules in `status.css`). Color/bg come from `var(--color-hf-status-{role})` / `-bg` (15% alpha); the domain→role map is in `status-map.json`. Never invent badge colors or use raw hex — the old per-domain badge token namespace was removed in Phase 1B.
41
47
  6. Canonical primary is `#24AFE8`. The portal renders `#26ADE4` (`$light__Blue` SCSS) — visually identical but tokens use canonical.
42
48
  7. Pagination active is subtle gray, NOT primary blue (`.hf-pagination__item.is-active`).
43
49
  8. Alerts use TOP 3px accent bar (`.hf-alert`), NOT left-border. Modal footer has NO top border.