@neuravision/construct 1.1.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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +227 -0
  3. package/components/README.md +566 -0
  4. package/components/_keyframes.css +23 -0
  5. package/components/_shared.css +120 -0
  6. package/components/accordion.css +124 -0
  7. package/components/alert.css +67 -0
  8. package/components/avatar.css +127 -0
  9. package/components/badge.css +67 -0
  10. package/components/banner.css +247 -0
  11. package/components/breadcrumbs.css +152 -0
  12. package/components/button.css +145 -0
  13. package/components/card.css +76 -0
  14. package/components/checkbox.css +120 -0
  15. package/components/chip.css +361 -0
  16. package/components/combobox.css +385 -0
  17. package/components/components.css +2 -0
  18. package/components/data-table.css +93 -0
  19. package/components/datepicker.css +268 -0
  20. package/components/divider.css +73 -0
  21. package/components/drawer.css +167 -0
  22. package/components/dropdown.css +401 -0
  23. package/components/empty-state.css +97 -0
  24. package/components/field.css +42 -0
  25. package/components/file-upload.css +111 -0
  26. package/components/icon.css +31 -0
  27. package/components/index.css +49 -0
  28. package/components/input.css +64 -0
  29. package/components/list.css +474 -0
  30. package/components/modal.css +164 -0
  31. package/components/navbar.css +587 -0
  32. package/components/pagination.css +131 -0
  33. package/components/popover.css +231 -0
  34. package/components/progress-bar.css +56 -0
  35. package/components/select-menu.css +267 -0
  36. package/components/select.css +30 -0
  37. package/components/sidebar.css +183 -0
  38. package/components/skeleton.css +38 -0
  39. package/components/skip-link.css +38 -0
  40. package/components/slider.css +305 -0
  41. package/components/spinner.css +72 -0
  42. package/components/switch.css +82 -0
  43. package/components/table.css +139 -0
  44. package/components/tabs.css +147 -0
  45. package/components/textarea.css +16 -0
  46. package/components/toast.css +71 -0
  47. package/components/toggle-group.css +196 -0
  48. package/components/toolbar.css +222 -0
  49. package/components/tooltip.css +124 -0
  50. package/docs/guidelines.md +141 -0
  51. package/foundations.css +299 -0
  52. package/package.json +66 -0
  53. package/tokens/README.md +179 -0
  54. package/tokens/tokens.css +434 -0
  55. package/tokens/tokens.js +1188 -0
  56. package/tokens/tokens.json +810 -0
  57. package/tokens/tokens.ts +1188 -0
@@ -0,0 +1,120 @@
1
+ .ct-check,
2
+ .ct-radio {
3
+ display: inline-flex;
4
+ align-items: center;
5
+ gap: var(--space-3);
6
+ font-size: var(--font-size-sm);
7
+ line-height: var(--line-height-sm);
8
+ color: var(--color-text-primary);
9
+ }
10
+
11
+ .ct-check__input,
12
+ .ct-radio__input {
13
+ appearance: none;
14
+ width: var(--space-6);
15
+ height: var(--space-6);
16
+ border: var(--border-thin) solid var(--color-border-default);
17
+ background: var(--color-bg-elevated);
18
+ cursor: pointer;
19
+ display: grid;
20
+ place-content: center;
21
+ position: relative;
22
+ transition: background var(--duration-fast) var(--easing-standard),
23
+ border-color var(--duration-fast) var(--easing-standard),
24
+ transform var(--duration-fast) var(--easing-standard);
25
+ }
26
+
27
+ /* Invisible touch-target expansion (WCAG 2.5.8: 44×44px minimum) */
28
+ @media (pointer: coarse) {
29
+ .ct-check__input::after,
30
+ .ct-radio__input::after {
31
+ content: '';
32
+ position: absolute;
33
+ inset: calc((var(--space-6) - 44px) / 2);
34
+ }
35
+ }
36
+
37
+ .ct-check__input {
38
+ border-radius: var(--radius-sm);
39
+ }
40
+
41
+ .ct-radio__input {
42
+ border-radius: 50%;
43
+ }
44
+
45
+ .ct-check__input::before {
46
+ content: '';
47
+ width: 10px;
48
+ height: 6px;
49
+ border: 2px solid var(--color-text-inverse);
50
+ border-block-start: 0;
51
+ border-inline-end: 0;
52
+ transform: rotate(-45deg) scale(0);
53
+ transition: transform var(--duration-fast) var(--easing-standard);
54
+ }
55
+
56
+ .ct-radio__input::before {
57
+ content: '';
58
+ width: 8px;
59
+ height: 8px;
60
+ border-radius: var(--radius-round);
61
+ background: var(--color-text-inverse);
62
+ transform: scale(0);
63
+ transition: transform var(--duration-fast) var(--easing-standard);
64
+ }
65
+
66
+ .ct-check__input:checked,
67
+ .ct-radio__input:checked {
68
+ background: var(--color-brand-primary);
69
+ border-color: var(--color-brand-primary);
70
+ }
71
+
72
+ .ct-check__input:checked::before,
73
+ .ct-radio__input:checked::before {
74
+ transform: scale(1);
75
+ }
76
+
77
+ @media (hover: hover) {
78
+ .ct-check__input:not(:disabled):hover,
79
+ .ct-radio__input:not(:disabled):hover {
80
+ border-color: var(--ct-control-border-hover);
81
+ }
82
+
83
+ .ct-check__input:checked:not(:disabled):hover,
84
+ .ct-radio__input:checked:not(:disabled):hover {
85
+ background: var(--color-brand-primary-hover);
86
+ border-color: var(--color-brand-primary-hover);
87
+ }
88
+
89
+ .ct-check__input:indeterminate:not(:disabled):hover {
90
+ background: var(--color-brand-primary-hover);
91
+ border-color: var(--color-brand-primary-hover);
92
+ }
93
+ }
94
+
95
+ .ct-check__input:disabled,
96
+ .ct-radio__input:disabled {
97
+ background: var(--color-bg-muted);
98
+ border-color: var(--color-border-subtle);
99
+ cursor: not-allowed;
100
+ }
101
+
102
+ .ct-check__input:focus-visible,
103
+ .ct-radio__input:focus-visible {
104
+ border-color: var(--ct-control-border-focus);
105
+ outline: 2px solid var(--color-focus-ring);
106
+ outline-offset: 2px;
107
+ }
108
+
109
+ .ct-check__input:indeterminate {
110
+ background: var(--color-brand-primary);
111
+ border-color: var(--color-brand-primary);
112
+ }
113
+
114
+ .ct-check__input:indeterminate::before {
115
+ width: 10px;
116
+ height: 0;
117
+ border-block-start: 2px solid var(--color-text-inverse);
118
+ border-inline-end: 0;
119
+ transform: scale(1);
120
+ }
@@ -0,0 +1,361 @@
1
+ /* ── Chip ────────────────────────────────────────────────────── */
2
+
3
+ .ct-chip {
4
+ --ct-chip-height: var(--control-height-md);
5
+ --ct-chip-padding-x: var(--space-3);
6
+ --ct-chip-font-size: var(--font-size-sm);
7
+ --ct-chip-line-height: var(--line-height-sm);
8
+ --ct-chip-gap: var(--space-2);
9
+ --ct-chip-remove-size: var(--icon-lg);
10
+ --ct-chip-bg: var(--color-bg-surface);
11
+ --ct-chip-border: var(--color-border-default);
12
+ --ct-chip-color: var(--color-text-secondary);
13
+ --ct-chip-accent: var(--ct-chip-color);
14
+
15
+ display: inline-flex;
16
+ align-items: center;
17
+ gap: var(--ct-chip-gap);
18
+ min-height: var(--ct-chip-height);
19
+ padding: 0 var(--ct-chip-padding-x);
20
+ border-radius: var(--radius-pill);
21
+ border: var(--border-thin) solid var(--ct-chip-border);
22
+ background: var(--ct-chip-bg);
23
+ color: var(--ct-chip-color);
24
+ font-size: var(--ct-chip-font-size);
25
+ font-weight: var(--font-weight-medium);
26
+ letter-spacing: var(--letter-spacing-wide);
27
+ line-height: var(--ct-chip-line-height);
28
+ white-space: nowrap;
29
+ }
30
+
31
+ /* ── Size variants ───────────────────────────────────────────── */
32
+
33
+ .ct-chip--sm {
34
+ --ct-chip-height: var(--control-height-sm);
35
+ --ct-chip-padding-x: var(--space-2);
36
+ --ct-chip-font-size: var(--font-size-xs);
37
+ --ct-chip-line-height: var(--line-height-xs);
38
+ --ct-chip-remove-size: var(--icon-sm);
39
+ }
40
+
41
+ .ct-chip--lg {
42
+ --ct-chip-height: var(--control-height-lg);
43
+ --ct-chip-padding-x: var(--space-4);
44
+ --ct-chip-font-size: var(--font-size-md);
45
+ --ct-chip-line-height: var(--line-height-md);
46
+ --ct-chip-remove-size: var(--icon-xl);
47
+ }
48
+
49
+ /* ── Interactive state ───────────────────────────────────────── */
50
+
51
+ .ct-chip--interactive {
52
+ cursor: pointer;
53
+ transition: background var(--duration-fast) var(--easing-standard),
54
+ border-color var(--duration-fast) var(--easing-standard),
55
+ color var(--duration-fast) var(--easing-standard),
56
+ transform var(--duration-fast) var(--easing-standard);
57
+ }
58
+
59
+ @media (hover: hover) {
60
+ .ct-chip--interactive:hover {
61
+ background: color-mix(in srgb, var(--ct-chip-accent) 18%, var(--ct-chip-bg));
62
+ border-color: var(--ct-chip-accent, var(--color-border-strong));
63
+ }
64
+ }
65
+
66
+ .ct-chip--interactive:active {
67
+ transform: translateY(1px);
68
+ }
69
+
70
+ .ct-chip--interactive:focus-visible {
71
+ outline: 2px solid var(--color-focus-ring);
72
+ outline-offset: 2px;
73
+ }
74
+
75
+ /* ── Selected / pressed state ────────────────────────────────── */
76
+
77
+ .ct-chip[aria-pressed='true'],
78
+ .ct-chip--selected {
79
+ --ct-chip-bg: var(--color-brand-primary);
80
+ --ct-chip-border: var(--color-brand-primary);
81
+ --ct-chip-color: var(--color-text-inverse);
82
+ --ct-chip-accent: var(--color-text-inverse);
83
+ }
84
+
85
+ /* ── Outline variant ─────────────────────────────────────────── */
86
+
87
+ .ct-chip--outline {
88
+ --ct-chip-bg: transparent;
89
+ }
90
+
91
+ .ct-chip--outline[aria-pressed='true'],
92
+ .ct-chip--outline.ct-chip--selected {
93
+ --ct-chip-bg: var(--color-brand-primary);
94
+ }
95
+
96
+ /* ── Semantic color variants (Subtle — default) ────────────── */
97
+
98
+ .ct-chip--info {
99
+ --ct-chip-bg: var(--color-state-info-surface);
100
+ --ct-chip-border: var(--color-state-info-border);
101
+ --ct-chip-color: var(--color-ocean-700);
102
+ --ct-chip-accent: var(--color-state-info);
103
+ }
104
+
105
+ .ct-chip--success {
106
+ --ct-chip-bg: var(--color-state-success-surface);
107
+ --ct-chip-border: var(--color-state-success-border);
108
+ --ct-chip-color: var(--color-green-700);
109
+ --ct-chip-accent: var(--color-state-success);
110
+ }
111
+
112
+ .ct-chip--warning {
113
+ --ct-chip-bg: var(--color-state-warning-surface);
114
+ --ct-chip-border: var(--color-state-warning-border);
115
+ --ct-chip-color: var(--color-state-warning-text);
116
+ --ct-chip-accent: var(--color-state-warning-text);
117
+ }
118
+
119
+ .ct-chip--danger {
120
+ --ct-chip-bg: var(--color-state-danger-surface);
121
+ --ct-chip-border: var(--color-state-danger-border);
122
+ --ct-chip-color: var(--color-red-700);
123
+ --ct-chip-accent: var(--color-state-danger);
124
+ }
125
+
126
+ /* ── Outline + Semantic: strong accent border ────────────────── */
127
+
128
+ .ct-chip--outline.ct-chip--info {
129
+ --ct-chip-border: var(--color-state-info);
130
+ }
131
+
132
+ .ct-chip--outline.ct-chip--success {
133
+ --ct-chip-border: var(--color-state-success);
134
+ }
135
+
136
+ .ct-chip--outline.ct-chip--warning {
137
+ --ct-chip-border: var(--color-state-warning-text);
138
+ }
139
+
140
+ .ct-chip--outline.ct-chip--danger {
141
+ --ct-chip-border: var(--color-state-danger);
142
+ }
143
+
144
+ /* ── Solid variant ───────────────────────────────────────────── */
145
+
146
+ .ct-chip--solid {
147
+ --ct-chip-bg: var(--color-bg-muted);
148
+ --ct-chip-border: var(--color-bg-muted);
149
+ --ct-chip-color: var(--color-text-primary);
150
+ }
151
+
152
+ @media (hover: hover) {
153
+ .ct-chip--solid:where(.ct-chip--interactive):hover {
154
+ filter: brightness(1.1);
155
+ }
156
+ }
157
+
158
+ .ct-chip--solid.ct-chip--info {
159
+ --ct-chip-bg: var(--color-state-info);
160
+ --ct-chip-border: var(--color-state-info);
161
+ --ct-chip-color: var(--color-text-inverse);
162
+ --ct-chip-accent: var(--color-text-inverse);
163
+ }
164
+
165
+ .ct-chip--solid.ct-chip--success {
166
+ --ct-chip-bg: var(--color-green-700);
167
+ --ct-chip-border: var(--color-green-700);
168
+ --ct-chip-color: var(--color-text-inverse);
169
+ --ct-chip-accent: var(--color-text-inverse);
170
+ }
171
+
172
+ .ct-chip--solid.ct-chip--warning {
173
+ --ct-chip-bg: var(--color-amber-500);
174
+ --ct-chip-border: var(--color-amber-500);
175
+ --ct-chip-color: var(--color-amber-900);
176
+ --ct-chip-accent: var(--color-amber-900);
177
+ }
178
+
179
+ .ct-chip--solid.ct-chip--danger {
180
+ --ct-chip-bg: var(--color-state-danger);
181
+ --ct-chip-border: var(--color-state-danger);
182
+ --ct-chip-color: var(--color-text-inverse);
183
+ --ct-chip-accent: var(--color-text-inverse);
184
+ }
185
+
186
+ /* ── Disabled state ──────────────────────────────────────────── */
187
+
188
+ .ct-chip[aria-disabled='true'] {
189
+ opacity: var(--opacity-disabled);
190
+ pointer-events: none;
191
+ }
192
+
193
+ /* ── Icon element ────────────────────────────────────────────── */
194
+
195
+ .ct-chip__icon {
196
+ width: 1em;
197
+ height: 1em;
198
+ flex-shrink: 0;
199
+ display: inline-flex;
200
+ align-items: center;
201
+ justify-content: center;
202
+ color: var(--ct-chip-accent);
203
+ }
204
+
205
+ /* ── Built-in semantic icons via CSS mask-image ──────────────── */
206
+
207
+ .ct-chip__icon--info,
208
+ .ct-chip__icon--success,
209
+ .ct-chip__icon--warning,
210
+ .ct-chip__icon--danger {
211
+ background-color: var(--ct-chip-accent);
212
+ -webkit-mask-size: contain;
213
+ mask-size: contain;
214
+ -webkit-mask-repeat: no-repeat;
215
+ mask-repeat: no-repeat;
216
+ -webkit-mask-position: center;
217
+ mask-position: center;
218
+ color: transparent;
219
+ }
220
+
221
+ /* info: circle-i */
222
+ .ct-chip__icon--info {
223
+ --_icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='7' fill='none' stroke='%23000' stroke-width='1.5'/%3E%3Cpath d='M8 7v4M8 5.5V5' stroke='%23000' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
224
+ -webkit-mask-image: var(--_icon);
225
+ mask-image: var(--_icon);
226
+ }
227
+
228
+ /* success: circle-check */
229
+ .ct-chip__icon--success {
230
+ --_icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='7' fill='none' stroke='%23000' stroke-width='1.5'/%3E%3Cpath d='M5 8.5l2 2 4-4' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
231
+ -webkit-mask-image: var(--_icon);
232
+ mask-image: var(--_icon);
233
+ }
234
+
235
+ /* warning: triangle-! */
236
+ .ct-chip__icon--warning {
237
+ --_icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 1.5l6.93 12H1.07z' fill='none' stroke='%23000' stroke-width='1.3' stroke-linejoin='round'/%3E%3Cpath d='M8 6.5v3M8 11.5v.5' stroke='%23000' stroke-width='1.3' stroke-linecap='round'/%3E%3C/svg%3E");
238
+ -webkit-mask-image: var(--_icon);
239
+ mask-image: var(--_icon);
240
+ }
241
+
242
+ /* danger: circle-x */
243
+ .ct-chip__icon--danger {
244
+ --_icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='7' fill='none' stroke='%23000' stroke-width='1.5'/%3E%3Cpath d='M5.5 5.5l5 5M10.5 5.5l-5 5' stroke='%23000' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
245
+ -webkit-mask-image: var(--_icon);
246
+ mask-image: var(--_icon);
247
+ }
248
+
249
+ /* ── Dot status indicator ────────────────────────────────────── */
250
+
251
+ .ct-chip__dot {
252
+ width: 8px;
253
+ height: 8px;
254
+ flex-shrink: 0;
255
+ border-radius: var(--radius-round);
256
+ background: var(--ct-chip-accent);
257
+ }
258
+
259
+ /* ── Avatar slot ─────────────────────────────────────────────── */
260
+
261
+ .ct-chip__avatar {
262
+ width: calc(var(--ct-chip-height) - var(--space-3));
263
+ height: calc(var(--ct-chip-height) - var(--space-3));
264
+ flex-shrink: 0;
265
+ border-radius: var(--radius-round);
266
+ object-fit: cover;
267
+ margin-inline-start: calc(-1 * var(--space-2));
268
+ }
269
+
270
+ /* ── Label with truncation ───────────────────────────────────── */
271
+
272
+ .ct-chip__label {
273
+ overflow: hidden;
274
+ text-overflow: ellipsis;
275
+ white-space: nowrap;
276
+ max-width: var(--ct-chip-max-width, none);
277
+ }
278
+
279
+ /* ── Checkmark for selected state ────────────────────────────── */
280
+
281
+ .ct-chip__check {
282
+ width: 0;
283
+ height: 1em;
284
+ overflow: hidden;
285
+ display: inline-flex;
286
+ align-items: center;
287
+ justify-content: center;
288
+ transition: width var(--duration-fast) var(--easing-standard);
289
+ }
290
+
291
+ .ct-chip__check::before {
292
+ content: '';
293
+ display: block;
294
+ width: 0.35em;
295
+ height: 0.65em;
296
+ border: solid var(--ct-chip-accent);
297
+ border-width: 0 2px 2px 0;
298
+ transform: rotate(45deg) translateY(-1px);
299
+ flex-shrink: 0;
300
+ }
301
+
302
+ .ct-chip[aria-pressed='true'] .ct-chip__check,
303
+ .ct-chip--selected .ct-chip__check {
304
+ width: 1em;
305
+ }
306
+
307
+ /* ── Balanced padding when remove button is present ──────────── */
308
+
309
+ .ct-chip:has(.ct-chip__remove) {
310
+ padding-inline-end: var(--space-2);
311
+ }
312
+
313
+ /* ── Remove button ───────────────────────────────────────────── */
314
+
315
+ .ct-chip__remove {
316
+ appearance: none;
317
+ border: none;
318
+ background: transparent;
319
+ color: inherit;
320
+ width: var(--ct-chip-remove-size);
321
+ height: var(--ct-chip-remove-size);
322
+ border-radius: var(--radius-round);
323
+ display: inline-flex;
324
+ align-items: center;
325
+ justify-content: center;
326
+ cursor: pointer;
327
+ transition: background var(--duration-fast) var(--easing-standard);
328
+ }
329
+
330
+ @media (hover: hover) {
331
+ .ct-chip__remove:hover {
332
+ background: color-mix(in srgb, var(--ct-chip-accent) 15%, transparent);
333
+ }
334
+ }
335
+
336
+ .ct-chip__remove:focus-visible {
337
+ outline: 2px solid var(--color-focus-ring);
338
+ outline-offset: 2px;
339
+ }
340
+
341
+ /* ── Touch-target minimums (WCAG 2.5.8: 44×44px) ──────────── */
342
+
343
+ @media (pointer: coarse) {
344
+ .ct-chip {
345
+ min-height: 44px;
346
+ }
347
+ }
348
+
349
+ /* ── Reduced motion ──────────────────────────────────────────── */
350
+
351
+ @media (prefers-reduced-motion: reduce) {
352
+ .ct-chip--interactive,
353
+ .ct-chip__remove,
354
+ .ct-chip__check {
355
+ transition: none;
356
+ }
357
+
358
+ .ct-chip--interactive:active {
359
+ transform: none;
360
+ }
361
+ }