@stackoverflow/stacks 1.1.0 → 1.3.1

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 (40) hide show
  1. package/dist/controllers/s-expandable-control.d.ts +1 -1
  2. package/dist/controllers/s-tooltip.d.ts +16 -1
  3. package/dist/css/stacks.css +887 -708
  4. package/dist/css/stacks.min.css +1 -1
  5. package/dist/js/stacks.js +174 -91
  6. package/dist/js/stacks.min.js +1 -1
  7. package/lib/css/atomic/misc.less +1 -1
  8. package/lib/css/atomic/typography.less +0 -6
  9. package/lib/css/atomic/width-height.less +1 -1
  10. package/lib/css/components/activity-indicator.less +18 -17
  11. package/lib/css/components/avatars.less +51 -131
  12. package/lib/css/components/badges.less +47 -0
  13. package/lib/css/components/breadcrumbs.less +4 -4
  14. package/lib/css/components/buttons.less +38 -54
  15. package/lib/css/components/empty-states.less +15 -0
  16. package/lib/css/components/{collapsible.less → expandable.less} +0 -0
  17. package/lib/css/components/inputs.less +44 -110
  18. package/lib/css/components/labels.less +98 -0
  19. package/lib/css/components/notices.less +190 -163
  20. package/lib/css/components/post-summary.less +34 -99
  21. package/lib/css/components/progress-bars.less +1 -1
  22. package/lib/css/components/prose.less +4 -4
  23. package/lib/css/components/spinner.less +39 -1
  24. package/lib/css/components/tables.less +1 -5
  25. package/lib/css/components/topbar.less +4 -1
  26. package/lib/css/components/uploader.less +70 -84
  27. package/lib/css/exports/constants-colors.less +63 -49
  28. package/lib/css/stacks-dynamic.less +0 -1
  29. package/lib/css/stacks-static.less +3 -2
  30. package/lib/ts/controllers/s-expandable-control.ts +23 -19
  31. package/lib/ts/controllers/s-modal.ts +16 -16
  32. package/lib/ts/controllers/s-navigation-tablist.ts +13 -13
  33. package/lib/ts/controllers/s-popover.ts +26 -18
  34. package/lib/ts/controllers/s-table.ts +31 -29
  35. package/lib/ts/controllers/s-tooltip.ts +62 -23
  36. package/lib/ts/controllers/s-uploader.ts +26 -12
  37. package/lib/ts/stacks.ts +8 -4
  38. package/package.json +25 -25
  39. package/lib/css/components/banners.less +0 -80
  40. package/lib/css/components/blank-states.less +0 -26
@@ -96,6 +96,44 @@
96
96
  }
97
97
  }
98
98
 
99
+ .is-loading {
100
+ position: relative;
101
+ padding-left: 2.2em;
102
+
103
+ &:before {
104
+ content: "";
105
+ position: absolute;
106
+ opacity: 0.3;
107
+ left: 0.6em;
108
+ top: calc(50% - 0.6em);
109
+ width: 1.23076923em;
110
+ height: 1.23076923em;
111
+ border-width: 2px;
112
+ border-style: solid;
113
+ border-color: currentColor;
114
+ border-radius: var(--br-circle);
115
+ }
116
+
117
+ &:after {
118
+ content: "";
119
+ position: absolute;
120
+ left: 0.6em;
121
+ top: calc(50% - 0.6em);
122
+ width: 1.23076923em;
123
+ height: 1.23076923em;
124
+ border-width: 2px;
125
+ border-style: solid;
126
+ border-color: transparent;
127
+ border-left-color: currentColor;
128
+ border-radius: var(--br-circle);
129
+ animation: s-spinner-rotate 0.9s infinite
130
+ cubic-bezier(0.5, 0.1, 0.5, 0.9);
131
+ // see _stacks-spinner.less for an explanation of the following two
132
+ filter: invert(0); // (*)
133
+ transform-origin: 50% 50% 1px; // (*)
134
+ }
135
+ }
136
+
99
137
  // Keyframes
100
138
  @keyframes s-spinner-rotate {
101
139
  from {
@@ -104,4 +142,4 @@
104
142
  to {
105
143
  transform: rotate(360deg);
106
144
  }
107
- }
145
+ }
@@ -311,11 +311,7 @@
311
311
 
312
312
  th:not(.is-enabled),
313
313
  td:not(.is-enabled) {
314
- opacity: 0.3;
315
-
316
- .highcontrast-mode({
317
- opacity: 0.8;
318
- });
314
+ opacity: calc(var(--_o-disabled) * 0.6); // 0.5 * 0.6 = 0.3
319
315
  }
320
316
  }
321
317
  }
@@ -130,6 +130,9 @@
130
130
  }
131
131
 
132
132
  .s-navigation {
133
+ .s-navigation--item:focus-visible {
134
+ box-shadow: var(--theme-topbar-search-shadow-focus);
135
+ }
133
136
  .s-navigation--item:not(.is-selected) {
134
137
  color: var(--theme-topbar-item-color);
135
138
  }
@@ -153,7 +156,7 @@
153
156
  // Search input
154
157
  --theme-topbar-search-color: @black-700;
155
158
  --theme-topbar-search-background: @white;
156
- --theme-topbar-search-placeholder: @black-200;
159
+ --theme-topbar-search-placeholder: @black-400;
157
160
  --theme-topbar-search-border: @black-200;
158
161
  --theme-topbar-search-border-focus: @blue-300;
159
162
  --theme-topbar-search-shadow-focus: 0 0 0 var(--su-static4) var(--focus-ring);
@@ -7,106 +7,91 @@
7
7
  // visit https://stackoverflow.design/
8
8
 
9
9
  .s-uploader {
10
- align-items: center;
11
- background-color: var(--black-025);
12
- border-radius: var(--br-lg);
13
- display: flex;
14
- min-height: var(--su-static128);
15
- justify-content: center;
16
- padding: var(--su8) var(--su16);
17
- position: relative;
18
- text-align: center;
19
-
20
- // Add the dashed border as an SVG background mask
21
- &:before {
22
- --s-uploader-background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='5' ry='5' stroke='%23000000' stroke-width='8' stroke-dasharray='7%2c 22' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e");
23
- content: '';
24
- display: block;
25
- position: absolute;
26
- top: 0;
27
- left: 0;
28
- right: 0;
29
- bottom: 0;
30
- background-color: var(--black-150);
31
- -webkit-mask-image: var(--s-uploader-background-image);
32
- mask-image: var(--s-uploader-background-image);
33
- border-radius: var(--br-lg);
10
+ --_bg: var(--black-025);
11
+ --_bg-focus: var(--black-050);
12
+ --_bg-bc: var(--black-150);
13
+ --_focus-ring-color: var(--focus-ring);
34
14
 
35
- .highcontrast-mode({
36
- background-color: var(--black-400);
37
- });
38
- }
15
+ // Static custom properties (not redefined but repeated enough to warrant a custom property)
16
+ --_bg-b-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='5' ry='5' stroke='%23000000' stroke-width='8' stroke-dasharray='7%2c 22' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e");
39
17
 
40
- &.has-error {
41
- background-color: var(--red-050);
18
+ .highcontrast-mode({
19
+ --_bg-bc-hc: var(--black-400);
20
+ });
42
21
 
43
- &:before {
44
- background-color: var(--red-400);
22
+ &.is-active {
23
+ --_bg: var(--black-050);
24
+ --_bg-bc: var(--black-200);
25
+ }
45
26
 
46
- .highcontrast-mode({
47
- background-color: var(--red-400);
48
- });
49
- }
27
+ &.is-disabled {
28
+ opacity: var(--_o-disabled-static);
29
+ }
50
30
 
51
- .s-link {
52
- color: var(--red-900);
53
- }
31
+ &.has-error {
32
+ --_bg: var(--red-050);
33
+ --_bg-focus: var(--red-100);
34
+ --_bg-bc: var(--red-400);
35
+ --_bg-bc-hc-state: var(--red-400);
36
+ --_focus-ring-color: var(--focus-ring-error);
37
+ --_link-fc: var(--red-900);
54
38
  }
55
39
 
56
40
  &.has-success {
57
- background-color: var(--green-025);
58
-
59
- &:before {
60
- background-color: var(--green-400);
61
-
62
- .highcontrast-mode({
63
- background-color: var(--green-400);
64
- });
65
- }
66
-
67
- .s-link {
68
- color: var(--green-900);
69
- }
41
+ --_bg: var(--green-025);
42
+ --_bg-focus: var(--green-050);
43
+ --_bg-bc: var(--green-400);
44
+ --_bg-bc-hc-state: var(--green-400);
45
+ --_focus-ring-color: var(--focus-ring-success);
46
+ --_link-fc: var(--green-900);
70
47
  }
71
48
 
72
49
  &.has-warning {
73
- background-color: var(--yellow-050);
74
-
75
- &:before {
76
- background-color: var(--yellow-400);
77
-
78
- .highcontrast-mode({
79
- background-color: var(--yellow-400);
80
- });
81
- }
82
-
83
- .s-link {
84
- color: var(--yellow-900);
85
- }
50
+ --_bg: var(--yellow-050);
51
+ --_bg-focus: var(--yellow-100);
52
+ --_bg-bc: var(--yellow-400);
53
+ --_bg-bc-hc-state: var(--yellow-400);
54
+ --_focus-ring-color: var(--focus-ring-warning);
55
+ --_link-fc: var(--yellow-900);
86
56
  }
87
57
 
88
- &.is-active {
89
- background-color: var(--black-050);
90
-
91
- &:before {
92
- background-color: var(--black-200);
93
-
94
- .highcontrast-mode({
95
- background-color: var(--black);
96
- });
58
+ &.has-error,
59
+ &.has-success,
60
+ &.has-warning {
61
+ .s-link {
62
+ color: var(--_link-fc);
97
63
  }
98
64
  }
99
65
 
100
- &.is-disabled {
101
- opacity: 0.5;
102
- }
103
-
104
66
  // This is to for safari shadow DOM
105
67
  // see https://github.com/StackExchange/Stacks/pull/690#issuecomment-861028193
106
68
  input[type="file"]::file-selector-button {
107
69
  cursor: pointer;
108
70
  }
109
-
71
+ .s-uploader--container {
72
+ align-items: center;
73
+ background-color: var(--_bg);
74
+ border-radius: var(--br-lg);
75
+ display: flex;
76
+ flex-direction: column;
77
+ justify-content: center;
78
+ min-height: var(--su-static128);
79
+ padding: var(--su8) var(--su16);
80
+ position: relative;
81
+ text-align: center;
82
+
83
+ // Add the dashed border as an SVG background mask
84
+ &:before {
85
+ -webkit-mask-image: var(--_bg-b-image);
86
+ mask-image: var(--_bg-b-image);
87
+ background-color: var(--_bg-bc-hc-state, var(--_bg-bc-hc, var(--_bg-bc)));
88
+ content: '';
89
+ border-radius: var(--br-lg);
90
+ display: block;
91
+ position: absolute;
92
+ inset: 0;
93
+ }
94
+ }
110
95
  .s-uploader--input {
111
96
  cursor: pointer;
112
97
  height: 100%;
@@ -114,13 +99,13 @@
114
99
  opacity: 0;
115
100
  position: absolute;
116
101
  width: 100%;
102
+ z-index: var(--zi-selected);
117
103
 
118
104
  &:focus:focus-visible + .s-uploader--container {
119
- background-color: var(--black-050);
120
- box-shadow: 0 0 0 var(--su-static4) var(--focus-ring);
105
+ background-color: var(--_bg-focus);
106
+ box-shadow: 0 0 0 var(--su-static4) var(--_focus-ring-color);
121
107
  }
122
108
  }
123
-
124
109
  .s-uploader--preview {
125
110
  max-width: 100%;
126
111
  pointer-events: none;
@@ -185,8 +170,7 @@
185
170
  object-fit: cover;
186
171
  }
187
172
  &:not(img) {
188
- --s-uploader--preview-document-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' fill='%23535A60' width='18' height='18' viewBox='0 0 18 18'%3E%3Cpath d='M3 3a2 2 0 012-2h6l4 4v10a2 2 0 01-2 2H5a2 2 0 01-2-2V3zm7-1.5V6h4.5L10 1.5z'%3E%3C/path%3E%3C/svg%3E");
189
- background-image: var(--s-uploader--preview-document-icon);
173
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' fill='%23535A60' width='18' height='18' viewBox='0 0 18 18'%3E%3Cpath d='M3 3a2 2 0 012-2h6l4 4v10a2 2 0 01-2 2H5a2 2 0 01-2-2V3zm7-1.5V6h4.5L10 1.5z'%3E%3C/path%3E%3C/svg%3E");
190
174
  background-position: center;
191
175
  background-repeat: no-repeat;
192
176
  }
@@ -207,4 +191,6 @@
207
191
  top: var(--su8);
208
192
  z-index: var(--zi-active);
209
193
  }
194
+
195
+ position: relative;
210
196
  }
@@ -247,7 +247,7 @@
247
247
  // Topbar Search input
248
248
  --theme-topbar-search-color: var(--black-700);
249
249
  --theme-topbar-search-background: var(--white);
250
- --theme-topbar-search-placeholder: var(--black-200);
250
+ --theme-topbar-search-placeholder: var(--black-400);
251
251
  --theme-topbar-search-border: var(--black-200);
252
252
  --theme-topbar-search-border-focus: var(--blue-300);
253
253
  --theme-topbar-search-shadow-focus: 0 0 0 var(--su-static4) var(--focus-ring);
@@ -409,6 +409,10 @@
409
409
  --focus-ring-error: hsla(@red-h, 62%, 47%, 15%);
410
410
  --focus-ring-muted: hsla(@black-h, @black-s, 15%, 10%);
411
411
 
412
+ // Opacity
413
+ --_o-disabled: 0.5;
414
+ --_o-disabled-static: 0.5;
415
+
412
416
  // Shadows
413
417
  --bs-sm: 0 1px 2px hsla(0, 0%, 0%, 0.05), 0 1px 4px hsla(0, 0%, 0%, 0.05), 0 2px 8px hsla(0, 0%, 0%, 0.05);
414
418
  --bs-md: 0 1px 3px hsla(0, 0%, 0%, 0.06), 0 2px 6px hsla(0, 0%, 0%, 0.06), 0 3px 8px hsla(0, 0%, 0%, 0.09);
@@ -605,6 +609,10 @@
605
609
  --focus-ring-error: hsla(@red-h, 62%, 52%, 30%);
606
610
  --focus-ring-muted: hsla(@black-h, @black-s, 100%, 10%);
607
611
 
612
+ // Opacity
613
+ --_o-disabled: 0.5;
614
+ --_o-disabled-static: 0.5;
615
+
608
616
  // Shadows
609
617
  --bs-sm: 0 1px 2px hsla(0, 0%, 0%, 0.1), 0 1px 4px hsla(0, 0%, 0%, 0.1), 0 2px 8px hsla(0, 0%, 0%, 0.1);
610
618
  --bs-md: 0 1px 3px hsla(0, 0%, 0%, 0.11), 0 2px 6px hsla(0, 0%, 0%, 0.11), 0 3px 8px hsla(0, 0%, 0%, 0.14);
@@ -709,10 +717,10 @@
709
717
  --black-900: hsl(@black-h, @black-s, 0%);
710
718
 
711
719
  // Orange
712
- --orange-050: hsl(@orange-h, 100%, 97.5%);
713
- --orange-100: hsl(@orange-h, 100%, 94%);
714
- --orange-200: hsl(@orange-h, 100%, 88%);
715
- --orange-300: hsl(@orange-h, 100%, 82%);
720
+ --orange-050: hsl(@orange-h, 100%, 93%);
721
+ --orange-100: hsl(@orange-h, 100%, 90%);
722
+ --orange-200: hsl(@orange-h, 100%, 86%);
723
+ --orange-300: hsl(@orange-h, 100%, 81%);
716
724
  --orange-400: hsl(@orange-h, 100%, 34%);
717
725
  --orange-500: hsl(@orange-h, 100%, 29%);
718
726
  --orange-600: hsl(@orange-h, 100%, 24%);
@@ -721,10 +729,10 @@
721
729
  --orange-900: hsl(@orange-h, 100%, 8%);
722
730
 
723
731
  // Blue
724
- --blue-050: hsl(@blue-h, 100%, 97.5%);
725
- --blue-100: hsl(@blue-h, 100%, 95%);
726
- --blue-200: hsl(@blue-h, 100%, 90%);
727
- --blue-300: hsl(@blue-h, 100%, 85%);
732
+ --blue-050: hsl(@blue-h, 100%, 94%);
733
+ --blue-100: hsl(@blue-h, 100%, 92%);
734
+ --blue-200: hsl(@blue-h, 100%, 89%);
735
+ --blue-300: hsl(@blue-h, 100%, 86%);
728
736
  --blue-400: hsl(@blue-h, 100%, 25%);
729
737
  --blue-500: hsl(@blue-h, 100%, 20%);
730
738
  --blue-600: hsl(@blue-h, 100%, 15%);
@@ -733,10 +741,10 @@
733
741
  --blue-900: hsl(@blue-h, 100%, 5%);
734
742
 
735
743
  // Powder
736
- --powder-050: hsl(@powder-h, 100%, 97.5%);
737
- --powder-100: hsl(@powder-h, 85%, 96%);
738
- --powder-200: hsl(@powder-h, 75%, 92%);
739
- --powder-300: hsl(@powder-h, 70%, 88%);
744
+ --powder-050: hsl(@powder-h, 100%, 95%);
745
+ --powder-100: hsl(@powder-h, 85%, 93%);
746
+ --powder-200: hsl(@powder-h, 75%, 89%);
747
+ --powder-300: hsl(@powder-h, 70%, 86%);
740
748
  --powder-400: hsl(@powder-h, 55%, 28%);
741
749
  --powder-500: hsl(@powder-h, 60%, 24%);
742
750
  --powder-600: hsl(@powder-h, 70%, 20%);
@@ -745,11 +753,11 @@
745
753
  --powder-900: hsl(@powder-h, 75%, 8%);
746
754
 
747
755
  // Green
748
- --green-025: hsl(@green-h, 100%, 97%);
749
- --green-050: hsl(@green-h, 100%, 96%);
750
- --green-100: hsl(@green-h, 100%, 94%);
751
- --green-200: hsl(@green-h, 100%, 88%);
752
- --green-300: hsl(@green-h, 100%, 82%);
756
+ --green-025: hsl(@green-h, 100%, 94%);
757
+ --green-050: hsl(@green-h, 100%, 91%);
758
+ --green-100: hsl(@green-h, 100%, 87%);
759
+ --green-200: hsl(@green-h, 100%, 83%);
760
+ --green-300: hsl(@green-h, 100%, 78%);
753
761
  --green-400: hsl(@green-h, 100%, 18%);
754
762
  --green-500: hsl(@green-h, 100%, 15%);
755
763
  --green-600: hsl(@green-h, 100%, 12%);
@@ -758,10 +766,10 @@
758
766
  --green-900: hsl(@green-h, 100%, 3%);
759
767
 
760
768
  // Yellow
761
- --yellow-050: hsl(@yellow-h, 100%, 95%);
762
- --yellow-100: hsl(@yellow-h, 100%, 92%);
763
- --yellow-200: hsl(@yellow-h, 95%, 85%);
764
- --yellow-300: hsl(@yellow-h, 95%, 78%);
769
+ --yellow-050: hsl(@yellow-h, 100%, 92%);
770
+ --yellow-100: hsl(@yellow-h, 100%, 88%);
771
+ --yellow-200: hsl(@yellow-h, 95%, 83%);
772
+ --yellow-300: hsl(@yellow-h, 95%, 75%);
765
773
  --yellow-400: hsl(@yellow-h, 100%, 28%);
766
774
  --yellow-500: hsl(@yellow-h, 100%, 26%);
767
775
  --yellow-600: hsl(@yellow-h, 100%, 23%);
@@ -770,10 +778,10 @@
770
778
  --yellow-900: hsl(@yellow-h, 100%, 7%);
771
779
 
772
780
  // Red
773
- --red-050: hsl(@red-h, 100%, 97.5%);
774
- --red-100: hsl(@red-h, 100%, 95%);
775
- --red-200: hsl(@red-h, 100%, 90%);
776
- --red-300: hsl(@red-h, 100%, 85%);
781
+ --red-050: hsl(@red-h, 100%, 94%);
782
+ --red-100: hsl(@red-h, 100%, 92%);
783
+ --red-200: hsl(@red-h, 100%, 89%);
784
+ --red-300: hsl(@red-h, 100%, 86%);
777
785
  --red-400: hsl(@red-h, 100%, 35%);
778
786
  --red-500: hsl(@red-h, 100%, 29%);
779
787
  --red-600: hsl(@red-h, 100%, 23%);
@@ -816,6 +824,9 @@
816
824
  --focus-ring-error: fade(@red-600, 90%);
817
825
  --focus-ring-muted: fade(@black-400, 95%);
818
826
 
827
+ // Opacity
828
+ --_o-disabled: 0.8;
829
+
819
830
  // Shadows
820
831
  --bs-sm: none;
821
832
  --bs-md: none;
@@ -865,10 +876,10 @@
865
876
  --black-900: hsl(@black-h, @black-s, 100%);
866
877
 
867
878
  // Orange
868
- --orange-050: hsl(@orange-h, 100%, 4%);
869
- --orange-100: hsl(@orange-h, 100%, 8%);
870
- --orange-200: hsl(@orange-h, 100%, 16%);
871
- --orange-300: hsl(@orange-h, 100%, 24%);
879
+ --orange-050: hsl(@orange-h, 100%, 7%);
880
+ --orange-100: hsl(@orange-h, 100%, 9%);
881
+ --orange-200: hsl(@orange-h, 100%, 15%);
882
+ --orange-300: hsl(@orange-h, 100%, 22%);
872
883
  --orange-400: hsl(@orange-h, 100%, 64%);
873
884
  --orange-500: hsl(@orange-h, 100%, 71%);
874
885
  --orange-600: hsl(@orange-h, 100%, 77%);
@@ -877,10 +888,10 @@
877
888
  --orange-900: hsl(@orange-h, 100%, 91%);
878
889
 
879
890
  // Blue
880
- --blue-050: hsl(@blue-h, 100%, 4%);
891
+ --blue-050: hsl(@blue-h, 100%, 7%);
881
892
  --blue-100: hsl(@blue-h, 100%, 11%);
882
- --blue-200: hsl(@blue-h, 100%, 18%);
883
- --blue-300: hsl(@blue-h, 100%, 25%);
893
+ --blue-200: hsl(@blue-h, 100%, 17%);
894
+ --blue-300: hsl(@blue-h, 100%, 24%);
884
895
  --blue-400: hsl(@blue-h, 100%, 75%);
885
896
  --blue-500: hsl(@blue-h, 100%, 80%);
886
897
  --blue-600: hsl(@blue-h, 100%, 85%);
@@ -889,10 +900,10 @@
889
900
  --blue-900: hsl(@blue-h, 100%, 95%);
890
901
 
891
902
  // Powder
892
- --powder-050: hsl(@powder-h, 100%, 6%);
893
- --powder-100: hsl(@powder-h, 90%, 8%);
903
+ --powder-050: hsl(@powder-h, 100%, 7%);
904
+ --powder-100: hsl(@powder-h, 95%, 10%);
894
905
  --powder-200: hsl(@powder-h, 80%, 14%);
895
- --powder-300: hsl(@powder-h, 60%, 20%);
906
+ --powder-300: hsl(@powder-h, 76%, 19%);
896
907
  --powder-400: hsl(@powder-h, 55%, 72%);
897
908
  --powder-500: hsl(@powder-h, 60%, 76%);
898
909
  --powder-600: hsl(@powder-h, 70%, 80%);
@@ -901,11 +912,11 @@
901
912
  --powder-900: hsl(@powder-h, 75%, 92%);
902
913
 
903
914
  // Green
904
- --green-025: hsl(@green-h, 100%, 3%);
905
- --green-050: hsl(@green-h, 100%, 4%);
906
- --green-100: hsl(@green-h, 100%, 6%);
907
- --green-200: hsl(@green-h, 100%, 12%);
908
- --green-300: hsl(@green-h, 100%, 18%);
915
+ --green-025: hsl(@green-h, 100%, 5%);
916
+ --green-050: hsl(@green-h, 100%, 6%);
917
+ --green-100: hsl(@green-h, 100%, 7%);
918
+ --green-200: hsl(@green-h, 100%, 11%);
919
+ --green-300: hsl(@green-h, 100%, 15%);
909
920
  --green-400: hsl(@green-h, 100%, 65%);
910
921
  --green-500: hsl(@green-h, 100%, 71%);
911
922
  --green-600: hsl(@green-h, 100%, 77%);
@@ -914,10 +925,10 @@
914
925
  --green-900: hsl(@green-h, 100%, 94%);
915
926
 
916
927
  // Yellow
917
- --yellow-050: hsl(@yellow-h, 100%, 4%);
918
- --yellow-100: hsl(@yellow-h, 100%, 8%);
919
- --yellow-200: hsl(@yellow-h, 95%, 16%);
920
- --yellow-300: hsl(@yellow-h, 95%, 24%);
928
+ --yellow-050: hsl(@yellow-h, 100%, 6%);
929
+ --yellow-100: hsl(@yellow-h, 100%, 9%);
930
+ --yellow-200: hsl(@yellow-h, 100%, 14%);
931
+ --yellow-300: hsl(@yellow-h, 100%, 20%);
921
932
  --yellow-400: hsl(@yellow-h, 100%, 55%);
922
933
  --yellow-500: hsl(@yellow-h, 100%, 63%);
923
934
  --yellow-600: hsl(@yellow-h, 100%, 71%);
@@ -926,10 +937,10 @@
926
937
  --yellow-900: hsl(@yellow-h, 100%, 95%);
927
938
 
928
939
  // Red
929
- --red-050: hsl(@red-h, 100%, 4%);
930
- --red-100: hsl(@red-h, 100%, 8%);
931
- --red-200: hsl(@red-h, 100%, 16%);
932
- --red-300: hsl(@red-h, 100%, 24%);
940
+ --red-050: hsl(@red-h, 100%, 9%);
941
+ --red-100: hsl(@red-h, 100%, 12%);
942
+ --red-200: hsl(@red-h, 100%, 17%);
943
+ --red-300: hsl(@red-h, 100%, 22%);
933
944
  --red-400: hsl(@red-h, 100%, 70%);
934
945
  --red-500: hsl(@red-h, 100%, 75%);
935
946
  --red-600: hsl(@red-h, 100%, 80%);
@@ -972,6 +983,9 @@
972
983
  --fc-medium: var(--black-700);
973
984
  --fc-light: var(--black-500);
974
985
 
986
+ // Opacity
987
+ --_o-disabled: 0.8;
988
+
975
989
  // Shadows
976
990
  --bs-sm: none;
977
991
  --bs-md: none;
@@ -18,7 +18,6 @@
18
18
  @import "base/body.less";
19
19
 
20
20
  // -- COMPONENTS
21
- @import "components/banners.less";
22
21
  @import "components/buttons.less";
23
22
  @import "components/links.less";
24
23
  @import "components/link-previews.less";
@@ -20,13 +20,14 @@
20
20
  @import "components/activity-indicator.less";
21
21
  @import "components/avatars.less";
22
22
  @import "components/badges.less";
23
- @import "components/blank-states.less";
23
+ @import "components/empty-states.less";
24
24
  @import "components/breadcrumbs.less";
25
25
  @import "components/button-groups.less";
26
26
  @import "components/cards.less";
27
27
  @import "components/code-blocks.less";
28
- @import "components/collapsible.less";
28
+ @import "components/expandable.less";
29
29
  @import "components/inputs.less";
30
+ @import "components/labels.less";
30
31
  @import "components/menu.less";
31
32
  @import "components/modals.less";
32
33
  @import "components/navigation.less";
@@ -1,7 +1,7 @@
1
1
  import * as Stacks from '../stacks';
2
2
 
3
3
  // Radio buttons only trigger a change event when they're *checked*, but not when
4
- // they're *unchecked*. Therefore, if we have an active `s-collapsible-control` in
4
+ // they're *unchecked*. Therefore, if we have an active `s-expandable-control` in
5
5
  // the document, we listen for change events on *all* radio buttons and find any
6
6
  // other radio buttons in the same `name` group, triggering a custom event on all
7
7
  // of them so the controller can re-evaluate.
@@ -20,7 +20,7 @@ function globalChangeListener(e: UIEvent) {
20
20
  if (other === e.target) {
21
21
  return;
22
22
  }
23
- var customEvent;
23
+ let customEvent;
24
24
  try {
25
25
  customEvent = new Event(RADIO_OFF_EVENT);
26
26
  } catch (ex) {
@@ -32,7 +32,7 @@ function globalChangeListener(e: UIEvent) {
32
32
  });
33
33
  }
34
34
 
35
- var refCount = 0;
35
+ let refCount = 0;
36
36
  function globalChangeListenerRequired(required: boolean) {
37
37
  if (required) {
38
38
  refCount++;
@@ -52,16 +52,16 @@ export class ExpandableController extends Stacks.StacksController {
52
52
  private events!: string[];
53
53
  private isCheckable!: boolean;
54
54
  private isRadio!: boolean;
55
- private lastKeydownClickTimestamp: number = 0;
55
+ private lastKeydownClickTimestamp = 0;
56
56
 
57
57
  initialize() {
58
58
  if (this.element.nodeName === "INPUT" && ["radio", "checkbox"].indexOf((<HTMLInputElement>this.element).type) >= 0) {
59
- this.isCollapsed = this._isCollapsedForCheckable;
59
+ this.isCollapsed = this._isCollapsedForCheckable.bind(this);
60
60
  this.events = ["change", RADIO_OFF_EVENT];
61
61
  this.isCheckable = true;
62
62
  this.isRadio = (<HTMLInputElement>this.element).type === "radio";
63
63
  } else {
64
- this.isCollapsed = this._isCollapsedForClickable;
64
+ this.isCollapsed = this._isCollapsedForClickable.bind(this);
65
65
  this.events = ["click", "keydown"];
66
66
  }
67
67
  this.listener = this.listener.bind(this);
@@ -71,7 +71,7 @@ export class ExpandableController extends Stacks.StacksController {
71
71
  // for non-checkable elements, the initial source of truth is the collapsed/expanded
72
72
  // state of the controlled element (unless the element doesn't exist)
73
73
  _isCollapsedForClickable() {
74
- var cc = this.controlledCollapsibles;
74
+ const cc = this.controlledExpandables;
75
75
  // the element is considered collapsed if *any* target element is collapsed
76
76
  return cc.length > 0 ? !cc.every(element => element.classList.contains("is-expanded")) : this.element.getAttribute("aria-expanded") === "false";
77
77
  };
@@ -82,7 +82,7 @@ export class ExpandableController extends Stacks.StacksController {
82
82
  };
83
83
 
84
84
 
85
- get controlledCollapsibles() {
85
+ get controlledExpandables() {
86
86
  const attr = this.element.getAttribute("aria-controls");
87
87
  if (!attr) {
88
88
  throw `[aria-controls="targetId1 ... targetIdN"] attribute required`;
@@ -104,8 +104,8 @@ export class ExpandableController extends Stacks.StacksController {
104
104
  if (!this.data.has("toggle-class")) {
105
105
  return;
106
106
  }
107
- var cl = this.element.classList;
108
- var toggleClass = this.data.get("toggle-class");
107
+ const cl = this.element.classList;
108
+ const toggleClass = this.data.get("toggle-class");
109
109
  if (!toggleClass) {
110
110
  throw "couldn't find toggle class"
111
111
  }
@@ -115,7 +115,7 @@ export class ExpandableController extends Stacks.StacksController {
115
115
  };
116
116
 
117
117
  listener(e: Event) {
118
- var newCollapsed;
118
+ let newCollapsed;
119
119
  if (this.isCheckable) {
120
120
  newCollapsed = !(<HTMLInputElement>this.element).checked;
121
121
  } else {
@@ -142,7 +142,7 @@ export class ExpandableController extends Stacks.StacksController {
142
142
  }
143
143
  }
144
144
  this.element.setAttribute("aria-expanded", newCollapsed ? "false" : "true");
145
- for (let controlledElement of this.controlledCollapsibles) {
145
+ for (const controlledElement of this.controlledExpandables) {
146
146
  controlledElement.classList.toggle("is-expanded", !newCollapsed);
147
147
  }
148
148
  this._dispatchShowHideEvent(!newCollapsed);
@@ -151,7 +151,7 @@ export class ExpandableController extends Stacks.StacksController {
151
151
 
152
152
  connect() {
153
153
  this.events.forEach(e => {
154
- this.element.addEventListener(e, this.listener);
154
+ this.element.addEventListener(e, this.listener.bind(this));
155
155
  }, this);
156
156
 
157
157
  if (this.isRadio) {
@@ -159,15 +159,19 @@ export class ExpandableController extends Stacks.StacksController {
159
159
  }
160
160
 
161
161
  // synchronize state -- in all cases, this means setting the correct `aria-expanded`
162
- // attribute; for checkable controls this also means setting the `is-collapsed` class
163
- this.element.setAttribute("aria-expanded", this.isCollapsed() ? "false" : "true");
162
+ // attribute; for checkable controls this also means setting the `is-collapsed` class.
163
+ // Note: aria-expanded is currently an invalid attribute on radio elements
164
+ // Support for aria-expanded is being debated by the W3C https://github.com/w3c/aria/issues/1404 as recently as June 2022
165
+ if (!this.isRadio) {
166
+ this.element.setAttribute("aria-expanded", this.isCollapsed() ? "false" : "true");
167
+ }
164
168
  if (this.isCheckable) {
165
- var cc = this.controlledCollapsibles;
169
+ const cc = this.controlledExpandables;
166
170
  if (cc.length) {
167
- var expected = !this.isCollapsed();
171
+ const expected = !this.isCollapsed();
168
172
  // if any element does not match the expected state, set them all to the expected state
169
173
  if (cc.some(element => element.classList.contains("is-expanded") !== expected)) {
170
- for (let controlledElement of this.controlledCollapsibles) {
174
+ for (const controlledElement of this.controlledExpandables) {
171
175
  controlledElement.classList.toggle("is-expanded", expected);
172
176
  }
173
177
  this._dispatchShowHideEvent(expected);
@@ -179,7 +183,7 @@ export class ExpandableController extends Stacks.StacksController {
179
183
 
180
184
  disconnect() {
181
185
  this.events.forEach(e => {
182
- this.element.removeEventListener(e, this.listener);
186
+ this.element.removeEventListener(e, this.listener.bind(this));
183
187
  }, this);
184
188
 
185
189
  if (this.isRadio) {