Skip to content

Commit f19d55e

Browse files
authored
New Stepper component (#41999)
* New Stepper component * more * Improvements to stepper, fix other playgrounds while here * bump bundlewatch
1 parent 4764618 commit f19d55e

File tree

9 files changed

+681
-10
lines changed

9 files changed

+681
-10
lines changed

.bundlewatch.config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@
2626
},
2727
{
2828
"path": "./dist/css/bootstrap.css",
29-
"maxSize": "38.0 kB"
29+
"maxSize": "38.75 kB"
3030
},
3131
{
3232
"path": "./dist/css/bootstrap.min.css",
33-
"maxSize": "36.75 kB"
33+
"maxSize": "37.25 kB"
3434
},
3535
{
3636
"path": "./dist/js/bootstrap.bundle.js",

scss/_stepper.scss

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
@use "sass:map";
2+
@use "config" as *;
3+
@use "variables" as *;
4+
@use "layout/breakpoints" as *;
5+
@use "mixins/border-radius" as *;
6+
@use "mixins/box-shadow" as *;
7+
@use "mixins/gradients" as *;
8+
@use "mixins/transition" as *;
9+
10+
// scss-docs-start stepper-variables
11+
$stepper-size: 2rem !default;
12+
$stepper-gap: 1rem !default;
13+
$stepper-track-size: .25rem !default;
14+
$stepper-bg: var(--bg-2) !default;
15+
$stepper-active-fg: var(--primary-contrast) !default;
16+
$stepper-active-bg: var(--primary-bg) !default;
17+
// $stepper-vertical-gap: .5rem !default;
18+
// scss-docs-end stepper-variables
19+
20+
// scss-docs-start stepper-horizontal-mixin
21+
@mixin stepper-horizontal() {
22+
display: inline-grid;
23+
grid-auto-columns: 1fr;
24+
grid-auto-flow: column;
25+
26+
.stepper-item {
27+
grid-template-rows: repeat(2, var(--stepper-size));
28+
grid-template-columns: auto;
29+
justify-items: center;
30+
31+
&::after {
32+
top: calc((var(--stepper-size) * .5) - (var(--stepper-track-size) * .5));
33+
right: 0;
34+
bottom: auto;
35+
left: calc(-50% - var(--stepper-gap));
36+
width: auto;
37+
height: var(--stepper-track-size);
38+
}
39+
40+
&:last-child::after {
41+
right: 50%;
42+
}
43+
}
44+
}
45+
// scss-docs-end stepper-horizontal-mixin
46+
47+
// scss-docs-start stepper-css
48+
.stepper {
49+
// scss-docs-start stepper-css-vars
50+
--stepper-size: #{$stepper-size};
51+
--stepper-gap: #{$stepper-gap};
52+
--stepper-bg: #{$stepper-bg};
53+
--stepper-track-size: #{$stepper-track-size};
54+
--stepper-active-color: #{$stepper-active-fg};
55+
--stepper-active-bg: #{$stepper-active-bg};
56+
// scss-docs-end stepper-css-vars
57+
58+
display: grid;
59+
grid-auto-rows: 1fr;
60+
grid-auto-flow: row;
61+
gap: var(--stepper-gap);
62+
padding-left: 0;
63+
list-style: none;
64+
counter-reset: stepper;
65+
}
66+
67+
.stepper-item {
68+
position: relative;
69+
display: grid;
70+
grid-template-rows: auto;
71+
grid-template-columns: var(--stepper-size) auto;
72+
gap: .5rem;
73+
place-items: center;
74+
justify-items: start;
75+
text-align: center;
76+
text-decoration: none;
77+
78+
79+
// The counter
80+
&::before {
81+
position: relative;
82+
z-index: 1;
83+
display: inline-block;
84+
width: var(--stepper-size);
85+
height: var(--stepper-size);
86+
padding: .5rem;
87+
font-weight: 600;
88+
line-height: 1;
89+
text-align: center;
90+
content: counter(stepper);
91+
counter-increment: stepper;
92+
background-color: var(--stepper-bg);
93+
@include border-radius(50%);
94+
}
95+
96+
// Connecting lines
97+
&::after {
98+
position: absolute;
99+
top: calc(var(--stepper-gap) * -1);
100+
bottom: 100%;
101+
left: calc((var(--stepper-size) * .5) - (var(--stepper-track-size) * .5));
102+
width: var(--stepper-track-size);
103+
content: "";
104+
background-color: var(--stepper-bg);
105+
}
106+
107+
// Avoid sibling selector for easier CSS overrides
108+
&:first-child::after {
109+
display: none;
110+
}
111+
112+
&.active {
113+
&::before,
114+
&::after {
115+
color: var(--theme-contrast, var(--stepper-active-color));
116+
background-color: var(--theme-bg, var(--stepper-active-bg));
117+
}
118+
}
119+
}
120+
121+
@each $breakpoint in map.keys($grid-breakpoints) {
122+
@include media-breakpoint-up($breakpoint) {
123+
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
124+
125+
.stepper-horizontal#{$infix} {
126+
@include stepper-horizontal();
127+
}
128+
}
129+
}
130+
131+
// scss-docs-start stepper-overflow
132+
.stepper-overflow {
133+
container-type: inline-size;
134+
overflow-x: auto;
135+
overscroll-behavior-x: contain;
136+
-webkit-overflow-scrolling: touch;
137+
138+
> .stepper {
139+
width: max-content;
140+
min-width: 100%;
141+
}
142+
}
143+
// scss-docs-end stepper-overflow

scss/bootstrap.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
@forward "popover";
3131
@forward "progress";
3232
@forward "spinner";
33+
@forward "stepper";
3334
@forward "toasts";
3435
@forward "tooltip";
3536
@forward "transitions";

site/data/sidebar.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
- title: Progress
108108
- title: Scrollspy
109109
- title: Spinner
110+
- title: Stepper
110111
- title: Toasts
111112
- title: Toggler
112113
- title: Tooltip

site/src/components/shortcodes/ButtonPlayground.astro

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const rounded = ['default', 'pill', 'square']
2525
</symbol>
2626
</svg>
2727

28-
<div class="bg-1 p-3 rounded-3">
28+
<div class="bg-1 p-3 fs-sm rounded-3">
2929
<div class="d-flex flex-wrap gap-3">
3030
<div class="vstack gap-1">
3131
<label class="form-label fw-semibold mb-0">Color</label>
@@ -38,7 +38,7 @@ const rounded = ['default', 'pill', 'square']
3838
aria-expanded="false"
3939
data-color="primary"
4040
>
41-
Primary
41+
<span>Primary</span>
4242
<svg class="bi ms-1" width="16" height="16" aria-hidden="true">
4343
<use href="#chevron-expand" />
4444
</svg>
@@ -89,7 +89,7 @@ const rounded = ['default', 'pill', 'square']
8989
aria-expanded="false"
9090
data-size=""
9191
>
92-
Medium
92+
<span>Medium</span>
9393
<svg class="bi ms-1" width="16" height="16" aria-hidden="true">
9494
<use href="#chevron-expand" />
9595
</svg>
@@ -270,7 +270,8 @@ const rounded = ['default', 'pill', 'square']
270270
const colorTitle = item.textContent?.trim() || 'Primary'
271271

272272
// Update button text and data attribute
273-
colorDropdownButton.textContent = colorTitle
273+
const labelSpan = colorDropdownButton.querySelector('span')
274+
if (labelSpan) labelSpan.textContent = colorTitle
274275
colorDropdownButton.dataset.color = colorName
275276

276277
// Update selected state
@@ -301,7 +302,8 @@ const rounded = ['default', 'pill', 'square']
301302
const sizeLabel = item.textContent?.trim() || 'Medium'
302303

303304
// Update button text and data attribute
304-
sizeDropdownButton.textContent = sizeLabel
305+
const labelSpan = sizeDropdownButton.querySelector('span')
306+
if (labelSpan) labelSpan.textContent = sizeLabel
305307
sizeDropdownButton.dataset.size = sizeValue
306308

307309
// Update selected state

site/src/components/shortcodes/DropdownPlacementPlayground.astro

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const logicalPlacements = [
2828
]
2929
---
3030

31-
<div class="bg-1 p-3 rounded-3 mb-3">
31+
<div class="bg-1 p-3 fs-sm rounded-3 mb-3">
3232
<div class="d-flex flex-wrap gap-3 align-items-end">
3333
<div class="vstack gap-1">
3434
<label class="form-label fw-semibold mb-0">Placement type</label>
@@ -67,7 +67,10 @@ const logicalPlacements = [
6767
data-placement="bottom-start"
6868
style="min-width: 160px;"
6969
>
70-
bottom-start
70+
<span>bottom-start</span>
71+
<svg class="bi ms-1" width="16" height="16" aria-hidden="true">
72+
<use href="#chevron-expand" />
73+
</svg>
7174
</button>
7275
<ul class="dropdown-menu" aria-labelledby="placement-dropdown">
7376
{physicalPlacements.map((p) => (
@@ -148,7 +151,8 @@ const logicalPlacements = [
148151
if (!placementDropdownButton || !previewToggle) return
149152

150153
// Update the placement selector button
151-
placementDropdownButton.textContent = placement
154+
const labelSpan = placementDropdownButton.querySelector('span')
155+
if (labelSpan) labelSpan.textContent = placement
152156
placementDropdownButton.dataset.placement = placement
153157

154158
// Update active state in dropdown

0 commit comments

Comments
 (0)