name: calibreapp/image-actions
runs-on: ubuntu-latest
permissions:
- # allow calibreapp/image-actions to update PRs
+ # allow calibreapp/image-actions to update PRs and commit compressed images
+ contents: write
pull-requests: write
steps:
- name: Clone repository
"@rollup/plugin-commonjs": "^29.0.0",
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-replace": "^6.0.3",
+ "@shikijs/transformers": "^3.7.0",
"@stackblitz/sdk": "^1.11.0",
"@types/js-yaml": "^4.0.9",
"@types/mime": "^4.0.0",
"astro": "^5.15.6",
"astro-auto-import": "^0.4.5",
"autoprefixer": "^10.4.22",
+ "bootstrap-vscode-theme": "^0.0.9",
"bundlewatch": "^0.4.1",
"clean-css-cli": "^5.6.3",
"clipboard": "^2.0.11",
"@shikijs/types": "3.15.0"
}
},
+ "node_modules/@shikijs/transformers": {
+ "version": "3.19.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-3.19.0.tgz",
+ "integrity": "sha512-e6vwrsyw+wx4OkcrDbL+FVCxwx8jgKiCoXzakVur++mIWVcgpzIi8vxf4/b4dVTYrV/nUx5RjinMf4tq8YV8Fw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/core": "3.19.0",
+ "@shikijs/types": "3.19.0"
+ }
+ },
+ "node_modules/@shikijs/transformers/node_modules/@shikijs/core": {
+ "version": "3.19.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.19.0.tgz",
+ "integrity": "sha512-L7SrRibU7ZoYi1/TrZsJOFAnnHyLTE1SwHG1yNWjZIVCqjOEmCSuK2ZO9thnRbJG6TOkPp+Z963JmpCNw5nzvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.19.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4",
+ "hast-util-to-html": "^9.0.5"
+ }
+ },
+ "node_modules/@shikijs/transformers/node_modules/@shikijs/types": {
+ "version": "3.19.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.19.0.tgz",
+ "integrity": "sha512-Z2hdeEQlzuntf/BZpFG8a+Fsw9UVXdML7w0o3TgSXV3yNESGon+bs9ITkQb3Ki7zxoXOOu5oJWqZ2uto06V9iQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
"node_modules/@shikijs/types": {
"version": "3.15.0",
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.15.0.tgz",
"dev": true,
"license": "MIT"
},
+ "node_modules/bootstrap-vscode-theme": {
+ "version": "0.0.9",
+ "resolved": "https://registry.npmjs.org/bootstrap-vscode-theme/-/bootstrap-vscode-theme-0.0.9.tgz",
+ "integrity": "sha512-++aMildSGVaDS7qD59FEh4Fh6bJol4Gpo7u3rbEPqcuReRY7zOvLn77sBoK9zhVe+YT7bkPiDut47jErweChdw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "vscode": "^1.0.0"
+ }
+ },
"node_modules/boxen": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz",
"@rollup/plugin-commonjs": "^29.0.0",
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-replace": "^6.0.3",
+ "@shikijs/transformers": "^3.7.0",
"@stackblitz/sdk": "^1.11.0",
"@types/js-yaml": "^4.0.9",
"@types/mime": "^4.0.0",
"astro": "^5.15.6",
"astro-auto-import": "^0.4.5",
"autoprefixer": "^10.4.22",
+ "bootstrap-vscode-theme": "^0.0.9",
"bundlewatch": "^0.4.1",
"clean-css-cli": "^5.6.3",
"clipboard": "^2.0.11",
import { defineConfig } from 'astro/config'
+import bootstrapLight from 'bootstrap-vscode-theme/themes/bootstrap-light.json'
+import bootstrapDark from 'bootstrap-vscode-theme/themes/bootstrap-dark.json'
+import { transformerNotationDiff } from '@shikijs/transformers'
import { bootstrap } from './src/libs/astro'
import { getConfig } from './src/libs/config'
syntaxHighlight: 'shiki',
shikiConfig: {
themes: {
- light: 'github-light',
- dark: 'github-dark'
- }
+ light: bootstrapLight,
+ dark: bootstrapDark
+ },
+ transformers: [
+ transformerNotationDiff(),
+ {
+ name: 'add-language-attribute',
+ pre(node) {
+ // Add data-language attribute to pre tag so Code component can access it
+ const lang = this.options.lang
+ if (lang) {
+ node.properties['dataLanguage'] = lang
+ }
+ }
+ }
+ ]
}
},
site,
import sidebarScroll from './partials/sidebar.js'
import snippets from './partials/snippets.js'
+import stickyNav from './partials/sticky.js'
sidebarScroll()
snippets()
+stickyNav()
--- /dev/null
+// NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT
+// IT'S ALL JUST JUNK FOR OUR DOCS!
+// ++++++++++++++++++++++++++++++++++++++++++
+
+/*
+ * JavaScript for Bootstrap's docs (https://getbootstrap.com/)
+ * Copyright 2011-2025 The Bootstrap Authors
+ * Licensed under the Creative Commons Attribution 3.0 Unported License.
+ * For details, see https://creativecommons.org/licenses/by/3.0/.
+ */
+
+export default () => {
+ const navbar = document.querySelector('.bd-sticky-navbar')
+
+ if (!navbar) {
+ return
+ }
+
+ const handleScroll = () => {
+ navbar.classList.toggle('is-stuck', window.scrollY > 0)
+ }
+
+ // Check initial state
+ handleScroll()
+
+ // Update on scroll
+ window.addEventListener('scroll', handleScroll, { passive: true })
+}
<li>
<a
href={url}
- class:list={['bd-links-link d-inline-block rounded', { active }]}
+ class:list={['bd-links-link', { active }]}
aria-current={active ? 'page' : undefined}
>
{item.title}
<li class="bd-links-span-all">
<a
href={`/docs/${getConfig().docs_version}/${groupSlug}/`}
- class:list={['bd-links-link d-inline-block rounded small', { active }]}
+ class:list={['bd-links-link', { active }]}
aria-current={active ? 'page' : undefined}
>
{group.title}
cssValue.replace(/var\((--[a-z0-9-]+)\)/gi, (match, varName) => {
const resolvedValue = cssVarValues[varName];
if (resolvedValue) {
- comments.push(`<span class="color-3">/* ${resolvedValue} */</span>`);
+ comments.push(`<span class="fg-3">/* ${resolvedValue} */</span>`);
}
return match;
});
cssValue.replace(/var\((--[a-z0-9-]+)\)/gi, (match, varName) => {
const resolvedValue = cssVarValues[varName];
if (resolvedValue) {
- comments.push(`<span class="color-3">/* ${resolvedValue} */</span>`);
+ comments.push(`<span class="fg-3">/* ${resolvedValue} */</span>`);
}
return match;
});
<link rel="preconnect" href=`https://${getConfig().algolia.app_id}-dsn.algolia.net` crossorigin />
+<link rel="preconnect" href="https://fonts.googleapis.com">
+<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+<link href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&display=swap" rel="stylesheet">
+
<title>{pageTitle}</title>
{robots && <meta name="robots" content={robots} />}
<script is:inline src={getVersionedDocsPath('assets/js/color-modes.js')}></script>
+<!-- CSS Anchor Positioning polyfill for Firefox/Safari -->
+<script is:inline type="module">
+ if (!CSS.supports('anchor-name', '--test')) {
+ import('https://unpkg.com/@oddbird/css-anchor-positioning/dist/css-anchor-positioning-fn.js')
+ .then(mod => mod.default())
+ }
+</script>
+
{import.meta.env.PROD && ScssProd && (
<Stylesheet direction={direction} layout={layout} />
<ScssProd.default />
const { addedIn, layout, title } = Astro.props
---
-<header class="navbar navbar-expand-lg bd-navbar sticky-top">
+<header class="navbar navbar-expand-lg bg-body bd-navbar border-bottom border-subtle sticky-top bd-sticky-navbar">
<nav class="container-2xl bd-gutter flex-wrap flex-lg-nowrap" aria-label="Main navigation">
{
layout === 'docs' && (
}
{layout !== 'docs' && <div class="d-lg-none" style="width: 4.25rem;" />}
- <a class="navbar-brand p-0 me-0 me-lg-2" href="/" aria-label="Bootstrap">
+ <a class="navbar-brand p-0 me-0 me-lg-2" href="/" aria-label="Bootstrap" style="color: var(--bs-indigo-500);">
<BootstrapWhiteFillIcon class="d-block my-1" height={32} width={40} />
</a>
<small class="d-lg-none ms-2">Open Collective</small>
</LinkItem>
<li class="nav-item py-2 py-lg-1 col-12 col-lg-auto">
- <div class="vr d-none d-lg-flex h-100 mx-lg-2 text-white"></div>
- <hr class="d-lg-none my-2 text-white-50" />
+ <div class="vr d-none d-lg-flex mx-lg-2 my-auto"></div>
+ <hr class="d-lg-none my-2" />
</li>
<Versions layout={layout} addedIn={addedIn} />
<li class="nav-item py-2 py-lg-1 col-12 col-lg-auto">
- <div class="vr d-none d-lg-flex h-100 mx-lg-2 text-white"></div>
- <hr class="d-lg-none my-2 text-white-50" />
+ <div class="vr d-none d-lg-flex mx-lg-2 my-auto"></div>
+ <hr class="d-lg-none my-2" />
</li>
<li class="nav-item dropdown">
type="button"
class="btn btn-link nav-link py-2 px-0 px-lg-2 dropdown-toggle"
data-bs-toggle="dropdown"
+ data-bs-target="#bd-versions-menu"
+ data-bs-placement="bottom-end"
aria-expanded="false"
- data-bs-display="static"
>
<span class="d-lg-none" aria-hidden="true">Bootstrap</span><span class="visually-hidden">Bootstrap </span> v{
getConfig().docs_version
}
<span class="visually-hidden">(switch to other versions)</span>
</button>
- <ul class="dropdown-menu dropdown-menu-end">
+ <ul class="dropdown-menu dropdown-menu-end" id="bd-versions-menu">
<li><h6 class="dropdown-header">v5 releases</h6></li>
<li>
<a
<section class="row g-3 g-md-5 mb-5 pb-5 justify-content-center">
<div class="col-lg-6 py-lg-4 pe-lg-5">
- <svg class="bi mb-2 fs-2 text-body-secondary" aria-hidden="true"><use xlink:href="#box-seam"></use></svg>
+ <svg class="bi mb-2 fs-2 fg-3" aria-hidden="true"><use xlink:href="#box-seam"></use></svg>
<h3 class="fw-semibold">Install via package manager</h3>
<p class="pe-lg-5">
Install Bootstrap’s source Sass and JavaScript files via npm, RubyGems, Composer, or Meteor. Package-managed
</p>
</div>
<div class="col-lg-6 py-lg-4 ps-lg-5 border-lg-start">
- <svg class="bi mb-2 fs-2 text-body-secondary" aria-hidden="true"><use xlink:href="#globe2"></use></svg>
+ <svg class="bi mb-2 fs-2 fg-3" aria-hidden="true"><use xlink:href="#globe2"></use></svg>
<h3 class="fw-semibold">Include via CDN</h3>
<p class="pe-lg-5">
When you only need to include Bootstrap’s compiled CSS or JS, you can use <a
<h4 class="fw-semibold">Read our getting started guides</h4>
<p>Get a jump on including Bootstrap's source files in a new project with our official guides.</p>
<div class="d-flex flex-wrap align-items-center justify-content-center gap-4 mt-4">
+ <a
+ class="d-flex flex-column align-items-center text-decoration-none animate-img"
+ href={getVersionedDocsPath('guides/npm/')}
+ >
+ <img
+ class="d-block mb-2"
+ src={getVersionedDocsPath('/assets/img/npm.svg')}
+ alt=""
+ width="72"
+ height="72"
+ loading="lazy"
+ />
+ <span class="fg-3">npm</span>
+ </a>
<a
class="d-flex flex-column align-items-center text-decoration-none animate-img"
href={getVersionedDocsPath('guides/webpack/')}
height="72"
loading="lazy"
/>
- <span class="text-body-secondary">Webpack</span>
+ <span class="fg-3">Webpack</span>
</a>
<a
class="d-flex flex-column align-items-center text-decoration-none animate-img"
height="72"
loading="lazy"
/>
- <span class="text-body-secondary">Parcel</span>
+ <span class="fg-3">Parcel</span>
</a>
<a
class="d-flex flex-column align-items-center text-decoration-none animate-img"
height="72"
loading="lazy"
/>
- <span class="text-body-secondary">Vite</span>
+ <span class="fg-3">Vite</span>
</a>
</div>
</div>
import fs from 'node:fs'
import path from 'node:path'
import { codeToHtml } from 'shiki'
+import { transformerNotationDiff } from '@shikijs/transformers'
+import bootstrapLight from 'bootstrap-vscode-theme/themes/bootstrap-light.json'
+import bootstrapDark from 'bootstrap-vscode-theme/themes/bootstrap-dark.json'
+import { replaceConfigInText } from '@libs/remark'
+
+interface Tab {
+ label: string
+ code: string
+ lang?: string
+}
interface Props {
+ /**
+ * If extra JS snippet should be added to StackBlitz or not.
+ * @default false
+ */
+ addStackblitzJs?: boolean
/**
* The CSS class(es) to be added to the `pre` HTML element when rendering code blocks in Markdown.
* Note that this prop is not used when the component is invoked directly.
* The CSS class(es) to be added to the `div` wrapper HTML element.
*/
containerClass?: string
+ /**
+ * The language attribute from Shiki/Astro markdown processing
+ */
+ 'data-language'?: string
/**
* The language to use for highlighting.
* @see https://prismjs.com/#supported-languages
* @default false
*/
nestedInExample?: boolean
+ /**
+ * If true, hides the highlight toolbar (language label and copy button).
+ * @default false
+ */
+ noToolbar?: boolean
+ /**
+ * Array of tabs to display a tabbed interface.
+ * When provided, this will render tabs instead of a single language label.
+ */
+ tabs?: Tab[]
}
-const { class: className, code, containerClass, fileMatch, filePath, lang, nestedInExample = false } = Astro.props
+const {
+ addStackblitzJs = false,
+ class: className,
+ code,
+ containerClass,
+ 'data-language': dataLanguage,
+ fileMatch,
+ filePath,
+ lang,
+ nestedInExample = false,
+ noToolbar = false,
+ tabs
+} = Astro.props
+
+// Extract language from multiple possible sources (for markdown code blocks)
+// Priority: lang prop > data-language attribute > className pattern
+let detectedLang = lang || dataLanguage
+if (!detectedLang && className) {
+ const langMatch = className.match(/language-(\w+)/)
+ if (langMatch) {
+ detectedLang = langMatch[1]
+ }
+}
+
+// Format language for display (e.g., "html" -> "HTML", "javascript" -> "JavaScript")
+const displayLang = detectedLang
+ ? detectedLang === 'js' ? 'JavaScript'
+ : detectedLang === 'ts' ? 'TypeScript'
+ : detectedLang === 'html' ? 'HTML'
+ : detectedLang === 'css' ? 'CSS'
+ : detectedLang === 'scss' ? 'SCSS'
+ : detectedLang === 'bash' || detectedLang === 'sh' ? 'Shell'
+ : detectedLang === 'powershell' ? 'PowerShell'
+ : detectedLang.toUpperCase()
+ : ''
let codeToDisplay = filePath
? fs.readFileSync(path.join(process.cwd(), filePath), 'utf8')
}
// Add line wrapper for shell languages to support shell prompts
-const shouldWrapLines = lang && ['bash', 'sh', 'powershell'].includes(lang)
+const shouldWrapLines = detectedLang && ['bash', 'sh', 'powershell'].includes(detectedLang)
+
+// Transformer to ensure class name is always 'astro-code' instead of 'shiki'
+const classTransformer = {
+ name: 'class-name-transformer',
+ pre(node: any) {
+ // Force replace all 'shiki' classes with 'astro-code'
+ const existingClasses = node.properties?.className || []
+ const newClasses = existingClasses.map((cls: any) => {
+ if (typeof cls === 'string') {
+ return cls.replace(/shiki/g, 'astro-code')
+ }
+ return cls
+ })
+ node.properties.className = newClasses
+ }
+}
+
+const lineWrapperTransformer = {
+ name: 'line-wrapper',
+ line(node: any) {
+ // Wrap non-comment lines in a span with .line class for shell prompt styling
+ const hasOnlyComments = node.children.every((child: any) =>
+ child.type === 'element' &&
+ child.properties?.class &&
+ Array.isArray(child.properties.class) &&
+ child.properties.class.some((cls: any) => typeof cls === 'string' && cls.includes('comment'))
+ )
+
+ if (!hasOnlyComments) {
+ node.properties = node.properties || {}
+ node.properties.class = node.properties.class
+ ? `${node.properties.class} line`
+ : 'line'
+ }
+ }
+}
+
+const transformers: any[] = [
+ transformerNotationDiff(), // Run diff transformer first, supports // [!code ++] and // [!code --] notation
+ classTransformer
+]
+if (shouldWrapLines) {
+ transformers.push(lineWrapperTransformer)
+}
+
+// Process tabs if provided
+let highlightedTabs: Array<{ label: string; code: string }> | null = null
+if (tabs && tabs.length > 0) {
+ highlightedTabs = await Promise.all(
+ tabs.map(async (tab) => {
+ // Replace config placeholders in the code
+ const processedCode = replaceConfigInText(tab.code)
+
+ const tabLang = tab.lang || detectedLang || 'bash'
+ const shouldWrapTabLines = ['bash', 'sh', 'powershell'].includes(tabLang)
+
+ const tabTransformers: any[] = [
+ transformerNotationDiff(),
+ classTransformer
+ ]
+ if (shouldWrapTabLines) {
+ tabTransformers.push(lineWrapperTransformer)
+ }
-const highlightedCode = codeToDisplay && lang
+ let tabHighlighted = await codeToHtml(processedCode, {
+ lang: tabLang,
+ themes: {
+ light: bootstrapLight,
+ dark: bootstrapDark
+ },
+ transformers: tabTransformers
+ })
+
+ // Replace 'shiki' with 'astro-code' in the generated HTML
+ tabHighlighted = tabHighlighted.replace(/class=(["'])shiki(\s+)/g, 'class=$1astro-code$2')
+ tabHighlighted = tabHighlighted.replace(/class=(["'])shiki(["'])/g, 'class=$1astro-code$2')
+ tabHighlighted = tabHighlighted.replace(/shiki-themes/g, 'astro-code-themes')
+
+ return {
+ label: tab.label,
+ code: tabHighlighted
+ }
+ })
+ )
+}
+
+let highlightedCode = codeToDisplay && detectedLang
? await codeToHtml(codeToDisplay, {
- lang,
+ lang: detectedLang,
themes: {
- light: 'github-light',
- dark: 'github-dark'
+ light: bootstrapLight,
+ dark: bootstrapDark
},
- transformers: shouldWrapLines ? [
- {
- name: 'line-wrapper',
- line(node) {
- // Wrap non-comment lines in a span with .line class for shell prompt styling
- const hasOnlyComments = node.children.every(child =>
- child.type === 'element' &&
- child.properties?.class &&
- Array.isArray(child.properties.class) &&
- child.properties.class.some((cls) => typeof cls === 'string' && cls.includes('comment'))
- )
-
- if (!hasOnlyComments) {
- node.properties = node.properties || {}
- node.properties.class = node.properties.class
- ? `${node.properties.class} line`
- : 'line'
- }
- }
- }
- ] : []
+ transformers
})
: null
+
+// Replace 'shiki' with 'astro-code' in the generated HTML
+if (highlightedCode) {
+ // Replace class="shiki" or class='shiki' (preserving other classes like has-diff)
+ highlightedCode = highlightedCode.replace(/class=(["'])shiki(\s+)/g, 'class=$1astro-code$2')
+ highlightedCode = highlightedCode.replace(/class=(["'])shiki(["'])/g, 'class=$1astro-code$2')
+ // Replace shiki-themes if it exists
+ highlightedCode = highlightedCode.replace(/shiki-themes/g, 'astro-code-themes')
+}
---
<script>
snippetButtonTooltip('.btn-clipboard', btnTitle)
snippetButtonTooltip('.btn-edit', btnEdit)
+ // Handle tab switching
+ document.querySelectorAll('.code-tabs').forEach((tabContainer) => {
+ const buttons = tabContainer.querySelectorAll('.code-tab-btn')
+ // Find the parent container that holds both tabs and content
+ // Could be .bd-code-snippet or .bd-example-snippet
+ const parentContainer = tabContainer.closest('.bd-code-snippet') ||
+ tabContainer.closest('.bd-example-snippet') ||
+ tabContainer.parentElement?.parentElement
+ const codeBlocks = parentContainer?.querySelectorAll('.code-tab-content')
+
+ buttons.forEach((button, index) => {
+ button.addEventListener('click', () => {
+ // Remove active class from all buttons and hide all code blocks
+ buttons.forEach((btn) => btn.classList.remove('active'))
+ codeBlocks?.forEach((block) => block.classList.remove('active'))
+
+ // Add active class to clicked button and show corresponding code block
+ button.classList.add('active')
+ codeBlocks?.[index]?.classList.add('active')
+ })
+ })
+ })
+
const clipboard = new ClipboardJS('.btn-clipboard', {
target: (trigger) => trigger.closest('.bd-code-snippet')?.querySelector('.highlight')!,
text: (trigger) => {
+ // For tabbed code, find the active tab's code
+ const snippet = trigger.closest('.bd-code-snippet')
+ const activeTab = snippet?.querySelector('.code-tab-content.active .astro-code')
+ if (activeTab) {
+ return activeTab.textContent?.trim()!
+ }
// Trim text to workaround a Firefox issue where the structure of the DOM (uncontrolled) is relevant for the
// copied text.
// https://github.com/zenorocha/clipboard.js/issues/439#issuecomment-312344621
- return trigger.closest('.bd-code-snippet')?.querySelector('.highlight')!.textContent?.trim()!
+ return snippet?.querySelector('.astro-code')!.textContent?.trim()!
}
})
})
</script>
-<div class:list={[{ 'bd-code-snippet': !nestedInExample }, containerClass]}>
- {
- nestedInExample
- ? (<></>)
- : Astro.slots.has('pre')
- ? (
- <slot name="pre" />
- )
- : (
- <div class="bd-clipboard">
- <button type="button" class="btn-clipboard">
- <svg class="bi" role="img" aria-label="Copy">
- <use xlink:href="#clipboard" />
+{nestedInExample ? (
+ <>
+ {!noToolbar && (
+ <div class="hstack highlight-toolbar">
+ {highlightedTabs ? (
+ <div class="code-tabs">
+ {highlightedTabs.map((tab, index) => (
+ <button
+ type="button"
+ class:list={['code-tab-btn', { active: index === 0 }]}
+ >
+ {tab.label}
+ </button>
+ ))}
+ </div>
+ ) : (
+ <div class="font-monospace text-uppercase fs-xs fg-3">{displayLang}</div>
+ )}
+ <div class="d-flex ms-auto">
+ {addStackblitzJs && (
+ <button type="button" class="btn-edit text-nowrap" title="Try it on StackBlitz">
+ <svg class="bi" aria-hidden="true">
+ <use xlink:href="#lightning-charge-fill" />
</svg>
</button>
+ )}
+ <button type="button" class="btn-clipboard mt-0 me-0" title="Copy to clipboard">
+ <svg class="bi" aria-hidden="true">
+ <use xlink:href="#clipboard" />
+ </svg>
+ </button>
+ </div>
+ </div>
+ )}
+ {highlightedTabs ? (
+ <>
+ {highlightedTabs.map((tab, index) => (
+ <div class:list={['code-tab-content', { active: index === 0 }]}>
+ <Fragment set:html={tab.code} />
</div>
- )
- }
- <div class="highlight">
- {
- highlightedCode ? (
- <Fragment set:html={highlightedCode} />
- ) : (
- /* prettier-ignore */ <pre class={className}><slot /></pre>
- )
- }
+ ))}
+ </>
+ ) : highlightedCode ? (
+ <Fragment set:html={highlightedCode} />
+ ) : (
+ /* prettier-ignore */ <pre class={className}><slot /></pre>
+ )}
+ </>
+) : (
+ <div class="bd-code-snippet">
+ {!noToolbar && (
+ <div class="hstack highlight-toolbar">
+ {highlightedTabs ? (
+ <div class="code-tabs">
+ {highlightedTabs.map((tab, index) => (
+ <button
+ type="button"
+ class:list={['code-tab-btn', { active: index === 0 }]}
+ >
+ {tab.label}
+ </button>
+ ))}
+ </div>
+ ) : (
+ <div class="font-monospace text-uppercase fs-xs fg-3">{displayLang}</div>
+ )}
+ <div class="d-flex ms-auto">
+ <button type="button" class="btn-clipboard mt-0 me-0" title="Copy to clipboard">
+ <svg class="bi" aria-hidden="true">
+ <use xlink:href="#clipboard" />
+ </svg>
+ </button>
+ </div>
+ </div>
+ )}
+
+ {highlightedTabs ? (
+ <>
+ {highlightedTabs.map((tab, index) => (
+ <div class:list={['code-tab-content', { active: index === 0 }]}>
+ <Fragment set:html={tab.code} />
+ </div>
+ ))}
+ </>
+ ) : highlightedCode ? (
+ <Fragment set:html={highlightedCode} />
+ ) : (
+ /* prettier-ignore */ <pre class={className}><slot /></pre>
+ )}
</div>
-</div>
+)}
---
<div class="bd-example-snippet bd-code-snippet">
- {
- showPreview && (
- <div id={id} class:list={['bd-example m-0 border-0', className]}>
- <Fragment set:html={markup} />
- </div>
- )
- }
-
- {
- showMarkup && (
- <>
- {showPreview && (
- <div class="d-flex align-items-center highlight-toolbar ps-3 pe-2 py-1 border-0 border-top border-bottom">
- <small class="font-monospace text-body-secondary text-uppercase">{lang}</small>
- <div class="d-flex ms-auto">
- <button
- type="button"
- class="btn-edit text-nowrap"
- title="Try it on StackBlitz"
- data-sb-js-snippet={addStackblitzJs ? true : undefined}
- >
- <svg class="bi" aria-hidden="true">
- <use xlink:href="#lightning-charge-fill" />
- </svg>
- </button>
- <button type="button" class="btn-clipboard mt-0 me-0" title="Copy to clipboard">
- <svg class="bi" aria-hidden="true">
- <use xlink:href="#clipboard" />
- </svg>
- </button>
- </div>
- </div>
- )}
- <Code code={simplifiedMarkup} lang={lang} nestedInExample={true} />
- </>
- )
- }
+ {showPreview && (
+ <div id={id} class:list={['bd-example', className]}>
+ <Fragment set:html={markup} />
+ </div>
+ )}
+ {showMarkup && (
+ <Code code={simplifiedMarkup} lang={lang} nestedInExample={true} addStackblitzJs={addStackblitzJs} />
+ )}
</div>
/* CSS variable display styles */
.css-var {
margin-left: 0.25rem;
- color: var(--bs-color-3);
+ color: var(--bs-fg-3);
}
.css-var-light {
<div class="bd-subtitle">
{frontmatter.description && <Fragment set:html={processMarkdownToHtml(frontmatter.description)} />}
</div>
- <div class="mb-3 mb-md-0 d-flex text-nowrap">
+ <div class="mb-3 mb-md-0 d-flex gap-2 text-nowrap">
{
frontmatter.added &&
((frontmatter.added.show_badge !== undefined && frontmatter.added.show_badge === true) ||
rel="noopener"
>
<GitHubIcon height={16} width={16} class="bi" />
- View on GitHub
+ Edit on GitHub
</a>
{
frontmatter.mdn && (
<a
- class="btn btn-secondary-text btn-sm"
+ class="btn btn-subtle btn-sm theme-secondary"
href={frontmatter.mdn}
title="View on MDN"
target="_blank"
<Ads />
</div>
- <div class="bd-content ps-lg-2">
+ <div class="bd-content prose ps-lg-2">
{
frontmatter.sections && (
<div class="grid grid-cols-3 mb-5">
{frontmatter.sections.map((section) => (
<a
- class="d-block text-decoration-none"
+ class="d-block text-decoration-none bg-1 hover-bg-2 rounded-3 p-3"
href={getVersionedDocsPath(
`${parentDirectory ? parentDirectory + '/' : ''}${getSlug(section.title)}/`
)}
>
- <strong class="d-block h5 mb-0">{section.title}</strong>
- <span class="text-secondary">{section.description}</span>
+ <strong class="d-block fs-5 mb-0">{section.title}</strong>
+ <span class="fs-6 fg-3">{section.description}</span>
</a>
))}
</div>
type="button"
aria-expanded="false"
data-bs-toggle="dropdown"
- {...layout !== 'examples' ? { 'data-bs-display': 'static' } : {}}
+ data-bs-target="#bd-theme-menu"
+ data-bs-placement="bottom-end"
aria-label="Toggle theme (auto)"
>
<svg class="bi my-1 theme-icon-active" aria-hidden="true"><use href="#circle-half"></use></svg>
<span class=`${layout === 'examples' ? 'visually-hidden' : 'd-lg-none ms-2'}` id="bd-theme-text">Toggle theme</span>
</button>
-<ul class=`dropdown-menu dropdown-menu-end${layout === 'examples' ? ' shadow' : ''}` aria-labelledby="bd-theme-text">
+<ul class="dropdown-menu" id="bd-theme-menu" aria-labelledby="bd-theme-text" style="--bs-dropdown-min-width: 8rem;">
<li>
<button
type="button"
- class="dropdown-item d-flex align-items-center"
+ class="dropdown-item"
data-bs-theme-value="light"
aria-pressed="false"
>
- <svg class="bi me-2 opacity-50" aria-hidden="true"><use href="#sun-fill"></use></svg>
+ <svg class="bi opacity-50" aria-hidden="true"><use href="#sun-fill"></use></svg>
Light
<svg class="bi ms-auto d-none" aria-hidden="true"><use href="#check2"></use></svg>
</button>
<li>
<button
type="button"
- class="dropdown-item d-flex align-items-center"
+ class="dropdown-item"
data-bs-theme-value="dark"
aria-pressed="false"
>
- <svg class="bi me-2 opacity-50" aria-hidden="true"><use href="#moon-stars-fill"></use></svg>
+ <svg class="bi opacity-50" aria-hidden="true"><use href="#moon-stars-fill"></use></svg>
Dark
<svg class="bi ms-auto d-none" aria-hidden="true"><use href="#check2"></use></svg>
</button>
<li>
<button
type="button"
- class="dropdown-item d-flex align-items-center active"
+ class="dropdown-item active"
data-bs-theme-value="auto"
aria-pressed="true"
>
- <svg class="bi me-2 opacity-50" aria-hidden="true"><use href="#circle-half"></use></svg>
+ <svg class="bi opacity-50" aria-hidden="true"><use href="#circle-half"></use></svg>
Auto
<svg class="bi ms-auto d-none" aria-hidden="true"><use href="#check2"></use></svg>
</button>
margin-top: -.25rem;
}
- .highlight {
- background-color: rgba($black, .05);
- }
+ // .highlight {
+ // background-color: rgba($black, .05);
+ // }
}
// Variations
display: none;
float: inline-end;
- + .highlight {
- margin-top: 0;
- }
+ // + .highlight {
+ // margin-top: 0;
+ // }
@include media-breakpoint-up(md) {
display: block;
display: block;
padding: .5em;
line-height: 1;
- color: var(--bs-body-color);
- background-color: var(--bd-pre-bg);
+ color: var(--bs-fg-3);
+ background-color: var(--bd-bg-1);
border: 0;
@include border-radius(.25rem);
&:hover {
- color: var(--bs-link-hover-color);
+ color: var(--bs-fg-body);
}
&:focus {
@use "../../../scss/vendor/rfs" as *;
@use "../../../scss/layout/breakpoints" as *;
@use "../../../scss/mixins/border-radius" as *;
+@use "../../../scss/mixins/box-shadow" as *;
@use "variables" as *;
//
// Docs examples
//
+// .bd-code-snippet
+// .bd-example
+// .highlight-toolbar
+// pre.astro-code
+
+// .bd-code-snippet
+// .highlight-toolbar
+// pre.astro-code
+
@layer custom {
.bd-code-snippet {
+ --bd-example-padding: 1.25rem;
+ --bd-example-inner-radius: calc(var(--bs-border-radius) - 1px);
+
margin: 0 ($bd-gutter-x * -.5) 1rem;
+ font-size: var(--#{$prefix}font-size-sm);
+ background-color: var(--bd-pre-bg);
border: solid var(--bs-border-color);
border-width: 1px 0;
border-width: 1px;
@include border-radius(var(--bs-border-radius));
}
+
+ .bd-example {
+ &:first-child {
+ @include border-top-radius(var(--bd-example-inner-radius));
+ }
+ &:last-child {
+ @include border-bottom-radius(var(--bd-example-inner-radius));
+ }
+ }
}
- .bd-example {
- --bd-example-padding: 1rem;
+ .highlight-toolbar {
+ padding-block: .375rem;
+ padding-inline-start: var(--bd-example-padding);
+ padding-inline-end: calc(var(--bd-example-padding) - .5em);
+ background-color: var(--bs-bg-1);
+ border-bottom: 1px solid var(--bs-border-color);
+ // @include border-top-radius(calc(var(--bs-border-radius) - 1px));
+
+ @include media-breakpoint-up(md) {
+ &:first-child {
+ @include border-top-radius(calc(var(--bs-border-radius) - 1px));
+ }
+
+ &:not(:first-child) {
+ border-top: 1px solid var(--bs-border-color);
+ }
+ }
+ }
+ .bd-example {
position: relative;
display: flow-root;
padding: var(--bd-example-padding);
- margin: 0 ($bd-gutter-x * -.5) 1rem;
- border: solid var(--bs-border-color);
- border-width: 1px 0;
+ font-size: var(--#{$prefix}font-size-base);
+ // margin: 0 ($bd-gutter-x * -.5) 1rem;
+ background-color: var(--bs-bg-body);
@include media-breakpoint-up(md) {
--bd-example-padding: 1.5rem;
margin-inline: 0;
- border-width: 1px;
- @include border-radius(var(--bs-border-radius));
+ // border-width: 1px;
+ // @include border-radius(var(--bs-border-radius));
}
+ p {
}
> .dropdown-menu {
- position: static;
- display: block;
+ max-width: 12rem;
+ // position: static;
+ // display: block;
}
> :last-child,
// Navbars
.fixed-top,
.sticky-top {
- position: static;
+ position: static !important; // stylelint-disable-line
margin: calc(-1 * var(--bd-example-padding)) calc(-1 * var(--bd-example-padding)) var(--bd-example-padding);
}
.fixed-bottom,
.sticky-bottom {
- position: static;
+ position: static !important; // stylelint-disable-line
margin: var(--bd-example-padding) calc(-1 * var(--bd-example-padding)) calc(-1 * var(--bd-example-padding));
}
// Code snippets
//
- .highlight {
+ .astro-code {
position: relative;
- padding: .75rem ($bd-gutter-x * .5);
background-color: var(--bd-pre-bg);
@include media-breakpoint-up(md) {
- padding: 1rem;
@include border-radius(calc(var(--bs-border-radius) - 1px));
}
-
- @include media-breakpoint-up(lg) {
- pre {
- margin-inline-end: 1.875rem;
- }
- }
-
- pre {
- // padding: .25rem 0 .875rem;
- // margin-top: .8125rem;
- margin-bottom: 0;
- overflow: overlay;
- white-space: pre;
- background-color: transparent;
- border: 0;
- }
-
- pre code {
- @include font-size(inherit);
- color: var(--bs-body-color); // Effectively the base text color
- word-wrap: normal;
- }
}
.bd-example-snippet .highlight pre {
margin-inline-end: 0;
}
- .highlight-toolbar {
- background-color: var(--bd-pre-bg);
-
- + .highlight {
- @include border-top-radius(0);
- }
- }
-
- .bd-file-ref {
- .highlight-toolbar {
- @include media-breakpoint-up(md) {
- @include border-top-radius(calc(var(--bs-border-radius) - 1px));
- }
- }
- }
-
- .bd-content .bd-code-snippet {
- margin-bottom: 1rem;
- }
}
@use "sass:color";
@use "../../../scss/colors" as *;
+@use "../../../scss/config" as *;
@use "../../../scss/variables" as *;
@use "../../../scss/vendor/rfs" as *;
@use "../../../scss/layout/breakpoints" as *;
@layer custom {
.bd-content {
+ @media (width >= 1024px) {
+ font-size: var(--#{$prefix}font-size-md);
+ }
+
+ > p {
+ margin-bottom: 1.25rem;
+ }
+
> h2,
> h3,
> h4 {
overflow-y: auto;
font-size: .75rem;
- thead th {
- border-block-end-color: currentcolor;
- }
+ // thead th {
+ // border-block-end-color: currentcolor;
+ // }
th,
td {
.bd-title {
--bs-heading-color: var(--bs-fg);
- @include font-size(3rem);
+ // @include font-size(3rem);
}
.bd-subtitle {
@use "../../../scss/layout/breakpoints" as *;
@use "../../../scss/vendor/rfs" as *;
+:root {
+ --bs-font-sans-serif: "Geist", sans-serif;
+ --bs-font-monospace: "Geist Mono", monospace;
+}
+
@layer custom {
+ .bd-sticky-navbar.is-stuck {
+ box-shadow: 0 .125rem .5rem rgba($black, .05);
+ }
+
.bd-navbar {
padding: .75rem 0;
- background-color: transparent;
- box-shadow: 0 .5rem 1rem rgba($black, .15), inset 0 -1px 0 rgba($white, .15);
-
- @media (forced-colors) {
- background-color: Canvas;
- }
-
- &::after {
- position: absolute;
- inset: 0;
- z-index: -1;
- display: block;
- content: "";
- background-image: linear-gradient(var(--bd-violet), color-mix(in srgb, var(--bd-violet), transparent 5%));
- // background-image: linear-gradient(color-mix(in srgb, var(--bs-gray-900), $black 10%), color-mix(in srgb, var(--bs-gray-900), $black 15%));
- }
+ // background-color: transparent;
+ // box-shadow: 0 .5rem 1rem rgba($black, .15), inset 0 -1px 0 rgba($white, .15);
+
+ // @media (forced-colors) {
+ // background-color: Canvas;
+ // }
+
+ // &::after {
+ // position: absolute;
+ // inset: 0;
+ // z-index: -1;
+ // display: block;
+ // content: "";
+ // background-image: linear-gradient(var(--bd-violet), color-mix(in srgb, var(--bd-violet), transparent 5%));
+ // // background-image: linear-gradient(color-mix(in srgb, var(--bs-gray-900), $black 10%), color-mix(in srgb, var(--bs-gray-900), $black 15%));
+ // }
.bd-navbar-toggle {
@include media-breakpoint-down(lg) {
}
.navbar-brand {
- color: $white;
+ // color: $white;
@include transition(transform .2s ease-in-out);
&:hover {
.navbar-toggler,
.nav-link {
padding-inline: $spacer * .25;
- color: rgba($white, .85);
+ // color: rgba($white, .85);
- &:hover,
- &:focus {
- color: $white;
- }
+ // &:hover,
+ // &:focus {
+ // color: $white;
+ // }
&.active {
font-weight: 600;
- color: $white;
+ // color: $white;
}
}
--bs-dropdown-padding-y: .25rem;
--bs-dropdown-link-hover-bg: color-mix(in srgb, var(--bd-violet), transparent 90%);
--bs-dropdown-link-active-bg: var(--bd-violet);
- @include rfs(.875rem, --bs-dropdown-font-size);
- @include font-size(.875rem);
- @include border-radius(.5rem);
+ // @include rfs(.875rem, --bs-dropdown-font-size);
+ // @include font-size(.875rem);
+ // @include border-radius(.5rem);
box-shadow: var(--#{$prefix}dropdown-box-shadow);
li + li {
@use "../../../scss/layout/breakpoints" as *;
@use "../../../scss/mixins/color-mode" as *;
@use "../../../scss/mixins/border-radius" as *;
+@use "../../../scss/mixins/focus-ring" as *;
// stylelint-disable selector-class-pattern
:root {
- --docsearch-primary-color: var(--bd-violet);
- --docsearch-logo-color: var(--bd-violet);
+ --docsearch-primary-color: var(--bs-indigo-500);
+ --docsearch-logo-color: var(--bs-indigo-500);
}
@include color-mode(dark, true) {
// From here, the values are copied from https://cdn.jsdelivr.net/npm/@docsearch/css@3
// in html[data-theme="dark"] selector
// and are slightly modified for formatting purpose
- --docsearch-text-color: #f5f6f7;
+ // --docsearch-text-color: #f5f6f7;
--docsearch-container-background: rgba(9, 10, 17, .8);
--docsearch-modal-background: #15172a;
--docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;
- --docsearch-searchbox-background: #090a11;
- --docsearch-searchbox-focus-background: #000;
+ // --docsearch-searchbox-background: #090a11;
+ // --docsearch-searchbox-focus-background: #000;
--docsearch-hit-color: #bec3c9;
--docsearch-hit-shadow: none;
--docsearch-hit-background: #090a11;
--docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, .3);
--docsearch-footer-background: #1e2136;
--docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, .5), 0 -4px 8px 0 rgba(0, 0, 0, .2);
- --docsearch-muted-color: #7f8497;
+ // --docsearch-muted-color: #7f8497;
}
-@layer custom {
- .bd-search {
- position: relative;
+.bd-search {
+ position: relative;
- @include media-breakpoint-up(lg) {
- position: absolute;
- top: .875rem;
- left: 50%;
- width: 200px;
- margin-inline-start: -100px;
- }
-
- @include media-breakpoint-up(xl) {
- width: 280px;
- margin-inline-start: -140px;
- }
+ @include media-breakpoint-up(lg) {
+ position: absolute;
+ top: .875rem;
+ left: 50%;
+ width: 200px;
+ margin-inline-start: -100px;
+ }
+ @include media-breakpoint-up(xl) {
+ width: 280px;
+ margin-inline-start: -140px;
}
- .DocSearch-Container {
- --docsearch-muted-color: var(--bs-secondary-color);
- --docsearch-hit-shadow: none;
+}
- position: fixed;
- z-index: 2000; // Make sure to be over all components showcased in the documentation
- cursor: auto; // Needed because of [role="button"] in Algolia search modal. Remove once https://github.com/algolia/docsearch/issues/1370 is tackled.
+.DocSearch-Container {
+ --docsearch-muted-color: var(--bs-fg-3);
+ --docsearch-hit-shadow: none;
- @include media-breakpoint-up(lg) {
- padding-block-start: 4rem;
- }
+ position: fixed;
+ z-index: 2000; // Make sure to be over all components showcased in the documentation
+ cursor: auto; // Needed because of [role="button"] in Algolia search modal. Remove once https://github.com/algolia/docsearch/issues/1370 is tackled.
+
+ @include media-breakpoint-up(lg) {
+ padding-block-start: 4rem;
+ }
+}
+
+.DocSearch-Button {
+ --docsearch-searchbox-background: var(--bs-bg-2);
+ --docsearch-searchbox-color: var(--bs-color-body);
+ --docsearch-searchbox-focus-background: var(--bs-bg-1);
+ --docsearch-searchbox-shadow: none;
+ --docsearch-text-color: var(--bs-color-body);
+ --docsearch-muted-color: var(--bs-fg-4);
+
+ width: 100%;
+ height: 36px;
+ padding-inline: 10px;
+ margin-left: 0;
+ border: 1px solid var(--bs-border-subtle);
+ @include border-radius(var(--bs-border-radius));
+
+ .DocSearch-Search-Icon {
+ width: 16px;
+ height: 16px;
+ opacity: .5;
}
- .DocSearch-Button {
- --docsearch-searchbox-background: #{rgba($black, .1)};
- --docsearch-searchbox-color: #{$white};
- --docsearch-searchbox-focus-background: #{rgba($black, .25)};
- --docsearch-searchbox-shadow: #{0 0 0 .25rem rgba($bd-accent, .4)};
- --docsearch-text-color: #{$white};
- --docsearch-muted-color: #{rgba($white, .65)};
+ &:active,
+ &:focus,
+ &:hover {
+ // @include focus-ring(true);
+ // --bs-focus-ring-offset: -1px;
- width: 100%;
- height: 38px; // Match Bootstrap inputs
- margin: 0;
- border: 1px solid rgba($white, .4);
- @include border-radius(.375rem);
+ // border-color: rgba($bd-accent, 1);
.DocSearch-Search-Icon {
- opacity: .65;
+ opacity: 1;
}
+ }
- &:active,
- &:focus,
- &:hover {
- border-color: rgba($bd-accent, 1);
-
- .DocSearch-Search-Icon {
- opacity: 1;
- }
- }
+ // @include media-breakpoint-down(lg) {
+ // &,
+ // &:hover,
+ // &:focus {
+ // background: transparent;
+ // border: 0;
+ // box-shadow: none;
+ // }
+ // &:focus {
+ // box-shadow: var(--docsearch-searchbox-shadow);
+ // }
+ // }
+}
- @include media-breakpoint-down(lg) {
- &,
- &:hover,
- &:focus {
- background: transparent;
- border: 0;
- box-shadow: none;
- }
- &:focus {
- box-shadow: var(--docsearch-searchbox-shadow);
- }
- }
+.DocSearch-Button-Keys,
+.DocSearch-Button-Placeholder {
+ @include media-breakpoint-down(lg) {
+ display: none;
}
+}
- .DocSearch-Button-Keys,
- .DocSearch-Button-Placeholder {
- @include media-breakpoint-down(lg) {
- display: none;
- }
- }
+.DocSearch-Button-Placeholder {
+ font-size: 14px;
+}
- .DocSearch-Button-Keys {
- min-width: 0;
- padding: .125rem .25rem;
- background: rgba($black, .25);
- @include border-radius(.25rem);
- }
+.DocSearch-Button-Keys {
+ display: flex;
+ gap: 2px;
+ align-items: center;
+ min-width: 0;
+ margin-top: .25rem;
+}
- .DocSearch-Button-Key {
- top: 0;
- width: auto;
- height: 1.25rem;
- padding-inline: .125rem;
- margin-inline-end: 0;
- font-size: .875rem;
- background: none;
- box-shadow: none;
- }
+.DocSearch-Button-Key {
+ position: static;
+ top: 0;
+ width: auto;
+ margin: 0;
+ font-size: var(--bs-font-size-sm);
+ background: none;
+ border-radius: 0; // stylelint-disable-line
+ box-shadow: none;
+}
- .DocSearch-Commands-Key {
- padding-inline-start: 1px;
- font-size: .875rem;
- background-color: rgba($black, .1);
- background-image: none;
- box-shadow: none;
- }
+.DocSearch-Commands-Key {
+ background-image: none;
+ box-shadow: none;
+}
- .DocSearch-Form {
- @include border-radius(var(--bs-border-radius));
- }
+.DocSearch-Form {
+ @include border-radius(var(--bs-border-radius));
+}
- .DocSearch-Hits {
- mark {
- padding: 0;
- }
+.DocSearch-Hits {
+ mark {
+ padding: 0;
}
+}
- .DocSearch-Hit {
- padding-block-end: 0;
- @include border-radius(0);
-
- a {
- @include border-radius(0);
- border: solid var(--bs-border-color);
- border-width: 0 1px 1px;
- }
+.DocSearch-Hit {
+ padding-block-end: 0;
+ @include border-radius(0);
- &:first-child a {
- @include border-top-radius(var(--bs-border-radius));
- border-top-width: 1px;
- }
- &:last-child a {
- @include border-bottom-radius(var(--bs-border-radius));
- }
+ a {
+ @include border-radius(0);
+ border: solid var(--bs-border-color);
+ border-width: 0 1px 1px;
}
- .DocSearch-Hit-icon {
- display: flex;
- align-items: center;
+ &:first-child a {
+ @include border-top-radius(var(--bs-border-radius));
+ border-top-width: 1px;
}
-
- // Fix --docsearch-logo-color that doesn't do anything
- .DocSearch-Logo svg .cls-1,
- .DocSearch-Logo svg .cls-2 {
- fill: var(--docsearch-logo-color);
+ &:last-child a {
+ @include border-bottom-radius(var(--bs-border-radius));
}
}
+
+.DocSearch-Hit-icon {
+ display: flex;
+ align-items: center;
+}
+
+// Fix --docsearch-logo-color that doesn't do anything
+.DocSearch-Logo svg .cls-1,
+.DocSearch-Logo svg .cls-2 {
+ fill: var(--docsearch-logo-color);
+}
@use "../../../scss/variables" as *;
@use "../../../scss/layout/breakpoints" as *;
@use "../../../scss/mixins/border-radius" as *;
+@use "../../../scss/mixins/focus-ring" as *;
@layer custom {
.bd-sidebar {
}
.bd-links-heading {
+ gap: .25rem;
color: var(--bs-emphasis-color);
}
.bd-links-heading .bi {
}
.bd-links-subgroup {
- margin-inline-start: 1.5rem;
+ margin-inline-start: 1.75rem;
color: var(--bs-emphasis-color);
}
.bd-links-nav {
- // @include media-breakpoint-down(lg) {
- // font-size: .875rem;
- // }
-
@include media-breakpoint-between(xs, lg) {
column-count: 2;
column-gap: 1.5rem;
}
.bd-links-link {
- padding: .1875rem .5rem;
+ display: block;
+ padding: .25rem .75rem;
margin-inline-start: 1rem;
margin-top: .125rem;
font-size: .875rem;
color: var(--bs-body-color);
text-decoration: none;
+ @include border-radius(var(--bs-border-radius));
- &:hover,
- &:focus,
- &.active {
- color: var(--bs-emphasis-color);
- background-color: var(--bd-sidebar-link-bg);
+ &:hover {
+ background-color: var(--bs-bg-1);
+ }
+
+ &:focus-visible {
+ @include focus-ring();
}
&.active {
- font-weight: 600;
+ font-weight: 500;
+ color: var(--bs-emphasis-color);
+ background-color: var(--bs-bg-2);
}
}
}
@use "../../../scss/config" as *;
@use "../../../scss/colors" as *;
@use "../../../scss/variables" as *;
+@use "../../../scss/mixins/border-radius" as *;
@use "../../../scss/mixins/color-mode" as *;
+@use "../../../scss/mixins/transition" as *;
// Shell prompt colors for light mode
:root,
--shell-prompt-color: #565c64;
}
-// Shell prompt colors for dark mode
-@include color-mode(dark, true) {
- --shell-prompt-color: #868e96;
+.astro-code {
+ display: flex;
+ padding: var(--bd-example-padding);
+ margin-bottom: 0;
+ line-height: 20px;
+ --bs-font-monospace: "Geist Mono";
+ background-color: var(--bd-pre-bg) !important; // stylelint-disable-line declaration-no-important
}
-.shiki {
- background-color: transparent !important; // stylelint-disable-line declaration-no-important
-}
+// Dark mode theming for Shiki
+// When using dual themes, Shiki generates inline styles with CSS variables
+// We need to switch from the light theme vars to dark theme vars
+[data-bs-theme="dark"] {
+ --shell-prompt-color: #868e96;
-// Shiki dual-theme support
-// Light mode: use default inline styles (no override needed)
-// Dark mode: override to use the dark theme CSS variables
-@include color-mode(dark, true) {
- .shiki,
- .shiki span {
+ .astro-code span,
+ .astro-code-themes span {
+ // Override Shiki's inline color with the dark theme color
color: var(--shiki-dark) !important; // stylelint-disable-line declaration-no-important
}
}
user-select: none;
}
}
+
+// Code tabs
+.code-tabs {
+ display: flex;
+ gap: .75rem;
+}
+
+.code-tab-btn {
+ position: relative;
+ padding: .25rem 0;
+ font-size: var(--bs-font-size-xs);
+ color: var(--bs-fg-3);
+ background: transparent;
+ border: 0;
+ @include border-radius(var(--bs-border-radius-sm));
+ @include transition(color .15s ease-in-out, background-color .15s ease-in-out);
+
+ &:hover {
+ color: var(--bs-fg-2);
+ }
+
+ &.active {
+ font-weight: 500;
+ color: var(--bs-fg-body);
+
+ &::after {
+ position: absolute;
+ bottom: -.5rem;
+ left: 0;
+ width: 100%;
+ height: 2px;
+ content: "";
+ background-color: var(--bs-border-emphasized);
+ }
+ }
+}
+
+.code-tab-content {
+ display: none;
+
+ &.active {
+ display: block;
+ }
+}
--bd-sidebar-link-bg: light-dark(color-mix(in srgb, var(--bd-violet), transparent 90%), color-mix(in srgb, var(--bd-violet), transparent 70%));
--bd-callout-link: var(--#{$prefix}blue-600);
--bd-callout-code-color: light-dark(var(--bs-pink-600), var(--bs-pink-300));
- --bd-pre-bg: light-dark(var(--bs-gray-025), color-mix(in srgb, var(--bs-gray-900), var(--bs-black) 25%));
+ --bd-pre-bg: var(--bs-bg-body);
--bd-swatch-shadow: inset 0 0 0 1px light-dark(rgb(0 0 0 / .1), rgb(255 255 255 / .1));
}