@hattisar/design-tokens 0.3.22 → 0.3.24
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.
- package/package.json +1 -1
- package/tokens.css +141 -120
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hattisar/design-tokens",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.24",
|
|
4
4
|
"description": "Shared design tokens (colors, type, radius, spacing, motion) and base primitives (reset, buttons, inputs, cards, modals, auth shell) for every Hattisar frontend.",
|
|
5
5
|
"main": "tokens.css",
|
|
6
6
|
"files": [
|
package/tokens.css
CHANGED
|
@@ -66,7 +66,10 @@
|
|
|
66
66
|
--color-border-raised: #d4d4d8;
|
|
67
67
|
--shadow-popover: 0 4px 6px -2px rgba(9, 9, 11, 0.08), 0 12px 28px -8px rgba(9, 9, 11, 0.18);
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
/* The one hover fill for every surface: a faint veil of the ink over
|
|
70
|
+
whatever it sits on, so it never reads as a second grey competing
|
|
71
|
+
with a header band or a card (those use --color-surface-tint). */
|
|
72
|
+
--color-hover: color-mix(in srgb, var(--color-text-primary) 6%, transparent);
|
|
70
73
|
--color-focus-ring: rgba(233, 235, 239, 0.2);
|
|
71
74
|
--color-focus-ring-danger: rgba(255, 138, 156, 0.24);
|
|
72
75
|
/* The ring drawn around a focused control. Ink at low alpha rather
|
|
@@ -128,7 +131,7 @@
|
|
|
128
131
|
--color-surface-tint/--color-border-strong/--color-text-primary
|
|
129
132
|
rather than their own literal hex, so the secondary button always
|
|
130
133
|
tracks the same neutral scale as the rest of the page. */
|
|
131
|
-
--color-secondary-fill: var(--color-
|
|
134
|
+
--color-secondary-fill: var(--color-background-card);
|
|
132
135
|
--color-secondary-border: var(--color-border-strong);
|
|
133
136
|
--color-secondary-text: var(--color-text-primary);
|
|
134
137
|
--color-secondary-hover: color-mix(in srgb, var(--color-text-primary) 6%, var(--color-secondary-fill));
|
|
@@ -260,6 +263,40 @@
|
|
|
260
263
|
and the gray column-header band right below ("search area look very
|
|
261
264
|
tight"). One token so the tiles and the toolbar can't drift apart. */
|
|
262
265
|
--toolbar-row-height: 48px;
|
|
266
|
+
/* The pinned page header on a list page (title + actions), and what the
|
|
267
|
+
table's column headers stick beneath. */
|
|
268
|
+
--page-heading-height: 52px;
|
|
269
|
+
|
|
270
|
+
/* Buttons, after Shopify's admin: a fully rounded pill, 28px tall,
|
|
271
|
+
medium-weight small type. One set of numbers for every button the
|
|
272
|
+
app draws (primary, secondary, destructive, the save bar). */
|
|
273
|
+
--button-height: 28px;
|
|
274
|
+
--button-radius: var(--radius-full);
|
|
275
|
+
--button-padding-x: var(--space-3);
|
|
276
|
+
--button-font-size: var(--text-sm);
|
|
277
|
+
--button-font-weight: var(--font-weight-medium);
|
|
278
|
+
|
|
279
|
+
/* The big rounded containers on a list page, after Shopify's Orders
|
|
280
|
+
page: the KPI strip, the search bar and the table card all share it. */
|
|
281
|
+
--list-card-radius: var(--radius-xl);
|
|
282
|
+
/* A table row under the cursor: a faint veil of the ink, deliberately
|
|
283
|
+
lighter than the header band (--color-surface-tint) so the two never
|
|
284
|
+
read as the same grey. */
|
|
285
|
+
--table-row-hover-bg: color-mix(in srgb, var(--color-text-primary) 4%, transparent);
|
|
286
|
+
/* The secondary button's surface, named once so every control that is a
|
|
287
|
+
secondary button in all but markup (More Actions, the dashboard
|
|
288
|
+
filters, the close x) reads the same two tokens as .btn-secondary
|
|
289
|
+
instead of keeping a private copy of the look. A white pill needs a
|
|
290
|
+
hairline of its own to read against the page. Primary is simply the
|
|
291
|
+
bare `button` rule (--color-cta). */
|
|
292
|
+
--button-secondary-bg: var(--color-secondary-fill);
|
|
293
|
+
--button-secondary-shadow: var(--shadow-btn-secondary), 0 0 0 1px var(--color-border);
|
|
294
|
+
|
|
295
|
+
/* The picture picker (ImagePicker.tsx) behind an item image, the org
|
|
296
|
+
logo and both user photos. Named so those four can't drift apart. */
|
|
297
|
+
--image-picker-size: 96px;
|
|
298
|
+
--image-picker-border: 1px dashed var(--color-border);
|
|
299
|
+
--image-picker-overlay: color-mix(in srgb, var(--color-background-card) 55%, transparent);
|
|
263
300
|
|
|
264
301
|
/* Depth, deliberately restrained. These were all `none`, which left
|
|
265
302
|
every surface separated by border alone — correct, legible, and
|
|
@@ -405,33 +442,46 @@
|
|
|
405
442
|
/* 58px, not 52 — asked for directly, matched against a reference
|
|
406
443
|
layout's own header height so this app's chrome proportions read
|
|
407
444
|
the same. */
|
|
408
|
-
--header-height:
|
|
445
|
+
--header-height: 8px;
|
|
446
|
+
/* Height of a title bar inside a panel or drawer (notifications,
|
|
447
|
+
support). Was --header-height before the top bar left the desktop
|
|
448
|
+
shell and that token shrank to the frame's own 4px + 4px margins. */
|
|
449
|
+
--panel-header-height: 58px;
|
|
409
450
|
|
|
410
451
|
/* Exact Hattisar Design match — aside/header sit at the same white
|
|
411
452
|
--hd-card-bg as every card (bordered, not tonally separate),
|
|
412
453
|
mirrored by dark theme's own literal Hattisar Design values below. */
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
--sidebar-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
--
|
|
454
|
+
/* The sidebar is dark chrome in every theme, built only from the
|
|
455
|
+
palette above: its fill is the theme's own near-black and its ink the
|
|
456
|
+
theme's own near-white, and every other shade is a mix of those two
|
|
457
|
+
(so a theme change re-tints all of them from one pair of tokens). */
|
|
458
|
+
--sidebar-bg: var(--color-text-primary);
|
|
459
|
+
--sidebar-active-text: var(--color-background-card);
|
|
460
|
+
--sidebar-text: color-mix(in srgb, var(--sidebar-active-text) 88%, var(--sidebar-bg));
|
|
461
|
+
--sidebar-text-muted: color-mix(in srgb, var(--sidebar-active-text) 70%, var(--sidebar-bg));
|
|
462
|
+
/* Hover (and the current row's fill), the dividers, and the search /
|
|
463
|
+
input fill: a veil of the ink over the chrome. */
|
|
464
|
+
--sidebar-hover-bg: color-mix(in srgb, var(--sidebar-active-text) 10%, transparent);
|
|
465
|
+
--sidebar-border: color-mix(in srgb, var(--sidebar-active-text) 14%, transparent);
|
|
466
|
+
--sidebar-input-bg: var(--sidebar-hover-bg);
|
|
467
|
+
/* The row you are on: the full ink on the same fill hover uses, the
|
|
468
|
+
weight doing the rest. */
|
|
469
|
+
--sidebar-active-ink: var(--sidebar-active-text);
|
|
470
|
+
--sidebar-rail-bg: var(--sidebar-bg);
|
|
471
|
+
/* The one background every popout shares (account menu, notifications,
|
|
472
|
+
support, the collapsed rail's hover menu, the search popup): the
|
|
473
|
+
sidebar colour one step lighter, so a popout reads as lifted off
|
|
474
|
+
the dark chrome rather than merging into it. */
|
|
475
|
+
--popout-bg: color-mix(in srgb, var(--sidebar-active-text) 12%, var(--sidebar-bg));
|
|
476
|
+
|
|
477
|
+
/* Sidebar and popout dimensions, named once. */
|
|
478
|
+
--sidebar-icon-size: 20px;
|
|
479
|
+
--sidebar-control-height: 36px;
|
|
480
|
+
--sidebar-badge-size: 22px;
|
|
481
|
+
--sidebar-avatar-size: 28px;
|
|
482
|
+
--popout-min-width: 180px;
|
|
483
|
+
--search-popup-width: 640px;
|
|
484
|
+
--mobile-nav-width: 350px;
|
|
435
485
|
|
|
436
486
|
/* Every left-hand nav rail in an app sharing this one width instead
|
|
437
487
|
of each picking its own. 260px, not 240 — asked for directly,
|
|
@@ -439,7 +489,7 @@
|
|
|
439
489
|
layout's own sidebar width, the same reason --header-height above
|
|
440
490
|
moved too. */
|
|
441
491
|
--sidebar-width: 260px;
|
|
442
|
-
--sidebar-width-collapsed:
|
|
492
|
+
--sidebar-width-collapsed: 56px;
|
|
443
493
|
--sidebar-height: calc(100vh - var(--header-height));
|
|
444
494
|
|
|
445
495
|
/* Two widths, because a page is one of two things.
|
|
@@ -520,7 +570,7 @@
|
|
|
520
570
|
--color-surface-raised: #1f1f23;
|
|
521
571
|
--color-border-raised: #3f3f46;
|
|
522
572
|
--shadow-popover: 0 4px 6px -2px rgba(0, 0, 0, 0.5), 0 16px 40px -10px rgba(0, 0, 0, 0.75);
|
|
523
|
-
--color-hover: var(--color-
|
|
573
|
+
--color-hover: color-mix(in srgb, var(--color-text-primary) 6%, transparent);
|
|
524
574
|
|
|
525
575
|
/* Brighter counterparts of :root's accent set — a dark badge fill
|
|
526
576
|
needs a light/saturated text color for contrast, the mirror image
|
|
@@ -618,16 +668,11 @@
|
|
|
618
668
|
surface as a card, which is where shadcn puts a sidebar too.
|
|
619
669
|
The frame still reads as structurally separate from content, which
|
|
620
670
|
was the point of the original choice. */
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
671
|
+
/* Dark theme: a fill one step below the page canvas (so the page frame
|
|
672
|
+
still reads against it) and the theme's own ink; every other sidebar
|
|
673
|
+
shade is the :root mix of the two. */
|
|
674
|
+
--sidebar-bg: color-mix(in srgb, var(--color-background-global) 40%, black);
|
|
624
675
|
--sidebar-active-text: var(--color-text-primary);
|
|
625
|
-
--sidebar-hover-bg: rgba(255, 255, 255, 0.06);
|
|
626
|
-
/* --color-cta is brand ink now (see :root's own --color-cta) — this
|
|
627
|
-
"current row" marker used to lean on the accent blue clearing AA
|
|
628
|
-
on #18181b without the on-surface variant's extra darkening. */
|
|
629
|
-
--sidebar-active-ink: var(--color-cta);
|
|
630
|
-
--sidebar-rail-bg: #18181b;
|
|
631
676
|
|
|
632
677
|
color-scheme: dark;
|
|
633
678
|
}
|
|
@@ -650,7 +695,7 @@
|
|
|
650
695
|
--color-surface-raised: #ffffff;
|
|
651
696
|
--color-border-raised: #d4d4d8;
|
|
652
697
|
--shadow-popover: 0 4px 6px -2px rgba(9, 9, 11, 0.08), 0 12px 28px -8px rgba(9, 9, 11, 0.18);
|
|
653
|
-
--color-hover: var(--color-
|
|
698
|
+
--color-hover: color-mix(in srgb, var(--color-text-primary) 6%, transparent);
|
|
654
699
|
|
|
655
700
|
--color-brand: var(--color-text-primary);
|
|
656
701
|
--color-brand-hover: color-mix(in srgb, var(--color-brand) 85%, black);
|
|
@@ -667,7 +712,7 @@
|
|
|
667
712
|
--shadow-btn-primary: 0 1px 2px rgba(0, 0, 0, 0.4);
|
|
668
713
|
--shadow-btn-secondary: 0 1px 2px rgba(0, 0, 0, 0.3);
|
|
669
714
|
|
|
670
|
-
--color-secondary-fill: var(--color-
|
|
715
|
+
--color-secondary-fill: var(--color-background-card);
|
|
671
716
|
--color-secondary-border: var(--color-border-strong);
|
|
672
717
|
--color-secondary-text: var(--color-text-primary);
|
|
673
718
|
--color-secondary-hover: color-mix(in srgb, var(--color-text-primary) 6%, var(--color-secondary-fill));
|
|
@@ -722,13 +767,8 @@
|
|
|
722
767
|
--shadow-md: none;
|
|
723
768
|
--shadow-lg: none;
|
|
724
769
|
|
|
725
|
-
--sidebar-bg: var(--color-
|
|
726
|
-
--sidebar-text:
|
|
727
|
-
--sidebar-text-muted: var(--color-text-muted);
|
|
728
|
-
--sidebar-active-text: var(--color-text-primary);
|
|
729
|
-
--sidebar-hover-bg: var(--color-surface-tint);
|
|
730
|
-
--sidebar-active-ink: var(--color-cta-on-surface);
|
|
731
|
-
--sidebar-rail-bg: var(--color-background-card);
|
|
770
|
+
--sidebar-bg: var(--color-text-primary);
|
|
771
|
+
--sidebar-active-text: var(--color-background-card);
|
|
732
772
|
|
|
733
773
|
color-scheme: light;
|
|
734
774
|
}
|
|
@@ -860,15 +900,11 @@ label {
|
|
|
860
900
|
line-height: 1.4;
|
|
861
901
|
}
|
|
862
902
|
|
|
863
|
-
/*
|
|
864
|
-
|
|
865
|
-
field
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
margin-left: 3px;
|
|
869
|
-
color: var(--color-danger);
|
|
870
|
-
font-weight: var(--font-weight-regular);
|
|
871
|
-
}
|
|
903
|
+
/* No star. Asked for directly, 2026-09-25: "remove star and always
|
|
904
|
+
show red border for require field unless its typed out or
|
|
905
|
+
selected". The field's own border is the one and only required
|
|
906
|
+
signal now — see input:required:invalid below — and it needs no
|
|
907
|
+
asterisk to explain what it already shows. */
|
|
872
908
|
|
|
873
909
|
/* Primary action — the call-to-action fill. Charcoal, the same ink as
|
|
874
910
|
--color-brand (see --color-cta above): had a hue of its own for a
|
|
@@ -878,10 +914,11 @@ button {
|
|
|
878
914
|
font: inherit;
|
|
879
915
|
cursor: pointer;
|
|
880
916
|
border: none;
|
|
881
|
-
border-radius: var(--radius
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
font-
|
|
917
|
+
border-radius: var(--button-radius);
|
|
918
|
+
min-height: var(--button-height);
|
|
919
|
+
padding: var(--space-1) var(--button-padding-x);
|
|
920
|
+
font-size: var(--button-font-size);
|
|
921
|
+
font-weight: var(--button-font-weight);
|
|
885
922
|
background: var(--color-cta);
|
|
886
923
|
color: var(--color-cta-text);
|
|
887
924
|
box-shadow: var(--shadow-btn-primary);
|
|
@@ -940,13 +977,14 @@ button:focus-visible {
|
|
|
940
977
|
font: inherit;
|
|
941
978
|
cursor: pointer;
|
|
942
979
|
border: none;
|
|
943
|
-
border-radius: var(--radius
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
font-
|
|
947
|
-
|
|
980
|
+
border-radius: var(--button-radius);
|
|
981
|
+
min-height: var(--button-height);
|
|
982
|
+
padding: var(--space-1) var(--button-padding-x);
|
|
983
|
+
font-size: var(--button-font-size);
|
|
984
|
+
font-weight: var(--button-font-weight);
|
|
985
|
+
background: var(--button-secondary-bg);
|
|
948
986
|
color: var(--color-secondary-text);
|
|
949
|
-
box-shadow: var(--
|
|
987
|
+
box-shadow: var(--button-secondary-shadow);
|
|
950
988
|
text-decoration: none;
|
|
951
989
|
}
|
|
952
990
|
|
|
@@ -976,10 +1014,11 @@ button:focus-visible {
|
|
|
976
1014
|
font: inherit;
|
|
977
1015
|
cursor: pointer;
|
|
978
1016
|
border: none;
|
|
979
|
-
border-radius: var(--radius
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
font-
|
|
1017
|
+
border-radius: var(--button-radius);
|
|
1018
|
+
min-height: var(--button-height);
|
|
1019
|
+
padding: var(--space-1) var(--button-padding-x);
|
|
1020
|
+
font-size: var(--button-font-size);
|
|
1021
|
+
font-weight: var(--button-font-weight);
|
|
983
1022
|
background: var(--color-destructive-fill);
|
|
984
1023
|
/* Bare button{} above defaults every <button> to box-shadow:
|
|
985
1024
|
var(--shadow-btn-primary), a layered bevel built around
|
|
@@ -1083,6 +1122,16 @@ textarea::placeholder {
|
|
|
1083
1122
|
a warehouse read as a plain text box beside fields that plainly
|
|
1084
1123
|
opened.
|
|
1085
1124
|
------------------------------------------------------------------ */
|
|
1125
|
+
/* Combobox's real `required` validation lives on an invisible sibling
|
|
1126
|
+
input (see Combobox.tsx's own comment) — it has no border of its
|
|
1127
|
+
own to turn red, so the visible box reads its sibling's validity
|
|
1128
|
+
instead. Placed after :focus in the file so it wins while the field
|
|
1129
|
+
is focused too: empty-and-required should read as an error the
|
|
1130
|
+
whole time it stays empty, not just at rest. */
|
|
1131
|
+
.combobox-input:has(~ input:required:invalid) {
|
|
1132
|
+
border-color: var(--color-danger);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1086
1135
|
select,
|
|
1087
1136
|
.combobox-input {
|
|
1088
1137
|
appearance: none;
|
|
@@ -1184,57 +1233,14 @@ input[type="date"]::-webkit-calendar-picker-indicator:hover {
|
|
|
1184
1233
|
* difference between one red label on a form and all of them.
|
|
1185
1234
|
*/
|
|
1186
1235
|
/*
|
|
1187
|
-
*
|
|
1188
|
-
*
|
|
1189
|
-
*
|
|
1190
|
-
*
|
|
1191
|
-
*
|
|
1192
|
-
*
|
|
1193
|
-
*
|
|
1194
|
-
*
|
|
1195
|
-
* every form.
|
|
1196
|
-
*
|
|
1197
|
-
* So the label becomes a wrapping flex row for required fields only,
|
|
1198
|
-
* and the three items are ordered explicitly: the text (order 0, an
|
|
1199
|
-
* anonymous flex item), the star (order 1), then the control, forced
|
|
1200
|
-
* onto the next line by a full-width basis. The result is the text and
|
|
1201
|
-
* its star on one line and the field under them — which is what the
|
|
1202
|
-
* label already looked like, with a star added and no markup touched.
|
|
1203
|
-
*/
|
|
1204
|
-
label:has(:required) {
|
|
1205
|
-
/*
|
|
1206
|
-
* !important because 212 labels in this app carry an inline
|
|
1207
|
-
* `style={{ display: "block" }}`, and an inline declaration beats
|
|
1208
|
-
* any stylesheet rule regardless of specificity. This is the case
|
|
1209
|
-
* the keyword exists for: overriding markup a stylesheet cannot
|
|
1210
|
-
* reach. Without it the star renders after the control on every one
|
|
1211
|
-
* of those labels instead of beside the words.
|
|
1212
|
-
*/
|
|
1213
|
-
display: flex !important;
|
|
1214
|
-
flex-wrap: wrap;
|
|
1215
|
-
align-items: baseline;
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
label:has(:required) > * {
|
|
1219
|
-
order: 2;
|
|
1220
|
-
flex-basis: 100%;
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
label:has(:required)::after {
|
|
1224
|
-
content: "*";
|
|
1225
|
-
order: 1;
|
|
1226
|
-
margin-left: 2px;
|
|
1227
|
-
color: var(--color-danger);
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
label:has(:required) {
|
|
1231
|
-
color: var(--color-danger);
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
/*
|
|
1235
|
-
* The controls inside go back to the normal ink. Colour inherits, so
|
|
1236
|
-
* without this a red label means red typing in the box under it — and
|
|
1237
|
-
* on a Combobox, red text in the dropdown as well.
|
|
1236
|
+
* No star, and no flex-row layout to hold one — the star this used to
|
|
1237
|
+
* draw beside the text is gone (2026-09-25), so the label goes back
|
|
1238
|
+
* to its own ordinary flow, inline `display: block` and all. The
|
|
1239
|
+
* label's own colour was never touched by this rule either way (see
|
|
1240
|
+
* label[data-required]'s own comment above), so the override just
|
|
1241
|
+
* below — forcing the control's typed text back to normal ink — is
|
|
1242
|
+
* dead weight now and kept only because removing it is out of scope
|
|
1243
|
+
* for this pass.
|
|
1238
1244
|
*/
|
|
1239
1245
|
label:has(:required) input,
|
|
1240
1246
|
label:has(:required) select,
|
|
@@ -1274,12 +1280,27 @@ textarea:user-invalid {
|
|
|
1274
1280
|
border-color: var(--color-danger);
|
|
1275
1281
|
}
|
|
1276
1282
|
|
|
1283
|
+
/* Required and currently empty — on from the moment the form opens,
|
|
1284
|
+
not waiting for a touch. Off the instant a value exists: typed text
|
|
1285
|
+
satisfies :invalid on a plain input/textarea directly; a picked
|
|
1286
|
+
option satisfies it on a select or Combobox's own hidden required
|
|
1287
|
+
input (see .combobox-input's own rule, and .tags-input's, both
|
|
1288
|
+
further down) the same way. */
|
|
1289
|
+
input:required:invalid,
|
|
1290
|
+
select:required:invalid,
|
|
1291
|
+
textarea:required:invalid {
|
|
1292
|
+
border-color: var(--color-danger);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1277
1295
|
input[aria-invalid="true"]:focus-visible,
|
|
1278
1296
|
select[aria-invalid="true"]:focus-visible,
|
|
1279
1297
|
textarea[aria-invalid="true"]:focus-visible,
|
|
1280
1298
|
input:user-invalid:focus-visible,
|
|
1281
1299
|
select:user-invalid:focus-visible,
|
|
1282
|
-
textarea:user-invalid:focus-visible
|
|
1300
|
+
textarea:user-invalid:focus-visible,
|
|
1301
|
+
input:required:invalid:focus-visible,
|
|
1302
|
+
select:required:invalid:focus-visible,
|
|
1303
|
+
textarea:required:invalid:focus-visible {
|
|
1283
1304
|
border-color: var(--color-danger);
|
|
1284
1305
|
box-shadow: 0 0 0 3px var(--color-focus-ring-danger);
|
|
1285
1306
|
}
|