@hattisar/design-tokens 0.3.9 → 0.3.11

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 +119 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hattisar/design-tokens",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
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
@@ -109,6 +109,25 @@
109
109
  --color-brand-active: color-mix(in srgb, var(--color-brand) 70%, black);
110
110
  --color-brand-text: #ffffff;
111
111
 
112
+ /* The main call to action.
113
+ Asked for by hex: "main CTA color #4285f4". It is the one place in
114
+ this palette with a hue — greys carry structure, colour carries
115
+ status, and now one blue carries "this is the button you press".
116
+ --color-brand stays charcoal ink: it is the focus ring, the active
117
+ tab marker and the checkbox tick, and turning all of those blue is
118
+ a bigger change than a call-to-action colour.
119
+
120
+ White on #4285f4 measures 3.56:1, below the 4.5:1 this file
121
+ promises everywhere else. It is recorded as a deliberate exception
122
+ in check-contrast.mjs rather than quietly left out of it — the
123
+ nearest shade that passes is #1a73e8 at 4.51:1, visually the same
124
+ blue, if the ratio matters more than the exact hex. */
125
+ --color-cta: #4285f4;
126
+ --color-cta-hover: color-mix(in srgb, var(--color-cta) 88%, black);
127
+ --color-cta-active: color-mix(in srgb, var(--color-cta) 76%, black);
128
+ --color-cta-text: #ffffff;
129
+
130
+
112
131
  /* Secondary button — bordered light-gray fill. Reuses
113
132
  --color-surface-tint/--color-border-strong/--color-text-primary
114
133
  rather than their own literal hex, so the secondary button always
@@ -404,6 +423,10 @@
404
423
  --color-brand-hover: color-mix(in srgb, var(--color-brand) 90%, black);
405
424
  --color-brand-active: color-mix(in srgb, var(--color-brand) 75%, black);
406
425
  --color-brand-text: #09090b;
426
+ --color-cta: #4285f4;
427
+ --color-cta-hover: color-mix(in srgb, var(--color-cta) 88%, black);
428
+ --color-cta-active: color-mix(in srgb, var(--color-cta) 76%, black);
429
+ --color-cta-text: #ffffff;
407
430
 
408
431
  --shadow-btn-primary: 0 1px 2px rgba(9, 9, 11, 0.12);
409
432
  --shadow-btn-secondary: 0 1px 2px rgba(9, 9, 11, 0.05);
@@ -508,6 +531,10 @@
508
531
  --color-brand-hover: color-mix(in srgb, var(--color-brand) 85%, black);
509
532
  --color-brand-active: color-mix(in srgb, var(--color-brand) 70%, black);
510
533
  --color-brand-text: #ffffff;
534
+ --color-cta: #4285f4;
535
+ --color-cta-hover: color-mix(in srgb, var(--color-cta) 88%, black);
536
+ --color-cta-active: color-mix(in srgb, var(--color-cta) 76%, black);
537
+ --color-cta-text: #ffffff;
511
538
 
512
539
  --shadow-btn-primary: 0 1px 2px rgba(0, 0, 0, 0.4);
513
540
  --shadow-btn-secondary: 0 1px 2px rgba(0, 0, 0, 0.3);
@@ -710,8 +737,10 @@ label[data-required]::after {
710
737
  font-weight: var(--font-weight-regular);
711
738
  }
712
739
 
713
- /* Primary action — highest-contrast neutral fill (--color-brand),
714
- paired text color (--color-brand-text). */
740
+ /* Primary action — the call-to-action fill. Charcoal until it was
741
+ given a colour of its own; --color-brand still carries structure
742
+ (focus ring, active tab, checkbox tick) and is deliberately not
743
+ this. */
715
744
  button {
716
745
  font: inherit;
717
746
  cursor: pointer;
@@ -720,8 +749,8 @@ button {
720
749
  padding: 5px 10px;
721
750
  font-size: var(--text-base);
722
751
  font-weight: 400;
723
- background: var(--color-brand);
724
- color: var(--color-brand-text);
752
+ background: var(--color-cta);
753
+ color: var(--color-cta-text);
725
754
  box-shadow: var(--shadow-btn-primary);
726
755
  transition:
727
756
  transform 0.05s ease,
@@ -730,11 +759,11 @@ button {
730
759
  }
731
760
 
732
761
  button:hover {
733
- background: var(--color-brand-hover);
762
+ background: var(--color-cta-hover);
734
763
  }
735
764
 
736
765
  button:active {
737
- background: var(--color-brand-active);
766
+ background: var(--color-cta-active);
738
767
  transform: scale(0.97);
739
768
  }
740
769
 
@@ -979,6 +1008,90 @@ input[type="date"]::-webkit-calendar-picker-indicator:hover {
979
1008
  box-shadow spread follows the border-radius exactly, so it reads as
980
1009
  one thickened edge — which fixes the original complaint without
981
1010
  leaving keyboard users hunting for which field they are in. */
1011
+ /* A required field says so on its label, in the red this app already
1012
+ uses for things that are wrong or missing.
1013
+ Asked for: "use our existing red for required field title" — the
1014
+ same convention Zoho uses, where a required label is red and the
1015
+ optional ones are not.
1016
+
1017
+ Written as `:has()` rather than a prop on forty forms: the label
1018
+ already wraps its control, so the markup that says "required" is
1019
+ already there and nothing needs to be repeated. A field that becomes
1020
+ optional stops being red on its own, which a hand-maintained flag
1021
+ would not.
1022
+
1023
+ The control inside is put back to the normal ink — colour is
1024
+ inherited, and a red label would otherwise mean red typing in the
1025
+ box under it. */
1026
+ /*
1027
+ * Descendant, not direct child. A Combobox already renders a hidden
1028
+ * `<input required>` so that native form validation still works the
1029
+ * way the `<select required>` it replaced did — but it is nested
1030
+ * inside the container div, so `> :required` could not see it and only
1031
+ * the handful of plain `<input required>` fields turned red. Every
1032
+ * picker in the app is a Combobox underneath, so this is the
1033
+ * difference between one red label on a form and all of them.
1034
+ */
1035
+ /*
1036
+ * And a star after the label text, the way Zoho marks a required
1037
+ * field.
1038
+ *
1039
+ * Harder than it looks, because the label's text is a bare text node:
1040
+ * `<label>First Name<input required></label>`. A plain `label::after`
1041
+ * renders after *everything* in the label — under the input, not
1042
+ * beside the words — and the only way to attach it to the text
1043
+ * directly would be to wrap that text in a span on every field of
1044
+ * every form.
1045
+ *
1046
+ * So the label becomes a wrapping flex row for required fields only,
1047
+ * and the three items are ordered explicitly: the text (order 0, an
1048
+ * anonymous flex item), the star (order 1), then the control, forced
1049
+ * onto the next line by a full-width basis. The result is the text and
1050
+ * its star on one line and the field under them — which is what the
1051
+ * label already looked like, with a star added and no markup touched.
1052
+ */
1053
+ label:has(:required) {
1054
+ /*
1055
+ * !important because 212 labels in this app carry an inline
1056
+ * `style={{ display: "block" }}`, and an inline declaration beats
1057
+ * any stylesheet rule regardless of specificity. This is the case
1058
+ * the keyword exists for: overriding markup a stylesheet cannot
1059
+ * reach. Without it the star renders after the control on every one
1060
+ * of those labels instead of beside the words.
1061
+ */
1062
+ display: flex !important;
1063
+ flex-wrap: wrap;
1064
+ align-items: baseline;
1065
+ }
1066
+
1067
+ label:has(:required) > * {
1068
+ order: 2;
1069
+ flex-basis: 100%;
1070
+ }
1071
+
1072
+ label:has(:required)::after {
1073
+ content: "*";
1074
+ order: 1;
1075
+ margin-left: 2px;
1076
+ color: var(--color-danger);
1077
+ }
1078
+
1079
+ label:has(:required) {
1080
+ color: var(--color-danger);
1081
+ }
1082
+
1083
+ /*
1084
+ * The controls inside go back to the normal ink. Colour inherits, so
1085
+ * without this a red label means red typing in the box under it — and
1086
+ * on a Combobox, red text in the dropdown as well.
1087
+ */
1088
+ label:has(:required) input,
1089
+ label:has(:required) select,
1090
+ label:has(:required) textarea,
1091
+ label:has(:required) .combobox-input {
1092
+ color: var(--color-text-primary);
1093
+ }
1094
+
982
1095
  input:focus-visible,
983
1096
  select:focus-visible,
984
1097
  textarea:focus-visible {