@servicetitan/hammer-token 2.5.0 → 3.0.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.
Files changed (147) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +332 -0
  3. package/build/web/core/component-variables.scss +1088 -131
  4. package/build/web/core/component.d.ts +558 -0
  5. package/build/web/core/component.js +6685 -249
  6. package/build/web/core/component.scss +557 -69
  7. package/build/web/core/css-utils/a2-border.css +47 -45
  8. package/build/web/core/css-utils/a2-color.css +443 -227
  9. package/build/web/core/css-utils/a2-font.css +0 -2
  10. package/build/web/core/css-utils/a2-spacing.css +476 -478
  11. package/build/web/core/css-utils/a2-utils.css +992 -772
  12. package/build/web/core/css-utils/border.css +47 -45
  13. package/build/web/core/css-utils/color.css +443 -227
  14. package/build/web/core/css-utils/font.css +0 -2
  15. package/build/web/core/css-utils/spacing.css +476 -478
  16. package/build/web/core/css-utils/utils.css +992 -772
  17. package/build/web/core/index.d.ts +6 -0
  18. package/build/web/core/index.js +1 -1
  19. package/build/web/core/primitive-variables.scss +148 -65
  20. package/build/web/core/primitive.d.ts +209 -0
  21. package/build/web/core/primitive.js +779 -61
  22. package/build/web/core/primitive.scss +207 -124
  23. package/build/web/core/semantic-variables.scss +363 -239
  24. package/build/web/core/semantic.d.ts +221 -0
  25. package/build/web/core/semantic.js +1613 -347
  26. package/build/web/core/semantic.scss +219 -137
  27. package/build/web/index.d.ts +3 -4
  28. package/build/web/index.js +0 -1
  29. package/build/web/types.d.ts +17 -0
  30. package/config.js +121 -497
  31. package/eslint.config.mjs +11 -1
  32. package/package.json +15 -5
  33. package/src/global/primitive/breakpoint.tokens.json +54 -0
  34. package/src/global/primitive/color.tokens.json +1092 -0
  35. package/src/global/primitive/duration.tokens.json +44 -0
  36. package/src/global/primitive/font.tokens.json +151 -0
  37. package/src/global/primitive/radius.tokens.json +94 -0
  38. package/src/global/primitive/size.tokens.json +174 -0
  39. package/src/global/primitive/transition.tokens.json +32 -0
  40. package/src/theme/core/background.tokens.json +1312 -0
  41. package/src/theme/core/border.tokens.json +192 -0
  42. package/src/theme/core/chart.tokens.json +982 -0
  43. package/src/theme/core/component/ai-mark.tokens.json +20 -0
  44. package/src/theme/core/component/alert.tokens.json +261 -0
  45. package/src/theme/core/component/announcement.tokens.json +460 -0
  46. package/src/theme/core/component/avatar.tokens.json +137 -0
  47. package/src/theme/core/component/badge.tokens.json +42 -0
  48. package/src/theme/core/component/breadcrumb.tokens.json +42 -0
  49. package/src/theme/core/component/button-toggle.tokens.json +428 -0
  50. package/src/theme/core/component/button.tokens.json +941 -0
  51. package/src/theme/core/component/calendar.tokens.json +391 -0
  52. package/src/theme/core/component/card.tokens.json +107 -0
  53. package/src/theme/core/component/checkbox.tokens.json +631 -0
  54. package/src/theme/core/component/chip.tokens.json +169 -0
  55. package/src/theme/core/component/combobox.tokens.json +269 -0
  56. package/src/theme/core/component/details.tokens.json +152 -0
  57. package/src/theme/core/component/dialog.tokens.json +87 -0
  58. package/src/theme/core/component/divider.tokens.json +23 -0
  59. package/src/theme/core/component/dnd.tokens.json +208 -0
  60. package/src/theme/core/component/drawer.tokens.json +61 -0
  61. package/src/theme/core/component/drilldown.tokens.json +61 -0
  62. package/src/theme/core/component/edit-card.tokens.json +381 -0
  63. package/src/theme/core/component/field-label.tokens.json +42 -0
  64. package/src/theme/core/component/field-message.tokens.json +74 -0
  65. package/src/theme/core/component/icon.tokens.json +42 -0
  66. package/src/theme/core/component/link.tokens.json +108 -0
  67. package/src/theme/core/component/list-view.tokens.json +82 -0
  68. package/src/theme/core/component/listbox.tokens.json +283 -0
  69. package/src/theme/core/component/menu.tokens.json +230 -0
  70. package/src/theme/core/component/overflow.tokens.json +84 -0
  71. package/src/theme/core/component/page.tokens.json +377 -0
  72. package/src/theme/core/component/pagination.tokens.json +63 -0
  73. package/src/theme/core/component/popover.tokens.json +122 -0
  74. package/src/theme/core/component/progress-bar.tokens.json +133 -0
  75. package/src/theme/core/component/radio.tokens.json +631 -0
  76. package/src/theme/core/component/segmented-control.tokens.json +175 -0
  77. package/src/theme/core/component/select-card.tokens.json +943 -0
  78. package/src/theme/core/component/side-nav.tokens.json +349 -0
  79. package/src/theme/core/component/skeleton.tokens.json +42 -0
  80. package/src/theme/core/component/spinner.tokens.json +96 -0
  81. package/src/theme/core/component/status-icon.tokens.json +164 -0
  82. package/src/theme/core/component/stepper.tokens.json +484 -0
  83. package/src/theme/core/component/switch.tokens.json +285 -0
  84. package/src/theme/core/component/tab.tokens.json +192 -0
  85. package/src/theme/core/component/text-field.tokens.json +160 -0
  86. package/src/theme/core/component/text.tokens.json +59 -0
  87. package/src/theme/core/component/toast.tokens.json +343 -0
  88. package/src/theme/core/component/toolbar.tokens.json +114 -0
  89. package/src/theme/core/component/tooltip.tokens.json +61 -0
  90. package/src/theme/core/focus.tokens.json +56 -0
  91. package/src/theme/core/foreground.tokens.json +416 -0
  92. package/src/theme/core/gradient.tokens.json +41 -0
  93. package/src/theme/core/opacity.tokens.json +25 -0
  94. package/src/theme/core/shadow.tokens.json +81 -0
  95. package/src/theme/core/status.tokens.json +74 -0
  96. package/src/theme/core/typography.tokens.json +163 -0
  97. package/src/utils/__tests__/css-utils-format-utils.test.js +312 -0
  98. package/src/utils/__tests__/sd-build-configs.test.js +306 -0
  99. package/src/utils/__tests__/sd-formats.test.js +950 -0
  100. package/src/utils/__tests__/sd-transforms.test.js +336 -0
  101. package/src/utils/__tests__/token-helpers.test.js +1160 -0
  102. package/src/utils/copy-css-utils-cli.js +13 -1
  103. package/src/utils/css-utils-format-utils.js +105 -176
  104. package/src/utils/figma/__tests__/sync-gradient.test.js +561 -0
  105. package/src/utils/figma/__tests__/token-conversion.test.js +117 -0
  106. package/src/utils/figma/__tests__/token-resolution.test.js +231 -0
  107. package/src/utils/figma/auth.js +355 -0
  108. package/src/utils/figma/constants.js +22 -0
  109. package/src/utils/figma/errors.js +80 -0
  110. package/src/utils/figma/figma-api.js +1069 -0
  111. package/src/utils/figma/get-token.js +348 -0
  112. package/src/utils/figma/sync-components.js +909 -0
  113. package/src/utils/figma/sync-main.js +692 -0
  114. package/src/utils/figma/sync-orchestration.js +683 -0
  115. package/src/utils/figma/sync-primitives.js +230 -0
  116. package/src/utils/figma/sync-semantic.js +1056 -0
  117. package/src/utils/figma/token-conversion.js +340 -0
  118. package/src/utils/figma/token-parsing.js +186 -0
  119. package/src/utils/figma/token-resolution.js +569 -0
  120. package/src/utils/figma/utils.js +199 -0
  121. package/src/utils/sd-build-configs.js +305 -0
  122. package/src/utils/sd-formats.js +965 -0
  123. package/src/utils/sd-transforms.js +165 -0
  124. package/src/utils/token-helpers.js +848 -0
  125. package/tsconfig.json +18 -0
  126. package/vitest.config.js +17 -0
  127. package/.turbo/turbo-build.log +0 -37
  128. package/build/web/core/raw.js +0 -229
  129. package/src/global/primitive/breakpoint.js +0 -19
  130. package/src/global/primitive/color.js +0 -231
  131. package/src/global/primitive/duration.js +0 -16
  132. package/src/global/primitive/font.js +0 -60
  133. package/src/global/primitive/radius.js +0 -31
  134. package/src/global/primitive/size.js +0 -55
  135. package/src/global/primitive/transition.js +0 -16
  136. package/src/theme/core/background.js +0 -170
  137. package/src/theme/core/border.js +0 -103
  138. package/src/theme/core/charts.js +0 -439
  139. package/src/theme/core/component/button.js +0 -708
  140. package/src/theme/core/component/checkbox.js +0 -405
  141. package/src/theme/core/focus.js +0 -35
  142. package/src/theme/core/foreground.js +0 -148
  143. package/src/theme/core/overlay.js +0 -137
  144. package/src/theme/core/shadow.js +0 -29
  145. package/src/theme/core/status.js +0 -49
  146. package/src/theme/core/typography.js +0 -82
  147. package/type/types.ts +0 -341
@@ -0,0 +1,163 @@
1
+ {
2
+ "typography": {
3
+ "paragraph": {
4
+ "size": {
5
+ "xsmall": {
6
+ "$type": "dimension",
7
+ "$value": "{font.size.200}",
8
+ "$extensions": {
9
+ "com.figma.scopes": ["FONT_SIZE"]
10
+ }
11
+ },
12
+ "small": {
13
+ "$type": "dimension",
14
+ "$value": "{font.size.300}",
15
+ "$extensions": {
16
+ "com.figma.scopes": ["FONT_SIZE"]
17
+ }
18
+ },
19
+ "default": {
20
+ "$type": "dimension",
21
+ "$value": "{font.size.400}",
22
+ "$extensions": {
23
+ "com.figma.scopes": ["FONT_SIZE"]
24
+ }
25
+ },
26
+ "large": {
27
+ "$type": "dimension",
28
+ "$value": "{font.size.500}",
29
+ "$extensions": {
30
+ "com.figma.scopes": ["FONT_SIZE"]
31
+ }
32
+ },
33
+ "xlarge": {
34
+ "$type": "dimension",
35
+ "$value": "{font.size.600}",
36
+ "$extensions": {
37
+ "com.figma.scopes": ["FONT_SIZE"]
38
+ }
39
+ }
40
+ },
41
+ "font-weight": {
42
+ "$type": "number",
43
+ "$value": "{font.weight.normal}",
44
+ "$extensions": {
45
+ "com.figma.scopes": ["FONT_STYLE"]
46
+ }
47
+ },
48
+ "font-family": {
49
+ "$type": "string",
50
+ "$value": "{font.family.base}",
51
+ "$extensions": {
52
+ "com.figma.scopes": ["FONT_FAMILY"]
53
+ }
54
+ }
55
+ },
56
+ "heading": {
57
+ "size": {
58
+ "xsmall": {
59
+ "$type": "dimension",
60
+ "$value": "{font.size.300}",
61
+ "$extensions": {
62
+ "com.figma.scopes": ["FONT_SIZE"]
63
+ }
64
+ },
65
+ "small": {
66
+ "$type": "dimension",
67
+ "$value": "{font.size.400}",
68
+ "$extensions": {
69
+ "com.figma.scopes": ["FONT_SIZE"]
70
+ }
71
+ },
72
+ "default": {
73
+ "$type": "dimension",
74
+ "$value": "{font.size.500}",
75
+ "$extensions": {
76
+ "com.figma.scopes": ["FONT_SIZE"]
77
+ }
78
+ },
79
+ "large": {
80
+ "$type": "dimension",
81
+ "$value": "{font.size.600}",
82
+ "$extensions": {
83
+ "com.figma.scopes": ["FONT_SIZE"]
84
+ }
85
+ },
86
+ "xlarge": {
87
+ "$type": "dimension",
88
+ "$value": "{font.size.800}",
89
+ "$extensions": {
90
+ "com.figma.scopes": ["FONT_SIZE"]
91
+ }
92
+ }
93
+ },
94
+ "font-weight": {
95
+ "$type": "number",
96
+ "$value": "{font.weight.bold}",
97
+ "$extensions": {
98
+ "com.figma.scopes": ["FONT_STYLE"]
99
+ }
100
+ },
101
+ "font-family": {
102
+ "$type": "string",
103
+ "$value": "{font.family.display}",
104
+ "$extensions": {
105
+ "com.figma.scopes": ["FONT_FAMILY"]
106
+ }
107
+ }
108
+ },
109
+ "label": {
110
+ "size": {
111
+ "xsmall": {
112
+ "$type": "dimension",
113
+ "$value": "{font.size.100}",
114
+ "$extensions": {
115
+ "com.figma.scopes": ["FONT_SIZE"]
116
+ }
117
+ },
118
+ "small": {
119
+ "$type": "dimension",
120
+ "$value": "{font.size.200}",
121
+ "$extensions": {
122
+ "com.figma.scopes": ["FONT_SIZE"]
123
+ }
124
+ },
125
+ "default": {
126
+ "$type": "dimension",
127
+ "$value": "{font.size.300}",
128
+ "$extensions": {
129
+ "com.figma.scopes": ["FONT_SIZE"]
130
+ }
131
+ },
132
+ "large": {
133
+ "$type": "dimension",
134
+ "$value": "{font.size.400}",
135
+ "$extensions": {
136
+ "com.figma.scopes": ["FONT_SIZE"]
137
+ }
138
+ },
139
+ "xlarge": {
140
+ "$type": "dimension",
141
+ "$value": "{font.size.500}",
142
+ "$extensions": {
143
+ "com.figma.scopes": ["FONT_SIZE"]
144
+ }
145
+ }
146
+ },
147
+ "font-weight": {
148
+ "$type": "number",
149
+ "$value": "{font.weight.semibold}",
150
+ "$extensions": {
151
+ "com.figma.scopes": ["FONT_STYLE"]
152
+ }
153
+ },
154
+ "font-family": {
155
+ "$type": "string",
156
+ "$value": "{font.family.base}",
157
+ "$extensions": {
158
+ "com.figma.scopes": ["FONT_FAMILY"]
159
+ }
160
+ }
161
+ }
162
+ }
163
+ }
@@ -0,0 +1,312 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ generateBorderClasses,
4
+ generateColorClasses,
5
+ generateFontClasses,
6
+ generateSpacingClasses,
7
+ } from "../css-utils-format-utils.js";
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // generateBorderClasses
11
+ // ---------------------------------------------------------------------------
12
+
13
+ describe("generateBorderClasses", () => {
14
+ describe("border-radius tokens", () => {
15
+ it("generates border-radius class with CSS var fallback", () => {
16
+ const result = generateBorderClasses("border-radius-small", "4px", {
17
+ prefix: "a2-",
18
+ cssVarPrefix: "a2-",
19
+ });
20
+ expect(result).toEqual([
21
+ ".a2-border-radius-small {border-radius: var(--a2-border-radius-small, 4px)}",
22
+ ]);
23
+ });
24
+
25
+ it("works without prefix or cssVarPrefix", () => {
26
+ const result = generateBorderClasses("border-radius-medium", "6px");
27
+ expect(result).toEqual([
28
+ ".border-radius-medium {border-radius: var(--border-radius-medium, 6px)}",
29
+ ]);
30
+ });
31
+ });
32
+
33
+ describe("border-width tokens", () => {
34
+ it("generates border-width class with CSS var fallback", () => {
35
+ const result = generateBorderClasses("border-width-thin", "1px", {
36
+ prefix: "a2-",
37
+ cssVarPrefix: "a2-",
38
+ });
39
+ expect(result).toEqual([
40
+ ".a2-border-width-thin {border-width: var(--a2-border-width-thin, 1px)}",
41
+ ]);
42
+ });
43
+ });
44
+
45
+ describe("border-color tokens", () => {
46
+ it("generates border-color class without dark value", () => {
47
+ const result = generateBorderClasses("border-color-default", "#ccc", {
48
+ prefix: "a2-",
49
+ cssVarPrefix: "a2-",
50
+ });
51
+ expect(result).toEqual([
52
+ ".a2-border-color-default {border-color: var(--a2-border-color-default, #ccc)}",
53
+ ]);
54
+ });
55
+
56
+ it("includes light-dark() in border-color class when darkValue is provided", () => {
57
+ const result = generateBorderClasses("border-color-default", "#ccc", {
58
+ prefix: "a2-",
59
+ cssVarPrefix: "a2-",
60
+ darkValue: "#333",
61
+ });
62
+ expect(result).toEqual([
63
+ ".a2-border-color-default {border-color: var(--a2-border-color-default, light-dark(#ccc, #333))}",
64
+ ]);
65
+ });
66
+ });
67
+
68
+ describe("unrecognized token names", () => {
69
+ it("returns empty array for unrecognized names", () => {
70
+ const result = generateBorderClasses(
71
+ "shadow-default",
72
+ "0 0 4px rgba(0,0,0,0.2)",
73
+ {
74
+ prefix: "a2-",
75
+ cssVarPrefix: "a2-",
76
+ },
77
+ );
78
+ expect(result).toEqual([]);
79
+ });
80
+ });
81
+ });
82
+
83
+ // ---------------------------------------------------------------------------
84
+ // generateColorClasses
85
+ // ---------------------------------------------------------------------------
86
+
87
+ describe("generateColorClasses", () => {
88
+ describe("background-color tokens", () => {
89
+ it("generates .bg-* class with background-color property", () => {
90
+ const result = generateColorClasses("background-color-primary", "#fff", {
91
+ prefix: "a2-",
92
+ cssVarPrefix: "a2-",
93
+ });
94
+ expect(result).toEqual([
95
+ ".a2-bg-primary {background-color: var(--a2-background-color-primary, #fff)}",
96
+ ]);
97
+ });
98
+
99
+ it("includes light-dark() when darkValue provided", () => {
100
+ const result = generateColorClasses("background-color-primary", "#fff", {
101
+ prefix: "a2-",
102
+ cssVarPrefix: "a2-",
103
+ darkValue: "#111",
104
+ });
105
+ expect(result[0]).toContain("light-dark(#fff, #111)");
106
+ });
107
+ });
108
+
109
+ describe("foreground-color tokens", () => {
110
+ it("generates .c-* class with color property", () => {
111
+ const result = generateColorClasses("foreground-color-default", "#333", {
112
+ prefix: "a2-",
113
+ cssVarPrefix: "a2-",
114
+ });
115
+ expect(result).toEqual([
116
+ ".a2-c-default {color: var(--a2-foreground-color-default, #333)}",
117
+ ]);
118
+ });
119
+ });
120
+
121
+ describe("overlay-color tokens", () => {
122
+ it("generates .bg-overlay-* class with background-color property", () => {
123
+ const result = generateColorClasses(
124
+ "overlay-color-scrim",
125
+ "rgba(0,0,0,0.5)",
126
+ { prefix: "a2-", cssVarPrefix: "a2-" },
127
+ );
128
+ expect(result).toEqual([
129
+ ".a2-bg-overlay-scrim {background-color: var(--a2-overlay-color-scrim, rgba(0,0,0,0.5))}",
130
+ ]);
131
+ });
132
+ });
133
+
134
+ describe("status-color tokens", () => {
135
+ it("generates 3 classes for non-danger status tokens", () => {
136
+ const result = generateColorClasses(
137
+ "status-color-success-default",
138
+ "#2d8a4e",
139
+ { prefix: "a2-", cssVarPrefix: "a2-" },
140
+ );
141
+ expect(result).toHaveLength(3);
142
+ // c-status class
143
+ expect(result[0]).toBe(
144
+ ".a2-c-status-success-default {color: var(--a2-status-color-success-default, #2d8a4e)}",
145
+ );
146
+ // bg-status class
147
+ expect(result[1]).toBe(
148
+ ".a2-bg-status-success-default {background-color: var(--a2-status-color-success-default, #2d8a4e)}",
149
+ );
150
+ // border-color-status class
151
+ expect(result[2]).toBe(
152
+ ".a2-border-color-status-success-default {border-color: var(--a2-status-color-success-default, #2d8a4e)}",
153
+ );
154
+ });
155
+
156
+ it("generates only 2 classes for danger status tokens (no border)", () => {
157
+ const result = generateColorClasses(
158
+ "status-color-danger-default",
159
+ "#e13212",
160
+ { prefix: "a2-", cssVarPrefix: "a2-" },
161
+ );
162
+ expect(result).toHaveLength(2);
163
+ expect(result.some((c) => c.includes("border-color"))).toBe(false);
164
+ });
165
+ });
166
+
167
+ describe("border-color tokens", () => {
168
+ it("generates border-color class with color property", () => {
169
+ const result = generateColorClasses("border-color-default", "#ddd", {
170
+ prefix: "a2-",
171
+ cssVarPrefix: "a2-",
172
+ });
173
+ expect(result).toEqual([
174
+ ".a2-border-color-default {border-color: var(--a2-border-color-default, #ddd)}",
175
+ ]);
176
+ });
177
+ });
178
+
179
+ describe("unrecognized token names", () => {
180
+ it("returns empty array", () => {
181
+ const result = generateColorClasses("typography-body-font-size", "1rem", {
182
+ prefix: "a2-",
183
+ });
184
+ expect(result).toEqual([]);
185
+ });
186
+ });
187
+
188
+ describe("no options", () => {
189
+ it("works without options (uses defaults)", () => {
190
+ const result = generateColorClasses("background-color-page", "#fff");
191
+ expect(result[0]).toBe(
192
+ ".bg-page {background-color: var(--background-color-page, #fff)}",
193
+ );
194
+ });
195
+ });
196
+ });
197
+
198
+ // ---------------------------------------------------------------------------
199
+ // generateFontClasses
200
+ // ---------------------------------------------------------------------------
201
+
202
+ describe("generateFontClasses", () => {
203
+ it("generates .ff-* class for font-family tokens", () => {
204
+ const result = generateFontClasses(
205
+ "typography-body-font-family",
206
+ "Arial, sans-serif",
207
+ { prefix: "a2-" },
208
+ );
209
+ expect(result).toEqual([".a2-ff-body {font-family: Arial, sans-serif}"]);
210
+ });
211
+
212
+ it("generates .fw-* class for font-weight tokens", () => {
213
+ const result = generateFontClasses("typography-body-font-weight", "400", {
214
+ prefix: "a2-",
215
+ });
216
+ expect(result).toEqual([".a2-fw-body {font-weight: 400}"]);
217
+ });
218
+
219
+ it("generates .fs-* class for font-size tokens", () => {
220
+ const result = generateFontClasses("typography-body-size", "1rem", {
221
+ prefix: "a2-",
222
+ });
223
+ expect(result).toEqual([".a2-fs-body {font-size: 1rem}"]);
224
+ });
225
+
226
+ it("returns empty array for non-typography tokens", () => {
227
+ const result = generateFontClasses("color-primary-500", "#fff", {
228
+ prefix: "a2-",
229
+ });
230
+ expect(result).toEqual([]);
231
+ });
232
+
233
+ it("returns empty array for typography token without recognized sub-type", () => {
234
+ const result = generateFontClasses("typography-body-line-height", "1.5", {
235
+ prefix: "a2-",
236
+ });
237
+ expect(result).toEqual([]);
238
+ });
239
+
240
+ it("works without prefix option (default empty)", () => {
241
+ const result = generateFontClasses(
242
+ "typography-heading-font-family",
243
+ "Georgia",
244
+ );
245
+ expect(result).toEqual([".ff-heading {font-family: Georgia}"]);
246
+ });
247
+ });
248
+
249
+ // ---------------------------------------------------------------------------
250
+ // generateSpacingClasses
251
+ // ---------------------------------------------------------------------------
252
+
253
+ describe("generateSpacingClasses", () => {
254
+ const name = "size-4";
255
+ const value = "1rem";
256
+ const opts = { prefix: "a2-", cssVarPrefix: "a2-" };
257
+
258
+ it("generates 14 classes total (6 directions × 2 + 2 shorthand)", () => {
259
+ const result = generateSpacingClasses(name, value, opts);
260
+ expect(result).toHaveLength(14);
261
+ });
262
+
263
+ it("generates margin-inline-start class", () => {
264
+ const result = generateSpacingClasses(name, value, opts);
265
+ expect(result).toContain(
266
+ ".a2-m-inline-start-4 {margin-inline-start: var(--a2-size-4, 1rem)}",
267
+ );
268
+ });
269
+
270
+ it("generates padding-inline-start class", () => {
271
+ const result = generateSpacingClasses(name, value, opts);
272
+ expect(result).toContain(
273
+ ".a2-p-inline-start-4 {padding-inline-start: var(--a2-size-4, 1rem)}",
274
+ );
275
+ });
276
+
277
+ it("generates all 6 directions (inline-start, inline-end, block-start, block-end, block, inline)", () => {
278
+ const result = generateSpacingClasses(name, value, opts);
279
+ const directions = [
280
+ "inline-start",
281
+ "inline-end",
282
+ "block-start",
283
+ "block-end",
284
+ "block",
285
+ "inline",
286
+ ];
287
+ for (const dir of directions) {
288
+ expect(result.some((c) => c.includes(`m-${dir}`))).toBe(true);
289
+ expect(result.some((c) => c.includes(`p-${dir}`))).toBe(true);
290
+ }
291
+ });
292
+
293
+ it("generates shorthand margin and padding classes", () => {
294
+ const result = generateSpacingClasses(name, value, opts);
295
+ expect(result).toContain(".a2-m-4 {margin: var(--a2-size-4, 1rem)}");
296
+ expect(result).toContain(".a2-p-4 {padding: var(--a2-size-4, 1rem)}");
297
+ });
298
+
299
+ it("uses cssVarPrefix in the CSS variable name", () => {
300
+ const result = generateSpacingClasses("size-8", "2rem", {
301
+ prefix: "",
302
+ cssVarPrefix: "a2-",
303
+ });
304
+ expect(result.every((c) => c.includes("var(--a2-size-8,"))).toBe(true);
305
+ });
306
+
307
+ it("works without options (default empty prefix)", () => {
308
+ const result = generateSpacingClasses("size-2", "0.5rem");
309
+ expect(result).toHaveLength(14);
310
+ expect(result[0]).toContain("var(--size-2,");
311
+ });
312
+ });