Skip to content

Commit ed2922a

Browse files
committed
Add steps component
1 parent ae30f0c commit ed2922a

File tree

4 files changed

+152
-0
lines changed

4 files changed

+152
-0
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"query-string": "^7.1.1",
4444
"react-instantsearch-hooks-web": "^6.38.1",
4545
"react-mailchimp-subscribe": "^2.1.3",
46+
"rehype-format": "^5.0.0",
4647
"rehype-katex": "^7.0.0",
4748
"rehype-mermaidjs": "^1.0.1",
4849
"remark-gfm": "^4.0.0",

src/components/Steps/Steps.astro

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
---
2+
import { processSteps } from "./rehype-steps"
3+
4+
const content = await Astro.slots.render("default")
5+
const { html } = processSteps(content)
6+
---
7+
8+
<Fragment set:html={html} />
9+
10+
<style is:global>
11+
.sl-steps {
12+
--bullet-size: calc(1.75 * 1rem);
13+
--bullet-margin: 0.375rem;
14+
15+
list-style: none;
16+
counter-reset: steps-counter var(--sl-steps-start, 0);
17+
padding-inline-start: 0;
18+
}
19+
20+
.sl-steps > li {
21+
counter-increment: steps-counter;
22+
position: relative;
23+
padding-inline-start: calc(var(--bullet-size) + 1rem);
24+
/* HACK: Keeps any `margin-bottom` inside the `<li>`’s padding box to avoid gaps in the hairline border. */
25+
padding-bottom: 1px;
26+
/* Prevent bullets from touching in short list items. */
27+
min-height: calc(var(--bullet-size) + var(--bullet-margin));
28+
}
29+
.sl-steps > li + li {
30+
/* Remove margin between steps. */
31+
margin-top: 0;
32+
}
33+
34+
/* Custom list marker element. */
35+
.sl-steps > li::before {
36+
content: counter(steps-counter);
37+
position: absolute;
38+
top: 0;
39+
inset-inline-start: 0;
40+
width: var(--bullet-size);
41+
height: var(--bullet-size);
42+
line-height: var(--bullet-size);
43+
44+
font-size: var(--sl-text-xs);
45+
font-weight: 600;
46+
text-align: center;
47+
color: hsl(0, 0%, 100%);
48+
background-color: hsl(224, 14%, 16%);
49+
border-radius: 99rem;
50+
box-shadow: inset 0 0 0 1px hsl(224, 10%, 23%);
51+
}
52+
53+
/* Vertical guideline linking list numbers. */
54+
.sl-steps > li:not(:last-of-type)::after {
55+
--guide-width: 1px;
56+
content: "";
57+
position: absolute;
58+
top: calc(var(--bullet-size) + var(--bullet-margin));
59+
bottom: var(--bullet-margin);
60+
inset-inline-start: calc((var(--bullet-size) - var(--guide-width)) / 2);
61+
width: var(--guide-width);
62+
background-color: #24262e;
63+
}
64+
65+
/* Adjust first item inside a step so that it aligns vertically with the number
66+
even if using a larger font size (e.g. a heading) */
67+
.sl-steps > li > :first-child {
68+
/*
69+
The `lh` unit is not yet supported by all browsers in our support matrix
70+
— see https://caniuse.com/mdn-css_types_length_lh
71+
In unsupported browsers we approximate this using our known line-heights.
72+
*/
73+
--lh: calc(1em * var(--sl-line-height));
74+
--shift-y: calc(0.5 * (var(--bullet-size) - var(--lh)));
75+
transform: translateY(var(--shift-y));
76+
margin-bottom: var(--shift-y);
77+
}
78+
.sl-steps > li > :first-child:where(h1, h2, h3, h4, h5, h6) {
79+
--lh: calc(1em * var(--sl-line-height-headings));
80+
}
81+
@supports (--prop: 1lh) {
82+
.sl-steps > li > :first-child {
83+
--lh: 1lh;
84+
}
85+
}
86+
87+
.sl-steps .expressive-code {
88+
margin-top: 1rem;
89+
margin-bottom: 1.25rem;
90+
}
91+
</style>

src/components/Steps/rehype-steps.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type { Element, Root } from "hast"
2+
import { rehype } from "rehype"
3+
import rehypeFormat from "rehype-format"
4+
import type { VFile } from "vfile"
5+
6+
const prettyPrintProcessor = rehype().data("settings", { fragment: true }).use(rehypeFormat)
7+
const prettyPrintHtml = (html: string) => prettyPrintProcessor.processSync({ value: html }).toString()
8+
9+
const stepsProcessor = rehype()
10+
.data("settings", { fragment: true })
11+
.use(function steps() {
12+
return (tree: Root, vfile: VFile) => {
13+
const rootElements = tree.children.filter((item): item is Element => item.type === "element")
14+
const [rootElement] = rootElements
15+
16+
// Ensure `role="list"` is set on the ordered list.
17+
// We use `list-style: none` in the styles for this component and need to ensure the list
18+
// retains its semantics in Safari, which will remove them otherwise.
19+
rootElement.properties.role = "list"
20+
// Add the required CSS class name, preserving existing classes if present.
21+
if (!Array.isArray(rootElement.properties.className)) {
22+
rootElement.properties.className = ["sl-steps"]
23+
} else {
24+
rootElement.properties.className.push("sl-steps")
25+
}
26+
27+
// Add the `start` attribute as a CSS custom property so we can use it as the starting index
28+
// of the steps custom counter.
29+
if (typeof rootElement.properties.start === "number") {
30+
const styles = [`--sl-steps-start: ${rootElement.properties.start - 1}`]
31+
if (rootElement.properties.style) styles.push(String(rootElement.properties.style))
32+
rootElement.properties.style = styles.join(";")
33+
}
34+
}
35+
})
36+
37+
/**
38+
* Process steps children: validates the HTML and adds `role="list"` to the ordered list.
39+
* @param html Inner HTML passed to the `<Steps>` component.
40+
*/
41+
export const processSteps = (html: string | undefined) => {
42+
const file = stepsProcessor.processSync({ value: html })
43+
return { html: file.toString() }
44+
}

src/content/docs/en/article-components.mdx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import Aside from "../../../components/Aside.astro"
1515
import MarkmapView from "../../../components/MarkmapView/index.astro"
1616
import RPCTable from "../../../components/RPCTable/RPCTable.astro"
1717
import { Tabs, TabsContent } from "../../../components/Tabs"
18+
import Steps from '../../../components/Steps/Steps.astro';
1819

1920
This is body text right under the article title. It typically is just paragraph text that's pretty straightforward. Then there's **bold text**, and _italic text_, and **_bold-italic text_**, and `inline-code` and **`bold inline code`** and even _`italic inline code`_ and **_`bold italic inline code`_**. And of course don't forget [links](#), and [**bold links**](#), and [_italic links_](#), and [**_bold-italic links_**](#).
2021

@@ -38,6 +39,21 @@ What else do we have?
3839

3940
##### H5 Heading
4041

42+
### Steps
43+
44+
45+
<Steps>
46+
47+
1. Import the component into your MDX file:
48+
49+
```js
50+
import { Steps } from '@astrojs/starlight/components';
51+
```
52+
53+
2. Wrap `<Steps>` around your ordered list items.
54+
55+
</Steps>
56+
4157
Let's see a horizontal rule.
4258

4359
---

0 commit comments

Comments
 (0)