@hotelfriendag/design-tokens 0.4.0 → 0.6.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 +95 -7
- package/UI_DESIGN.md +18 -2
- package/ai-rules/CLAUDE.md +46 -24
- package/ai-rules/cursorrules.template +11 -10
- package/ai-rules/github-copilot-instructions.md +18 -7
- package/ai-rules/system-prompt-compact.md +10 -4
- package/components.html +341 -87
- package/generate-tokens.cjs +218 -2
- package/package.json +9 -1
- package/pre-built/_ionic.scss +51 -0
- package/pre-built/_tokens.scss +3 -2
- package/pre-built/components.css +226 -18
- package/pre-built/dark.css +30 -0
- package/pre-built/tailwind.additive.css +2 -1
- package/pre-built/tailwind.css +2 -1
- package/pre-built/tailwind.preset.js +36 -35
- package/pre-built/themes.json +75 -0
- package/pre-built/tokens.css +3 -2
- package/pre-built/tokens.d.ts +1 -0
- package/pre-built/tokens.js +3 -2
- package/pre-built/tokens.ts +3 -2
- package/pre-built/utilities.css +165 -0
- package/scripts/validate-tokens.cjs +173 -5
- package/src/components.css +226 -18
- package/states-canonical.json +1 -1
- package/tokens.figma.json +25 -3
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`, `/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-
|
|
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: #
|
|
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,26 @@ 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).
|
|
109
|
+
|
|
110
|
+
### Utilities (non-Tailwind)
|
|
111
|
+
|
|
112
|
+
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:
|
|
113
|
+
|
|
114
|
+
```css
|
|
115
|
+
@import "@hotelfriendag/design-tokens/tokens.css";
|
|
116
|
+
@import "@hotelfriendag/design-tokens/utilities.css"; /* .bg-hf-*, .text-hf-*, .border-hf-*, .shadow-hf-* */
|
|
117
|
+
```
|
|
118
|
+
|
|
90
119
|
### TypeScript / CSS-in-JS
|
|
91
120
|
|
|
92
121
|
```ts
|
|
@@ -140,13 +169,15 @@ App code should reference **semantic** tokens (`accent`, `fg`, `bg-*`, `border`,
|
|
|
140
169
|
| `.hf-switch` | Toggle — styled checkbox, 40×22 track, accent when on | components.html#inputs |
|
|
141
170
|
| `.hf-card` + `__header/__title/__description/__body/__footer` (`--flat`) | Elevated card — 12px radius, card shadow | components.html#cards |
|
|
142
171
|
| `.hf-drawer` + `__backdrop/__panel/__header/__body/__footer` (`--left`) | Side-panel — backdrop + sliding panel | components.html#layout |
|
|
172
|
+
| `.hf-table` (+ `__num`, `--static`) | Table — thead section bg, subtle row dividers, warm hover; style a plain `<table>` | components.html#table |
|
|
173
|
+
| `.hf-empty` + `__icon/__title/__text/__action` (`--compact`, `--row`) | Empty state — page-level, in-card, in-table-row | components.html#empty |
|
|
143
174
|
| `.hf-pill` + `.status-{domain}-{state}` | Status badge — 6px radius, 15% bg + 100% text + 1px border | components.html#status |
|
|
144
175
|
| `.hf-tab` / `.hf-tab--sm` / `.hf-pill-tabs` | Underline + segmented tabs | components.html#tabs |
|
|
145
176
|
| `.hf-pagination` + `__item` / `__ellipsis` | Subtle gray active (NOT accent!) — 34×34, 8px radius | components.html#pagination |
|
|
146
177
|
| `.hf-modal` + `__header/__title/__body/__footer/__close` | 6px radius, footer no top border | components.html#modal |
|
|
147
178
|
| `.hf-alert` + `--success/--info/--warn/--error` | White bg + 3px top accent bar + 26×26 squared icon | components.html#alerts |
|
|
148
179
|
| `.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 |
|
|
180
|
+
| `.hf-toast` + `--success/--error/--warn/--info` | Floating notification — 9px radius; variant colors the icon + 3px left edge | components.html#alerts |
|
|
150
181
|
| `.hf-check` / `.hf-radio` | Custom checkbox+radio — 18×18, filled accent on check | components.html#inputs |
|
|
151
182
|
| `.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
183
|
| `.skeleton` + `.hf-spin` | Loading-state primitives (shimmer + rotation keyframes) | components.html#empty |
|
|
@@ -223,11 +254,12 @@ Copy-paste markup for the main `.hf-*` components — framework-agnostic (works
|
|
|
223
254
|
### Toast
|
|
224
255
|
|
|
225
256
|
```html
|
|
226
|
-
<div class="hf-toast">
|
|
257
|
+
<div class="hf-toast hf-toast--success">
|
|
227
258
|
<span class="hf-toast__icon"><!-- icon svg --></span>
|
|
228
259
|
<span class="hf-toast__text">Booking <strong>#2841</strong> confirmed</span>
|
|
229
260
|
<button class="hf-toast__close" aria-label="Dismiss">✕</button>
|
|
230
261
|
</div>
|
|
262
|
+
<!-- variants color the icon + 3px left edge: --success | --error | --warn | --info -->
|
|
231
263
|
```
|
|
232
264
|
|
|
233
265
|
### Dropdown menu
|
|
@@ -264,6 +296,21 @@ Copy-paste markup for the main `.hf-*` components — framework-agnostic (works
|
|
|
264
296
|
<button class="hf-btn hf-btn--icon hf-btn--outline-default" aria-label="Edit">✎</button>
|
|
265
297
|
```
|
|
266
298
|
|
|
299
|
+
### Density / touch mode
|
|
300
|
+
|
|
301
|
+
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.
|
|
302
|
+
|
|
303
|
+
```html
|
|
304
|
+
<html data-density="touch"> <!-- 48px controls -->
|
|
305
|
+
<div data-density="comfortable">…</div> <!-- 44px, scoped -->
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
| density | button | small | input |
|
|
309
|
+
|---|---|---|---|
|
|
310
|
+
| default | 40px | 32px | 39px |
|
|
311
|
+
| `comfortable` | 44px | 36px | 44px |
|
|
312
|
+
| `touch` | 48px | 40px | 48px |
|
|
313
|
+
|
|
267
314
|
### Form controls & field
|
|
268
315
|
|
|
269
316
|
```html
|
|
@@ -288,6 +335,11 @@ Copy-paste markup for the main `.hf-*` components — framework-agnostic (works
|
|
|
288
335
|
|
|
289
336
|
<!-- toggle: a styled checkbox -->
|
|
290
337
|
<input type="checkbox" class="hf-switch" checked />
|
|
338
|
+
|
|
339
|
+
<!-- standalone invalid (no .hf-form-field wrapper) — for framework form state, e.g. Angular.
|
|
340
|
+
Either trigger gives the red border + red focus ring: -->
|
|
341
|
+
<input class="hf-input" aria-invalid="true" />
|
|
342
|
+
<input class="hf-input hf-input--error" /> <!-- alias class, same effect -->
|
|
291
343
|
```
|
|
292
344
|
|
|
293
345
|
### Card
|
|
@@ -330,6 +382,42 @@ Copy-paste markup for the main `.hf-*` components — framework-agnostic (works
|
|
|
330
382
|
<!-- slide from the left: add .hf-drawer--left on .hf-drawer -->
|
|
331
383
|
```
|
|
332
384
|
|
|
385
|
+
### Table
|
|
386
|
+
|
|
387
|
+
```html
|
|
388
|
+
<!-- style a plain <table>; wrap in a bordered container for the card look -->
|
|
389
|
+
<div class="rounded-lg border border-hf-border overflow-hidden">
|
|
390
|
+
<table class="hf-table">
|
|
391
|
+
<thead>
|
|
392
|
+
<tr><th>Guest</th><th>Status</th><th class="hf-table__num">Total</th></tr>
|
|
393
|
+
</thead>
|
|
394
|
+
<tbody>
|
|
395
|
+
<tr>
|
|
396
|
+
<td>Amanda Peterson</td>
|
|
397
|
+
<td><span class="hf-pill status-booking-confirmed">Confirmed</span></td>
|
|
398
|
+
<td class="hf-table__num">€ 1,240</td>
|
|
399
|
+
</tr>
|
|
400
|
+
</tbody>
|
|
401
|
+
</table>
|
|
402
|
+
</div>
|
|
403
|
+
<!-- disable the row hover (static summary tables): add .hf-table--static -->
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Empty state
|
|
407
|
+
|
|
408
|
+
```html
|
|
409
|
+
<!-- empty when the request SUCCEEDS with no results; use .skeleton while it's IN FLIGHT -->
|
|
410
|
+
<div class="hf-empty">
|
|
411
|
+
<div class="hf-empty__icon"><!-- icon svg --></div>
|
|
412
|
+
<h3 class="hf-empty__title">No bookings found</h3>
|
|
413
|
+
<p class="hf-empty__text">Try adjusting your filters.</p>
|
|
414
|
+
<div class="hf-empty__action">
|
|
415
|
+
<button class="hf-btn hf-btn--primary">New booking</button>
|
|
416
|
+
</div>
|
|
417
|
+
</div>
|
|
418
|
+
<!-- --compact (in-card) · --row (inside an .hf-table colspan cell) -->
|
|
419
|
+
```
|
|
420
|
+
|
|
333
421
|
## Cheat-sheet — common tokens & utilities
|
|
334
422
|
|
|
335
423
|
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 +431,7 @@ Tailwind v4 derives utilities from the `@theme` tokens (`bg-hf-*`, `text-hf-*`,
|
|
|
343
431
|
| `bg-hf-accent-subtle` / `-subtler` | `--color-hf-accent-subtle` | light tints |
|
|
344
432
|
| `text-hf-fg` | `--color-hf-fg` | body `#2B2B2B` |
|
|
345
433
|
| `text-hf-fg-muted` / `-subtle` / `-faint` | `--color-hf-fg-*` | secondary → placeholder |
|
|
346
|
-
| `text-hf-on-accent` | `--color-hf-on-accent` | `#
|
|
434
|
+
| `text-hf-on-accent` | `--color-hf-on-accent` | `#0B2F46` (AA text on accent — D1) |
|
|
347
435
|
|
|
348
436
|
**Surfaces & borders**
|
|
349
437
|
|
|
@@ -366,7 +454,7 @@ Tailwind v4 derives utilities from the `@theme` tokens (`bg-hf-*`, `text-hf-*`,
|
|
|
366
454
|
|
|
367
455
|
**→ https://unpkg.com/@hotelfriendag/design-tokens/components.html**
|
|
368
456
|
|
|
369
|
-
Always serves the latest; pin a version with `…/design-tokens@0.
|
|
457
|
+
Always serves the latest; pin a version with `…/design-tokens@0.5.0/components.html`. Or open the copy in your own project:
|
|
370
458
|
|
|
371
459
|
```bash
|
|
372
460
|
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
|
|
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,
|
|
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
|
---
|
package/ai-rules/CLAUDE.md
CHANGED
|
@@ -4,29 +4,30 @@
|
|
|
4
4
|
|
|
5
5
|
## Project: {{PROJECT_NAME}}
|
|
6
6
|
|
|
7
|
-
This project follows the **HotelFriend Design System
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
- `
|
|
15
|
-
- `
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:**
|
|
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
|
|
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
|
|
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-
|
|
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`
|
|
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 `
|
|
22
|
-
2. Open `
|
|
23
|
-
3. Reuse `.hf-*` primitives from
|
|
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
|
|
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
|
|
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
|
|
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`
|
|
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
|
|
33
|
-
- Building modals/alerts/dropdowns/pagination from scratch when
|
|
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-
|
|
37
|
-
2. Use `.hf-*` primitives BEFORE writing new component CSS — check
|
|
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
|
|
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.
|