Skip to content

Commit 6550e15

Browse files
dancormierb-kelly
andauthored
refactor(button): style with pseudo-private custom properties (#1038)
* refactor(button): style with pseudo-private custom properties * refactor(btn): increase badge opacity * refactor(button-group): add radio layout (#1135) * refactor(button-group): remove --container; add radio layout * refactor(button-group): standardize less * Conform btn styles to standard * Prefix component custom properties * Fix --radio selector * match styles to template (mostly) * Ensure unset docs buttons have base class * Move contextual styles within variants * Remove unneeded todo * Improve radio-based button styling * fix muted filled interactive background colors * Match PPCP format * Ensure btn groups have correct hover styling * Adjust button group styling * drop trailing whitespace * Remove commented code #1038 (comment) * Fix radio button group focus * typo fixes * Fix primary variant dark mode interaction bg colors * Compensate for form wrappers within button group buttons Co-authored-by: Ben Kelly <[email protected]>
1 parent cb696ec commit 6550e15

File tree

8 files changed

+511
-662
lines changed

8 files changed

+511
-662
lines changed

docs/_data/button-groups.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
"description": "Base button group style."
77
},
88
{
9-
"class": ".s-btn-group--container",
10-
"applies": "form",
11-
"description": "When required to wrap one of our buttons within a button group in a form, apply this class to the form to maintain the intended visuals."
9+
"class": ".s-btn-group--radio",
10+
"applies": ".s-btn-group",
11+
"description": "Applies styling to button groups built using radio and label elements."
1212
}
1313
]
1414
}

docs/_includes/header.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@
4444

4545
<ol class="s-topbar--content ml0 sm:fl1 overflow-hidden">
4646
<li class="d-none sm:d-inline-flex ml-auto">
47-
<button class="s-topbar--item s-btn__unset c-pointer p8 ml-auto js-search-btn">
47+
<button class="s-topbar--item s-btn s-btn__unset c-pointer p8 ml-auto js-search-btn">
4848
{% icon "Search", "js-search-icon" %}
4949
{% icon "Clear", "d-none js-search-close-icon" %}
5050
</button>
5151
</li>
5252
<li>
53-
<button type="button" class="s-topbar--item s-btn__unset c-pointer stacks-theme-button"
53+
<button type="button" class="s-topbar--item s-btn s-btn__unset c-pointer stacks-theme-button"
5454
aria-controls="theming-popover"
5555
data-controller="s-popover"
5656
data-action="s-popover#toggle"

docs/_includes/layouts/home.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@
154154
{% icon "Search", "s-input-icon s-input-icon__search" %}
155155
</div>
156156

157-
<button class="s-btn__unset c-pointer flex--item stacks-theme-button stacks-theme-button__home"
157+
<button class="s-btn s-btn__unset c-pointer flex--item stacks-theme-button stacks-theme-button__home"
158158
aria-controls="theming-popover"
159159
data-controller="s-popover"
160160
data-action="s-popover#toggle"

docs/product/components/button-groups.html

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,25 +62,25 @@
6262
<div class="s-btn-group">
6363
<button class="s-btn s-btn__muted s-btn__outlined" role="button">Newest</button>
6464
<button class="s-btn s-btn__muted s-btn__outlined" role="button">Frequent</button>
65-
<form class="s-btn-group--container">
65+
<form>
6666
<button class="s-btn s-btn__muted s-btn__outlined is-selected" role="button" aria-current="true">Votes</button>
6767
</form>
6868
<button class="s-btn s-btn__muted s-btn__outlined" role="button">Active</button>
69-
<form class="s-btn-group--container">
69+
<form>
7070
<button class="s-btn s-btn__muted s-btn__outlined" role="button">Unanswered</button>
7171
</form>
7272
</div>
7373
{% endhighlight %}
7474
<div class="stacks-preview--example">
75-
<div class="s-btn-group js-btn-group">
76-
<button class="s-btn s-btn__muted s-btn__outlined js-btn-group-item" role="button">Newest</button>
77-
<button class="s-btn s-btn__muted s-btn__outlined js-btn-group-item" role="button">Frequent</button>
78-
<form class="s-btn-group--container">
79-
<button class="s-btn s-btn__muted s-btn__outlined is-selected js-btn-group-item" role="button" aria-current="true">Votes</button>
75+
<div class="s-btn-group">
76+
<button class="s-btn s-btn__muted s-btn__outlined" role="button">Newest</button>
77+
<button class="s-btn s-btn__muted s-btn__outlined" role="button">Frequent</button>
78+
<form>
79+
<button class="s-btn s-btn__muted s-btn__outlined is-selected" role="button" aria-current="true">Votes</button>
8080
</form>
81-
<button class="s-btn s-btn__muted s-btn__outlined js-btn-group-item" role="button">Active</button>
82-
<form class="s-btn-group--container">
83-
<button class="s-btn s-btn__muted s-btn__outlined js-btn-group-item" role="button">Unanswered</button>
81+
<button class="s-btn s-btn__muted s-btn__outlined" role="button">Active</button>
82+
<form>
83+
<button class="s-btn s-btn__muted s-btn__outlined" role="button">Unanswered</button>
8484
</form>
8585
</div>
8686
</div>
@@ -123,13 +123,13 @@
123123
<span class="s-btn--number">197</span>
124124
</span>
125125
</button>
126-
<button class="s-btn s-btn__muted s-btn__outlined js-btn-group-item" role="button">
126+
<button class="s-btn s-btn__muted s-btn__outlined js-btn-group-item s-btn__dropdown" role="button">
127127
Inactive
128128
<span class="s-btn--badge">
129129
<span class="s-btn--number">37</span>
130130
</span>
131131
</button>
132-
<button class="s-btn s-btn__muted s-btn__outlined is-selected js-btn-group-item" role="button">
132+
<button class="s-btn s-btn__muted s-btn__outlined is-selected js-btn-group-item" role="button" aria-current="true">
133133
All
134134
<span class="s-btn--badge">
135135
<span class="s-btn--number">234</span>
@@ -140,6 +140,49 @@
140140
</div>
141141
</section>
142142

143+
<section class="stacks-section">
144+
{% header "h2", "Radio" %}
145+
<p class="stacks-copy">
146+
Button groups can be implemented using radio elements with the modifier class <code class="stacks-code">.s-btn-group--radio</code>. This modifier class assumes all buttons are styled <code class="stacks-code">label</code> elements.
147+
</p>
148+
<div class="stacks-preview">
149+
{% highlight html %}
150+
<div class="s-btn-group">
151+
<div class="s-btn-group s-btn-group--radio">
152+
<input class="s-btn--radio" type="radio" name="example-btn-group" id="example-btn-group-1" />
153+
<label class="s-btn s-btn__muted s-btn__outlined" for="example-btn-group-1">
154+
Active
155+
</label>
156+
<input class="s-btn--radio" type="radio" name="example-btn-group" id="example-btn-group-2" />
157+
<label class="s-btn s-btn__muted s-btn__outlined" for="example-btn-group-2">
158+
Inactive
159+
</label>
160+
<input class="s-btn--radio" type="radio" name="example-btn-group" id="example-btn-group-3" checked />
161+
<label class="s-btn s-btn__muted s-btn__outlined" for="example-btn-group-3">
162+
All
163+
</label>
164+
</div>
165+
</div>
166+
{% endhighlight %}
167+
<div class="stacks-preview--example">
168+
<div class="s-btn-group s-btn-group--radio">
169+
<input class="s-btn--radio" type="radio" name="example-btn-group" id="example-btn-group-1" />
170+
<label class="s-btn s-btn__muted s-btn__outlined" for="example-btn-group-1">
171+
Active
172+
</label>
173+
<input class="s-btn--radio" type="radio" name="example-btn-group" id="example-btn-group-2" />
174+
<label class="s-btn s-btn__muted s-btn__outlined" for="example-btn-group-2">
175+
Inactive
176+
</label>
177+
<input class="s-btn--radio" type="radio" name="example-btn-group" id="example-btn-group-3" checked />
178+
<label class="s-btn s-btn__muted s-btn__outlined" for="example-btn-group-3">
179+
All
180+
</label>
181+
</div>
182+
</div>
183+
</div>
184+
</section>
185+
143186
<script>
144187
document.querySelectorAll('.js-btn-group-item').forEach(buttonGroup => {
145188
buttonGroup.addEventListener('click', e => {

docs/product/components/buttons.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -369,18 +369,18 @@
369369
</div>
370370
</td>
371371
<td class="va-middle ta-center px4">
372-
<button class="ws-nowrap s-btn {{ class.class }} {% if class.class2 %}{{ class.class2 }}{% endif %} {{ btn.class }}" type="button">
373-
Active <span class="s-btn--badge"><span class="s-btn--number">198</span></span>
372+
<button class="ws-nowrap s-btn {{ class.class }} {% if class.class2 %}{{ class.class2 }}{% endif %}" type="button">
373+
Active <span class="{{ btn.class }}"><span class="s-btn--number">198</span></span>
374374
</button>
375375
</td>
376376
<td class="va-middle ta-center px4">
377-
<button class="ws-nowrap s-btn {{ class.class }} {% if class.class2 %}{{ class.class2 }}{% endif %} {{ btn.class }} is-selected" type="button" aria-pressed="true">
378-
Active <span class="s-btn--badge"><span class="s-btn--number">198</span></span>
377+
<button class="ws-nowrap s-btn {{ class.class }} {% if class.class2 %}{{ class.class2 }}{% endif %} is-selected" type="button" aria-pressed="true">
378+
Active <span class="{{ btn.class }}"><span class="s-btn--number">198</span></span>
379379
</button>
380380
</td>
381381
<td class="va-middle ta-center px4">
382-
<button class="ws-nowrap s-btn {{ class.class }} {% if class.class2 %}{{ class.class2 }}{% endif %} {{ btn.class }}" type="button" disabled>
383-
Active <span class="s-btn--badge"><span class="s-btn--number">198</span></span>
382+
<button class="ws-nowrap s-btn {{ class.class }} {% if class.class2 %}{{ class.class2 }}{% endif %}" type="button" disabled>
383+
Active <span class="{{ btn.class }}"><span class="s-btn--number">198</span></span>
384384
</button>
385385
</td>
386386
</tr>

lib/css/atomic/colors.less

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// visit https://stackoverflow.design/
88
//
99
// ============================================================================
10-
// UTLITY OVERRIDES
10+
// UTILITY OVERRIDES
1111
// Instead of re-adding colors to unique class names, use these atomic classes
1212
// for text and background colors
1313
// ============================================================================
Lines changed: 59 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,80 @@
11
.s-btn-group {
2-
display: flex;
3-
flex-wrap: wrap;
4-
margin-bottom: 1px; // Compensate for buttons having a margin bottom of -1px to account for row wrapping
5-
6-
.s-btn {
7-
margin-bottom: -1px; // When wrapping we need to account for the border
8-
white-space: nowrap; // When the buttons wrap, they get super tall and mess up the whole layout
2+
// CONTEXTUAL STYLES
3+
#stacks-internals #screen-sm({
4+
.s-btn {
5+
&.s-btn__dropdown {
6+
padding-right: 1.2em;
97

10-
// If it isn't the last button, we should slide the button over to account for border thickness
11-
&:not(:last-child) {
12-
margin-right: -1px;
13-
}
8+
&:after {
9+
right: 0.4em;
10+
}
11+
}
1412

15-
// We don't want border-radii on interior buttons
16-
&:not(:first-child):not(:last-child) {
17-
border-radius: 0;
13+
padding-left: 0.4em;
14+
padding-right: 0.4em;
1815
}
16+
}, @force-selector: true);
1917

20-
// Kill the right border radius on the first button
21-
&:first-child:not(:only-child) {
22-
border-top-right-radius: 0;
23-
border-bottom-right-radius: 0;
24-
}
18+
// VARIANTS
19+
&:not(&--radio) .s-btn:not(:first-child):not(:last-child),
20+
&&--radio .s-btn:not(:first-of-type):not(:last-of-type) {
21+
border-radius: 0;
22+
}
2523

26-
// Kill the left border radius on the last button
27-
&:last-child:not(:only-child) {
28-
border-top-left-radius: 0;
29-
border-bottom-left-radius: 0;
30-
}
24+
&:not(&--radio) .s-btn:first-child:not(:only-child),
25+
&&--radio .s-btn:first-of-type:not(:last-of-type) {
26+
border-top-right-radius: 0;
27+
border-bottom-right-radius: 0;
28+
}
3129

32-
// When the button is active or selected, it should pop above its siblings
33-
&.is-selected {
34-
z-index: var(--zi-selected);
35-
}
30+
&:not(&--radio) .s-btn:last-child:not(:only-child),
31+
&&--radio .s-btn:last-of-type:not(:first-of-type) {
32+
border-top-left-radius: 0;
33+
border-bottom-left-radius: 0;
34+
}
3635

37-
.highcontrast-mode({
38-
&.is-selected {
39-
background-color: var(--black-400);
40-
color: var(--white);
36+
&:not(&--radio) .s-btn:not(:last-child),
37+
&&--radio .s-btn:not(:last-of-type) {
38+
margin-right: calc(var(--su-static1) * -1);
39+
}
4140

42-
.s-btn--number {
43-
color: var(--black);
44-
}
41+
// CHILD ELEMENTS
42+
form {
43+
&:not(:first-child):not(:last-child) {
44+
.s-btn {
45+
border-radius: 0;
4546
}
46-
});
47-
48-
&:active {
49-
z-index: var(--zi-active);
5047
}
51-
52-
#stacks-internals #screen-sm({
53-
padding-left: 0.4em;
54-
padding-right: 0.4em;
55-
}, @force-selector: true);
56-
}
57-
58-
#stacks-internals #screen-sm({
59-
.s-btn.s-btn__dropdown {
60-
padding-right: 1.2em;
61-
62-
&:after {
63-
right: 0.4em;
48+
&:last-child:not(:only-child) {
49+
.s-btn:not(:last-child) {
50+
border-radius: 0;
51+
}
52+
}
53+
&:first-child:not(:only-child) {
54+
.s-btn:not(:first-child) {
55+
border-radius: 0;
6456
}
6557
}
66-
}, @force-selector: true);
6758

68-
.s-btn-group--container {
6959
display: flex;
60+
margin-right: calc(var(--su-static1) * -1); // -1px
61+
}
7062

71-
.s-btn {
72-
margin-right: -1px; // We should slide buttons over to account for border thickness
63+
.s-btn {
64+
&:active {
65+
z-index: var(--zi-active);
7366
}
74-
75-
&:not(:first-child):not(:last-child) .s-btn {
76-
border-radius: 0;
67+
&.is-selected,
68+
&--radio:checked + .s-btn {
69+
z-index: var(--zi-selected); // When the button is active or selected, it should pop above its siblings
7770
}
7871

79-
&:first-child .s-btn {
80-
border-top-right-radius: 0;
81-
border-bottom-right-radius: 0;
82-
margin-left: 0;
83-
}
84-
85-
&:last-child .s-btn {
86-
border-top-left-radius: 0;
87-
border-bottom-left-radius: 0;
88-
}
72+
margin-bottom: calc(var(--su-static1) * -1); // When wrapping we need to account for the border
73+
white-space: nowrap; // When the buttons wrap, they get super tall and mess up the whole layout
8974
}
75+
76+
// STATIC COMPONENT STYLES
77+
display: flex;
78+
flex-wrap: wrap;
79+
margin-bottom: var(--su-static1); // Compensate for buttons having a margin bottom of -1px to account for row wrapping
9080
}

0 commit comments

Comments
 (0)