diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6fedacf1e0e..d4f2bd96cac 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -275,7 +275,7 @@ To add a language, you will need its BCP-47 tag and a label. See [“Adding a ne
- Components that require client-side JavaScript or CSS should use JavaScript/CSS features that are well-supported by browsers.
- You can find a list of supported browsers and their versions using this [browserslist query](https://browsersl.ist/#q=%3E+0.5%25%2C+not+dead%2C+Chrome+%3E%3D+88%2C+Edge+%3E%3D+88%2C+Firefox+%3E%3D+98%2C+Safari+%3E%3D+15.4%2C+iOS+%3E%3D+15.4%2C+not+op_mini+all). To check whether or not a feature is supported, you can visit the [Can I use](https://caniuse.com) website and search for the feature.
+ You can find a list of supported browsers and their versions using this [browserslist query](https://browsersl.ist/#q=%3E+0.5%25%2C+not+dead%2C+Chrome+%3E%3D+99%2C+Edge+%3E%3D+99%2C+Firefox+%3E%3D+98%2C+Safari+%3E%3D+15.4%2C+iOS+%3E%3D+15.4%2C+not+op_mini+all). To check whether or not a feature is supported, you can visit the [Can I use](https://caniuse.com) website and search for the feature.
[discord]: https://astro.build/chat
[issues]: https://github.com/withastro/starlight/issues
diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs
index 08eb61e6a98..c9adcccacc7 100644
--- a/docs/astro.config.mjs
+++ b/docs/astro.config.mjs
@@ -29,6 +29,8 @@ const ogUrl = new URL('og.jpg?v=1', site).href;
const ogImageAlt = 'Make your docs shine with Starlight';
export default defineConfig({
+ // TODO(HiDeoo) Remove this, only used to avoid screenshoting the dev toolbar.
+ devToolbar: { enabled: false },
site,
trailingSlash: 'always',
integrations: [
diff --git a/docs/src/content/docs/guides/css-and-tailwind.mdx b/docs/src/content/docs/guides/css-and-tailwind.mdx
index 405b8e27c34..55f33f0b921 100644
--- a/docs/src/content/docs/guides/css-and-tailwind.mdx
+++ b/docs/src/content/docs/guides/css-and-tailwind.mdx
@@ -48,6 +48,13 @@ Customize the styles applied to your Starlight site by providing additional CSS
You can see all the CSS custom properties used by Starlight that you can set to customize your site in the [`props.css` file on GitHub](https://github.com/withastro/starlight/blob/main/packages/starlight/style/props.css).
+Starlight uses [cascade layers](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Cascade_layers) internally so that any custom unlayered CSS will override the default styles.
+If you are using cascade layers in your custom CSS, you can use the [`@layer`](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer) CSS at-rule to define the order of precedence for different layers including the ones used by Starlight:
+
+```css
+@layer my-reset, starlight, my-layer;
+```
+
## Tailwind CSS
Tailwind CSS support in Astro projects is provided by the [Astro Tailwind integration](https://docs.astro.build/en/guides/integrations-guide/tailwind/).
diff --git a/packages/local-prod-visual-diff/.gitignore b/packages/local-prod-visual-diff/.gitignore
new file mode 100644
index 00000000000..750e145aa20
--- /dev/null
+++ b/packages/local-prod-visual-diff/.gitignore
@@ -0,0 +1 @@
+screenshots/
diff --git a/packages/local-prod-visual-diff/diff.setup.ts b/packages/local-prod-visual-diff/diff.setup.ts
new file mode 100644
index 00000000000..2d57c072c2c
--- /dev/null
+++ b/packages/local-prod-visual-diff/diff.setup.ts
@@ -0,0 +1,9 @@
+import fs from 'node:fs/promises';
+import path from 'node:path';
+
+// Delete the dev and diff screenshots directories before running the tests.
+await fs.rm(path.join('screenshots', 'dev'), { force: true, recursive: true });
+await fs.rm(path.join('screenshots', 'diff'), { force: true, recursive: true });
+
+// Ensure the diff screenshots directory exists.
+await fs.mkdir(path.join('screenshots', 'diff'), { recursive: true });
diff --git a/packages/local-prod-visual-diff/diff.test.ts b/packages/local-prod-visual-diff/diff.test.ts
new file mode 100644
index 00000000000..c2ccdfa6c5d
--- /dev/null
+++ b/packages/local-prod-visual-diff/diff.test.ts
@@ -0,0 +1,154 @@
+import fs from 'node:fs/promises';
+import path from 'node:path';
+import { expect, test, type Page } from '@playwright/test';
+import pixelmatch from 'pixelmatch';
+import { PNG } from 'pngjs';
+
+const prodUrl = new URL('https://starlight.astro.build');
+
+// A list of all the routes to visually compare between the production and development environments.
+// This could be based on the sitemap but it was not worth the effort for this PR.
+const routePaths = [
+ '/',
+ '/getting-started/',
+ '/manual-setup/',
+ '/environmental-impact/',
+ '/guides/pages/',
+ '/guides/authoring-content/',
+ '/guides/components/',
+ '/guides/css-and-tailwind/',
+ '/guides/customization/',
+ '/guides/i18n/',
+ '/guides/overriding-components/',
+ '/guides/sidebar/',
+ '/guides/site-search/',
+ '/reference/configuration/',
+ '/resources/plugins/',
+ '/resources/community-content/',
+ '/resources/showcase/',
+];
+
+// The maximum number of mismatched pixels between the production and development screenshots.
+const maxDiffPixels = 10;
+
+for (const routePath of routePaths) {
+ test.only(`CSS Layers: ${routePath}`, async ({ page }) => {
+ const prodScreenshotPath = getScreenshotPath(routePath, 'prod');
+
+ // Take a screenshot of the production route if it doesn't exist.
+ if (!(await exists(prodScreenshotPath))) {
+ await page.goto(new URL(routePath, prodUrl).toString());
+ await takeScreenshot(page, prodScreenshotPath);
+ }
+
+ const devScreenshotPath = getScreenshotPath(routePath, 'dev');
+
+ // Take a screenshot of the development route.
+ await page.goto(routePath);
+ await takeScreenshot(page, devScreenshotPath);
+
+ // Compare the screenshots between the production and development routes.
+ const { diffPixels, diffImage } = await compareScreenshots(
+ prodScreenshotPath,
+ devScreenshotPath
+ );
+
+ // Save the diff image if the number of mismatched pixels is greater than the maximum allowed.
+ if (diffPixels >= maxDiffPixels) {
+ await fs.writeFile(getScreenshotPath(routePath, 'diff'), PNG.sync.write(diffImage));
+ }
+
+ // Assert the number of mismatched pixels is less than the maximum allowed.
+ expect(diffPixels).toBeLessThan(maxDiffPixels);
+ });
+}
+
+async function compareScreenshots(screenshotPath1: string, screenshotPath2: string) {
+ let screenshot1: PNG = await getScreenshot(screenshotPath1);
+ let screenshot2: PNG = await getScreenshot(screenshotPath2);
+
+ let diffSize: ScreenshotSize;
+
+ // If the screenshots have different dimensions, resize them to the largest dimensions so we can
+ // see where the differences start to appear.
+ if (screenshot1.width !== screenshot2.width || screenshot1.height !== screenshot2.height) {
+ diffSize = {
+ width: Math.max(screenshot1.width, screenshot2.width),
+ height: Math.max(screenshot1.height, screenshot2.height),
+ };
+
+ screenshot1 = resizeScreenshot(screenshot1, diffSize);
+ screenshot2 = resizeScreenshot(screenshot2, diffSize);
+ } else {
+ diffSize = { width: screenshot1.width, height: screenshot1.height };
+ }
+
+ const diffImage = new PNG(diffSize);
+
+ const diffPixels = pixelmatch(
+ screenshot1.data,
+ screenshot2.data,
+ diffImage.data,
+ diffSize.width,
+ diffSize.height,
+ { threshold: 0.2 }
+ );
+
+ return { diffPixels, diffImage };
+}
+
+function resizeScreenshot(screenshot: PNG, size: ScreenshotSize) {
+ const resized = new PNG(size);
+ PNG.bitblt(screenshot, resized, 0, 0, screenshot.width, screenshot.height);
+ return resized;
+}
+
+async function getScreenshot(screenshotPath: string) {
+ const data = await fs.readFile(screenshotPath);
+ return PNG.sync.read(data);
+}
+
+async function takeScreenshot(page: Page, screenshotPath: string) {
+ // Ensure all images are loaded before taking the screenshot.
+ for (const lazyImage of await page.locator('img[loading="lazy"]:visible').all()) {
+ await lazyImage.scrollIntoViewIfNeeded();
+ }
+
+ const overviewLink = page.getByRole('link', { name: 'Overview', exact: true });
+
+ // Scroll to the top of the page to ensure the screenshot is consistent.
+ if (await overviewLink.isVisible()) {
+ // Use the Overview link when possible to avoid mismatched pixels due to ToC highlighting.
+ overviewLink.click();
+ } else {
+ await page.evaluate(() => window.scrollTo(0, 0));
+ }
+ await page.waitForTimeout(500);
+
+ await page.screenshot({ path: screenshotPath, fullPage: true });
+}
+
+function getScreenshotPath(routePath: string, type: ScreenshotType) {
+ return path.join(
+ 'screenshots',
+ type,
+ routePath.replace(/\//g, '-').replace(/^-/, '').replace(/-$/, '').replace(/^$/, 'index') +
+ '.png'
+ );
+}
+
+async function exists(filePath: string) {
+ try {
+ await fs.access(filePath, fs.constants.F_OK);
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+type ScreenshotType = 'dev' | 'prod' | 'diff';
+
+interface ScreenshotSize {
+ width: number;
+ height: number;
+}
diff --git a/packages/local-prod-visual-diff/package.json b/packages/local-prod-visual-diff/package.json
new file mode 100644
index 00000000000..cdaa53bc2bd
--- /dev/null
+++ b/packages/local-prod-visual-diff/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "starlight-local-prod-visual-diff",
+ "version": "0.1.0",
+ "description": "Visual diffing between local and production builds of Starlight",
+ "private": true,
+ "scripts": {
+ "test": "playwright install --with-deps chromium && playwright test"
+ },
+ "license": "MIT",
+ "dependencies": {
+ "@playwright/test": "^1.45.0",
+ "@types/pixelmatch": "^5.2.6",
+ "@types/pngjs": "^6.0.5",
+ "pixelmatch": "^6.0.0",
+ "pngjs": "^7.0.0"
+ },
+ "type": "module"
+}
diff --git a/packages/local-prod-visual-diff/playwright.config.ts b/packages/local-prod-visual-diff/playwright.config.ts
new file mode 100644
index 00000000000..27fb0e622d7
--- /dev/null
+++ b/packages/local-prod-visual-diff/playwright.config.ts
@@ -0,0 +1,34 @@
+import { defineConfig, devices } from '@playwright/test';
+
+const baseURL = 'http://localhost:4321';
+
+export default defineConfig({
+ forbidOnly: !!process.env['CI'],
+ projects: [
+ {
+ name: 'setup diff',
+ testMatch: /diff\.setup\.ts/,
+ },
+ {
+ name: 'Chrome Stable',
+ use: {
+ ...devices['Desktop Chrome'],
+ headless: true,
+ },
+ dependencies: ['setup diff'],
+ },
+ ],
+ testMatch: '*.test.ts',
+ use: {
+ baseURL,
+ },
+ webServer: [
+ {
+ command: 'pnpm run dev',
+ // command: 'pnpm run build && pnpm run preview',
+ cwd: '../../docs',
+ reuseExistingServer: !process.env['CI'],
+ url: baseURL,
+ },
+ ],
+});
diff --git a/packages/starlight/components/Banner.astro b/packages/starlight/components/Banner.astro
index 107baa707de..e519f1f7043 100644
--- a/packages/starlight/components/Banner.astro
+++ b/packages/starlight/components/Banner.astro
@@ -7,17 +7,19 @@ const { banner } = Astro.props.entry.data;
{banner &&
}
diff --git a/packages/starlight/components/ContentNotice.astro b/packages/starlight/components/ContentNotice.astro
index 9c354f9313b..7f27ec5dd0b 100644
--- a/packages/starlight/components/ContentNotice.astro
+++ b/packages/starlight/components/ContentNotice.astro
@@ -16,16 +16,18 @@ const { icon, label } = Astro.props;
diff --git a/packages/starlight/components/ContentPanel.astro b/packages/starlight/components/ContentPanel.astro
index 3f23fcd0537..6d461322ac4 100644
--- a/packages/starlight/components/ContentPanel.astro
+++ b/packages/starlight/components/ContentPanel.astro
@@ -7,23 +7,25 @@ import type { Props } from '../props';
diff --git a/packages/starlight/components/EditLink.astro b/packages/starlight/components/EditLink.astro
index 74be4c8f79d..a0f73d59f52 100644
--- a/packages/starlight/components/EditLink.astro
+++ b/packages/starlight/components/EditLink.astro
@@ -15,13 +15,15 @@ const { editUrl, labels } = Astro.props;
}
diff --git a/packages/starlight/components/Footer.astro b/packages/starlight/components/Footer.astro
index f75b5e40703..c96f16f3a80 100644
--- a/packages/starlight/components/Footer.astro
+++ b/packages/starlight/components/Footer.astro
@@ -25,34 +25,36 @@ import { Icon } from '../components';
diff --git a/packages/starlight/components/Header.astro b/packages/starlight/components/Header.astro
index 87b97fb1c6f..f494087a989 100644
--- a/packages/starlight/components/Header.astro
+++ b/packages/starlight/components/Header.astro
@@ -15,7 +15,7 @@ const shouldRenderSearch =
config.pagefind || config.components.Search !== '@astrojs/starlight/components/Search.astro';
---
-
diff --git a/packages/starlight/components/MobileMenuFooter.astro b/packages/starlight/components/MobileMenuFooter.astro
index 62eb23e62d4..f54d4065d42 100644
--- a/packages/starlight/components/MobileMenuFooter.astro
+++ b/packages/starlight/components/MobileMenuFooter.astro
@@ -6,28 +6,29 @@ import type { Props } from '../props';
---
diff --git a/packages/starlight/components/MobileMenuToggle.astro b/packages/starlight/components/MobileMenuToggle.astro
index b48e12300f7..8328fd87f29 100644
--- a/packages/starlight/components/MobileMenuToggle.astro
+++ b/packages/starlight/components/MobileMenuToggle.astro
@@ -53,45 +53,49 @@ const { labels } = Astro.props;
diff --git a/packages/starlight/components/MobileTableOfContents.astro b/packages/starlight/components/MobileTableOfContents.astro
index 74342e3c5c3..691e404d8fe 100644
--- a/packages/starlight/components/MobileTableOfContents.astro
+++ b/packages/starlight/components/MobileTableOfContents.astro
@@ -28,82 +28,84 @@ const { labels, toc } = Astro.props;
}
diff --git a/packages/starlight/components/Page.astro b/packages/starlight/components/Page.astro
index 0ab90d1bacb..f5e9bcb3794 100644
--- a/packages/starlight/components/Page.astro
+++ b/packages/starlight/components/Page.astro
@@ -1,13 +1,19 @@
---
import type { Props } from '../props';
+// Important that this is the first import so it can override cascade layers order.
+import 'virtual:starlight/user-css';
+
+// Starlight nested cascade layers definitions which specify the default order of internal layers.
+import '../style/layers.css';
+
// Built-in CSS styles.
import '../style/props.css';
import '../style/reset.css';
import '../style/shiki.css';
+import '../style/asides.css';
import '../style/util.css';
-// Components — can override built-in CSS, but not user CSS.
import Banner from 'virtual:starlight/components/Banner';
import ContentPanel from 'virtual:starlight/components/ContentPanel';
import FallbackContentNotice from 'virtual:starlight/components/FallbackContentNotice';
@@ -25,12 +31,6 @@ import SkipLink from 'virtual:starlight/components/SkipLink';
import ThemeProvider from 'virtual:starlight/components/ThemeProvider';
import TwoColumnContent from 'virtual:starlight/components/TwoColumnContent';
-// Remark component CSS (needs to override `MarkdownContent.astro`)
-import '../style/asides.css';
-
-// Important that this is the last import so it can override built-in styles.
-import 'virtual:starlight/user-css';
-
const pagefindEnabled =
Astro.props.entry.slug !== '404' &&
!Astro.props.entry.slug.endsWith('/404') &&
diff --git a/packages/starlight/components/PageFrame.astro b/packages/starlight/components/PageFrame.astro
index 3398034369c..4111c97c0dd 100644
--- a/packages/starlight/components/PageFrame.astro
+++ b/packages/starlight/components/PageFrame.astro
@@ -23,72 +23,76 @@ const { hasSidebar, labels } = Astro.props;
diff --git a/packages/starlight/components/PageSidebar.astro b/packages/starlight/components/PageSidebar.astro
index 97caff9fab9..c177f967a3a 100644
--- a/packages/starlight/components/PageSidebar.astro
+++ b/packages/starlight/components/PageSidebar.astro
@@ -21,39 +21,41 @@ import TableOfContents from 'virtual:starlight/components/TableOfContents';
}
diff --git a/packages/starlight/components/PageTitle.astro b/packages/starlight/components/PageTitle.astro
index 8c6d932b871..0a4bfd7c1fd 100644
--- a/packages/starlight/components/PageTitle.astro
+++ b/packages/starlight/components/PageTitle.astro
@@ -6,11 +6,13 @@ import type { Props } from '../props';
{Astro.props.entry.data.title}
diff --git a/packages/starlight/components/Pagination.astro b/packages/starlight/components/Pagination.astro
index 0c92c31e0c8..a766cdbf47b 100644
--- a/packages/starlight/components/Pagination.astro
+++ b/packages/starlight/components/Pagination.astro
@@ -35,44 +35,46 @@ const isRtl = dir === 'rtl';
diff --git a/packages/starlight/components/Search.astro b/packages/starlight/components/Search.astro
index 713e9415136..6b4680f044e 100644
--- a/packages/starlight/components/Search.astro
+++ b/packages/starlight/components/Search.astro
@@ -163,304 +163,308 @@ const pagefindTranslations = {
diff --git a/packages/starlight/components/Select.astro b/packages/starlight/components/Select.astro
index 8be1685985c..97982797b34 100644
--- a/packages/starlight/components/Select.astro
+++ b/packages/starlight/components/Select.astro
@@ -28,60 +28,64 @@ interface Props {
diff --git a/packages/starlight/components/SidebarPersister.astro b/packages/starlight/components/SidebarPersister.astro
index ae485c97b9f..c3cab33547b 100644
--- a/packages/starlight/components/SidebarPersister.astro
+++ b/packages/starlight/components/SidebarPersister.astro
@@ -66,7 +66,9 @@ declare global {
diff --git a/packages/starlight/components/SidebarSublist.astro b/packages/starlight/components/SidebarSublist.astro
index b521ba13ef5..64754e3858e 100644
--- a/packages/starlight/components/SidebarSublist.astro
+++ b/packages/starlight/components/SidebarSublist.astro
@@ -59,93 +59,95 @@ const { sublist, nested } = Astro.props;
diff --git a/packages/starlight/components/SiteTitle.astro b/packages/starlight/components/SiteTitle.astro
index 2f4b829943e..5e64b4d2087 100644
--- a/packages/starlight/components/SiteTitle.astro
+++ b/packages/starlight/components/SiteTitle.astro
@@ -35,20 +35,22 @@ const { siteTitle, siteTitleHref } = Astro.props;
diff --git a/packages/starlight/components/SkipLink.astro b/packages/starlight/components/SkipLink.astro
index 307c1ef9a6e..42fdce057b0 100644
--- a/packages/starlight/components/SkipLink.astro
+++ b/packages/starlight/components/SkipLink.astro
@@ -8,20 +8,22 @@ const { labels } = Astro.props;
{labels['skipLink.label']}
diff --git a/packages/starlight/components/SocialIcons.astro b/packages/starlight/components/SocialIcons.astro
index 59557068f80..b82d29e851f 100644
--- a/packages/starlight/components/SocialIcons.astro
+++ b/packages/starlight/components/SocialIcons.astro
@@ -22,12 +22,14 @@ const links = Object.entries(config.social || {}) as [Platform, SocialConfig][];
}
diff --git a/packages/starlight/components/TableOfContents/TableOfContentsList.astro b/packages/starlight/components/TableOfContents/TableOfContentsList.astro
index 8ea9eb84280..21f37c9e689 100644
--- a/packages/starlight/components/TableOfContents/TableOfContentsList.astro
+++ b/packages/starlight/components/TableOfContents/TableOfContentsList.astro
@@ -26,52 +26,54 @@ const { toc, isMobile = false, depth = 0 } = Astro.props;
diff --git a/packages/starlight/components/TwoColumnContent.astro b/packages/starlight/components/TwoColumnContent.astro
index 5eefb3e79ac..e26ee22267f 100644
--- a/packages/starlight/components/TwoColumnContent.astro
+++ b/packages/starlight/components/TwoColumnContent.astro
@@ -16,41 +16,43 @@ import type { Props } from '../props';
diff --git a/packages/starlight/integrations/expressive-code/index.ts b/packages/starlight/integrations/expressive-code/index.ts
index c3fd38546b7..68a0a85c942 100644
--- a/packages/starlight/integrations/expressive-code/index.ts
+++ b/packages/starlight/integrations/expressive-code/index.ts
@@ -80,6 +80,7 @@ export function getStarlightEcConfigPreprocessor({
const {
themes: themesInput,
+ cascadeLayer,
customizeTheme,
styleOverrides: { textMarkers: textMarkersStyleOverrides, ...otherStyleOverrides } = {},
useStarlightDarkModeSwitch,
@@ -135,6 +136,7 @@ export function getStarlightEcConfigPreprocessor({
// Return the default selector
return `[data-theme='${theme.name}']`;
},
+ cascadeLayer: cascadeLayer ?? 'starlight.expressive-code',
styleOverrides: {
borderRadius: '0px',
borderWidth: '1px',
diff --git a/packages/starlight/style/asides.css b/packages/starlight/style/asides.css
index adbb0558f48..10459cf46ee 100644
--- a/packages/starlight/style/asides.css
+++ b/packages/starlight/style/asides.css
@@ -1,49 +1,51 @@
-.starlight-aside {
- padding: 1rem;
- border-inline-start: 0.25rem solid var(--sl-color-asides-border);
- color: var(--sl-color-white);
-}
-.starlight-aside--note {
- --sl-color-asides-text-accent: var(--sl-color-blue-high);
- --sl-color-asides-border: var(--sl-color-blue);
- background-color: var(--sl-color-blue-low);
-}
-.starlight-aside--tip {
- --sl-color-asides-text-accent: var(--sl-color-purple-high);
- --sl-color-asides-border: var(--sl-color-purple);
- background-color: var(--sl-color-purple-low);
-}
-.starlight-aside--caution {
- --sl-color-asides-text-accent: var(--sl-color-orange-high);
- --sl-color-asides-border: var(--sl-color-orange);
- background-color: var(--sl-color-orange-low);
-}
-.starlight-aside--danger {
- --sl-color-asides-text-accent: var(--sl-color-red-high);
- --sl-color-asides-border: var(--sl-color-red);
- background-color: var(--sl-color-red-low);
-}
+@layer starlight.asides {
+ .starlight-aside {
+ padding: 1rem;
+ border-inline-start: 0.25rem solid var(--sl-color-asides-border);
+ color: var(--sl-color-white);
+ }
+ .starlight-aside--note {
+ --sl-color-asides-text-accent: var(--sl-color-blue-high);
+ --sl-color-asides-border: var(--sl-color-blue);
+ background-color: var(--sl-color-blue-low);
+ }
+ .starlight-aside--tip {
+ --sl-color-asides-text-accent: var(--sl-color-purple-high);
+ --sl-color-asides-border: var(--sl-color-purple);
+ background-color: var(--sl-color-purple-low);
+ }
+ .starlight-aside--caution {
+ --sl-color-asides-text-accent: var(--sl-color-orange-high);
+ --sl-color-asides-border: var(--sl-color-orange);
+ background-color: var(--sl-color-orange-low);
+ }
+ .starlight-aside--danger {
+ --sl-color-asides-text-accent: var(--sl-color-red-high);
+ --sl-color-asides-border: var(--sl-color-red);
+ background-color: var(--sl-color-red-low);
+ }
-.starlight-aside__title {
- display: flex;
- gap: 0.5rem;
- align-items: center;
- font-size: var(--sl-text-h5);
- font-weight: 600;
- line-height: var(--sl-line-height-headings);
- color: var(--sl-color-asides-text-accent);
-}
+ .starlight-aside__title {
+ display: flex;
+ gap: 0.5rem;
+ align-items: center;
+ font-size: var(--sl-text-h5);
+ font-weight: 600;
+ line-height: var(--sl-line-height-headings);
+ color: var(--sl-color-asides-text-accent);
+ }
-.starlight-aside__icon {
- font-size: 1.333em;
- width: 1em;
- height: 1em;
-}
+ .starlight-aside__icon {
+ font-size: 1.333em;
+ width: 1em;
+ height: 1em;
+ }
-.starlight-aside__title + .starlight-aside__content {
- margin-top: 0.5rem;
-}
+ .starlight-aside__title + .starlight-aside__content {
+ margin-top: 0.5rem;
+ }
-.starlight-aside__content a {
- color: var(--sl-color-asides-text-accent);
+ .starlight-aside__content a {
+ color: var(--sl-color-asides-text-accent);
+ }
}
diff --git a/packages/starlight/style/layers.css b/packages/starlight/style/layers.css
new file mode 100644
index 00000000000..5c418a3f4fb
--- /dev/null
+++ b/packages/starlight/style/layers.css
@@ -0,0 +1 @@
+@layer starlight.base, starlight.reset, starlight.shiki, starlight.components, starlight.content, starlight.user-components, starlight.asides, starlight.expressive-code, starlight.utils;
diff --git a/packages/starlight/style/markdown.css b/packages/starlight/style/markdown.css
index 596ae6120a9..ef92a02f449 100644
--- a/packages/starlight/style/markdown.css
+++ b/packages/starlight/style/markdown.css
@@ -1,217 +1,223 @@
-.sl-markdown-content
- :not(a, strong, em, del, span, input, code)
- + :not(a, strong, em, del, span, input, code, :where(.not-content *)) {
- margin-top: 1rem;
-}
+@layer starlight.content {
+ .sl-markdown-content
+ :not(a, strong, em, del, span, input, code)
+ + :not(a, strong, em, del, span, input, code, :where(.not-content *)) {
+ margin-top: 1rem;
+ }
-/* Headings after non-headings have more spacing. */
-.sl-markdown-content
- :not(h1, h2, h3, h4, h5, h6)
- + :is(h1, h2, h3, h4, h5, h6):not(:where(.not-content *)) {
- margin-top: 1.5em;
-}
+ /* Headings after non-headings have more spacing. */
+ .sl-markdown-content
+ :not(h1, h2, h3, h4, h5, h6)
+ + :is(h1, h2, h3, h4, h5, h6):not(:where(.not-content *)) {
+ margin-top: 1.5em;
+ }
-.sl-markdown-content li + li:not(:where(.not-content *)),
-.sl-markdown-content dt + dt:not(:where(.not-content *)),
-.sl-markdown-content dt + dd:not(:where(.not-content *)),
-.sl-markdown-content dd + dd:not(:where(.not-content *)) {
- margin-top: 0.25rem;
-}
+ .sl-markdown-content li + li:not(:where(.not-content *)),
+ .sl-markdown-content dt + dt:not(:where(.not-content *)),
+ .sl-markdown-content dt + dd:not(:where(.not-content *)),
+ .sl-markdown-content dd + dd:not(:where(.not-content *)) {
+ margin-top: 0.25rem;
+ }
-.sl-markdown-content li:not(:where(.not-content *)) {
- overflow-wrap: anywhere;
-}
+ .sl-markdown-content li:not(:where(.not-content *)) {
+ overflow-wrap: anywhere;
+ }
-.sl-markdown-content
- li
- > :last-child:not(li, ul, ol):not(a, strong, em, del, span, input, :where(.not-content *)) {
- margin-bottom: 1.25rem;
-}
+ .sl-markdown-content
+ li
+ > :last-child:not(li, ul, ol):not(a, strong, em, del, span, input, :where(.not-content *)) {
+ margin-bottom: 1.25rem;
+ }
-.sl-markdown-content dt:not(:where(.not-content *)) {
- font-weight: 700;
-}
-.sl-markdown-content dd:not(:where(.not-content *)) {
- padding-inline-start: 1rem;
-}
+ .sl-markdown-content dt:not(:where(.not-content *)) {
+ font-weight: 700;
+ }
+ .sl-markdown-content dd:not(:where(.not-content *)) {
+ padding-inline-start: 1rem;
+ }
-.sl-markdown-content :is(h1, h2, h3, h4, h5, h6):not(:where(.not-content *)) {
- color: var(--sl-color-white);
- line-height: var(--sl-line-height-headings);
- font-weight: 600;
-}
+ .sl-markdown-content :is(h1, h2, h3, h4, h5, h6):not(:where(.not-content *)) {
+ color: var(--sl-color-white);
+ line-height: var(--sl-line-height-headings);
+ font-weight: 600;
+ }
-.sl-markdown-content :is(img, picture, video, canvas, svg, iframe):not(:where(.not-content *)) {
- display: block;
- max-width: 100%;
- height: auto;
-}
+ .sl-markdown-content :is(img, picture, video, canvas, svg, iframe):not(:where(.not-content *)) {
+ display: block;
+ max-width: 100%;
+ height: auto;
+ }
-.sl-markdown-content h1:not(:where(.not-content *)) {
- font-size: var(--sl-text-h1);
-}
-.sl-markdown-content h2:not(:where(.not-content *)) {
- font-size: var(--sl-text-h2);
-}
-.sl-markdown-content h3:not(:where(.not-content *)) {
- font-size: var(--sl-text-h3);
-}
-.sl-markdown-content h4:not(:where(.not-content *)) {
- font-size: var(--sl-text-h4);
-}
-.sl-markdown-content h5:not(:where(.not-content *)) {
- font-size: var(--sl-text-h5);
-}
-.sl-markdown-content h6:not(:where(.not-content *)) {
- font-size: var(--sl-text-h6);
-}
+ .sl-markdown-content h1:not(:where(.not-content *)) {
+ font-size: var(--sl-text-h1);
+ }
+ .sl-markdown-content h2:not(:where(.not-content *)) {
+ font-size: var(--sl-text-h2);
+ }
+ .sl-markdown-content h3:not(:where(.not-content *)) {
+ font-size: var(--sl-text-h3);
+ }
+ .sl-markdown-content h4:not(:where(.not-content *)) {
+ font-size: var(--sl-text-h4);
+ }
+ .sl-markdown-content h5:not(:where(.not-content *)) {
+ font-size: var(--sl-text-h5);
+ }
+ .sl-markdown-content h6:not(:where(.not-content *)) {
+ font-size: var(--sl-text-h6);
+ }
-.sl-markdown-content a:not(:where(.not-content *)) {
- color: var(--sl-color-text-accent);
-}
-.sl-markdown-content a:hover:not(:where(.not-content *)) {
- color: var(--sl-color-white);
-}
+ .sl-markdown-content a:not(:where(.not-content *)) {
+ color: var(--sl-color-text-accent);
+ }
+ .sl-markdown-content a:hover:not(:where(.not-content *)) {
+ color: var(--sl-color-white);
+ }
-.sl-markdown-content code:not(:where(.not-content *)) {
- background-color: var(--sl-color-bg-inline-code);
- margin-block: -0.125rem;
- padding: 0.125rem 0.375rem;
- font-size: var(--sl-text-code-sm);
-}
-.sl-markdown-content :is(h1, h2, h3, h4, h5, h6) code {
- font-size: inherit;
-}
+ .sl-markdown-content code:not(:where(.not-content *)) {
+ background-color: var(--sl-color-bg-inline-code);
+ margin-block: -0.125rem;
+ padding: 0.125rem 0.375rem;
+ font-size: var(--sl-text-code-sm);
+ }
+ .sl-markdown-content :is(h1, h2, h3, h4, h5, h6) code {
+ font-size: inherit;
+ }
-.sl-markdown-content pre:not(:where(.not-content *)) {
- border: 1px solid var(--sl-color-gray-5);
- padding: 0.75rem 1rem;
- font-size: var(--sl-text-code);
- tab-size: 2;
-}
+ .sl-markdown-content pre:not(:where(.not-content *)) {
+ border: 1px solid var(--sl-color-gray-5);
+ padding: 0.75rem 1rem;
+ font-size: var(--sl-text-code);
+ tab-size: 2;
+ }
-.sl-markdown-content pre code:not(:where(.not-content *)) {
- all: unset;
- font-family: var(--__sl-font-mono);
-}
+ .sl-markdown-content pre code:not(:where(.not-content *)) {
+ all: unset;
+ font-family: var(--__sl-font-mono);
+ }
-.sl-markdown-content blockquote:not(:where(.not-content *)) {
- border-inline-start: 1px solid var(--sl-color-gray-5);
- padding-inline-start: 1rem;
-}
+ .sl-markdown-content blockquote:not(:where(.not-content *)) {
+ border-inline-start: 1px solid var(--sl-color-gray-5);
+ padding-inline-start: 1rem;
+ }
-/* Table styling */
-.sl-markdown-content table:not(:where(.not-content *)) {
- display: block;
- overflow: auto;
- border-spacing: 0;
-}
-.sl-markdown-content :is(th, td):not(:where(.not-content *)) {
- border-bottom: 1px solid var(--sl-color-gray-5);
- padding: 0.5rem 1rem;
- /* Align text to the top of the row in multiline tables. */
- vertical-align: baseline;
-}
-.sl-markdown-content :is(th:first-child, td:first-child):not(:where(.not-content *)) {
- padding-inline-start: 0;
-}
-.sl-markdown-content :is(th:last-child, td:last-child):not(:where(.not-content *)) {
- padding-inline-end: 0;
-}
-.sl-markdown-content th:not(:where(.not-content *)) {
- color: var(--sl-color-white);
- font-weight: 600;
-}
-/* Align headings to the start of the line unless set by the `align` attribute. */
-.sl-markdown-content th:not([align]):not(:where(.not-content *)) {
- text-align: start;
-}
-/* s,
s, and s inside asides */
-.sl-markdown-content .starlight-aside :is(th, td, hr, blockquote):not(:where(.not-content *)) {
- border-color: var(--sl-color-gray-4);
-}
-@supports (border-color: color-mix(in srgb, var(--sl-color-asides-text-accent) 30%, transparent)) {
+ /* Table styling */
+ .sl-markdown-content table:not(:where(.not-content *)) {
+ display: block;
+ overflow: auto;
+ border-spacing: 0;
+ }
+ .sl-markdown-content :is(th, td):not(:where(.not-content *)) {
+ border-bottom: 1px solid var(--sl-color-gray-5);
+ padding: 0.5rem 1rem;
+ /* Align text to the top of the row in multiline tables. */
+ vertical-align: baseline;
+ }
+ .sl-markdown-content :is(th:first-child, td:first-child):not(:where(.not-content *)) {
+ padding-inline-start: 0;
+ }
+ .sl-markdown-content :is(th:last-child, td:last-child):not(:where(.not-content *)) {
+ padding-inline-end: 0;
+ }
+ .sl-markdown-content th:not(:where(.not-content *)) {
+ color: var(--sl-color-white);
+ font-weight: 600;
+ }
+ /* Align headings to the start of the line unless set by the `align` attribute. */
+ .sl-markdown-content th:not([align]):not(:where(.not-content *)) {
+ text-align: start;
+ }
+ /* s,
s, and s inside asides */
.sl-markdown-content .starlight-aside :is(th, td, hr, blockquote):not(:where(.not-content *)) {
- border-color: color-mix(in srgb, var(--sl-color-asides-text-accent) 30%, transparent);
+ border-color: var(--sl-color-gray-4);
+ }
+ @supports (
+ border-color: color-mix(in srgb, var(--sl-color-asides-text-accent) 30%, transparent)
+ ) {
+ .sl-markdown-content .starlight-aside :is(th, td, hr, blockquote):not(:where(.not-content *)) {
+ border-color: color-mix(in srgb, var(--sl-color-asides-text-accent) 30%, transparent);
+ }
}
-}
-/* inside asides */
-@supports (border-color: color-mix(in srgb, var(--sl-color-asides-text-accent) 12%, transparent)) {
- .sl-markdown-content .starlight-aside code:not(:where(.not-content *)) {
- background-color: color-mix(in srgb, var(--sl-color-asides-text-accent) 12%, transparent);
+ /* inside asides */
+ @supports (
+ border-color: color-mix(in srgb, var(--sl-color-asides-text-accent) 12%, transparent)
+ ) {
+ .sl-markdown-content .starlight-aside code:not(:where(.not-content *)) {
+ background-color: color-mix(in srgb, var(--sl-color-asides-text-accent) 12%, transparent);
+ }
}
-}
-.sl-markdown-content hr:not(:where(.not-content *)) {
- border: 0;
- border-bottom: 1px solid var(--sl-color-hairline);
-}
+ .sl-markdown-content hr:not(:where(.not-content *)) {
+ border: 0;
+ border-bottom: 1px solid var(--sl-color-hairline);
+ }
-/* and styles */
-.sl-markdown-content details:not(:where(.not-content *)) {
- --sl-details-border-color: var(--sl-color-gray-5);
- --sl-details-border-color--hover: var(--sl-color-text-accent);
+ /* and styles */
+ .sl-markdown-content details:not(:where(.not-content *)) {
+ --sl-details-border-color: var(--sl-color-gray-5);
+ --sl-details-border-color--hover: var(--sl-color-text-accent);
- border-inline-start: 2px solid var(--sl-details-border-color);
- padding-inline-start: 1rem;
-}
-.sl-markdown-content details:not([open]):hover:not(:where(.not-content *)),
-.sl-markdown-content details:has(> summary:hover):not(:where(.not-content *)) {
- border-color: var(--sl-details-border-color--hover);
-}
-.sl-markdown-content summary:not(:where(.not-content *)) {
- color: var(--sl-color-white);
- cursor: pointer;
- display: block; /* Needed to hide the default marker in some browsers. */
- font-weight: 600;
- /* Expand the outline so that the marker cannot distort it. */
- margin-inline-start: -0.5rem;
- padding-inline-start: 0.5rem;
-}
-.sl-markdown-content details[open] > summary:not(:where(.not-content *)) {
- margin-bottom: 1rem;
-}
+ border-inline-start: 2px solid var(--sl-details-border-color);
+ padding-inline-start: 1rem;
+ }
+ .sl-markdown-content details:not([open]):hover:not(:where(.not-content *)),
+ .sl-markdown-content details:has(> summary:hover):not(:where(.not-content *)) {
+ border-color: var(--sl-details-border-color--hover);
+ }
+ .sl-markdown-content summary:not(:where(.not-content *)) {
+ color: var(--sl-color-white);
+ cursor: pointer;
+ display: block; /* Needed to hide the default marker in some browsers. */
+ font-weight: 600;
+ /* Expand the outline so that the marker cannot distort it. */
+ margin-inline-start: -0.5rem;
+ padding-inline-start: 0.5rem;
+ }
+ .sl-markdown-content details[open] > summary:not(:where(.not-content *)) {
+ margin-bottom: 1rem;
+ }
-/* marker styles */
-.sl-markdown-content summary:not(:where(.not-content *))::marker,
-.sl-markdown-content summary:not(:where(.not-content *))::-webkit-details-marker {
- display: none;
-}
-.sl-markdown-content summary:not(:where(.not-content *))::before {
- --sl-details-marker-size: 1.25rem;
-
- background-color: currentColor;
- content: '';
- display: inline-block;
- height: var(--sl-details-marker-size);
- width: var(--sl-details-marker-size);
- margin-inline: calc((var(--sl-details-marker-size) / 4) * -1) 0.25rem;
- vertical-align: middle;
- -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14.8 11.3 10.6 7a1 1 0 1 0-1.4 1.5l3.5 3.5-3.5 3.5a1 1 0 0 0 0 1.4 1 1 0 0 0 .7.3 1 1 0 0 0 .7-.3l4.2-4.2a1 1 0 0 0 0-1.4Z'/%3E%3C/svg%3E%0A");
- mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14.8 11.3 10.6 7a1 1 0 1 0-1.4 1.5l3.5 3.5-3.5 3.5a1 1 0 0 0 0 1.4 1 1 0 0 0 .7.3 1 1 0 0 0 .7-.3l4.2-4.2a1 1 0 0 0 0-1.4Z'/%3E%3C/svg%3E%0A");
- -webkit-mask-repeat: no-repeat;
- mask-repeat: no-repeat;
-}
-@media (prefers-reduced-motion: no-preference) {
+ /* marker styles */
+ .sl-markdown-content summary:not(:where(.not-content *))::marker,
+ .sl-markdown-content summary:not(:where(.not-content *))::-webkit-details-marker {
+ display: none;
+ }
.sl-markdown-content summary:not(:where(.not-content *))::before {
- transition: transform 0.2s ease-in-out;
+ --sl-details-marker-size: 1.25rem;
+
+ background-color: currentColor;
+ content: '';
+ display: inline-block;
+ height: var(--sl-details-marker-size);
+ width: var(--sl-details-marker-size);
+ margin-inline: calc((var(--sl-details-marker-size) / 4) * -1) 0.25rem;
+ vertical-align: middle;
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14.8 11.3 10.6 7a1 1 0 1 0-1.4 1.5l3.5 3.5-3.5 3.5a1 1 0 0 0 0 1.4 1 1 0 0 0 .7.3 1 1 0 0 0 .7-.3l4.2-4.2a1 1 0 0 0 0-1.4Z'/%3E%3C/svg%3E%0A");
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14.8 11.3 10.6 7a1 1 0 1 0-1.4 1.5l3.5 3.5-3.5 3.5a1 1 0 0 0 0 1.4 1 1 0 0 0 .7.3 1 1 0 0 0 .7-.3l4.2-4.2a1 1 0 0 0 0-1.4Z'/%3E%3C/svg%3E%0A");
+ -webkit-mask-repeat: no-repeat;
+ mask-repeat: no-repeat;
+ }
+ @media (prefers-reduced-motion: no-preference) {
+ .sl-markdown-content summary:not(:where(.not-content *))::before {
+ transition: transform 0.2s ease-in-out;
+ }
+ }
+ .sl-markdown-content details[open] > summary:not(:where(.not-content *))::before {
+ transform: rotateZ(90deg);
+ }
+ [dir='rtl'] .sl-markdown-content summary:not(:where(.not-content *))::before,
+ .sl-markdown-content [dir='rtl'] summary:not(:where(.not-content *))::before {
+ transform: rotateZ(180deg);
+ }
+ /* with only a paragraph automatically added when using MDX */
+ .sl-markdown-content summary:not(:where(.not-content *)) p:only-child {
+ display: inline;
}
-}
-.sl-markdown-content details[open] > summary:not(:where(.not-content *))::before {
- transform: rotateZ(90deg);
-}
-[dir='rtl'] .sl-markdown-content summary:not(:where(.not-content *))::before,
-.sl-markdown-content [dir='rtl'] summary:not(:where(.not-content *))::before {
- transform: rotateZ(180deg);
-}
-/* with only a paragraph automatically added when using MDX */
-.sl-markdown-content summary:not(:where(.not-content *)) p:only-child {
- display: inline;
-}
-/* styles inside asides */
-.sl-markdown-content .starlight-aside details:not(:where(.not-content *)) {
- --sl-details-border-color: var(--sl-color-asides-border);
- --sl-details-border-color--hover: var(--sl-color-asides-text-accent);
+ /* styles inside asides */
+ .sl-markdown-content .starlight-aside details:not(:where(.not-content *)) {
+ --sl-details-border-color: var(--sl-color-asides-border);
+ --sl-details-border-color--hover: var(--sl-color-asides-text-accent);
+ }
}
diff --git a/packages/starlight/style/props.css b/packages/starlight/style/props.css
index 09c0305d110..83eafa9b0e4 100644
--- a/packages/starlight/style/props.css
+++ b/packages/starlight/style/props.css
@@ -1,184 +1,186 @@
-:root,
-::backdrop {
- /* Colors (dark mode) */
- --sl-color-white: hsl(0, 0%, 100%); /* “white” */
- --sl-color-gray-1: hsl(224, 20%, 94%);
- --sl-color-gray-2: hsl(224, 6%, 77%);
- --sl-color-gray-3: hsl(224, 6%, 56%);
- --sl-color-gray-4: hsl(224, 7%, 36%);
- --sl-color-gray-5: hsl(224, 10%, 23%);
- --sl-color-gray-6: hsl(224, 14%, 16%);
- --sl-color-black: hsl(224, 10%, 10%);
-
- --sl-hue-orange: 41;
- --sl-color-orange-low: hsl(var(--sl-hue-orange), 39%, 22%);
- --sl-color-orange: hsl(var(--sl-hue-orange), 82%, 63%);
- --sl-color-orange-high: hsl(var(--sl-hue-orange), 82%, 87%);
- --sl-hue-green: 101;
- --sl-color-green-low: hsl(var(--sl-hue-green), 39%, 22%);
- --sl-color-green: hsl(var(--sl-hue-green), 82%, 63%);
- --sl-color-green-high: hsl(var(--sl-hue-green), 82%, 80%);
- --sl-hue-blue: 234;
- --sl-color-blue-low: hsl(var(--sl-hue-blue), 54%, 20%);
- --sl-color-blue: hsl(var(--sl-hue-blue), 100%, 60%);
- --sl-color-blue-high: hsl(var(--sl-hue-blue), 100%, 87%);
- --sl-hue-purple: 281;
- --sl-color-purple-low: hsl(var(--sl-hue-purple), 39%, 22%);
- --sl-color-purple: hsl(var(--sl-hue-purple), 82%, 63%);
- --sl-color-purple-high: hsl(var(--sl-hue-purple), 82%, 89%);
- --sl-hue-red: 339;
- --sl-color-red-low: hsl(var(--sl-hue-red), 39%, 22%);
- --sl-color-red: hsl(var(--sl-hue-red), 82%, 63%);
- --sl-color-red-high: hsl(var(--sl-hue-red), 82%, 87%);
-
- --sl-color-accent-low: hsl(224, 54%, 20%);
- --sl-color-accent: hsl(224, 100%, 60%);
- --sl-color-accent-high: hsl(224, 100%, 85%);
-
- --sl-color-text: var(--sl-color-gray-2);
- --sl-color-text-accent: var(--sl-color-accent-high);
- --sl-color-text-invert: var(--sl-color-accent-low);
- --sl-color-bg: var(--sl-color-black);
- --sl-color-bg-nav: var(--sl-color-gray-6);
- --sl-color-bg-sidebar: var(--sl-color-gray-6);
- --sl-color-bg-inline-code: var(--sl-color-gray-5);
- --sl-color-bg-accent: var(--sl-color-accent-high);
- --sl-color-hairline-light: var(--sl-color-gray-5);
- --sl-color-hairline: var(--sl-color-gray-6);
- --sl-color-hairline-shade: var(--sl-color-black);
-
- --sl-color-backdrop-overlay: hsla(223, 13%, 10%, 0.66);
-
- /* Shadows (dark mode) */
- --sl-shadow-sm: 0px 1px 1px hsla(0, 0%, 0%, 0.12), 0px 2px 1px hsla(0, 0%, 0%, 0.24);
- --sl-shadow-md: 0px 8px 4px hsla(0, 0%, 0%, 0.08), 0px 5px 2px hsla(0, 0%, 0%, 0.08),
- 0px 3px 2px hsla(0, 0%, 0%, 0.12), 0px 1px 1px hsla(0, 0%, 0%, 0.15);
- --sl-shadow-lg: 0px 25px 7px hsla(0, 0%, 0%, 0.03), 0px 16px 6px hsla(0, 0%, 0%, 0.1),
- 0px 9px 5px hsla(223, 13%, 10%, 0.33), 0px 4px 4px hsla(0, 0%, 0%, 0.75),
- 0px 4px 2px hsla(0, 0%, 0%, 0.25);
-
- /* Text size and line height */
- --sl-text-2xs: 0.75rem; /* 12px */
- --sl-text-xs: 0.8125rem; /* 13px */
- --sl-text-sm: 0.875rem; /* 14px */
- --sl-text-base: 1rem; /* 16px */
- --sl-text-lg: 1.125rem; /* 18px */
- --sl-text-xl: 1.25rem; /* 20px */
- --sl-text-2xl: 1.5rem; /* 24px */
- --sl-text-3xl: 1.8125rem; /* 29px */
- --sl-text-4xl: 2.1875rem; /* 35px */
- --sl-text-5xl: 2.625rem; /* 42px */
- --sl-text-6xl: 4rem; /* 64px */
-
- --sl-text-body: var(--sl-text-base);
- --sl-text-body-sm: var(--sl-text-xs);
- --sl-text-code: var(--sl-text-sm);
- --sl-text-code-sm: var(--sl-text-xs);
- --sl-text-h1: var(--sl-text-4xl);
- --sl-text-h2: var(--sl-text-3xl);
- --sl-text-h3: var(--sl-text-2xl);
- --sl-text-h4: var(--sl-text-xl);
- --sl-text-h5: var(--sl-text-lg);
-
- --sl-line-height: 1.75;
- --sl-line-height-headings: 1.2;
-
- --sl-font-system: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
- 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
- 'Segoe UI Symbol', 'Noto Color Emoji';
- --sl-font-system-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
- 'Courier New', monospace;
- --__sl-font: var(--sl-font, var(--sl-font-system)), var(--sl-font-system);
- --__sl-font-mono: var(--sl-font-mono, var(--sl-font-system-mono)), var(--sl-font-system-mono);
-
- /** Key layout values */
- --sl-nav-height: 3.5rem;
- --sl-nav-pad-x: 1rem;
- --sl-nav-pad-y: 0.75rem;
- --sl-mobile-toc-height: 3rem;
- --sl-sidebar-width: 18.75rem;
- --sl-sidebar-pad-x: 1rem;
- --sl-content-width: 45rem;
- --sl-content-pad-x: 1rem;
- --sl-menu-button-size: 2rem;
- --sl-nav-gap: var(--sl-content-pad-x);
- /* Offset required to show outline inside an element instead of round the outside */
- --sl-outline-offset-inside: -0.1875rem;
-
- /* Global z-index values */
- --sl-z-index-toc: 4;
- --sl-z-index-menu: 5;
- --sl-z-index-navbar: 10;
- --sl-z-index-skiplink: 20;
-}
+@layer starlight.base {
+ :root,
+ ::backdrop {
+ /* Colors (dark mode) */
+ --sl-color-white: hsl(0, 0%, 100%); /* “white” */
+ --sl-color-gray-1: hsl(224, 20%, 94%);
+ --sl-color-gray-2: hsl(224, 6%, 77%);
+ --sl-color-gray-3: hsl(224, 6%, 56%);
+ --sl-color-gray-4: hsl(224, 7%, 36%);
+ --sl-color-gray-5: hsl(224, 10%, 23%);
+ --sl-color-gray-6: hsl(224, 14%, 16%);
+ --sl-color-black: hsl(224, 10%, 10%);
+
+ --sl-hue-orange: 41;
+ --sl-color-orange-low: hsl(var(--sl-hue-orange), 39%, 22%);
+ --sl-color-orange: hsl(var(--sl-hue-orange), 82%, 63%);
+ --sl-color-orange-high: hsl(var(--sl-hue-orange), 82%, 87%);
+ --sl-hue-green: 101;
+ --sl-color-green-low: hsl(var(--sl-hue-green), 39%, 22%);
+ --sl-color-green: hsl(var(--sl-hue-green), 82%, 63%);
+ --sl-color-green-high: hsl(var(--sl-hue-green), 82%, 80%);
+ --sl-hue-blue: 234;
+ --sl-color-blue-low: hsl(var(--sl-hue-blue), 54%, 20%);
+ --sl-color-blue: hsl(var(--sl-hue-blue), 100%, 60%);
+ --sl-color-blue-high: hsl(var(--sl-hue-blue), 100%, 87%);
+ --sl-hue-purple: 281;
+ --sl-color-purple-low: hsl(var(--sl-hue-purple), 39%, 22%);
+ --sl-color-purple: hsl(var(--sl-hue-purple), 82%, 63%);
+ --sl-color-purple-high: hsl(var(--sl-hue-purple), 82%, 89%);
+ --sl-hue-red: 339;
+ --sl-color-red-low: hsl(var(--sl-hue-red), 39%, 22%);
+ --sl-color-red: hsl(var(--sl-hue-red), 82%, 63%);
+ --sl-color-red-high: hsl(var(--sl-hue-red), 82%, 87%);
+
+ --sl-color-accent-low: hsl(224, 54%, 20%);
+ --sl-color-accent: hsl(224, 100%, 60%);
+ --sl-color-accent-high: hsl(224, 100%, 85%);
+
+ --sl-color-text: var(--sl-color-gray-2);
+ --sl-color-text-accent: var(--sl-color-accent-high);
+ --sl-color-text-invert: var(--sl-color-accent-low);
+ --sl-color-bg: var(--sl-color-black);
+ --sl-color-bg-nav: var(--sl-color-gray-6);
+ --sl-color-bg-sidebar: var(--sl-color-gray-6);
+ --sl-color-bg-inline-code: var(--sl-color-gray-5);
+ --sl-color-bg-accent: var(--sl-color-accent-high);
+ --sl-color-hairline-light: var(--sl-color-gray-5);
+ --sl-color-hairline: var(--sl-color-gray-6);
+ --sl-color-hairline-shade: var(--sl-color-black);
+
+ --sl-color-backdrop-overlay: hsla(223, 13%, 10%, 0.66);
+
+ /* Shadows (dark mode) */
+ --sl-shadow-sm: 0px 1px 1px hsla(0, 0%, 0%, 0.12), 0px 2px 1px hsla(0, 0%, 0%, 0.24);
+ --sl-shadow-md: 0px 8px 4px hsla(0, 0%, 0%, 0.08), 0px 5px 2px hsla(0, 0%, 0%, 0.08),
+ 0px 3px 2px hsla(0, 0%, 0%, 0.12), 0px 1px 1px hsla(0, 0%, 0%, 0.15);
+ --sl-shadow-lg: 0px 25px 7px hsla(0, 0%, 0%, 0.03), 0px 16px 6px hsla(0, 0%, 0%, 0.1),
+ 0px 9px 5px hsla(223, 13%, 10%, 0.33), 0px 4px 4px hsla(0, 0%, 0%, 0.75),
+ 0px 4px 2px hsla(0, 0%, 0%, 0.25);
+
+ /* Text size and line height */
+ --sl-text-2xs: 0.75rem; /* 12px */
+ --sl-text-xs: 0.8125rem; /* 13px */
+ --sl-text-sm: 0.875rem; /* 14px */
+ --sl-text-base: 1rem; /* 16px */
+ --sl-text-lg: 1.125rem; /* 18px */
+ --sl-text-xl: 1.25rem; /* 20px */
+ --sl-text-2xl: 1.5rem; /* 24px */
+ --sl-text-3xl: 1.8125rem; /* 29px */
+ --sl-text-4xl: 2.1875rem; /* 35px */
+ --sl-text-5xl: 2.625rem; /* 42px */
+ --sl-text-6xl: 4rem; /* 64px */
+
+ --sl-text-body: var(--sl-text-base);
+ --sl-text-body-sm: var(--sl-text-xs);
+ --sl-text-code: var(--sl-text-sm);
+ --sl-text-code-sm: var(--sl-text-xs);
+ --sl-text-h1: var(--sl-text-4xl);
+ --sl-text-h2: var(--sl-text-3xl);
+ --sl-text-h3: var(--sl-text-2xl);
+ --sl-text-h4: var(--sl-text-xl);
+ --sl-text-h5: var(--sl-text-lg);
+
+ --sl-line-height: 1.75;
+ --sl-line-height-headings: 1.2;
+
+ --sl-font-system: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
+ Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji',
+ 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
+ --sl-font-system-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
+ 'Courier New', monospace;
+ --__sl-font: var(--sl-font, var(--sl-font-system)), var(--sl-font-system);
+ --__sl-font-mono: var(--sl-font-mono, var(--sl-font-system-mono)), var(--sl-font-system-mono);
+
+ /** Key layout values */
+ --sl-nav-height: 3.5rem;
+ --sl-nav-pad-x: 1rem;
+ --sl-nav-pad-y: 0.75rem;
+ --sl-mobile-toc-height: 3rem;
+ --sl-sidebar-width: 18.75rem;
+ --sl-sidebar-pad-x: 1rem;
+ --sl-content-width: 45rem;
+ --sl-content-pad-x: 1rem;
+ --sl-menu-button-size: 2rem;
+ --sl-nav-gap: var(--sl-content-pad-x);
+ /* Offset required to show outline inside an element instead of round the outside */
+ --sl-outline-offset-inside: -0.1875rem;
+
+ /* Global z-index values */
+ --sl-z-index-toc: 4;
+ --sl-z-index-menu: 5;
+ --sl-z-index-navbar: 10;
+ --sl-z-index-skiplink: 20;
+ }
-:root[data-theme='light'],
-[data-theme='light'] ::backdrop {
- /* Colours (light mode) */
- --sl-color-white: hsl(224, 10%, 10%);
- --sl-color-gray-1: hsl(224, 14%, 16%);
- --sl-color-gray-2: hsl(224, 10%, 23%);
- --sl-color-gray-3: hsl(224, 7%, 36%);
- --sl-color-gray-4: hsl(224, 6%, 56%);
- --sl-color-gray-5: hsl(224, 6%, 77%);
- --sl-color-gray-6: hsl(224, 20%, 94%);
- --sl-color-gray-7: hsl(224, 19%, 97%);
- --sl-color-black: hsl(0, 0%, 100%);
-
- --sl-color-orange-high: hsl(var(--sl-hue-orange), 80%, 25%);
- --sl-color-orange: hsl(var(--sl-hue-orange), 90%, 60%);
- --sl-color-orange-low: hsl(var(--sl-hue-orange), 90%, 88%);
- --sl-color-green-high: hsl(var(--sl-hue-green), 80%, 22%);
- --sl-color-green: hsl(var(--sl-hue-green), 90%, 46%);
- --sl-color-green-low: hsl(var(--sl-hue-green), 85%, 90%);
- --sl-color-blue-high: hsl(var(--sl-hue-blue), 80%, 30%);
- --sl-color-blue: hsl(var(--sl-hue-blue), 90%, 60%);
- --sl-color-blue-low: hsl(var(--sl-hue-blue), 88%, 90%);
- --sl-color-purple-high: hsl(var(--sl-hue-purple), 90%, 30%);
- --sl-color-purple: hsl(var(--sl-hue-purple), 90%, 60%);
- --sl-color-purple-low: hsl(var(--sl-hue-purple), 80%, 90%);
- --sl-color-red-high: hsl(var(--sl-hue-red), 80%, 30%);
- --sl-color-red: hsl(var(--sl-hue-red), 90%, 60%);
- --sl-color-red-low: hsl(var(--sl-hue-red), 80%, 90%);
-
- --sl-color-accent-high: hsl(234, 80%, 30%);
- --sl-color-accent: hsl(234, 90%, 60%);
- --sl-color-accent-low: hsl(234, 88%, 90%);
-
- --sl-color-text-accent: var(--sl-color-accent);
- --sl-color-text-invert: var(--sl-color-black);
- --sl-color-bg-nav: var(--sl-color-gray-7);
- --sl-color-bg-sidebar: var(--sl-color-bg);
- --sl-color-bg-inline-code: var(--sl-color-gray-6);
- --sl-color-bg-accent: var(--sl-color-accent);
- --sl-color-hairline-light: var(--sl-color-gray-6);
- --sl-color-hairline-shade: var(--sl-color-gray-6);
-
- --sl-color-backdrop-overlay: hsla(225, 9%, 36%, 0.66);
-
- /* Shadows (light mode) */
- --sl-shadow-sm: 0px 1px 1px hsla(0, 0%, 0%, 0.06), 0px 2px 1px hsla(0, 0%, 0%, 0.06);
- --sl-shadow-md: 0px 8px 4px hsla(0, 0%, 0%, 0.03), 0px 5px 2px hsla(0, 0%, 0%, 0.03),
- 0px 3px 2px hsla(0, 0%, 0%, 0.06), 0px 1px 1px hsla(0, 0%, 0%, 0.06);
- --sl-shadow-lg: 0px 25px 7px rgba(0, 0, 0, 0.01), 0px 16px 6px hsla(0, 0%, 0%, 0.03),
- 0px 9px 5px hsla(223, 13%, 10%, 0.08), 0px 4px 4px hsla(0, 0%, 0%, 0.16),
- 0px 4px 2px hsla(0, 0%, 0%, 0.04);
-}
+ :root[data-theme='light'],
+ [data-theme='light'] ::backdrop {
+ /* Colours (light mode) */
+ --sl-color-white: hsl(224, 10%, 10%);
+ --sl-color-gray-1: hsl(224, 14%, 16%);
+ --sl-color-gray-2: hsl(224, 10%, 23%);
+ --sl-color-gray-3: hsl(224, 7%, 36%);
+ --sl-color-gray-4: hsl(224, 6%, 56%);
+ --sl-color-gray-5: hsl(224, 6%, 77%);
+ --sl-color-gray-6: hsl(224, 20%, 94%);
+ --sl-color-gray-7: hsl(224, 19%, 97%);
+ --sl-color-black: hsl(0, 0%, 100%);
+
+ --sl-color-orange-high: hsl(var(--sl-hue-orange), 80%, 25%);
+ --sl-color-orange: hsl(var(--sl-hue-orange), 90%, 60%);
+ --sl-color-orange-low: hsl(var(--sl-hue-orange), 90%, 88%);
+ --sl-color-green-high: hsl(var(--sl-hue-green), 80%, 22%);
+ --sl-color-green: hsl(var(--sl-hue-green), 90%, 46%);
+ --sl-color-green-low: hsl(var(--sl-hue-green), 85%, 90%);
+ --sl-color-blue-high: hsl(var(--sl-hue-blue), 80%, 30%);
+ --sl-color-blue: hsl(var(--sl-hue-blue), 90%, 60%);
+ --sl-color-blue-low: hsl(var(--sl-hue-blue), 88%, 90%);
+ --sl-color-purple-high: hsl(var(--sl-hue-purple), 90%, 30%);
+ --sl-color-purple: hsl(var(--sl-hue-purple), 90%, 60%);
+ --sl-color-purple-low: hsl(var(--sl-hue-purple), 80%, 90%);
+ --sl-color-red-high: hsl(var(--sl-hue-red), 80%, 30%);
+ --sl-color-red: hsl(var(--sl-hue-red), 90%, 60%);
+ --sl-color-red-low: hsl(var(--sl-hue-red), 80%, 90%);
+
+ --sl-color-accent-high: hsl(234, 80%, 30%);
+ --sl-color-accent: hsl(234, 90%, 60%);
+ --sl-color-accent-low: hsl(234, 88%, 90%);
+
+ --sl-color-text-accent: var(--sl-color-accent);
+ --sl-color-text-invert: var(--sl-color-black);
+ --sl-color-bg-nav: var(--sl-color-gray-7);
+ --sl-color-bg-sidebar: var(--sl-color-bg);
+ --sl-color-bg-inline-code: var(--sl-color-gray-6);
+ --sl-color-bg-accent: var(--sl-color-accent);
+ --sl-color-hairline-light: var(--sl-color-gray-6);
+ --sl-color-hairline-shade: var(--sl-color-gray-6);
+
+ --sl-color-backdrop-overlay: hsla(225, 9%, 36%, 0.66);
+
+ /* Shadows (light mode) */
+ --sl-shadow-sm: 0px 1px 1px hsla(0, 0%, 0%, 0.06), 0px 2px 1px hsla(0, 0%, 0%, 0.06);
+ --sl-shadow-md: 0px 8px 4px hsla(0, 0%, 0%, 0.03), 0px 5px 2px hsla(0, 0%, 0%, 0.03),
+ 0px 3px 2px hsla(0, 0%, 0%, 0.06), 0px 1px 1px hsla(0, 0%, 0%, 0.06);
+ --sl-shadow-lg: 0px 25px 7px rgba(0, 0, 0, 0.01), 0px 16px 6px hsla(0, 0%, 0%, 0.03),
+ 0px 9px 5px hsla(223, 13%, 10%, 0.08), 0px 4px 4px hsla(0, 0%, 0%, 0.16),
+ 0px 4px 2px hsla(0, 0%, 0%, 0.04);
+ }
-@media (min-width: 50em) {
- :root {
- --sl-nav-height: 4rem;
- --sl-nav-pad-x: 1.5rem;
- --sl-text-h1: var(--sl-text-5xl);
- --sl-text-h2: var(--sl-text-4xl);
- --sl-text-h3: var(--sl-text-3xl);
- --sl-text-h4: var(--sl-text-2xl);
+ @media (min-width: 50em) {
+ :root {
+ --sl-nav-height: 4rem;
+ --sl-nav-pad-x: 1.5rem;
+ --sl-text-h1: var(--sl-text-5xl);
+ --sl-text-h2: var(--sl-text-4xl);
+ --sl-text-h3: var(--sl-text-3xl);
+ --sl-text-h4: var(--sl-text-2xl);
+ }
}
-}
-@media (min-width: 72rem) {
- :root {
- --sl-content-pad-x: 1.5rem;
- --sl-mobile-toc-height: 0rem;
+ @media (min-width: 72rem) {
+ :root {
+ --sl-content-pad-x: 1.5rem;
+ --sl-mobile-toc-height: 0rem;
+ }
}
}
diff --git a/packages/starlight/style/reset.css b/packages/starlight/style/reset.css
index 0867542a8c4..f2855a468e7 100644
--- a/packages/starlight/style/reset.css
+++ b/packages/starlight/style/reset.css
@@ -1,48 +1,50 @@
-*,
-*::before,
-*::after {
- box-sizing: border-box;
-}
+@layer starlight.reset {
+ *,
+ *::before,
+ *::after {
+ box-sizing: border-box;
+ }
-* {
- margin: 0;
-}
+ * {
+ margin: 0;
+ }
-html {
- color-scheme: dark;
- accent-color: var(--sl-color-accent);
-}
+ html {
+ color-scheme: dark;
+ accent-color: var(--sl-color-accent);
+ }
-html[data-theme='light'] {
- color-scheme: light;
-}
+ html[data-theme='light'] {
+ color-scheme: light;
+ }
-body {
- font-family: var(--__sl-font);
- line-height: var(--sl-line-height);
- -webkit-font-smoothing: antialiased;
- color: var(--sl-color-text);
- background-color: var(--sl-color-bg);
-}
+ body {
+ font-family: var(--__sl-font);
+ line-height: var(--sl-line-height);
+ -webkit-font-smoothing: antialiased;
+ color: var(--sl-color-text);
+ background-color: var(--sl-color-bg);
+ }
-input,
-button,
-textarea,
-select {
- font: inherit;
-}
+ input,
+ button,
+ textarea,
+ select {
+ font: inherit;
+ }
-p,
-h1,
-h2,
-h3,
-h4,
-h5,
-h6,
-code {
- overflow-wrap: anywhere;
-}
+ p,
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6,
+ code {
+ overflow-wrap: anywhere;
+ }
-code {
- font-family: var(--__sl-font-mono);
+ code {
+ font-family: var(--__sl-font-mono);
+ }
}
diff --git a/packages/starlight/style/shiki.css b/packages/starlight/style/shiki.css
index b35288bdf8c..85bea575079 100644
--- a/packages/starlight/style/shiki.css
+++ b/packages/starlight/style/shiki.css
@@ -1,13 +1,15 @@
-:root {
- --astro-code-color-text: var(--sl-color-white);
- --astro-code-color-background: var(--sl-color-gray-6);
- --astro-code-token-constant: var(--sl-color-blue-high);
- --astro-code-token-string: var(--sl-color-green-high);
- --astro-code-token-comment: var(--sl-color-gray-2);
- --astro-code-token-keyword: var(--sl-color-purple-high);
- --astro-code-token-parameter: var(--sl-color-red-high);
- --astro-code-token-function: var(--sl-color-red-high);
- --astro-code-token-string-expression: var(--sl-color-green-high);
- --astro-code-token-punctuation: var(--sl-color-gray-2);
- --astro-code-token-link: var(--sl-color-blue-high);
+@layer starlight.shiki {
+ :root {
+ --astro-code-color-text: var(--sl-color-white);
+ --astro-code-color-background: var(--sl-color-gray-6);
+ --astro-code-token-constant: var(--sl-color-blue-high);
+ --astro-code-token-string: var(--sl-color-green-high);
+ --astro-code-token-comment: var(--sl-color-gray-2);
+ --astro-code-token-keyword: var(--sl-color-purple-high);
+ --astro-code-token-parameter: var(--sl-color-red-high);
+ --astro-code-token-function: var(--sl-color-red-high);
+ --astro-code-token-string-expression: var(--sl-color-green-high);
+ --astro-code-token-punctuation: var(--sl-color-gray-2);
+ --astro-code-token-link: var(--sl-color-blue-high);
+ }
}
diff --git a/packages/starlight/style/util.css b/packages/starlight/style/util.css
index 74868532205..bb839538b33 100644
--- a/packages/starlight/style/util.css
+++ b/packages/starlight/style/util.css
@@ -1,61 +1,63 @@
-.sr-only {
- position: absolute;
- width: 1px;
- height: 1px;
- padding: 0;
- margin: -1px;
- overflow: hidden;
- clip: rect(0, 0, 0, 0);
- white-space: nowrap;
- border-width: 0;
-}
+@layer starlight.utils {
+ .sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
+ }
-.sl-hidden {
- display: none;
-}
-.sl-flex {
- display: flex;
-}
-.sl-block {
- display: block;
-}
-@media (min-width: 50rem) {
- .md\:sl-hidden {
+ .sl-hidden {
display: none;
}
- .md\:sl-flex {
+ .sl-flex {
display: flex;
}
- .md\:sl-block {
+ .sl-block {
display: block;
}
-}
-@media (min-width: 72rem) {
- .lg\:sl-hidden {
- display: none;
+ @media (min-width: 50rem) {
+ .md\:sl-hidden {
+ display: none;
+ }
+ .md\:sl-flex {
+ display: flex;
+ }
+ .md\:sl-block {
+ display: block;
+ }
}
- .lg\:sl-flex {
- display: flex;
+ @media (min-width: 72rem) {
+ .lg\:sl-hidden {
+ display: none;
+ }
+ .lg\:sl-flex {
+ display: flex;
+ }
+ .lg\:sl-block {
+ display: block;
+ }
}
- .lg\:sl-block {
- display: block;
+ [data-theme='light'] .light\:sl-hidden {
+ display: none;
+ }
+ [data-theme='dark'] .dark\:sl-hidden {
+ display: none;
}
-}
-[data-theme='light'] .light\:sl-hidden {
- display: none;
-}
-[data-theme='dark'] .dark\:sl-hidden {
- display: none;
-}
-
-/*
-Flip an element around the y-axis when in an RTL context.
-Primarily useful for things where we can’t rely on writing direction like icons.
-
-
-In a LTR context: → In a RTL context: ←
-*/
-[dir='rtl'] .rtl\:flip:not(:where([dir='rtl'] [dir='ltr'] *)) {
- transform: matrix(-1, 0, 0, 1, 0, 0);
+ /**
+ * Flip an element around the y-axis when in an RTL context.
+ * Primarily useful for things where we can’t rely on writing direction like icons.
+ *
+ *
+ *
+ * In a LTR context: → In a RTL context: ←
+ */
+ [dir='rtl'] .rtl\:flip:not(:where([dir='rtl'] [dir='ltr'] *)) {
+ transform: matrix(-1, 0, 0, 1, 0, 0);
+ }
}
diff --git a/packages/starlight/user-components/Badge.astro b/packages/starlight/user-components/Badge.astro
index 821cd0a32e3..2a0fa991ded 100644
--- a/packages/starlight/user-components/Badge.astro
+++ b/packages/starlight/user-components/Badge.astro
@@ -21,121 +21,123 @@ const {
{text}
diff --git a/packages/starlight/user-components/Card.astro b/packages/starlight/user-components/Card.astro
index 6b95e5bf88d..7ad33a9aa9d 100644
--- a/packages/starlight/user-components/Card.astro
+++ b/packages/starlight/user-components/Card.astro
@@ -19,47 +19,49 @@ const { icon, title } = Astro.props;
diff --git a/packages/starlight/user-components/CardGrid.astro b/packages/starlight/user-components/CardGrid.astro
index df05287e49c..7a21e8e7320 100644
--- a/packages/starlight/user-components/CardGrid.astro
+++ b/packages/starlight/user-components/CardGrid.astro
@@ -9,27 +9,29 @@ const { stagger = false } = Astro.props;
diff --git a/packages/starlight/user-components/FileTree.astro b/packages/starlight/user-components/FileTree.astro
index 1e079cf2cda..b2893aa4c54 100644
--- a/packages/starlight/user-components/FileTree.astro
+++ b/packages/starlight/user-components/FileTree.astro
@@ -14,127 +14,129 @@ const html = processFileTree(fileTreeHtml, t('fileTree.directory'));
diff --git a/packages/starlight/user-components/Icon.astro b/packages/starlight/user-components/Icon.astro
index 7575cc78702..5937f4b8f1d 100644
--- a/packages/starlight/user-components/Icon.astro
+++ b/packages/starlight/user-components/Icon.astro
@@ -24,10 +24,12 @@ const a11yAttrs = label ? ({ 'aria-label': label } as const) : ({ 'aria-hidden':
/>
diff --git a/packages/starlight/user-components/LinkButton.astro b/packages/starlight/user-components/LinkButton.astro
index 3bf68f073d7..015ae30ba82 100644
--- a/packages/starlight/user-components/LinkButton.astro
+++ b/packages/starlight/user-components/LinkButton.astro
@@ -26,51 +26,53 @@ const {
diff --git a/packages/starlight/user-components/LinkCard.astro b/packages/starlight/user-components/LinkCard.astro
index 8cf6fbd90fd..d78d0e5ca68 100644
--- a/packages/starlight/user-components/LinkCard.astro
+++ b/packages/starlight/user-components/LinkCard.astro
@@ -21,56 +21,58 @@ const { title, description, ...attributes } = Astro.props;
diff --git a/packages/starlight/user-components/Steps.astro b/packages/starlight/user-components/Steps.astro
index c66dffb1f43..99c0815a375 100644
--- a/packages/starlight/user-components/Steps.astro
+++ b/packages/starlight/user-components/Steps.astro
@@ -8,79 +8,83 @@ const { html } = processSteps(content);
diff --git a/packages/starlight/user-components/Tabs.astro b/packages/starlight/user-components/Tabs.astro
index f3108806686..3e2ece5fc5c 100644
--- a/packages/starlight/user-components/Tabs.astro
+++ b/packages/starlight/user-components/Tabs.astro
@@ -98,43 +98,45 @@ if (isSynced) {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ac9b53a9b93..74c1bd3ba28 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -146,6 +146,24 @@ importers:
specifier: ^1.3.8
version: 1.3.8
+ packages/local-prod-visual-diff:
+ dependencies:
+ '@playwright/test':
+ specifier: ^1.45.0
+ version: 1.45.0
+ '@types/pixelmatch':
+ specifier: ^5.2.6
+ version: 5.2.6
+ '@types/pngjs':
+ specifier: ^6.0.5
+ version: 6.0.5
+ pixelmatch:
+ specifier: ^6.0.0
+ version: 6.0.0
+ pngjs:
+ specifier: ^7.0.0
+ version: 7.0.0
+
packages/markdoc:
dependencies:
'@astrojs/markdoc':
@@ -1767,7 +1785,6 @@ packages:
hasBin: true
dependencies:
playwright: 1.45.0
- dev: true
/@rollup/pluginutils@5.1.0:
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
@@ -2054,6 +2071,18 @@ packages:
resolution: {integrity: sha512-H6qeTp03jrknklSn4bpT1/9+1xCAEIU2CnjcWPkicJy8A1SKuthanbvoHYMiv79/2W3Xn1XE4gfSJFzt2U3JSw==}
dev: true
+ /@types/pixelmatch@5.2.6:
+ resolution: {integrity: sha512-wC83uexE5KGuUODn6zkm9gMzTwdY5L0chiK+VrKcDfEjzxh1uadlWTvOmAbCpnM9zx/Ww3f8uKlYQVnO/TrqVg==}
+ dependencies:
+ '@types/node': 18.16.19
+ dev: false
+
+ /@types/pngjs@6.0.5:
+ resolution: {integrity: sha512-0k5eKfrA83JOZPppLtS2C7OUtyNAl2wKNxfyYl9Q5g9lPkgBl/9hNyAu6HuEH2J4XmIv2znEpkDd0SaZVxW6iQ==}
+ dependencies:
+ '@types/node': 18.16.19
+ dev: false
+
/@types/sax@1.2.4:
resolution: {integrity: sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==}
dependencies:
@@ -3651,7 +3680,6 @@ packages:
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
- dev: true
optional: true
/fsevents@2.3.3:
@@ -5874,6 +5902,13 @@ packages:
engines: {node: '>= 6'}
dev: false
+ /pixelmatch@6.0.0:
+ resolution: {integrity: sha512-FYpL4XiIWakTnIqLqvt3uN4L9B3TsuHIvhLILzTiJZMJUsGvmKNeL4H3b6I99LRyerK9W4IuOXw+N28AtRgK2g==}
+ hasBin: true
+ dependencies:
+ pngjs: 7.0.0
+ dev: false
+
/pkg-dir@4.2.0:
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
engines: {node: '>=8'}
@@ -5892,7 +5927,6 @@ packages:
resolution: {integrity: sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ==}
engines: {node: '>=18'}
hasBin: true
- dev: true
/playwright@1.45.0:
resolution: {integrity: sha512-4z3ac3plDfYzGB6r0Q3LF8POPR20Z8D0aXcxbJvmfMgSSq1hkcgvFRXJk9rUq5H/MJ0Ktal869hhOdI/zUTeLA==}
@@ -5902,7 +5936,11 @@ packages:
playwright-core: 1.45.0
optionalDependencies:
fsevents: 2.3.2
- dev: true
+
+ /pngjs@7.0.0:
+ resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==}
+ engines: {node: '>=14.19.0'}
+ dev: false
/postcss-import@15.1.0(postcss@8.4.45):
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}