diff --git a/package.json b/package.json
index 05780f0e4..baa25c0eb 100644
--- a/package.json
+++ b/package.json
@@ -43,6 +43,7 @@
"query-string": "^7.1.1",
"react-instantsearch-hooks-web": "^6.38.1",
"react-mailchimp-subscribe": "^2.1.3",
+ "rehype-format": "^5.0.0",
"rehype-katex": "^7.0.0",
"rehype-mermaidjs": "^1.0.1",
"remark-gfm": "^4.0.0",
diff --git a/src/components/Steps/Steps.astro b/src/components/Steps/Steps.astro
new file mode 100644
index 000000000..907fddc42
--- /dev/null
+++ b/src/components/Steps/Steps.astro
@@ -0,0 +1,91 @@
+---
+import { processSteps } from "./rehype-steps"
+
+const content = await Astro.slots.render("default")
+const { html } = processSteps(content)
+---
+
+
+
+
diff --git a/src/components/Steps/rehype-steps.ts b/src/components/Steps/rehype-steps.ts
new file mode 100644
index 000000000..9e4ade24d
--- /dev/null
+++ b/src/components/Steps/rehype-steps.ts
@@ -0,0 +1,44 @@
+import type { Element, Root } from "hast"
+import { rehype } from "rehype"
+import rehypeFormat from "rehype-format"
+import type { VFile } from "vfile"
+
+const prettyPrintProcessor = rehype().data("settings", { fragment: true }).use(rehypeFormat)
+const prettyPrintHtml = (html: string) => prettyPrintProcessor.processSync({ value: html }).toString()
+
+const stepsProcessor = rehype()
+ .data("settings", { fragment: true })
+ .use(function steps() {
+ return (tree: Root, vfile: VFile) => {
+ const rootElements = tree.children.filter((item): item is Element => item.type === "element")
+ const [rootElement] = rootElements
+
+ // Ensure `role="list"` is set on the ordered list.
+ // We use `list-style: none` in the styles for this component and need to ensure the list
+ // retains its semantics in Safari, which will remove them otherwise.
+ rootElement.properties.role = "list"
+ // Add the required CSS class name, preserving existing classes if present.
+ if (!Array.isArray(rootElement.properties.className)) {
+ rootElement.properties.className = ["sl-steps"]
+ } else {
+ rootElement.properties.className.push("sl-steps")
+ }
+
+ // Add the `start` attribute as a CSS custom property so we can use it as the starting index
+ // of the steps custom counter.
+ if (typeof rootElement.properties.start === "number") {
+ const styles = [`--sl-steps-start: ${rootElement.properties.start - 1}`]
+ if (rootElement.properties.style) styles.push(String(rootElement.properties.style))
+ rootElement.properties.style = styles.join(";")
+ }
+ }
+ })
+
+/**
+ * Process steps children: validates the HTML and adds `role="list"` to the ordered list.
+ * @param html Inner HTML passed to the `` component.
+ */
+export const processSteps = (html: string | undefined) => {
+ const file = stepsProcessor.processSync({ value: html })
+ return { html: file.toString() }
+}
diff --git a/src/content/docs/en/article-components.mdx b/src/content/docs/en/article-components.mdx
index 29c813eb5..0905d63da 100644
--- a/src/content/docs/en/article-components.mdx
+++ b/src/content/docs/en/article-components.mdx
@@ -15,6 +15,7 @@ import Aside from "../../../components/Aside.astro"
import MarkmapView from "../../../components/MarkmapView/index.astro"
import RPCTable from "../../../components/RPCTable/RPCTable.astro"
import { Tabs, TabsContent } from "../../../components/Tabs"
+import Steps from '../../../components/Steps/Steps.astro';
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_**](#).
@@ -38,6 +39,21 @@ What else do we have?
##### H5 Heading
+### Steps
+
+
+
+
+1. Import the component into your MDX file:
+
+ ```js
+ import { Steps } from '@astrojs/starlight/components';
+ ```
+
+2. Wrap `` around your ordered list items.
+
+
+
Let's see a horizontal rule.
---