@hattisar/design-tokens 0.3.6 → 0.3.8

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 (2) hide show
  1. package/package.json +1 -1
  2. package/tokens.css +190 -44
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hattisar/design-tokens",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
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
@@ -32,21 +32,40 @@
32
32
  >=4.5:1) and for a real 3-step surface ladder — surface-tint used
33
33
  to equal background-global exactly, which meant hover states had
34
34
  no visible target to land on. */
35
- --color-background-global: #f7f8fa;
35
+ --color-background-global: #f4f4f5;
36
36
  --color-background-card: #ffffff;
37
- --color-surface-tint: #f0f2f6;
37
+ --color-surface-tint: #e8e8ea;
38
38
  --color-input: #ffffff;
39
- --color-text-primary: #10141a;
39
+ --color-text-primary: #09090b;
40
40
  /* ~6.4:1 on card */
41
- --color-text-secondary: #4b5563;
41
+ --color-text-secondary: #52525b;
42
42
  /* ~4.6:1 on card — was #8f99a8 at 2.9:1, failing AA body text */
43
- --color-text-muted: #5f6874;
43
+ --color-text-muted: #6b6b74;
44
44
  /* Derived from text-primary's own RGB (28,33,39) rather than a
45
45
  separately maintained literal, so border and text can never drift
46
46
  apart the way they used to (border was rgb(17,20,24), a different
47
47
  dark from text-primary's rgb(28,33,39)). */
48
- --color-border: rgba(16, 20, 26, 0.09);
49
- --color-border-strong: rgba(16, 20, 26, 0.16);
48
+ --color-border: #e4e4e7;
49
+ --color-border-strong: #d4d4d8;
50
+
51
+ /* Anything that floats over the page: dropdowns, menus, popovers,
52
+ modals, tooltips.
53
+ Reported: "we might need to add little background shadow for all
54
+ dropdown, popup so it differentiate from background. hard to view
55
+ now."
56
+ They all had a shadow already. The problem was that they were
57
+ painted --color-background-card and opened *over* cards — white on
58
+ white, separated by a 9%-alpha hairline and a shadow tuned so
59
+ softly it read as nothing. A surface only looks raised against
60
+ something.
61
+ So a floating surface gets its own tier: in dark it is a real step
62
+ above the card, and in light — where a popover over a white card
63
+ has nowhere lighter to go — the separation comes from a stronger
64
+ edge and a shadow with actual weight instead. */
65
+ --color-surface-raised: #ffffff;
66
+ --color-border-raised: #d4d4d8;
67
+ --shadow-popover: 0 4px 6px -2px rgba(9, 9, 11, 0.08), 0 12px 28px -8px rgba(9, 9, 11, 0.18);
68
+
50
69
  --color-hover: var(--color-surface-tint);
51
70
  --color-focus-ring: rgba(233, 235, 239, 0.2);
52
71
  --color-focus-ring-danger: rgba(255, 138, 156, 0.24);
@@ -54,7 +73,7 @@
54
73
  than a saturated blue: this palette has no brand hue, and a blue
55
74
  ring here would be the one place colour appeared without meaning
56
75
  anything — the ten status tiers are what colour is for. */
57
- --color-focus-ring: rgba(16, 20, 26, 0.14);
76
+ --color-focus-ring: rgba(9, 9, 11, 0.14);
58
77
  --color-focus-ring-danger: rgba(199, 10, 36, 0.2);
59
78
 
60
79
  /* Named accents — the same 8 hues most of the status tiers below
@@ -191,10 +210,10 @@
191
210
  popovers), lg for a modal. Tinted with the ink colour rather than
192
211
  pure black so a shadow reads as the surface's own shade instead of
193
212
  a grey smudge. */
194
- --shadow-xs: 0 1px 2px rgba(16, 20, 26, 0.04);
195
- --shadow-sm: 0 1px 2px rgba(16, 20, 26, 0.05), 0 2px 8px -3px rgba(16, 20, 26, 0.08);
196
- --shadow-md: 0 2px 4px -1px rgba(16, 20, 26, 0.04), 0 8px 20px -4px rgba(16, 20, 26, 0.12);
197
- --shadow-lg: 0 4px 8px -2px rgba(16, 20, 26, 0.06), 0 20px 48px -12px rgba(16, 20, 26, 0.22);
213
+ --shadow-xs: 0 1px 2px rgba(9, 9, 11, 0.04);
214
+ --shadow-sm: 0 1px 2px rgba(9, 9, 11, 0.05), 0 2px 8px -3px rgba(9, 9, 11, 0.08);
215
+ --shadow-md: 0 2px 4px -1px rgba(9, 9, 11, 0.04), 0 8px 20px -4px rgba(9, 9, 11, 0.12);
216
+ --shadow-lg: 0 4px 8px -2px rgba(9, 9, 11, 0.06), 0 20px 48px -12px rgba(9, 9, 11, 0.22);
198
217
 
199
218
  /* One shared tier for every FloatingPanel-based dropdown/menu
200
219
  (row actions, column picker, combobox, select, table-actions,
@@ -227,6 +246,21 @@
227
246
  regions of a page rather than elements inside one. Without them
228
247
  every large gap in the app was a hardcoded number, and they were
229
248
  all slightly different. */
249
+ /* The narrowest a form field may be before it stops being readable.
250
+ The document forms laid fields out as `flex: 1; minWidth: 140`,
251
+ which on a wide screen fits six controls onto one row at about
252
+ 130px each — narrow enough to clip "Customer's own reference"
253
+ mid-word. Every form field grid is built on this instead. */
254
+ --field-min-width: 260px;
255
+ /* For a control whose value is a word, not a sentence — a variant
256
+ name, a unit, a percentage — inside a line-item row. */
257
+ /* The metadata rail on a master-data form. Narrower than the
258
+ document rail (340px), which has to hold a party summary and a
259
+ Notes textarea — this holds status, tags and a note, and a
260
+ narrower rail keeps the content column the obvious subject. */
261
+ --record-rail-width: 300px;
262
+ --field-min-width-narrow: 140px;
263
+
230
264
  --space-16: 64px;
231
265
  --space-20: 80px;
232
266
 
@@ -320,22 +354,27 @@
320
354
  sits below the hover/tint tier. Modest steps between each (not a
321
355
  dramatic jump) plus real borders is what keeps a dark theme
322
356
  reading crisp instead of washed out — see --color-border below. */
323
- --color-background-global: #0d0f13;
324
- --color-background-card: #16191e;
325
- --color-surface-tint: #1f232a;
357
+ --color-background-global: #09090b;
358
+ --color-background-card: #18181b;
359
+ --color-surface-tint: #27272a;
326
360
  --color-input: var(--color-background-card);
327
361
  /* Not pure white — pure white text on a near-black surface causes
328
362
  halation (a glow/vibrate effect on the eye over long sessions).
329
363
  ~92% white instead. */
330
- --color-text-primary: #e9ebef;
364
+ --color-text-primary: #fafafa;
331
365
  /* ~7:1 on card */
332
- --color-text-secondary: #9ba4b2;
366
+ --color-text-secondary: #d4d4d8;
333
367
  /* ~4.7:1 on card — was #5f6b7c at 2.7:1, failing AA body text */
334
- --color-text-muted: #8b94a2;
368
+ --color-text-muted: #a1a1aa;
335
369
  /* Derived from text-primary's own RGB (233,233,236) — same
336
370
  single-source-of-truth reasoning as :root's own --color-border. */
337
- --color-border: rgba(233, 235, 239, 0.10);
338
- --color-border-strong: rgba(233, 235, 239, 0.18);
371
+ --color-border: #27272a;
372
+ --color-border-strong: #3f3f46;
373
+ /* A genuine step above the card, which is what makes a dropdown read
374
+ as floating rather than pasted on. */
375
+ --color-surface-raised: #1f1f23;
376
+ --color-border-raised: #3f3f46;
377
+ --shadow-popover: 0 4px 6px -2px rgba(0, 0, 0, 0.5), 0 16px 40px -10px rgba(0, 0, 0, 0.75);
339
378
  --color-hover: var(--color-surface-tint);
340
379
 
341
380
  /* Brighter counterparts of :root's accent set — a dark badge fill
@@ -360,10 +399,10 @@
360
399
  --color-brand: var(--color-text-primary);
361
400
  --color-brand-hover: color-mix(in srgb, var(--color-brand) 90%, black);
362
401
  --color-brand-active: color-mix(in srgb, var(--color-brand) 75%, black);
363
- --color-brand-text: #0d0f13;
402
+ --color-brand-text: #09090b;
364
403
 
365
- --shadow-btn-primary: 0 1px 2px rgba(16, 20, 26, 0.12);
366
- --shadow-btn-secondary: 0 1px 2px rgba(16, 20, 26, 0.05);
404
+ --shadow-btn-primary: 0 1px 2px rgba(9, 9, 11, 0.12);
405
+ --shadow-btn-secondary: 0 1px 2px rgba(9, 9, 11, 0.05);
367
406
 
368
407
  --color-secondary-fill: var(--color-surface-tint);
369
408
  --color-secondary-border: var(--color-border-strong);
@@ -417,12 +456,15 @@
417
456
  --shadow-md: 0 2px 4px -1px rgba(0, 0, 0, 0.4), 0 8px 22px -4px rgba(0, 0, 0, 0.6);
418
457
  --shadow-lg: 0 4px 8px -2px rgba(0, 0, 0, 0.45), 0 24px 56px -12px rgba(0, 0, 0, 0.75);
419
458
 
420
- /* Deliberately NOT --color-background-card — the sidebar is now a
421
- distinct chrome rail, true black, sitting a full step below the
422
- page canvas rather than matching the card surface. Common
423
- enterprise-dark-theme pattern (VS Code's activity bar, Slack's
424
- rail): the frame reads as structurally separate from content. */
425
- --sidebar-bg: #000000;
459
+ /* A distinct chrome rail, still — but a step *above* the canvas now
460
+ rather than below it. It used to be true black under a #0d0f13
461
+ page, which read as a rail. Against zinc-950 the same black is
462
+ three values away from the page and the rail disappeared, so the
463
+ separation now comes from the other direction: zinc-900, the same
464
+ surface as a card, which is where shadcn puts a sidebar too.
465
+ The frame still reads as structurally separate from content, which
466
+ was the point of the original choice. */
467
+ --sidebar-bg: #18181b;
426
468
  --sidebar-text: var(--color-text-primary);
427
469
  --sidebar-text-muted: var(--color-text-muted);
428
470
  /* Two distinct white-overlay intensities on top of --sidebar-bg —
@@ -433,7 +475,7 @@
433
475
  --sidebar-active-text: var(--color-text-primary);
434
476
  --sidebar-hover-bg: rgba(255, 255, 255, 0.06);
435
477
  --sidebar-active-icon: var(--sidebar-active-text);
436
- --sidebar-rail-bg: #000000;
478
+ --sidebar-rail-bg: #18181b;
437
479
 
438
480
  color-scheme: dark;
439
481
  }
@@ -444,15 +486,18 @@
444
486
  takes the nearest declaration in the tree, so relying on :root
445
487
  alone would lose to a dark attribute set higher up. */
446
488
  [data-theme="light"] {
447
- --color-background-global: #f7f8fa;
489
+ --color-background-global: #f4f4f5;
448
490
  --color-background-card: #ffffff;
449
- --color-surface-tint: #f0f2f6;
491
+ --color-surface-tint: #e8e8ea;
450
492
  --color-input: #ffffff;
451
- --color-text-primary: #10141a;
452
- --color-text-secondary: #4b5563;
453
- --color-text-muted: #5f6874;
454
- --color-border: rgba(16, 20, 26, 0.09);
455
- --color-border-strong: rgba(16, 20, 26, 0.16);
493
+ --color-text-primary: #09090b;
494
+ --color-text-secondary: #52525b;
495
+ --color-text-muted: #6b6b74;
496
+ --color-border: #e4e4e7;
497
+ --color-border-strong: #d4d4d8;
498
+ --color-surface-raised: #ffffff;
499
+ --color-border-raised: #d4d4d8;
500
+ --shadow-popover: 0 4px 6px -2px rgba(9, 9, 11, 0.08), 0 12px 28px -8px rgba(9, 9, 11, 0.18);
456
501
  --color-hover: var(--color-surface-tint);
457
502
 
458
503
  --color-brand: var(--color-text-primary);
@@ -517,7 +562,7 @@
517
562
  --shadow-lg: none;
518
563
 
519
564
  --sidebar-bg: var(--color-background-card);
520
- --sidebar-text: #10141a;
565
+ --sidebar-text: #09090b;
521
566
  --sidebar-text-muted: var(--color-text-muted);
522
567
  --sidebar-active-bg: var(--color-surface-tint);
523
568
  --sidebar-active-text: var(--color-text-primary);
@@ -811,17 +856,118 @@ textarea::placeholder {
811
856
  bottom padding is what actually centers the selected value; the
812
857
  native arrow's box was throwing that off by consuming space
813
858
  asymmetrically. The open option list itself stays OS-native — no
814
- browser lets CSS reach inside it. */
815
- select {
859
+ browser lets CSS reach inside it.
860
+
861
+ `input[list]` gets the same chevron. It opens a dropdown of
862
+ suggestions exactly as a select does, but Chrome draws no affordance
863
+ for one at all — so a datalist field read as a plain text box while
864
+ sitting between two selects that both had an arrow. The difference
865
+ that remains, that you may also type a value not on the list, is the
866
+ right one to leave invisible: typing into a text box is
867
+ self-evidently allowed. */
868
+ /* ------------------------------------------------------------------
869
+ Choosing one of something: four controls, one appearance.
870
+
871
+ They are four because they do different jobs, and collapsing them
872
+ would cost something real each time:
873
+
874
+ select a closed set small enough to scroll. Cheap,
875
+ native, keyboard- and mobile-friendly.
876
+ Combobox a closed set too large to scroll — there are
877
+ 4,021 customers here, and a <select> of 4,021
878
+ options is not a control, it is a punishment.
879
+ input[type=date] a date. The native one carries the OS picker,
880
+ typed entry, locale order and mobile wheels;
881
+ a hand-built calendar loses all four and is a
882
+ well-known accessibility swamp.
883
+
884
+ Which to reach for:
885
+ under ~15 fixed options, closed -> select
886
+ anything else -> Combobox
887
+
888
+ Not input[list]. A datalist accepts typed values, which is why it
889
+ looked like the right control for an open set — but its suggestion
890
+ popup is drawn by the browser and no CSS reaches inside it, so the
891
+ field opens looking like nothing else on the page. Combobox's
892
+ `onCreate` covers the open set with the app's own dropdown: where
893
+ there is no table to insert into, "creating" is just accepting the
894
+ typed text (see ChannelSelect, VariantSelect).
895
+
896
+ What they share is the mark at the right-hand end that says "this
897
+ opens something", and it is the same mark in the same place on all
898
+ of them. It was not: the Combobox had none at all, so a customer or
899
+ a warehouse read as a plain text box beside fields that plainly
900
+ opened.
901
+ ------------------------------------------------------------------ */
902
+ select,
903
+ .combobox-input {
816
904
  appearance: none;
817
905
  -webkit-appearance: none;
818
- line-height: normal;
819
906
  padding-right: 32px;
820
907
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none'%3E%3Cpath d='M1 1L5 5L9 1' stroke='%23727676' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
821
908
  background-repeat: no-repeat;
822
909
  background-position: right 10px center;
823
910
  }
824
911
 
912
+ /* Only a native select needs this, and sharing it was a bug worth
913
+ recording. `line-height: normal` is here to re-centre a select's
914
+ text after appearance:none strips the native box — a select does not
915
+ honour the 1.4 the other fields use. A .combobox-input is a real
916
+ <input> and does, so inheriting `normal` (about 1.2) made it roughly
917
+ three pixels shorter than the text field and the date field beside
918
+ it: same padding, same border, visibly smaller box.
919
+
920
+ The chevron above is what the two share. The line-height is not. */
921
+ select {
922
+ line-height: normal;
923
+ }
924
+
925
+ /* A date field's calendar button is drawn by the browser, not by us —
926
+ it cannot be swapped for the chevron above, but it can be given the
927
+ same weight and the same position, so a row of dates and selects
928
+ reads as one row of controls rather than two kinds. Sized and dimmed
929
+ to match; `color-scheme` (set on :root and both themes) is what
930
+ makes it and the popup follow the theme. */
931
+ input[type="date"]::-webkit-calendar-picker-indicator {
932
+ opacity: 0.55;
933
+ cursor: pointer;
934
+ padding: 0;
935
+ margin-right: 2px;
936
+ }
937
+
938
+ input[type="date"]::-webkit-calendar-picker-indicator:hover {
939
+ opacity: 1;
940
+ }
941
+
942
+ /* A button that carries no button chrome — a chevron toggle, a
943
+ "Forgot password?" link, an inline remove. Seven of these were made
944
+ bare by hand with `style={{ background: "none", border: "none",
945
+ padding: 0 }}`, and every one of them missed the same thing: the
946
+ bare `button` rule above also sets `box-shadow:
947
+ var(--shadow-btn-primary)`, which an inline background and border
948
+ do not clear.
949
+ That was invisible while every --shadow-* token was `none`. Turning
950
+ them on gave each of these buttons a floating rectangle its author
951
+ had explicitly tried to remove — the "extra layer" behind
952
+ "Additional information". Declared once here so the next one cannot
953
+ miss it. */
954
+ .btn-bare {
955
+ background: none;
956
+ border: none;
957
+ box-shadow: none;
958
+ padding: 0;
959
+ font: inherit;
960
+ color: inherit;
961
+ cursor: pointer;
962
+ text-align: inherit;
963
+ }
964
+
965
+ .btn-bare:hover:not(:disabled) {
966
+ background: none;
967
+ box-shadow: none;
968
+ transform: none;
969
+ }
970
+
825
971
  /* Focus: the border goes to brand ink and a soft ring is drawn *by the
826
972
  field itself*, with box-shadow rather than outline.
827
973
  The border alone was the previous answer, after an `outline` with an
@@ -1137,10 +1283,10 @@ a.card:hover {
1137
1283
  }
1138
1284
 
1139
1285
  .modal-panel {
1140
- background: var(--color-background-card);
1141
- border: 1px solid var(--color-border);
1286
+ background: var(--color-surface-raised);
1287
+ border: 1px solid var(--color-border-raised);
1142
1288
  border-radius: var(--radius-lg);
1143
- box-shadow: var(--shadow-lg);
1289
+ box-shadow: var(--shadow-popover);
1144
1290
  width: 100%;
1145
1291
  max-width: 640px;
1146
1292
  max-height: 80vh;