@gtivr4/a1-design-system-react 0.2.3 → 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,332 @@
1
+ /* ─── ChoiceGroup ──────────────────────────────────────────────────────────── */
2
+
3
+ .a1-choice-group {
4
+ border: none;
5
+ margin: 0;
6
+ padding: 0;
7
+ min-width: 0;
8
+
9
+ /* Tile size defaults (default) */
10
+ --a1-cg-padding: var(--component-choice-group-default-padding);
11
+ --a1-cg-icon-size: var(--component-choice-group-default-icon-size);
12
+ --a1-cg-indicator-size: var(--component-choice-group-default-indicator-size);
13
+ --a1-cg-content-gap: var(--component-choice-group-default-content-gap);
14
+ --a1-cg-min-width: var(--component-choice-group-default-min-width);
15
+ --a1-cg-label-size: var(--semantic-font-size-body-md);
16
+ --a1-cg-subtext-size: var(--semantic-font-size-body-sm);
17
+ --a1-cg-legend-size: var(--semantic-font-size-body-sm);
18
+ --a1-cg-section-label-size: var(--semantic-font-size-body-xs);
19
+
20
+ /* Gap between tiles — tracks size */
21
+ --a1-cg-gap: var(--component-choice-group-gap-md);
22
+
23
+ /* Spacing between legend/messages and the items container */
24
+ --a1-cg-group-gap: var(--component-choice-group-default-group-gap);
25
+ --a1-cg-items-top-gap: var(--component-choice-group-default-items-top-gap);
26
+
27
+ display: flex;
28
+ flex-direction: column;
29
+ gap: var(--a1-cg-group-gap);
30
+ }
31
+
32
+ /* ─── Tile sizes — affect only tile padding and child element sizes ─────────── */
33
+
34
+ .a1-choice-group--compact {
35
+ --a1-cg-padding: var(--component-choice-group-compact-padding);
36
+ --a1-cg-icon-size: var(--component-choice-group-compact-icon-size);
37
+ --a1-cg-indicator-size: var(--component-choice-group-compact-indicator-size);
38
+ --a1-cg-content-gap: var(--component-choice-group-compact-content-gap);
39
+ --a1-cg-min-width: var(--component-choice-group-compact-min-width);
40
+ --a1-cg-label-size: var(--semantic-font-size-body-sm);
41
+ --a1-cg-subtext-size: var(--semantic-font-size-body-xs);
42
+ --a1-cg-legend-size: var(--semantic-font-size-body-xs);
43
+ --a1-cg-gap: var(--component-choice-group-gap-sm);
44
+ --a1-cg-group-gap: var(--component-choice-group-compact-group-gap);
45
+ --a1-cg-items-top-gap: var(--component-choice-group-compact-items-top-gap);
46
+ }
47
+
48
+ .a1-choice-group--comfortable {
49
+ --a1-cg-padding: var(--component-choice-group-comfortable-padding);
50
+ --a1-cg-icon-size: var(--component-choice-group-comfortable-icon-size);
51
+ --a1-cg-indicator-size: var(--component-choice-group-comfortable-indicator-size);
52
+ --a1-cg-content-gap: var(--component-choice-group-comfortable-content-gap);
53
+ --a1-cg-min-width: var(--component-choice-group-comfortable-min-width);
54
+ --a1-cg-label-size: var(--semantic-font-size-body-lg);
55
+ --a1-cg-subtext-size: var(--semantic-font-size-body-md);
56
+ --a1-cg-legend-size: var(--semantic-font-size-body-md);
57
+ --a1-cg-section-label-size: var(--semantic-font-size-body-sm);
58
+ --a1-cg-gap: var(--component-choice-group-gap-lg);
59
+ --a1-cg-group-gap: var(--component-choice-group-comfortable-group-gap);
60
+ --a1-cg-items-top-gap: var(--component-choice-group-comfortable-items-top-gap);
61
+ }
62
+
63
+ @media (--bp-md-up) {
64
+ .a1-choice-group--comfortable {
65
+ --a1-cg-legend-size: var(--semantic-font-size-body-lg);
66
+ }
67
+ }
68
+
69
+ /* ─── Legend ────────────────────────────────────────────────────────────────── */
70
+
71
+ .a1-choice-group__legend {
72
+ display: block;
73
+ width: 100%;
74
+ padding: 0;
75
+ font-family: var(--component-paragraph-font-family);
76
+ font-size: var(--a1-cg-legend-size);
77
+ font-weight: var(--component-field-label-font-weight);
78
+ color: var(--semantic-color-text-default);
79
+ line-height: var(--semantic-font-line-height-body);
80
+ }
81
+
82
+ .a1-choice-group__legend-inner {
83
+ display: inline-flex;
84
+ align-items: center;
85
+ gap: var(--base-spacing-6);
86
+ }
87
+
88
+ /* ─── Group messages (hint / error / success) ────────────────────────────────── */
89
+
90
+ .a1-choice-group__message {
91
+ margin: 0;
92
+ font-family: var(--component-paragraph-font-family);
93
+ font-size: var(--a1-cg-legend-size);
94
+ line-height: var(--semantic-font-line-height-body);
95
+ }
96
+
97
+ .a1-choice-group__message--hint { color: var(--semantic-color-text-muted); }
98
+ .a1-choice-group__message--error {
99
+ color: var(--semantic-color-status-error-background);
100
+ font-weight: var(--base-font-weight-medium);
101
+ }
102
+ .a1-choice-group__message--success {
103
+ color: var(--semantic-color-status-success-background);
104
+ font-weight: var(--base-font-weight-medium);
105
+ }
106
+
107
+ /* ─── Sections ───────────────────────────────────────────────────────────────── */
108
+
109
+ .a1-choice-group__section-divider {
110
+ border: none;
111
+ border-top: 1px solid var(--semantic-color-border-subtle);
112
+ margin: var(--a1-cg-gap) 0 0;
113
+ padding: 0;
114
+ }
115
+
116
+ .a1-choice-group__section {
117
+ display: flex;
118
+ flex-direction: column;
119
+ }
120
+
121
+ .a1-choice-group__section-label {
122
+ margin: 0 0 var(--a1-cg-items-top-gap);
123
+ font-family: var(--component-paragraph-font-family);
124
+ font-size: var(--a1-cg-section-label-size);
125
+ font-weight: var(--base-font-weight-semibold);
126
+ color: var(--semantic-color-text-muted);
127
+ line-height: var(--semantic-font-line-height-body);
128
+ }
129
+
130
+ /* Items inside a section — the section label handles the top spacing */
131
+ .a1-choice-group__section .a1-choice-group__items {
132
+ margin-top: 0;
133
+ }
134
+
135
+ /* ─── Items flex layout ─────────────────────────────────────────────────────── */
136
+
137
+ .a1-choice-group__items {
138
+ display: flex;
139
+ flex-wrap: wrap;
140
+ gap: var(--a1-cg-gap);
141
+ margin-top: var(--a1-cg-items-top-gap);
142
+ }
143
+
144
+ /* Fixed column count when --a1-cg-columns is set */
145
+ .a1-choice-group--fixed-columns .a1-choice-group__items > .a1-choice-item {
146
+ flex: 0 0 calc((100% - (var(--a1-cg-columns) - 1) * var(--a1-cg-gap)) / var(--a1-cg-columns));
147
+ }
148
+
149
+ /* ─── Choice item (card tile) ───────────────────────────────────────────────── */
150
+
151
+ .a1-choice-item {
152
+ position: relative;
153
+ display: flex;
154
+ flex-direction: column;
155
+ flex: 1 1 var(--a1-cg-min-width);
156
+ gap: var(--a1-cg-content-gap);
157
+ overflow: hidden;
158
+ /* Start padding offsets all content clear of the indicator in the top-start corner */
159
+ padding-block: var(--a1-cg-padding);
160
+ padding-inline-end: var(--a1-cg-padding);
161
+ padding-inline-start: calc(var(--a1-cg-padding) + var(--a1-cg-indicator-size) + var(--base-spacing-8));
162
+ border: var(--component-choice-group-border-width) solid var(--semantic-color-border-subtle);
163
+ border-radius: var(--component-choice-group-border-radius);
164
+ background-color: var(--semantic-color-surface-page);
165
+ cursor: pointer;
166
+ transition:
167
+ border-color var(--semantic-motion-duration-fast) var(--semantic-motion-easing-standard),
168
+ background-color var(--semantic-motion-duration-fast) var(--semantic-motion-easing-standard),
169
+ box-shadow var(--semantic-motion-duration-fast) var(--semantic-motion-easing-standard);
170
+ }
171
+
172
+ .a1-choice-item--disabled {
173
+ cursor: not-allowed;
174
+ opacity: 0.5;
175
+ background-color: var(--semantic-color-surface-raised);
176
+ }
177
+
178
+ .a1-choice-item--disabled .a1-choice-item__input {
179
+ pointer-events: none;
180
+ }
181
+
182
+ /* Hover (unselected, non-disabled) */
183
+ .a1-choice-item:not(.a1-choice-item--disabled):not(:has(.a1-choice-item__input:checked)):hover {
184
+ border-color: var(--semantic-color-action-border);
185
+ background-color: var(--semantic-color-action-surface);
186
+ }
187
+
188
+ /* Selected */
189
+ .a1-choice-item:not(.a1-choice-item--disabled):has(.a1-choice-item__input:checked) {
190
+ border-color: var(--semantic-color-action-background);
191
+ background-color: var(--semantic-color-action-surface);
192
+ box-shadow: inset 0 0 0 1px var(--semantic-color-action-background);
193
+ }
194
+
195
+ /* Selected hover */
196
+ .a1-choice-item:not(.a1-choice-item--disabled):has(.a1-choice-item__input:checked):hover {
197
+ border-color: var(--semantic-color-action-background-hover);
198
+ box-shadow: inset 0 0 0 1px var(--semantic-color-action-background-hover);
199
+ }
200
+
201
+ /* Focus ring on card when hidden input receives keyboard focus */
202
+ .a1-choice-item:has(.a1-choice-item__input:focus-visible) {
203
+ outline: var(--component-field-focus-ring-width) solid var(--component-field-focus-ring-color);
204
+ outline-offset: 2px;
205
+ }
206
+
207
+ /* ─── Hidden input ──────────────────────────────────────────────────────────── */
208
+
209
+ .a1-choice-item__input {
210
+ position: absolute;
211
+ width: 1px;
212
+ height: 1px;
213
+ padding: 0;
214
+ margin: -1px;
215
+ overflow: hidden;
216
+ clip: rect(0, 0, 0, 0);
217
+ white-space: nowrap;
218
+ border: 0;
219
+ }
220
+
221
+ /* ─── Icon ──────────────────────────────────────────────────────────────────── */
222
+
223
+ .a1-choice-item__icon {
224
+ display: flex;
225
+ align-items: center;
226
+ justify-content: center;
227
+ width: var(--a1-cg-icon-size);
228
+ height: var(--a1-cg-icon-size);
229
+ color: var(--semantic-color-action-background);
230
+ flex-shrink: 0;
231
+ }
232
+
233
+ .a1-choice-item__icon .a1-icon {
234
+ font-size: var(--a1-cg-icon-size);
235
+ }
236
+
237
+ /* ─── Label and subtext ─────────────────────────────────────────────────────── */
238
+
239
+ .a1-choice-item__content {
240
+ display: flex;
241
+ flex-direction: column;
242
+ gap: var(--base-spacing-2);
243
+ flex: 1;
244
+ min-width: 0;
245
+ }
246
+
247
+ .a1-choice-item__label {
248
+ font-family: var(--component-paragraph-font-family);
249
+ font-size: var(--a1-cg-label-size);
250
+ font-weight: var(--base-font-weight-medium);
251
+ color: var(--semantic-color-text-default);
252
+ line-height: var(--semantic-font-line-height-body);
253
+ overflow-wrap: break-word;
254
+ }
255
+
256
+ .a1-choice-item__subtext {
257
+ font-family: var(--component-paragraph-font-family);
258
+ font-size: var(--a1-cg-subtext-size);
259
+ font-weight: var(--base-font-weight-regular);
260
+ color: var(--semantic-color-text-muted);
261
+ line-height: var(--semantic-font-line-height-body);
262
+ overflow-wrap: break-word;
263
+ }
264
+
265
+ /* ─── Selection indicator ───────────────────────────────────────────────────── */
266
+
267
+ .a1-choice-item__indicator {
268
+ position: absolute;
269
+ top: calc(var(--a1-cg-padding) + var(--base-spacing-4));
270
+ inset-inline-start: var(--a1-cg-padding);
271
+ width: var(--a1-cg-indicator-size);
272
+ height: var(--a1-cg-indicator-size);
273
+ border: 1.5px solid var(--semantic-color-border-default);
274
+ background-color: var(--semantic-color-surface-page);
275
+ background-repeat: no-repeat;
276
+ background-position: center;
277
+ transition:
278
+ border-color var(--semantic-motion-duration-fast),
279
+ background-color var(--semantic-motion-duration-fast);
280
+ }
281
+
282
+ /* Single-select (radio) — circle */
283
+ .a1-choice-group--single .a1-choice-item__indicator {
284
+ border-radius: 50%;
285
+ }
286
+
287
+ /* Multi-select (checkbox) — rounded square */
288
+ .a1-choice-group--multiple .a1-choice-item__indicator {
289
+ border-radius: var(--base-radius-sm);
290
+ }
291
+
292
+ /* Hover indicator border (unselected only) */
293
+ .a1-choice-item:not(.a1-choice-item--disabled):not(:has(.a1-choice-item__input:checked)):hover
294
+ .a1-choice-item__indicator {
295
+ border-color: var(--semantic-color-action-background);
296
+ }
297
+
298
+ /* Selected indicator — filled */
299
+ .a1-choice-item:has(.a1-choice-item__input:checked) .a1-choice-item__indicator {
300
+ border-color: var(--semantic-color-action-background);
301
+ background-color: var(--semantic-color-action-background);
302
+ }
303
+
304
+ /* Radio selected: white dot */
305
+ .a1-choice-group--single
306
+ .a1-choice-item:has(.a1-choice-item__input:checked)
307
+ .a1-choice-item__indicator {
308
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Ccircle cx='5' cy='5' r='3' fill='%23ffffff'/%3E%3C/svg%3E");
309
+ background-size: 100%;
310
+ }
311
+
312
+ /* Checkbox selected: white checkmark */
313
+ .a1-choice-group--multiple
314
+ .a1-choice-item:has(.a1-choice-item__input:checked)
315
+ .a1-choice-item__indicator {
316
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpolyline points='2,6 5,9.5 10,3' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
317
+ background-size: 80%;
318
+ }
319
+
320
+ /* ─── Error state ───────────────────────────────────────────────────────────── */
321
+
322
+ .a1-choice-group--error
323
+ .a1-choice-item:not(.a1-choice-item--disabled):not(:has(.a1-choice-item__input:checked)) {
324
+ border-color: var(--semantic-color-status-error-border);
325
+ box-shadow: inset 0 0 0 1px var(--semantic-color-status-error-border);
326
+ }
327
+
328
+ .a1-choice-group--error
329
+ .a1-choice-item:not(.a1-choice-item--disabled):not(:has(.a1-choice-item__input:checked))
330
+ .a1-choice-item__indicator {
331
+ border-color: var(--semantic-color-status-error-border);
332
+ }
package/src/index.js CHANGED
@@ -33,6 +33,7 @@ export { NumberField } from "./components/field/NumberField.jsx";
33
33
  export { TimeField } from "./components/field/TimeField.jsx";
34
34
  export { TextareaField } from "./components/field/TextareaField.jsx";
35
35
  export { CheckboxGroup } from "./components/checkbox-group/CheckboxGroup.jsx";
36
+ export { ChoiceGroup } from "./components/choice-group/ChoiceGroup.jsx";
36
37
  export { RadioGroup } from "./components/radio-group/RadioGroup.jsx";
37
38
  export { Switch } from "./components/switch/Switch.jsx";
38
39
  export { MessageBadge, MessageEmptyState } from "./components/message/Message.jsx";