@stackoverflow/stacks 3.0.0-beta.2 → 3.0.0-beta.21

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 (60) hide show
  1. package/dist/css/stacks.css +6068 -5972
  2. package/dist/css/stacks.min.css +1 -1
  3. package/lib/atomic/flex.less +6 -34
  4. package/lib/atomic/height.less +22 -0
  5. package/lib/atomic/misc.less +21 -15
  6. package/lib/atomic/positioning.less +34 -0
  7. package/lib/atomic/sizing.less +76 -0
  8. package/lib/atomic/spacing.less +35 -75
  9. package/lib/atomic/typography.less +52 -13
  10. package/lib/atomic/width.less +27 -0
  11. package/lib/base/body.less +2 -4
  12. package/lib/base/configuration-static.less +3 -3
  13. package/lib/base/internal.less +3 -5
  14. package/lib/base/reset-normalize.less +1 -1
  15. package/lib/components/activity-indicator/activity-indicator.less +1 -17
  16. package/lib/components/avatar/avatar.less +2 -2
  17. package/lib/components/badge/badge.less +179 -173
  18. package/lib/components/banner/banner.less +11 -10
  19. package/lib/components/bling/bling.less +130 -0
  20. package/lib/components/button/button.less +233 -376
  21. package/lib/components/button-group/button-group.less +2 -1
  22. package/lib/components/checkbox_radio/checkbox_radio.less +195 -113
  23. package/lib/components/code-block/code-block.fixtures.ts +2 -2
  24. package/lib/components/code-block/code-block.less +1 -1
  25. package/lib/components/description/description.less +15 -1
  26. package/lib/components/empty-state/empty-state.less +17 -6
  27. package/lib/components/form-group/form-group.less +25 -0
  28. package/lib/components/input-icon/input-icon.less +3 -3
  29. package/lib/components/input_textarea/input_textarea.less +71 -58
  30. package/lib/components/menu/menu.less +83 -21
  31. package/lib/components/modal/modal.less +10 -10
  32. package/lib/components/navigation/navigation.less +62 -37
  33. package/lib/components/notice/notice.less +89 -74
  34. package/lib/components/pagination/pagination.less +44 -43
  35. package/lib/components/popover/popover.less +8 -10
  36. package/lib/components/post-summary/post-summary.less +6 -5
  37. package/lib/components/progress-bar/progress-bar.less +1 -1
  38. package/lib/components/prose/prose.less +5 -5
  39. package/lib/components/select/select.less +26 -37
  40. package/lib/components/sidebar-widget/sidebar-widget.less +2 -2
  41. package/lib/components/table/table.less +0 -8
  42. package/lib/components/tag/tag.less +69 -71
  43. package/lib/components/toast/toast.less +4 -2
  44. package/lib/components/topbar/topbar.less +4 -4
  45. package/lib/components/user-card/user-card.less +118 -92
  46. package/lib/components/vote/vote.less +134 -0
  47. package/lib/exports/color-sets.less +78 -77
  48. package/lib/exports/constants-helpers.less +4 -8
  49. package/lib/exports/constants-type.less +18 -36
  50. package/lib/exports/mixins.less +71 -0
  51. package/lib/stacks-static.less +7 -5
  52. package/lib/test/visual-test-utils.ts +42 -10
  53. package/lib/tsconfig.json +1 -1
  54. package/package.json +1 -1
  55. package/lib/atomic/width-height.less +0 -194
  56. package/lib/components/award-bling/award-bling.less +0 -32
  57. package/lib/components/check-control/check-control.less +0 -17
  58. package/lib/components/check-group/check-group.less +0 -19
  59. package/lib/components/skeleton/skeleton.less +0 -73
  60. package/lib/exports/spacing-mixins.less +0 -67
@@ -17,7 +17,7 @@
17
17
  // CHILD ELEMENTS
18
18
  form {
19
19
  display: flex;
20
- margin-right: calc(var(--su-static1) * -1); // -1px
20
+ margin-right: var(--sun1);
21
21
  }
22
22
 
23
23
  // --_bu-py values set below to ensure btn-group height matches same-sized button height
@@ -69,6 +69,7 @@
69
69
  flex-direction: column;
70
70
  }
71
71
 
72
+ font-weight: 400;
72
73
  white-space: nowrap; // When the buttons wrap, they get super tall and mess up the whole layout
73
74
  }
74
75
 
@@ -1,161 +1,243 @@
1
1
  .s-checkbox,
2
2
  .s-radio {
3
- --_ch-baw: var(--su-static1);
4
- --_ch-bc: var(--bc-dark);
5
- --_ch-bg: var(--white);
6
- --_ch-bg-image: unset;
3
+ --_ch-ai: center;
4
+ --_ch-fc: unset;
5
+ --_ch-after-fc: unset;
6
+ --_ch-cursor: pointer;
7
+ --_ch-label-fc: var(--_ch-fc);
8
+ --_ch-gap: var(--su8);
9
+
10
+ // inputs
11
+ --_ch-input-baw: var(--su-static1);
12
+ --_ch-input-bc: var(--black-350);
13
+ --_ch-input-bg: var(--white);
14
+ --_ch-input-bg-image: unset;
15
+ --_ch-input-br: var(--su-static1);
16
+ --_ch-input-cursor: pointer;
17
+ --_ch-input-h: calc(var(--su-static12) + var(--su-static2)); // 14px
18
+
19
+ @media (forced-colors: active) { // This is for Windows High Contrast Mode
20
+ &:checked,
21
+ &:indeterminate {
22
+ --_ch-input-bg: ButtonText !important;
23
+ }
24
+ }
7
25
 
8
- // CONTEXTUAL STYLES
9
26
  fieldset[disabled] &,
10
- &[disabled] {
11
- cursor: not-allowed;
12
- opacity: var(--_o-disabled-static);
27
+ &:has(> input[disabled]) {
28
+ --_ch-fc: var(--black-300);
29
+ --_ch-input-bc: var(--black-300);
30
+ --_ch-input-cursor: not-allowed;
13
31
  }
14
32
 
15
- .s-check-control & {
16
- &[disabled] + .s-label {
17
- &:extend(.is-disabled .s-label);
18
- }
33
+ &:has(> input[disabled]:checked) {
34
+ --_ch-input-bc: var(--theme-secondary-300);
19
35
  }
20
36
 
21
- .s-check-group & {
22
- margin-top: calc(var(--su2) + var(--su1)); // 3px
23
- }
37
+ // MODIFIERS
38
+ &&__checkmark {
39
+ --_ch-fc: var(--theme-secondary);
40
+ --_ch-gap: var(--su4);
41
+ --_ch-after-fc: unset;
42
+
43
+ // CONTEXTUAL STYLES
44
+ &:has(> input:focus-visible) {
45
+ .focus-styles(true, false);
46
+ }
24
47
 
25
- input& {
26
- flex-shrink: 0;
27
- }
48
+ &:has(> input:checked) {
49
+ --_ch-after-fc: var(--_ch-fc);
50
+ }
28
51
 
29
- // INTERACTION
30
- &:focus {
31
- .focus-styles();
32
- }
52
+ &:has(> input[disabled]) {
53
+ --_ch-cursor: not-allowed;
54
+ }
33
55
 
34
- background-color: var(--_ch-bg);
35
- border: var(--_ch-baw) solid var(--_ch-bc);
36
-
37
- appearance: none;
38
- cursor: pointer;
39
- font-size: inherit;
40
- height: 1em;
41
- margin: 0; // A guard against Core's default margins
42
- outline: 0;
43
- vertical-align: middle;
44
- width: 1em;
45
- }
56
+ &:has(> input:checked[disabled]) {
57
+ --_ch-fc: var(--theme-secondary-300);
58
+ }
46
59
 
47
- .s-checkbox {
48
- // Less variables for check svg fill color
49
- @ch-bg-image-fill: .set-white()[default];
50
- @ch-bg-image-fill-dark: .set-white-dark()[default];
51
- @ch-bg-image-fill-esc: escape("@{ch-bg-image-fill}"); // color escaped for URL usage
52
- @ch-bg-image-fill-dark-esc: escape("@{ch-bg-image-fill-dark}"); // color escaped for URL usage
60
+ // CHILD ELEMENTS
61
+ input,
62
+ &:after {
63
+ height: var(--su16);
64
+ margin-left: 100%;
65
+ width: var(--su16);
66
+ }
53
67
 
54
- // CONTEXTUAL STYLES
55
- .dark-mode({
56
- &:checked {
57
- --_ch-bg-image: url("data:image/svg+xml;,%3Csvg width='11' height='11' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 3.41L8.59 2 4 6.59 2.41 5 1 6.41l3 3z' fill='@{ch-bg-image-fill-dark-esc}'/%3E%3C/svg%3E");
68
+ &:after {
69
+ background-color: var(--_ch-after-fc);
70
+ content: "";
71
+ flex-shrink: 0;
72
+ margin-left: auto;
73
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='m14 3.88-.44.44-7.34 7.35-.44.44-.44-.44-2.9-2.9L2 8.34l.89-.88.44.44 2.45 2.45 6.9-6.9.44-.44z'/%3E%3C/svg%3E"); // check16
74
+ mask-size: contain;
75
+ mask-repeat: no-repeat;
58
76
  }
59
- &:indeterminate {
60
- --_ch-bg-image: url("data:image/svg+xml;,%3Csvg width='11' height='11' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2 4.5 h7 v2 h-7 z' fill='@{ch-bg-image-fill-dark-esc}'/%3E%3C/svg%3E");
77
+
78
+ input {
79
+ appearance: none;
80
+ opacity: 0;
81
+ position: absolute;
82
+ right: 0;
83
+ z-index: -1;
61
84
  }
62
- });
63
85
 
64
- .highcontrast-dark-mode({
65
- &:checked, &:indeterminate {
66
- --_ch-bc: var(--black) !important;
67
- --_ch-bg: var(--black);
86
+ .s-description {
87
+ margin: var(--su2) 0 0;
88
+ padding: 0;
68
89
  }
69
- });
70
90
 
71
- @media (forced-colors: active) {
72
- &:checked,
73
- &:indeterminate {
74
- --_ch-bg: ButtonText !important;
91
+ .s-label {
92
+ flex-grow: 1;
75
93
  }
76
- }
77
94
 
78
- // STATES
79
- &:checked, &:indeterminate {
80
- --_ch-bc: var(--theme-secondary) !important;
81
- --_ch-bg: var(--theme-secondary);
82
- }
95
+ border-radius: var(--br-md);
96
+ color: var(--_ch-fc);
97
+ cursor: var(--_ch-cursor);
83
98
 
84
- &:checked {
85
- --_ch-bg-image: url("data:image/svg+xml;,%3Csvg width='11' height='11' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 3.41L8.59 2 4 6.59 2.41 5 1 6.41l3 3z' fill='@{ch-bg-image-fill-esc}'/%3E%3C/svg%3E");
99
+ width: 100%;
86
100
  }
87
101
 
88
- &:indeterminate {
89
- --_ch-bg-image: url("data:image/svg+xml;,%3Csvg width='11' height='11' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2 4.5 h7 v2 h-7 z' fill='@{ch-bg-image-fill-esc}'/%3E%3C/svg%3E");
102
+ // CHILD ELEMENTS
103
+ &:has(> input[disabled]) {
104
+ .s-label {
105
+ --_ch-label-fc: var(--black-300);
106
+ }
90
107
  }
91
108
 
92
- background-image: var(--_ch-bg-image);
109
+ &:not(> input[disabled]) {
110
+ &:has(input:checked),
111
+ &:has(input:indeterminate) {
112
+ .s-label {
113
+ --_ch-label-fc: var(--black-600);
114
+ }
115
+ }
116
+ }
93
117
 
94
- background-position: center center;
95
- background-repeat: no-repeat;
96
- background-size: contain;
97
- border-radius: var(--br-md);
98
- }
118
+ &:has(> .s-label .s-description),
119
+ &:has(> .s-label .s-input-message) {
120
+ --_ch-ai: flex-start;
99
121
 
100
- .s-radio {
101
-
102
- // CONTEXTUAL STYLES
103
- .highcontrast-dark-mode({
104
- &:checked {
105
- --_ch-bc: var(--black);
106
- outline: var(--su-static1) solid var(--black);
122
+ input {
123
+ margin-top: var(--su2);
107
124
  }
108
- });
109
-
110
- // STATES
111
- &:checked {
112
- --_ch-baw: 0.30769231em;
113
- --_ch-bc: var(--theme-secondary);
114
- --_ch-bg: var(--white);
115
125
  }
116
126
 
117
- border-radius: var(--br-circle);
118
- }
127
+ // Validation states
128
+ &:has(> input:not(:checked)) {
129
+ &.has-error {
130
+ --_ch-input-bc: var(--red-400);
131
+ }
132
+ &.has-success {
133
+ --_ch-input-bc: var(--green-400);
134
+ }
135
+ &.has-warning {
136
+ --_ch-input-bc: var(--yellow-400);
137
+ }
138
+ }
119
139
 
120
- .s-checkbox,
121
- .s-radio:not(:checked) {
122
- .validation-states(ch);
123
- }
140
+ input {
141
+ &:focus-visible {
142
+ .focus-styles();
143
+ }
124
144
 
125
- .s-check-control { // TODO would _love_ to use .s-check instead, with no class on the input itself
126
- --_cc-ai: center;
145
+ &:not(:checked) {
146
+ .validation-states(ch-input);
147
+ }
127
148
 
128
- // CONTEXTUAL STYLES
129
- .s-check-group & {
130
- --_cc-ai: flex-start; // manually align the checkboxes and radios to the top of the group
149
+ appearance: none;
150
+ background-color: var(--_ch-input-bg);
151
+ border: var(--_ch-input-baw) solid var(--_ch-input-bc);
152
+ border-radius: var(--_ch-input-br);
153
+ cursor: var(--_ch-input-cursor);
154
+ height: var(--_ch-input-h);
155
+
156
+ aspect-ratio: 1 / 1;
157
+ flex-shrink: 0;
158
+ font-size: inherit;
159
+ margin: 0; // A guard against Core's default margins
160
+ outline: 0;
161
+ vertical-align: middle;
131
162
  }
132
163
 
133
- // CHILD ELEMENTS
164
+
134
165
  .s-label {
166
+ color: var(--_ch-label-fc);
167
+ font-size: var(--fs-body1);
135
168
  font-weight: normal;
136
169
  }
137
170
 
138
- align-items: var(--_cc-ai);
171
+ align-items: var(--_ch-ai);
139
172
  display: flex;
140
- gap: var(--su8);
173
+ gap: var(--_ch-gap);
141
174
  }
142
175
 
143
- .s-check-group {
144
- --_cg-fd: column;
176
+ .s-checkbox{
177
+ // Less variables for check svg fill color
178
+ @ch-bg-image-fill: .set-white()[default];
179
+ @ch-bg-image-fill-dark: .set-white-dark()[default];
180
+ @ch-bg-image-fill-esc: escape("@{ch-bg-image-fill}"); // color escaped for URL usage
181
+ @ch-bg-image-fill-dark-esc: escape("@{ch-bg-image-fill-dark}"); // color escaped for URL usage
145
182
 
146
- // MODIFIERS
147
- &&__horizontal {
148
- --_cg-fd: row;
149
- }
183
+ // CONTEXTUAL STYLES
184
+ .dark-mode({
185
+ input {
186
+ &:checked {
187
+ --_ch-input-bg-image: url("data:image/svg+xml;,%3Csvg width='11' height='11' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 3.41L8.59 2 4 6.59 2.41 5 1 6.41l3 3z' fill='@{ch-bg-image-fill-dark-esc}'/%3E%3C/svg%3E");
188
+ }
189
+ &:indeterminate {
190
+ --_ch-input-bg-image: url("data:image/svg+xml;,%3Csvg width='11' height='11' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2 4.5 h7 v2 h-7 z' fill='@{ch-bg-image-fill-dark-esc}'/%3E%3C/svg%3E");
191
+ }
192
+ }
193
+ });
150
194
 
151
195
  // CHILD ELEMENTS
152
- // TODO HACK? <legend> isn't respecting gap...
153
- legend.s-label {
154
- margin-bottom: var(--su8);
155
- }
196
+ input {
197
+ fieldset[disabled] &,
198
+ &[disabled] {
199
+ &:checked, &:indeterminate {
200
+ --_ch-input-bc: var(--theme-secondary-300);
201
+ --_ch-input-bg: var(--theme-secondary-300);
202
+ }
203
+ }
156
204
 
157
- flex-direction: var(--_cg-fd);
205
+ &:checked, &:indeterminate {
206
+ --_ch-input-bc: var(--theme-secondary);
207
+ --_ch-input-bg: var(--theme-secondary);
208
+ }
158
209
 
159
- display: flex;
160
- gap: var(--su8);
210
+ &:checked {
211
+ --_ch-input-bg-image: url("data:image/svg+xml;,%3Csvg width='11' height='11' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 3.41L8.59 2 4 6.59 2.41 5 1 6.41l3 3z' fill='@{ch-bg-image-fill-esc}'/%3E%3C/svg%3E");
212
+ }
213
+
214
+ &:indeterminate {
215
+ --_ch-input-bg-image: url("data:image/svg+xml;,%3Csvg width='11' height='11' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2 4.5 h7 v2 h-7 z' fill='@{ch-bg-image-fill-esc}'/%3E%3C/svg%3E");
216
+ }
217
+
218
+ background-image: var(--_ch-input-bg-image);
219
+
220
+ background-position: center center;
221
+ background-repeat: no-repeat;
222
+ background-size: contain;
223
+ }
161
224
  }
225
+
226
+ .s-radio {
227
+ input {
228
+ fieldset[disabled] &,
229
+ &[disabled] {
230
+ &:checked {
231
+ --_ch-input-bc: var(--theme-secondary-300);
232
+ }
233
+ }
234
+
235
+ --_ch-input-br: var(--br-circle);
236
+
237
+ &:checked {
238
+ --_ch-input-baw: calc(var(--_ch-input-h) / 3); // 14px / 3 = ~4.67px
239
+ --_ch-input-bc: var(--theme-secondary);
240
+ --_ch-input-bg: var(--white);
241
+ }
242
+ }
243
+ }
@@ -55,9 +55,9 @@ const cssFixture = `
55
55
 
56
56
  const htmlFixture = `
57
57
  <form class="d-flex gy4 fd-column">
58
- <label class="flex--item s-label" for="question-title">Question title</label>
58
+ <label class="s-label" for="question-title">Question title</label>
59
59
  <div class="d-flex ps-relative">
60
- <input class="flex--item s-input" type="text" id="question-title" placeholder="e.g. Why doesn’t Stack Overflow use a custom web font?"/>
60
+ <input class="s-input" type="text" id="question-title" placeholder="e.g. Why doesn’t Stack Overflow use a custom web font?"/>
61
61
  </div>
62
62
  </form>
63
63
  `;
@@ -95,7 +95,7 @@
95
95
  border-width: 0 var(--su-static1) 0 0;
96
96
  color: var(--black-350);
97
97
  float: left;
98
- margin: calc(var(--su12) * -1);
98
+ margin: var(--sun12);
99
99
  margin-right: var(--su12);
100
100
  padding: var(--su12);
101
101
  padding-right: var(--su6);
@@ -1,9 +1,23 @@
1
1
  .s-description {
2
+ --_de-fc: var(--black-400);
3
+
2
4
  .is-disabled & {
3
5
  opacity: var(--_o-disabled-static);
4
6
  }
5
7
 
6
- color: var(--fc-light);
8
+ .has-error & {
9
+ --_de-fc: var(--red-400);
10
+ }
11
+
12
+ .has-success & {
13
+ --_de-fc: var(--green-400);
14
+ }
15
+
16
+ .has-warning & {
17
+ --_de-fc: var(--yellow-400);
18
+ }
19
+
20
+ color: var(--_de-fc);
7
21
  font-size: var(--fs-caption);
8
22
  padding: 0 var(--su2); // Helps the label visually line up with inputs
9
23
  }
@@ -1,15 +1,26 @@
1
1
  .s-empty-state {
2
2
  // CHILD ELEMENTS
3
3
  p {
4
- strong {
5
- color: var(--fc-dark);
6
- }
4
+ color: var(--black-400);
5
+ margin-bottom: var(--su32);
6
+ }
7
+
8
+ &--title {
9
+ font-weight: 600;
10
+ font-size: var(--fs-body3);
11
+ margin-bottom: var(--su8);
12
+ color: var(--black-400);
13
+ }
14
+
15
+ .svg-spot {
16
+ margin-bottom: var(--su24);
17
+ }
7
18
 
8
- font-size: var(--fs-body1);
9
- margin-bottom: var(--su12);
19
+ // Remove margin from <p> if there are no buttons
20
+ &:last-child {
21
+ margin-bottom: 0;
10
22
  }
11
23
 
12
- color: var(--fc-light);
13
24
  text-align: center;
14
25
  margin-left: auto;
15
26
  margin-right: auto;
@@ -0,0 +1,25 @@
1
+ .s-form-group {
2
+ --_fg-fd: column;
3
+ --_fg-gap: var(--su8);
4
+
5
+ // CONTEXTUAL STYLES
6
+ &.s-menu {
7
+ --_fg-gap: 0;
8
+ }
9
+
10
+ // MODIFIERS
11
+ &&__horizontal {
12
+ --_fg-fd: row;
13
+ }
14
+
15
+ // CHILD ELEMENTS
16
+ legend.s-label {
17
+ margin-bottom: var(--su8);
18
+ }
19
+
20
+
21
+ flex-direction: var(--_fg-fd);
22
+
23
+ display: flex;
24
+ gap: var(--_fg-gap);
25
+ }
@@ -1,6 +1,6 @@
1
1
  .s-input-icon {
2
2
  --_ii-fc: unset;
3
- --_ii-r: 0.7em;
3
+ --_ii-r: calc((var(--su-static8) + var(--su-static2))); // 10px
4
4
 
5
5
  // MODIFIERS
6
6
  .has-error & {
@@ -32,13 +32,13 @@
32
32
  --_ii-r: auto;
33
33
 
34
34
  color: var(--black-400);
35
- left: 0.7em;
35
+ left: calc((var(--su-static8) + var(--su-static2))); // 10px
36
36
  }
37
37
 
38
38
  color: var(--_ii-fc);
39
39
  right: var(--_ii-r);
40
40
 
41
- margin-top: calc((var(--su-static8) + var(--su-static1)) * -1); // -9px - Half the icon's height at 18px for centering;
41
+ margin-top: calc(var(--sun8) + var(--sun2)); // -10px - Half the icon's height at 20px for centering
42
42
  pointer-events: none;
43
43
  position: absolute;
44
44
  top: 50%;