@servicetitan/hammer-token 2.5.2 → 3.0.1

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 (146) hide show
  1. package/CHANGELOG.md +52 -2
  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 +23 -51
  8. package/build/web/core/css-utils/a2-color.css +221 -233
  9. package/build/web/core/css-utils/a2-font.css +1 -29
  10. package/build/web/core/css-utils/a2-spacing.css +238 -483
  11. package/build/web/core/css-utils/a2-utils.css +496 -781
  12. package/build/web/core/css-utils/border.css +23 -51
  13. package/build/web/core/css-utils/color.css +221 -233
  14. package/build/web/core/css-utils/font.css +1 -29
  15. package/build/web/core/css-utils/spacing.css +238 -483
  16. package/build/web/core/css-utils/utils.css +496 -781
  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 -245
  24. package/build/web/core/semantic.d.ts +221 -0
  25. package/build/web/core/semantic.js +1592 -347
  26. package/build/web/core/semantic.scss +219 -140
  27. package/build/web/index.d.ts +3 -4
  28. package/build/web/types.d.ts +17 -0
  29. package/config.js +121 -496
  30. package/eslint.config.mjs +11 -1
  31. package/package.json +15 -5
  32. package/src/global/primitive/breakpoint.tokens.json +54 -0
  33. package/src/global/primitive/color.tokens.json +1092 -0
  34. package/src/global/primitive/duration.tokens.json +44 -0
  35. package/src/global/primitive/font.tokens.json +151 -0
  36. package/src/global/primitive/radius.tokens.json +94 -0
  37. package/src/global/primitive/size.tokens.json +174 -0
  38. package/src/global/primitive/transition.tokens.json +32 -0
  39. package/src/theme/core/background.tokens.json +1312 -0
  40. package/src/theme/core/border.tokens.json +192 -0
  41. package/src/theme/core/chart.tokens.json +982 -0
  42. package/src/theme/core/component/ai-mark.tokens.json +20 -0
  43. package/src/theme/core/component/alert.tokens.json +261 -0
  44. package/src/theme/core/component/announcement.tokens.json +460 -0
  45. package/src/theme/core/component/avatar.tokens.json +137 -0
  46. package/src/theme/core/component/badge.tokens.json +42 -0
  47. package/src/theme/core/component/breadcrumb.tokens.json +42 -0
  48. package/src/theme/core/component/button-toggle.tokens.json +428 -0
  49. package/src/theme/core/component/button.tokens.json +941 -0
  50. package/src/theme/core/component/calendar.tokens.json +391 -0
  51. package/src/theme/core/component/card.tokens.json +107 -0
  52. package/src/theme/core/component/checkbox.tokens.json +631 -0
  53. package/src/theme/core/component/chip.tokens.json +169 -0
  54. package/src/theme/core/component/combobox.tokens.json +269 -0
  55. package/src/theme/core/component/details.tokens.json +152 -0
  56. package/src/theme/core/component/dialog.tokens.json +87 -0
  57. package/src/theme/core/component/divider.tokens.json +23 -0
  58. package/src/theme/core/component/dnd.tokens.json +208 -0
  59. package/src/theme/core/component/drawer.tokens.json +61 -0
  60. package/src/theme/core/component/drilldown.tokens.json +61 -0
  61. package/src/theme/core/component/edit-card.tokens.json +381 -0
  62. package/src/theme/core/component/field-label.tokens.json +42 -0
  63. package/src/theme/core/component/field-message.tokens.json +65 -0
  64. package/src/theme/core/component/icon.tokens.json +42 -0
  65. package/src/theme/core/component/link.tokens.json +108 -0
  66. package/src/theme/core/component/list-view.tokens.json +82 -0
  67. package/src/theme/core/component/listbox.tokens.json +283 -0
  68. package/src/theme/core/component/menu.tokens.json +230 -0
  69. package/src/theme/core/component/overflow.tokens.json +84 -0
  70. package/src/theme/core/component/page.tokens.json +377 -0
  71. package/src/theme/core/component/pagination.tokens.json +63 -0
  72. package/src/theme/core/component/popover.tokens.json +122 -0
  73. package/src/theme/core/component/progress-bar.tokens.json +133 -0
  74. package/src/theme/core/component/radio.tokens.json +631 -0
  75. package/src/theme/core/component/segmented-control.tokens.json +175 -0
  76. package/src/theme/core/component/select-card.tokens.json +943 -0
  77. package/src/theme/core/component/side-nav.tokens.json +349 -0
  78. package/src/theme/core/component/skeleton.tokens.json +42 -0
  79. package/src/theme/core/component/spinner.tokens.json +96 -0
  80. package/src/theme/core/component/status-icon.tokens.json +164 -0
  81. package/src/theme/core/component/stepper.tokens.json +484 -0
  82. package/src/theme/core/component/switch.tokens.json +285 -0
  83. package/src/theme/core/component/tab.tokens.json +192 -0
  84. package/src/theme/core/component/text-field.tokens.json +160 -0
  85. package/src/theme/core/component/text.tokens.json +59 -0
  86. package/src/theme/core/component/toast.tokens.json +343 -0
  87. package/src/theme/core/component/toolbar.tokens.json +114 -0
  88. package/src/theme/core/component/tooltip.tokens.json +61 -0
  89. package/src/theme/core/focus.tokens.json +56 -0
  90. package/src/theme/core/foreground.tokens.json +416 -0
  91. package/src/theme/core/gradient.tokens.json +41 -0
  92. package/src/theme/core/opacity.tokens.json +25 -0
  93. package/src/theme/core/shadow.tokens.json +81 -0
  94. package/src/theme/core/status.tokens.json +74 -0
  95. package/src/theme/core/typography.tokens.json +163 -0
  96. package/src/utils/__tests__/css-utils-format-utils.test.js +312 -0
  97. package/src/utils/__tests__/sd-build-configs.test.js +306 -0
  98. package/src/utils/__tests__/sd-formats.test.js +942 -0
  99. package/src/utils/__tests__/sd-transforms.test.js +336 -0
  100. package/src/utils/__tests__/token-helpers.test.js +1160 -0
  101. package/src/utils/copy-css-utils-cli.js +13 -1
  102. package/src/utils/css-utils-format-utils.js +105 -176
  103. package/src/utils/figma/__tests__/sync-gradient.test.js +561 -0
  104. package/src/utils/figma/__tests__/token-conversion.test.js +117 -0
  105. package/src/utils/figma/__tests__/token-resolution.test.js +231 -0
  106. package/src/utils/figma/auth.js +355 -0
  107. package/src/utils/figma/constants.js +22 -0
  108. package/src/utils/figma/errors.js +80 -0
  109. package/src/utils/figma/figma-api.js +1069 -0
  110. package/src/utils/figma/get-token.js +348 -0
  111. package/src/utils/figma/sync-components.js +909 -0
  112. package/src/utils/figma/sync-main.js +692 -0
  113. package/src/utils/figma/sync-orchestration.js +683 -0
  114. package/src/utils/figma/sync-primitives.js +230 -0
  115. package/src/utils/figma/sync-semantic.js +1056 -0
  116. package/src/utils/figma/token-conversion.js +340 -0
  117. package/src/utils/figma/token-parsing.js +186 -0
  118. package/src/utils/figma/token-resolution.js +569 -0
  119. package/src/utils/figma/utils.js +199 -0
  120. package/src/utils/sd-build-configs.js +305 -0
  121. package/src/utils/sd-formats.js +948 -0
  122. package/src/utils/sd-transforms.js +165 -0
  123. package/src/utils/token-helpers.js +848 -0
  124. package/tsconfig.json +18 -0
  125. package/vitest.config.js +17 -0
  126. package/.turbo/turbo-build.log +0 -37
  127. package/build/web/core/raw.js +0 -234
  128. package/src/global/primitive/breakpoint.js +0 -19
  129. package/src/global/primitive/color.js +0 -231
  130. package/src/global/primitive/duration.js +0 -16
  131. package/src/global/primitive/font.js +0 -60
  132. package/src/global/primitive/radius.js +0 -31
  133. package/src/global/primitive/size.js +0 -55
  134. package/src/global/primitive/transition.js +0 -16
  135. package/src/theme/core/background.js +0 -170
  136. package/src/theme/core/border.js +0 -103
  137. package/src/theme/core/charts.js +0 -464
  138. package/src/theme/core/component/button.js +0 -708
  139. package/src/theme/core/component/checkbox.js +0 -405
  140. package/src/theme/core/focus.js +0 -35
  141. package/src/theme/core/foreground.js +0 -148
  142. package/src/theme/core/overlay.js +0 -137
  143. package/src/theme/core/shadow.js +0 -29
  144. package/src/theme/core/status.js +0 -49
  145. package/src/theme/core/typography.js +0 -82
  146. package/type/types.ts +0 -344
@@ -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
+ });