@hotelfriendag/design-tokens 0.3.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.
@@ -0,0 +1,243 @@
1
+ // HotelFriend design tokens — CommonJS module
2
+ // Generated from docs/tokens.figma.json — do NOT edit by hand.
3
+ //
4
+ // Works with both `require("@hotelfriendag/design-tokens").tokens` and
5
+ // ESM `import { tokens } from "@hotelfriendag/design-tokens"` via Node CJS-ESM interop.
6
+
7
+ const tokens = {
8
+ "color": {
9
+ "blue": {
10
+ "50": "#E9F6FC",
11
+ "100": "#EFF6FF",
12
+ "500": "#24AFE8",
13
+ "600": "#149AD1"
14
+ },
15
+ "gray": {
16
+ "25": "#FBFBFC",
17
+ "50": "#F6F7FB",
18
+ "100": "#F1F3F6",
19
+ "200": "#E4E8EF",
20
+ "300": "#D1D6DD",
21
+ "400": "#AEBCCF",
22
+ "500": "#99A1B7",
23
+ "550": "#7E8EA6",
24
+ "600": "#50627E",
25
+ "700": "#485B78",
26
+ "800": "#4B5675",
27
+ "900": "#2B2B2B",
28
+ "950": "#252F4A"
29
+ },
30
+ "green": {
31
+ "500": "#59B59D"
32
+ },
33
+ "amber": {
34
+ "400": "#FFBD5A",
35
+ "600": "#FFC900"
36
+ },
37
+ "red": {
38
+ "400": "#EA6565",
39
+ "500": "#FB5A56",
40
+ "700": "#D9534F"
41
+ },
42
+ "orange": {
43
+ "500": "#F87921",
44
+ "700": "#A55505"
45
+ },
46
+ "violet": {
47
+ "500": "#5761D8"
48
+ },
49
+ "slate": {
50
+ "500": "#7F8FA4"
51
+ },
52
+ "olive": {
53
+ "700": "#5B7A02"
54
+ },
55
+ "white": "#FFFFFF",
56
+ "black": "#000000",
57
+ "accent": {
58
+ "default": "{primitive.color.blue.500}",
59
+ "hover": "{primitive.color.blue.600}",
60
+ "subtle": "{primitive.color.blue.50}",
61
+ "subtler": "{primitive.color.blue.100}"
62
+ },
63
+ "on-accent": "{primitive.color.white}",
64
+ "bg": {
65
+ "page": "{primitive.color.gray.25}",
66
+ "section": "{primitive.color.gray.50}",
67
+ "muted": "{primitive.color.gray.100}",
68
+ "surface": "{primitive.color.white}"
69
+ },
70
+ "fg": {
71
+ "default": "{primitive.color.gray.900}",
72
+ "muted": "{primitive.color.gray.800}",
73
+ "subtle": "{primitive.color.gray.550}",
74
+ "faint": "{primitive.color.gray.400}"
75
+ },
76
+ "border": {
77
+ "default": "{primitive.color.gray.300}",
78
+ "subtle": "{primitive.color.gray.200}",
79
+ "strong": "{primitive.color.gray.400}"
80
+ },
81
+ "tab": {
82
+ "fg-inactive": "{primitive.color.gray.700}",
83
+ "count-fg": "{primitive.color.gray.700}"
84
+ },
85
+ "pagination": {
86
+ "fg-inactive": "{primitive.color.gray.700}",
87
+ "fg-active": "{primitive.color.gray.950}"
88
+ },
89
+ "input": {
90
+ "border": "#DBDFE9"
91
+ },
92
+ "menu": {
93
+ "bg-hover": "#F5F5F5"
94
+ },
95
+ "status": {
96
+ "success": "{primitive.color.green.500}",
97
+ "success-bg": "rgba(89,181,157,0.15)",
98
+ "warning": "{primitive.color.amber.400}",
99
+ "warning-bg": "rgba(255,189,90,0.15)",
100
+ "warning-strong": "{primitive.color.amber.600}",
101
+ "warning-strong-bg": "rgba(255,201,0,0.15)",
102
+ "error": "{primitive.color.red.400}",
103
+ "error-bg": "rgba(234,101,101,0.15)",
104
+ "error-strong": "{primitive.color.red.700}",
105
+ "error-alt": "{primitive.color.red.500}",
106
+ "info": "{semantic.color.accent.default}",
107
+ "info-bg": "rgba(36,175,232,0.15)",
108
+ "cancel": "{primitive.color.slate.500}",
109
+ "cancel-bg": "rgba(127,143,164,0.15)",
110
+ "coral": "{primitive.color.orange.500}",
111
+ "coral-bg": "rgba(248,121,33,0.15)",
112
+ "violet": "{primitive.color.violet.500}",
113
+ "violet-bg": "rgba(87,97,216,0.15)",
114
+ "olive": "{primitive.color.olive.700}",
115
+ "olive-bg": "rgba(91,122,2,0.15)",
116
+ "orange-deep": "{primitive.color.orange.700}",
117
+ "orange-deep-bg": "rgba(165,85,5,0.15)"
118
+ }
119
+ },
120
+ "fontFamily": {
121
+ "primary": "Roboto"
122
+ },
123
+ "fontWeight": {
124
+ "regular": "400",
125
+ "medium": "500",
126
+ "semibold": "600"
127
+ },
128
+ "fontSize": {
129
+ "xs": "11px",
130
+ "sm": "13px",
131
+ "base": "14px",
132
+ "md": "15px",
133
+ "lg": "16px",
134
+ "xl": "18px",
135
+ "2xl": "20px",
136
+ "h2": "24px",
137
+ "page": "26px",
138
+ "hero": "30px"
139
+ },
140
+ "lineHeight": {
141
+ "headings": "1.2",
142
+ "body": "1.5",
143
+ "tight": "1"
144
+ },
145
+ "typography": {
146
+ "pageTitle": {
147
+ "fontFamily": "{fontFamily.primary}",
148
+ "fontWeight": "{fontWeight.semibold}",
149
+ "fontSize": "{fontSize.page}",
150
+ "lineHeight": "{lineHeight.headings}"
151
+ },
152
+ "btn": {
153
+ "fontFamily": "{fontFamily.primary}",
154
+ "fontWeight": "{fontWeight.semibold}",
155
+ "fontSize": "{fontSize.md}",
156
+ "lineHeight": "{lineHeight.body}"
157
+ },
158
+ "body": {
159
+ "fontFamily": "{fontFamily.primary}",
160
+ "fontWeight": "{fontWeight.regular}",
161
+ "fontSize": "{fontSize.base}",
162
+ "lineHeight": "{lineHeight.body}"
163
+ },
164
+ "label": {
165
+ "fontFamily": "{fontFamily.primary}",
166
+ "fontWeight": "{fontWeight.medium}",
167
+ "fontSize": "{fontSize.base}",
168
+ "lineHeight": "{lineHeight.body}"
169
+ },
170
+ "gridHeader": {
171
+ "fontFamily": "{fontFamily.primary}",
172
+ "fontWeight": "{fontWeight.medium}",
173
+ "fontSize": "{fontSize.sm}",
174
+ "lineHeight": "{lineHeight.body}"
175
+ },
176
+ "tagLabel": {
177
+ "fontFamily": "{fontFamily.primary}",
178
+ "fontWeight": "{fontWeight.medium}",
179
+ "fontSize": "{fontSize.xs}",
180
+ "lineHeight": "{lineHeight.headings}"
181
+ }
182
+ },
183
+ "spacing": {
184
+ "0": "0px",
185
+ "1": "4px",
186
+ "2": "8px",
187
+ "3": "12px",
188
+ "4": "16px",
189
+ "5": "20px",
190
+ "6": "24px",
191
+ "7": "30px",
192
+ "8": "32px"
193
+ },
194
+ "radius": {
195
+ "sm": "6px",
196
+ "md": "8px",
197
+ "lg": "9px",
198
+ "xl": "12px",
199
+ "pill": "99px"
200
+ },
201
+ "shadow": {
202
+ "default": "0px 1px 8px 0px rgba(0,0,0,0.1)",
203
+ "subtle": "0px 2px 4px 0px rgba(0,0,0,0.05)",
204
+ "wrapper": "0px 3px 4px 0px rgba(0,0,0,0.03)",
205
+ "card": "0px 12px 24px 0px rgba(26,26,30,0.06)",
206
+ "modal": "0px 6px 18px 0px rgba(0,0,0,0.1)",
207
+ "outline": "0px 1px 2px 0px rgba(72,91,120,0.18)",
208
+ "hover": "0px 2px 3px 0px rgba(0,0,0,0.06)"
209
+ },
210
+ "opacity": {
211
+ "disabled": "0.6",
212
+ "tint": "0.15"
213
+ },
214
+ "size": {
215
+ "sidebarExpanded": "215px",
216
+ "sidebarCollapsed": "80px",
217
+ "headerHeight": "60px",
218
+ "contentMax": "1440px",
219
+ "btnDefault": "40px",
220
+ "btnSm": "32px",
221
+ "btnSearch": "38px",
222
+ "inputDefault": "39px",
223
+ "tapTarget": "44px"
224
+ },
225
+ "zIndex": {
226
+ "sidebar": "1000",
227
+ "dropdown": "1001",
228
+ "datepicker": "3000",
229
+ "stickyTopbar": "4000",
230
+ "modalBackdrop": "10050",
231
+ "modalDialog": "10051",
232
+ "preloader": "99999",
233
+ "topmostOverlay": "1000001"
234
+ },
235
+ "motion": {
236
+ "fast": "0.1s",
237
+ "default": "0.2s ease-in-out",
238
+ "slow": "0.3s",
239
+ "bouncy": "0.4s cubic-bezier(0.88, -0.99, 0, 1.81)"
240
+ }
241
+ };
242
+
243
+ module.exports = { tokens };
@@ -0,0 +1,240 @@
1
+ // HotelFriend design tokens — TypeScript const
2
+ // Generated from docs/tokens.figma.json — do NOT edit by hand.
3
+
4
+ export const tokens = {
5
+ "color": {
6
+ "blue": {
7
+ "50": "#E9F6FC",
8
+ "100": "#EFF6FF",
9
+ "500": "#24AFE8",
10
+ "600": "#149AD1"
11
+ },
12
+ "gray": {
13
+ "25": "#FBFBFC",
14
+ "50": "#F6F7FB",
15
+ "100": "#F1F3F6",
16
+ "200": "#E4E8EF",
17
+ "300": "#D1D6DD",
18
+ "400": "#AEBCCF",
19
+ "500": "#99A1B7",
20
+ "550": "#7E8EA6",
21
+ "600": "#50627E",
22
+ "700": "#485B78",
23
+ "800": "#4B5675",
24
+ "900": "#2B2B2B",
25
+ "950": "#252F4A"
26
+ },
27
+ "green": {
28
+ "500": "#59B59D"
29
+ },
30
+ "amber": {
31
+ "400": "#FFBD5A",
32
+ "600": "#FFC900"
33
+ },
34
+ "red": {
35
+ "400": "#EA6565",
36
+ "500": "#FB5A56",
37
+ "700": "#D9534F"
38
+ },
39
+ "orange": {
40
+ "500": "#F87921",
41
+ "700": "#A55505"
42
+ },
43
+ "violet": {
44
+ "500": "#5761D8"
45
+ },
46
+ "slate": {
47
+ "500": "#7F8FA4"
48
+ },
49
+ "olive": {
50
+ "700": "#5B7A02"
51
+ },
52
+ "white": "#FFFFFF",
53
+ "black": "#000000",
54
+ "accent": {
55
+ "default": "{primitive.color.blue.500}",
56
+ "hover": "{primitive.color.blue.600}",
57
+ "subtle": "{primitive.color.blue.50}",
58
+ "subtler": "{primitive.color.blue.100}"
59
+ },
60
+ "on-accent": "{primitive.color.white}",
61
+ "bg": {
62
+ "page": "{primitive.color.gray.25}",
63
+ "section": "{primitive.color.gray.50}",
64
+ "muted": "{primitive.color.gray.100}",
65
+ "surface": "{primitive.color.white}"
66
+ },
67
+ "fg": {
68
+ "default": "{primitive.color.gray.900}",
69
+ "muted": "{primitive.color.gray.800}",
70
+ "subtle": "{primitive.color.gray.550}",
71
+ "faint": "{primitive.color.gray.400}"
72
+ },
73
+ "border": {
74
+ "default": "{primitive.color.gray.300}",
75
+ "subtle": "{primitive.color.gray.200}",
76
+ "strong": "{primitive.color.gray.400}"
77
+ },
78
+ "tab": {
79
+ "fg-inactive": "{primitive.color.gray.700}",
80
+ "count-fg": "{primitive.color.gray.700}"
81
+ },
82
+ "pagination": {
83
+ "fg-inactive": "{primitive.color.gray.700}",
84
+ "fg-active": "{primitive.color.gray.950}"
85
+ },
86
+ "input": {
87
+ "border": "#DBDFE9"
88
+ },
89
+ "menu": {
90
+ "bg-hover": "#F5F5F5"
91
+ },
92
+ "status": {
93
+ "success": "{primitive.color.green.500}",
94
+ "success-bg": "rgba(89,181,157,0.15)",
95
+ "warning": "{primitive.color.amber.400}",
96
+ "warning-bg": "rgba(255,189,90,0.15)",
97
+ "warning-strong": "{primitive.color.amber.600}",
98
+ "warning-strong-bg": "rgba(255,201,0,0.15)",
99
+ "error": "{primitive.color.red.400}",
100
+ "error-bg": "rgba(234,101,101,0.15)",
101
+ "error-strong": "{primitive.color.red.700}",
102
+ "error-alt": "{primitive.color.red.500}",
103
+ "info": "{semantic.color.accent.default}",
104
+ "info-bg": "rgba(36,175,232,0.15)",
105
+ "cancel": "{primitive.color.slate.500}",
106
+ "cancel-bg": "rgba(127,143,164,0.15)",
107
+ "coral": "{primitive.color.orange.500}",
108
+ "coral-bg": "rgba(248,121,33,0.15)",
109
+ "violet": "{primitive.color.violet.500}",
110
+ "violet-bg": "rgba(87,97,216,0.15)",
111
+ "olive": "{primitive.color.olive.700}",
112
+ "olive-bg": "rgba(91,122,2,0.15)",
113
+ "orange-deep": "{primitive.color.orange.700}",
114
+ "orange-deep-bg": "rgba(165,85,5,0.15)"
115
+ }
116
+ },
117
+ "fontFamily": {
118
+ "primary": "Roboto"
119
+ },
120
+ "fontWeight": {
121
+ "regular": "400",
122
+ "medium": "500",
123
+ "semibold": "600"
124
+ },
125
+ "fontSize": {
126
+ "xs": "11px",
127
+ "sm": "13px",
128
+ "base": "14px",
129
+ "md": "15px",
130
+ "lg": "16px",
131
+ "xl": "18px",
132
+ "2xl": "20px",
133
+ "h2": "24px",
134
+ "page": "26px",
135
+ "hero": "30px"
136
+ },
137
+ "lineHeight": {
138
+ "headings": "1.2",
139
+ "body": "1.5",
140
+ "tight": "1"
141
+ },
142
+ "typography": {
143
+ "pageTitle": {
144
+ "fontFamily": "{fontFamily.primary}",
145
+ "fontWeight": "{fontWeight.semibold}",
146
+ "fontSize": "{fontSize.page}",
147
+ "lineHeight": "{lineHeight.headings}"
148
+ },
149
+ "btn": {
150
+ "fontFamily": "{fontFamily.primary}",
151
+ "fontWeight": "{fontWeight.semibold}",
152
+ "fontSize": "{fontSize.md}",
153
+ "lineHeight": "{lineHeight.body}"
154
+ },
155
+ "body": {
156
+ "fontFamily": "{fontFamily.primary}",
157
+ "fontWeight": "{fontWeight.regular}",
158
+ "fontSize": "{fontSize.base}",
159
+ "lineHeight": "{lineHeight.body}"
160
+ },
161
+ "label": {
162
+ "fontFamily": "{fontFamily.primary}",
163
+ "fontWeight": "{fontWeight.medium}",
164
+ "fontSize": "{fontSize.base}",
165
+ "lineHeight": "{lineHeight.body}"
166
+ },
167
+ "gridHeader": {
168
+ "fontFamily": "{fontFamily.primary}",
169
+ "fontWeight": "{fontWeight.medium}",
170
+ "fontSize": "{fontSize.sm}",
171
+ "lineHeight": "{lineHeight.body}"
172
+ },
173
+ "tagLabel": {
174
+ "fontFamily": "{fontFamily.primary}",
175
+ "fontWeight": "{fontWeight.medium}",
176
+ "fontSize": "{fontSize.xs}",
177
+ "lineHeight": "{lineHeight.headings}"
178
+ }
179
+ },
180
+ "spacing": {
181
+ "0": "0px",
182
+ "1": "4px",
183
+ "2": "8px",
184
+ "3": "12px",
185
+ "4": "16px",
186
+ "5": "20px",
187
+ "6": "24px",
188
+ "7": "30px",
189
+ "8": "32px"
190
+ },
191
+ "radius": {
192
+ "sm": "6px",
193
+ "md": "8px",
194
+ "lg": "9px",
195
+ "xl": "12px",
196
+ "pill": "99px"
197
+ },
198
+ "shadow": {
199
+ "default": "0px 1px 8px 0px rgba(0,0,0,0.1)",
200
+ "subtle": "0px 2px 4px 0px rgba(0,0,0,0.05)",
201
+ "wrapper": "0px 3px 4px 0px rgba(0,0,0,0.03)",
202
+ "card": "0px 12px 24px 0px rgba(26,26,30,0.06)",
203
+ "modal": "0px 6px 18px 0px rgba(0,0,0,0.1)",
204
+ "outline": "0px 1px 2px 0px rgba(72,91,120,0.18)",
205
+ "hover": "0px 2px 3px 0px rgba(0,0,0,0.06)"
206
+ },
207
+ "opacity": {
208
+ "disabled": "0.6",
209
+ "tint": "0.15"
210
+ },
211
+ "size": {
212
+ "sidebarExpanded": "215px",
213
+ "sidebarCollapsed": "80px",
214
+ "headerHeight": "60px",
215
+ "contentMax": "1440px",
216
+ "btnDefault": "40px",
217
+ "btnSm": "32px",
218
+ "btnSearch": "38px",
219
+ "inputDefault": "39px",
220
+ "tapTarget": "44px"
221
+ },
222
+ "zIndex": {
223
+ "sidebar": "1000",
224
+ "dropdown": "1001",
225
+ "datepicker": "3000",
226
+ "stickyTopbar": "4000",
227
+ "modalBackdrop": "10050",
228
+ "modalDialog": "10051",
229
+ "preloader": "99999",
230
+ "topmostOverlay": "1000001"
231
+ },
232
+ "motion": {
233
+ "fast": "0.1s",
234
+ "default": "0.2s ease-in-out",
235
+ "slow": "0.3s",
236
+ "bouncy": "0.4s cubic-bezier(0.88, -0.99, 0, 1.81)"
237
+ }
238
+ } as const;
239
+
240
+ export type Tokens = typeof tokens;
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env bash
2
+ # integration-smoke.sh — sanity checks for design-system as a consumed submodule/package.
3
+ #
4
+ # Runs:
5
+ # 1. `npm run build` is byte-stable (no drift in pre-built/ after rebuild)
6
+ # 2. `npm run validate` passes
7
+ # 3. Critical hf-prefixed tokens exist in pre-built/tailwind.css
8
+ # 4. Critical .hf-* primitives exist in pre-built/components.css
9
+ # 5. Every path in package.json `exports` resolves to an existing file
10
+ #
11
+ # Exits non-zero on any failure. Suitable for CI in this repo AND for running
12
+ # from a consumer project (e.g. `cd design-system && ./scripts/integration-smoke.sh`)
13
+ # right after `git submodule update --remote` to confirm the bumped pin is healthy.
14
+
15
+ set -euo pipefail
16
+
17
+ cd "$(dirname "$0")/.."
18
+
19
+ fail() { printf '✗ %s\n' "$*" >&2; exit 1; }
20
+ ok() { printf '✓ %s\n' "$*"; }
21
+
22
+ # --- 1. Build is reproducible ----------------------------------------------
23
+ echo "→ rebuild + drift check"
24
+ npm run --silent build >/dev/null
25
+ if ! git diff --quiet -- pre-built/; then
26
+ git --no-pager diff --stat -- pre-built/
27
+ fail "pre-built/ drifts after rebuild — sources and outputs are out of sync"
28
+ fi
29
+ ok "pre-built/ is byte-stable"
30
+
31
+ # --- 2. Validator passes ---------------------------------------------------
32
+ echo "→ validate-tokens"
33
+ npm run --silent validate >/dev/null
34
+ ok "validate-tokens: all checks passed"
35
+
36
+ # --- 3. Tailwind v4 @theme has the canonical hf-prefixed tokens ------------
37
+ echo "→ tailwind.css canonical tokens"
38
+ required_tw=(
39
+ "--color-hf-accent"
40
+ "--color-hf-accent-hover"
41
+ "--color-hf-fg"
42
+ "--color-hf-border"
43
+ "--text-hf-base"
44
+ "--radius-hf-sm"
45
+ "--spacing-hf-7"
46
+ "--font-hf-sans"
47
+ "--shadow-hf-modal"
48
+ )
49
+ for name in "${required_tw[@]}"; do
50
+ grep -qF -- "$name" pre-built/tailwind.css \
51
+ || fail "missing token in pre-built/tailwind.css: $name (Phase 1A regression?)"
52
+ done
53
+ ok "tailwind.css carries all canonical hf-prefixed tokens"
54
+
55
+ # --- 4. components.css has the headline .hf-* primitives -------------------
56
+ echo "→ components.css primitives"
57
+ required_hf=(
58
+ ".hf-modal"
59
+ ".hf-pill"
60
+ ".hf-tab"
61
+ ".hf-pagination"
62
+ ".hf-alert"
63
+ ".hf-check"
64
+ ".hf-radio"
65
+ ".hf-dropdown-menu"
66
+ )
67
+ for cls in "${required_hf[@]}"; do
68
+ grep -qF -- "$cls" pre-built/components.css \
69
+ || fail "missing primitive in pre-built/components.css: $cls"
70
+ done
71
+ ok "components.css contains all headline .hf-* primitives"
72
+
73
+ # --- 5. Every exports path resolves ----------------------------------------
74
+ echo "→ package.json exports resolution"
75
+ node --input-type=module -e '
76
+ import("node:fs").then(({readFileSync, existsSync}) => {
77
+ const pkg = JSON.parse(readFileSync("package.json", "utf8"));
78
+ const flatten = (v) => typeof v === "string"
79
+ ? [v]
80
+ : Object.values(v).flatMap(flatten);
81
+ const targets = Object.values(pkg.exports || {}).flatMap(flatten);
82
+ const missing = targets.filter(p => !existsSync(p.replace(/^\.\//, "")));
83
+ if (missing.length) {
84
+ console.error("missing exports targets:\n - " + missing.join("\n - "));
85
+ process.exit(1);
86
+ }
87
+ });
88
+ ' || fail "package.json exports references files that do not exist"
89
+ ok "all package.json exports resolve to existing files"
90
+
91
+ printf '\n✓ integration smoke OK — design-system is ready to consume.\n'
@@ -0,0 +1,55 @@
1
+ #!/bin/sh
2
+ # Pre-commit hook — runs validate-tokens.cjs to catch design-system drift
3
+ # before commit. Drop into .husky/pre-commit (with husky) or .git/hooks/pre-commit.
4
+ #
5
+ # Phase 3C (RFC-0001 §4.6) — drift detection enforcement.
6
+ #
7
+ # Setup:
8
+ #
9
+ # With husky (recommended):
10
+ # pnpm add -D husky
11
+ # pnpm husky init
12
+ # cp docs/portable-design/scripts/pre-commit.sh .husky/pre-commit
13
+ # chmod +x .husky/pre-commit
14
+ #
15
+ # Without husky (raw git hook):
16
+ # cp docs/portable-design/scripts/pre-commit.sh .git/hooks/pre-commit
17
+ # chmod +x .git/hooks/pre-commit
18
+ #
19
+ # The hook is silent on success and prints a clear error list on failure.
20
+
21
+ set -e
22
+
23
+ # Resolve the bundle path. Adjust if your bundle lives elsewhere.
24
+ BUNDLE_DIR="docs/portable-design"
25
+
26
+ if [ ! -d "$BUNDLE_DIR" ]; then
27
+ # If bundle moved, try common alternatives
28
+ for alt in "design-system" "packages/design-tokens" "."; do
29
+ if [ -f "$alt/scripts/validate-tokens.cjs" ]; then
30
+ BUNDLE_DIR="$alt"
31
+ break
32
+ fi
33
+ done
34
+ fi
35
+
36
+ if [ ! -f "$BUNDLE_DIR/scripts/validate-tokens.cjs" ]; then
37
+ echo "⚠ pre-commit: validate-tokens.cjs not found at $BUNDLE_DIR/scripts/"
38
+ echo " Skipping design-system drift check. Update BUNDLE_DIR in this hook."
39
+ exit 0
40
+ fi
41
+
42
+ # Only run if any token-related file is staged
43
+ STAGED=$(git diff --cached --name-only)
44
+ if echo "$STAGED" | grep -qE "$BUNDLE_DIR/(tokens\.figma\.json|status-map\.json|pre-built/.*\.(css|js|ts|cjs|scss)|.*\.md|components\.html|generate-tokens\.cjs)"; then
45
+ echo "→ Running design-system validator (changes detected in bundle)…"
46
+ cd "$BUNDLE_DIR"
47
+ if ! node scripts/validate-tokens.cjs; then
48
+ echo ""
49
+ echo "✗ pre-commit: design-system drift detected. Fix the issues above and re-commit."
50
+ echo " To bypass (NOT recommended), use: git commit --no-verify"
51
+ exit 1
52
+ fi
53
+ fi
54
+
55
+ exit 0