'site/src/assets/application.js',
'site/src/assets/partials/*.js',
'site/src/assets/search.js',
- 'site/src/assets/snippets.js',
'site/src/assets/stackblitz.js',
'site/src/plugins/*.js'
],
{
"$schema": "http://json.schemastore.org/prettierrc",
"arrowParens": "always",
+ "plugins": ["prettier-plugin-astro"],
"printWidth": 120,
"semi": false,
"singleQuote": true,
+import { fileURLToPath } from 'node:url'
+
import { defineConfig } from 'astro/config'
import astroBrokenLinksChecker from 'astro-broken-links-checker'
import bootstrapLight from 'bootstrap-vscode-theme/themes/bootstrap-light.json'
import { algoliaPlugin } from './src/plugins/algolia-plugin'
import { stackblitzPlugin } from './src/plugins/stackblitz-plugin'
+// Resolve `@bootstrap` to the same on-disk Bootstrap bundle the docs ship, so
+// every docs script imports from a single module instance (no duplicated
+// component registries). Mirrors the `@bootstrap` alias in `tsconfig.json`.
+const bootstrapBundlePath = fileURLToPath(new URL('../dist/js/bootstrap.bundle.js', import.meta.url))
+
const isDev = process.env.NODE_ENV === 'development'
const site = isDev
},
site,
vite: {
- plugins: [algoliaPlugin(), stackblitzPlugin()]
+ plugins: [algoliaPlugin(), stackblitzPlugin()],
+ resolve: {
+ alias: {
+ '@bootstrap': bootstrapBundlePath
+ }
+ }
}
})
-// NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT
-// IT'S ALL JUST JUNK FOR OUR DOCS!
-// ++++++++++++++++++++++++++++++++++++++++++
+// NOTICE: Internal docs helpers — not shipped in Bootstrap; not for reuse.
/*!
* JavaScript for Bootstrap's docs (https://getbootstrap.com/)
-// NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT
-// IT'S ALL JUST JUNK FOR OUR DOCS!
-// ++++++++++++++++++++++++++++++++++++++++++
+// NOTICE: Internal docs helpers — not shipped in Bootstrap; not for reuse.
/*
* JavaScript for Bootstrap's docs (https://getbootstrap.com/)
-// NOTICE!!! Initially embedded in our docs this JavaScript
-// file contains elements that can help you create reproducible
-// use cases in StackBlitz for instance.
-// In a real project please adapt this content to your needs.
-// ++++++++++++++++++++++++++++++++++++++++++
+// NOTICE: Embedded as-is into StackBlitz playgrounds via `?raw` import.
+// Adapt to your needs in real projects.
/*
* JavaScript for Bootstrap's docs (https://getbootstrap.com/)
Popover,
Toast,
Carousel
-} from '../../../../dist/js/bootstrap.bundle.js'
+} from '@bootstrap'
export default () => {
// --------
-// NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT
-// IT'S ALL JUST JUNK FOR OUR DOCS!
-// ++++++++++++++++++++++++++++++++++++++++++
+// NOTICE: Internal docs helpers — not shipped in Bootstrap; not for reuse.
/*
* JavaScript for Bootstrap's docs (https://getbootstrap.com/)
-// NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT
-// IT'S ALL JUST JUNK FOR OUR DOCS!
-// ++++++++++++++++++++++++++++++++++++++++++
+// NOTICE: Internal docs helpers — not shipped in Bootstrap; not for reuse.
/*
* JavaScript for Bootstrap's docs (https://getbootstrap.com/)
* For details, see https://creativecommons.org/licenses/by/3.0/.
*/
-import { Drawer } from '../../../../dist/js/bootstrap.bundle.js'
+import { Drawer } from '@bootstrap'
export default () => {
const tocSidebar = document.querySelector('#bdTocSidebar')
-// NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT
-// IT'S ALL JUST JUNK FOR OUR DOCS!
-// ++++++++++++++++++++++++++++++++++++++++++
+// NOTICE: Internal docs helpers — not shipped in Bootstrap; not for reuse.
/*!
* JavaScript for Bootstrap's docs (https://getbootstrap.com/)
+++ /dev/null
-/*
- * JavaScript for Bootstrap's docs (https://getbootstrap.com/)
- * Copyright 2011-2026 The Bootstrap Authors
- * Licensed under the Creative Commons Attribution 3.0 Unported License.
- * For details, see https://creativecommons.org/licenses/by/3.0/.
- */
-
-// Note that this file is not published; we only include it in scripts.html
-// for StackBlitz to work
-
-import snippets from 'js/partials/snippets.js'
-
-snippets()
-// NOTICE!!! Initially embedded in our docs this JavaScript
-// file contains elements that can help you create reproducible
-// use cases in StackBlitz for instance.
-// In a real project please adapt this content to your needs.
-// ++++++++++++++++++++++++++++++++++++++++++
+// NOTICE: Drives the "Open in StackBlitz" button on docs examples; bundles
+// `partials/snippets.js` as raw text into the generated playground.
/*!
* JavaScript for Bootstrap's docs (https://getbootstrap.com/)
---
+
---
<script
is:inline
async
src="https://cdn.carbonads.com/carbon.js?serve=CKYIKKJL&placement=getbootstrapcom&format=responsive"
- id="_carbonads_js"
-></script>
+ id="_carbonads_js"></script>
---
+// Loaded only on docs pages from `Scripts.astro`. Astro extracts/bundles
+// `<script>` tags per component, so this stays in its own file to keep the
+// StackBlitz SDK import out of non-docs entry chunks.
---
<script>
---
-import { getData } from '@libs/data'
+import { getData, type SidebarItem, type SidebarSubItem } from '@libs/data'
import { getConfig } from '@libs/config'
import { docsPages, getDocsPageSlug } from '@libs/content'
import { getSlug } from '@libs/utils'
{group.icon && (
<svg
class="bi me-2"
- style={group.icon_color && `color: light-dark(var(--bs-${group.icon_color}-500), var(--bs-${group.icon_color}-400));`}
+ style={
+ group.icon_color &&
+ `color: light-dark(var(--bs-${group.icon_color}-500), var(--bs-${group.icon_color}-400));`
+ }
aria-hidden="true"
>
<use href={`#${group.icon}`} />
{group.title}
</strong>
<ul class="nav flex-column bd-links-nav">
- {group.pages?.map((item: any) => {
+ {group.pages?.map((item: SidebarItem) => {
// Handle sub-groups
if (item.group && item.pages) {
return (
<>
- <li class="bd-links-subgroup fw-semibold mt-3">
- {item.group}
- </li>
- {item.pages.map((page: any) => {
+ <li class="bd-links-subgroup fw-semibold mt-3">{item.group}</li>
+ {item.pages.map((page: SidebarSubItem) => {
const docSlug = getSlug(page.title)
const unversionedPageSlug = `${groupSlug}/${docSlug}`
)
}
- const addedMeta = page.meta?.find((m: any) => m.added)
+ const addedMeta = page.meta?.find((m) => m.added)
return (
<li>
}
// Handle regular pages
+ if (!item.title) {
+ return null
+ }
+
const docSlug = getSlug(item.title)
const unversionedPageSlug = `${groupSlug}/${docSlug}`
)
}
- const itemAddedMeta = item.meta?.find((m: any) => m.added)
+ const itemAddedMeta = item.meta?.find((m) => m.added)
return (
<li>
---
interface ClassItem {
- class: string;
- description: string;
+ class: string
+ description: string
}
interface Props {
- classes: ClassItem[];
- className?: string;
+ classes: ClassItem[]
+ className?: string
}
-const {
- classes,
- className = "table reference-table"
-} = Astro.props;
+const { classes, className = 'table reference-table' } = Astro.props
// Format class names with dot prefix if needed
-const tableData = classes.map(item => ({
+const tableData = classes.map((item) => ({
class: item.class.startsWith('.') ? item.class : `.${item.class}`,
description: item.description
-}));
+}))
---
<div class="table-responsive bd-reference-table">
</tr>
</thead>
<tbody>
- {tableData.map((row) => (
- <tr>
- <td><code>{row.class}</code></td>
- <td>{row.description}</td>
- </tr>
- ))}
+ {
+ tableData.map((row) => (
+ <tr>
+ <td>
+ <code>{row.class}</code>
+ </td>
+ <td>{row.description}</td>
+ </tr>
+ ))
+ }
</tbody>
</table>
</div>
const { version } = Astro.props
---
-<span
- class="badge bg-3 ms-auto fg-3 fw-normal me--1"
- data-bs-toggle="tooltip"
- data-bs-title={`Added in v${version}`}
->
+<span class="badge bg-3 ms-auto fg-3 fw-normal me--1" data-bs-toggle="tooltip" data-bs-title={`Added in v${version}`}>
New
<span class="visually-hidden">{` (Added in v${version})`}</span>
</span>
---
<div class="bd-page-meta w-100 d-grid md:grid-cols-2 lg:d-flex gap-5 row-gap-2 px-4 py-3 bg-1 fg-2 fs-sm rounded-3">
- {frontmatter.js === 'required' && (
- <div class="d-flex align-items-center gap-2">
- <svg class="bi fg-warning" aria-hidden="true"><use href="#filetype-js" /></svg>
- Requires JavaScript
- </div>
- )}
- {frontmatter.js === 'optional' && (
- <div class="d-flex align-items-center gap-2">
- <svg class="bi fg-warning opacity-50" aria-hidden="true"><use href="#filetype-js" /></svg>
- JavaScript is optional
- </div>
- )}
- {frontmatter.css_layer && (
- <div class="d-flex align-items-center gap-2">
- <svg class="bi fg-accent" aria-hidden="true"><use href="#css" /></svg>
- <span>In <code class="fg-body">{frontmatter.css_layer}</code> layer</span>
- </div>
- )}
- {frontmatter.css_media === 'container' && (
- <div class="d-flex align-items-center gap-2">
- <svg class="bi" aria-hidden="true" style="color: var(--bs-pink-500);"><use href="#bounding-box-fill" /></svg>
- Container queries
- </div>
- )}
- {frontmatter.css_media === 'viewport' && (
- <div class="d-flex align-items-center gap-2">
- <svg class="bi" aria-hidden="true" style="color: var(--bs-purple-500);"><use href="#aspect-ratio-fill" /></svg>
- Viewport queries
- </div>
- )}
- {depsCount > 0 && (
- <div class="d-flex align-items-center gap-2">
- <svg class="bi fg-2" aria-hidden="true"><use href="#box-seam-fill" /></svg>
- <div>
- Depends on
- {deps.map((dep, i) => (
- <Fragment>{i > 0 && ', '}{dep.url?.startsWith('http') ? (
- <a class="fg-2 text-decoration-none" href={dep.url} target="_blank" rel="noopener">{dep.title}</a>
- ) : dep.url ? (
- <a class="fg-2 text-decoration-none" href={dep.url}>{dep.title}</a>
- ) : (
- <a class="fg-2 text-decoration-none" href={getVersionedDocsPath(`components/${getSlug(dep.title)}/`)}>{dep.title}</a>
- )}</Fragment>
- ))}
+ {
+ frontmatter.js === 'required' && (
+ <div class="d-flex align-items-center gap-2">
+ <svg class="bi fg-warning" aria-hidden="true">
+ <use href="#filetype-js" />
+ </svg>
+ Requires JavaScript
</div>
- </div>
- )}
- {frontmatter.added &&
- ((frontmatter.added.show_badge === undefined || frontmatter.added.show_badge === true)) && (
- <div class="d-flex align-items-center gap-2">
- <svg class="bi fg-success" aria-hidden="true"><use href="#plus" /></svg>
- Added in v{frontmatter.added.version}
- </div>
- )}
- {frontmatter.mdn && (
- <div class="d-flex align-items-center gap-2">
- <MdnIcon height={16} width={16} class="bi fg-2" />
- <a class="fg-2 text-decoration-none" href={frontmatter.mdn} target="_blank" rel="noopener">MDN Reference</a>
- </div>
- )}
- {frontmatter.csstricks && (
- <div class="d-flex align-items-center gap-2">
- <CssTricksIcon height={16} width={16} class="bi fg-2" />
- <a class="fg-2 text-decoration-none" href={typeof frontmatter.csstricks === 'string' ? frontmatter.csstricks : frontmatter.csstricks.url} target="_blank" rel="noopener">
- {typeof frontmatter.csstricks === 'string' ? 'CSS-Tricks' : (frontmatter.csstricks.label || 'CSS-Tricks')}
- </a>
- </div>
- )}
+ )
+ }
+ {
+ frontmatter.js === 'optional' && (
+ <div class="d-flex align-items-center gap-2">
+ <svg class="bi fg-warning opacity-50" aria-hidden="true">
+ <use href="#filetype-js" />
+ </svg>
+ JavaScript is optional
+ </div>
+ )
+ }
+ {
+ frontmatter.css_layer && (
+ <div class="d-flex align-items-center gap-2">
+ <svg class="bi fg-accent" aria-hidden="true">
+ <use href="#css" />
+ </svg>
+ <span>
+ In <code class="fg-body">{frontmatter.css_layer}</code> layer
+ </span>
+ </div>
+ )
+ }
+ {
+ frontmatter.css_media === 'container' && (
+ <div class="d-flex align-items-center gap-2">
+ <svg class="bi" aria-hidden="true" style="color: var(--bs-pink-500);">
+ <use href="#bounding-box-fill" />
+ </svg>
+ Container queries
+ </div>
+ )
+ }
+ {
+ frontmatter.css_media === 'viewport' && (
+ <div class="d-flex align-items-center gap-2">
+ <svg class="bi" aria-hidden="true" style="color: var(--bs-purple-500);">
+ <use href="#aspect-ratio-fill" />
+ </svg>
+ Viewport queries
+ </div>
+ )
+ }
+ {
+ depsCount > 0 && (
+ <div class="d-flex align-items-center gap-2">
+ <svg class="bi fg-2" aria-hidden="true">
+ <use href="#box-seam-fill" />
+ </svg>
+ <div>
+ Depends on
+ {deps.map((dep, i) => (
+ <Fragment>
+ {i > 0 && ', '}
+ {dep.url?.startsWith('http') ? (
+ <a class="fg-2 text-decoration-none" href={dep.url} target="_blank" rel="noopener">
+ {dep.title}
+ </a>
+ ) : dep.url ? (
+ <a class="fg-2 text-decoration-none" href={dep.url}>
+ {dep.title}
+ </a>
+ ) : (
+ <a class="fg-2 text-decoration-none" href={getVersionedDocsPath(`components/${getSlug(dep.title)}/`)}>
+ {dep.title}
+ </a>
+ )}
+ </Fragment>
+ ))}
+ </div>
+ </div>
+ )
+ }
+ {
+ frontmatter.added && (frontmatter.added.show_badge === undefined || frontmatter.added.show_badge === true) && (
+ <div class="d-flex align-items-center gap-2">
+ <svg class="bi fg-success" aria-hidden="true">
+ <use href="#plus" />
+ </svg>
+ Added in v{frontmatter.added.version}
+ </div>
+ )
+ }
+ {
+ frontmatter.mdn && (
+ <div class="d-flex align-items-center gap-2">
+ <MdnIcon height={16} width={16} class="bi fg-2" />
+ <a class="fg-2 text-decoration-none" href={frontmatter.mdn} target="_blank" rel="noopener">
+ MDN Reference
+ </a>
+ </div>
+ )
+ }
+ {
+ frontmatter.csstricks && (
+ <div class="d-flex align-items-center gap-2">
+ <CssTricksIcon height={16} width={16} class="bi fg-2" />
+ <a
+ class="fg-2 text-decoration-none"
+ href={typeof frontmatter.csstricks === 'string' ? frontmatter.csstricks : frontmatter.csstricks.url}
+ target="_blank"
+ rel="noopener"
+ >
+ {typeof frontmatter.csstricks === 'string' ? 'CSS-Tricks' : frontmatter.csstricks.label || 'CSS-Tricks'}
+ </a>
+ </div>
+ )
+ }
<div class="d-flex align-items-center gap-2">
- <svg class="bi fg-2" aria-hidden="true"><use href="#github" /></svg>
- <a class="fg-2 text-decoration-none" href={`${getConfig().repo}/blob/v${getConfig().current_version}/site/src/content/docs/${id}`} target="_blank" rel="noopener">View & Edit</a>
+ <svg class="bi fg-2" aria-hidden="true"><use href="#github"></use></svg>
+ <a
+ class="fg-2 text-decoration-none"
+ href={`${getConfig().repo}/blob/v${getConfig().current_version}/site/src/content/docs/${id}`}
+ target="_blank"
+ rel="noopener">View & Edit</a
+ >
</div>
</div>
---
-import { readFileSync } from 'node:fs';
-import { join } from 'node:path';
+import { readFileSync } from 'node:fs'
+import { join } from 'node:path'
interface ReferenceItem {
- class: string;
- styles?: string | string[] | Record<string, string>;
- description?: string;
- comment?: string; // Optional manual comment to append
- [key: string]: any; // Allow additional properties
+ class: string
+ styles?: string | string[] | Record<string, string>
+ description?: string
+ comment?: string // Optional manual comment to append
+ [key: string]: any // Allow additional properties
}
interface Props {
- className?: string;
- columns?: Array<{ label: string; key: string }>;
- data?: Array<any>;
- reference?: Array<ReferenceItem>; // Direct prop for reference data
+ className?: string
+ columns?: Array<{ label: string; key: string }>
+ data?: Array<any>
+ reference?: Array<ReferenceItem> // Direct prop for reference data
}
-const {
- className = "table reference-table",
- columns,
- data,
- reference
-} = Astro.props;
+const { className = 'table reference-table', columns, data, reference } = Astro.props
// Use explicit reference prop or data prop
-const referenceData = reference || data || [];
+const referenceData = reference || data || []
// Parse CSS variables from _root.scss at build time
function parseCSSVariables(): Record<string, string> {
try {
- const projectRoot = process.cwd();
- const rootScssPath = join(projectRoot, 'scss/_root.scss');
- const scssContent = readFileSync(rootScssPath, 'utf-8');
+ const projectRoot = process.cwd()
+ const rootScssPath = join(projectRoot, 'scss/_root.scss')
+ const scssContent = readFileSync(rootScssPath, 'utf-8')
- const cssVarValues: Record<string, string> = {};
+ const cssVarValues: Record<string, string> = {}
// Match CSS variable declarations: --#{$prefix}variable-name: value;
// This regex captures the variable name and its value
- const varRegex = /--#\{\$prefix\}([a-z0-9-]+):\s*([^;]+);/gi;
- let match;
+ const varRegex = /--#\{\$prefix\}([a-z0-9-]+):\s*([^;]+);/gi
+ let match
while ((match = varRegex.exec(scssContent)) !== null) {
- const varName = `--bs-${match[1]}`;
- let value = match[2].trim();
+ const varName = `--bs-${match[1]}`
+ let value = match[2].trim()
// Clean up SCSS interpolation syntax (e.g., #{$variable})
- value = value.replace(/#\{[^}]+\}/g, '').trim();
+ value = value.replace(/#\{[^}]+\}/g, '').trim()
// Remove inline comments
- value = value.replace(/\/\/.*$/gm, '').trim();
+ value = value.replace(/\/\/.*$/gm, '').trim()
// Only store if we have a clean value (not empty after removing interpolations)
if (value) {
- cssVarValues[varName] = value;
+ cssVarValues[varName] = value
}
}
- return cssVarValues;
+ return cssVarValues
} catch (error) {
- console.warn('Could not parse CSS variables from _root.scss:', error);
- return {};
+ console.warn('Could not parse CSS variables from _root.scss:', error)
+ return {}
}
}
-const cssVarValues = parseCSSVariables();
+const cssVarValues = parseCSSVariables()
// Function to add CSS variable value comments
function addVarComments(cssValue: string): string {
- const comments: string[] = [];
+ const comments: string[] = []
// Collect resolved values for all CSS variables
cssValue.replace(/var\((--[a-z0-9-]+)\)/gi, (match, varName) => {
- const resolvedValue = cssVarValues[varName];
+ const resolvedValue = cssVarValues[varName]
if (resolvedValue) {
- comments.push(`<span class="fg-3">/* ${resolvedValue} */</span>`);
+ comments.push(`<span class="fg-3">/* ${resolvedValue} */</span>`)
}
- return match;
- });
+ return match
+ })
// Append comments after the last semicolon or at the end
if (comments.length > 0) {
- const hasSemicolon = cssValue.trimEnd().endsWith(';');
- return `${cssValue}${hasSemicolon ? '' : ';'} ${comments.join(' ')}`;
+ const hasSemicolon = cssValue.trimEnd().endsWith(';')
+ return `${cssValue}${hasSemicolon ? '' : ';'} ${comments.join(' ')}`
}
- return cssValue;
+ return cssValue
}
// If no explicit columns provided, infer from the first data item
-const inferredColumns = columns || (() => {
- if (referenceData.length === 0) {
- return [
- { label: 'Class', key: 'class' },
- { label: 'Styles', key: 'styles' }
- ];
- }
+const inferredColumns =
+ columns ||
+ (() => {
+ if (referenceData.length === 0) {
+ return [
+ { label: 'Class', key: 'class' },
+ { label: 'Styles', key: 'styles' }
+ ]
+ }
- const firstItem = referenceData[0];
- return Object.keys(firstItem)
- .filter(key => key !== 'comment') // Exclude comment field from columns
- .map(key => ({
- label: key.charAt(0).toUpperCase() + key.slice(1),
- key: key
- }));
-})();
+ const firstItem = referenceData[0]
+ return Object.keys(firstItem)
+ .filter((key) => key !== 'comment') // Exclude comment field from columns
+ .map((key) => ({
+ label: key.charAt(0).toUpperCase() + key.slice(1),
+ key: key
+ }))
+ })()
// Transform frontmatter format to table format
const tableData = referenceData.map((item: ReferenceItem) => {
- const transformedItem: Record<string, any> = {};
+ const transformedItem: Record<string, any> = {}
- inferredColumns.forEach(column => {
- const key = column.key;
- let value = item[key];
+ inferredColumns.forEach((column) => {
+ const key = column.key
+ let value = item[key]
if (key === 'class' && typeof value === 'string' && !value.startsWith('.')) {
- value = `.${value}`;
+ value = `.${value}`
}
if (key === 'styles') {
- let processedStyles = '';
+ let processedStyles = ''
if (typeof value === 'string') {
- processedStyles = addVarComments(value);
+ processedStyles = addVarComments(value)
} else if (typeof value === 'object' && !Array.isArray(value)) {
// Handle object syntax: { prop: value, prop2: value2 }
processedStyles = Object.entries(value)
.map(([prop, val]) => {
- const cssLine = `${prop}: ${val};`;
- return addVarComments(cssLine);
+ const cssLine = `${prop}: ${val};`
+ return addVarComments(cssLine)
})
- .join('<br/>');
+ .join('<br/>')
} else if (Array.isArray(value)) {
- processedStyles = value.map((style: any) => {
- if (typeof style === 'string') {
- const formattedStyle = style.includes(':') ? style + (style.endsWith(';') ? '' : ';') : style;
- return addVarComments(formattedStyle);
- }
- if (typeof style === 'object') {
- const cssLine = Object.entries(style).map(([prop, val]) => `${prop}: ${val};`).join(' ');
- return addVarComments(cssLine);
- }
- return style;
- }).join('<br/>');
+ processedStyles = value
+ .map((style: any) => {
+ if (typeof style === 'string') {
+ const formattedStyle = style.includes(':') ? style + (style.endsWith(';') ? '' : ';') : style
+ return addVarComments(formattedStyle)
+ }
+ if (typeof style === 'object') {
+ const cssLine = Object.entries(style)
+ .map(([prop, val]) => `${prop}: ${val};`)
+ .join(' ')
+ return addVarComments(cssLine)
+ }
+ return style
+ })
+ .join('<br/>')
} else {
- processedStyles = value || '';
+ processedStyles = value || ''
}
// Append manual comment if provided in frontmatter
if (item.comment) {
- processedStyles += `<br/><span class="color-3">/* ${item.comment} */</span>`;
+ processedStyles += `<br/><span class="color-3">/* ${item.comment} */</span>`
}
- transformedItem[key] = processedStyles;
+ transformedItem[key] = processedStyles
} else {
- transformedItem[key] = value;
+ transformedItem[key] = value
}
- });
+ })
- return transformedItem;
-});
+ return transformedItem
+})
---
<div class="table-responsive bd-reference-table">
<table class={className}>
<thead>
<tr>
- {inferredColumns.map(column => (
- <th scope="col">{column.label}</th>
- ))}
+ {inferredColumns.map((column) => <th scope="col">{column.label}</th>)}
</tr>
</thead>
<tbody>
- {tableData.map((row: any) => (
- <tr>
- {inferredColumns.map(column => (
- <td>
- {column.key === 'styles' ? (
- <Fragment set:html={row[column.key]} />
- ) : (
- row[column.key]
- )}
- </td>
- ))}
- </tr>
- ))}
+ {
+ tableData.map((row: any) => (
+ <tr>
+ {inferredColumns.map((column) => (
+ <td>{column.key === 'styles' ? <Fragment set:html={row[column.key]} /> : row[column.key]}</td>
+ ))}
+ </tr>
+ ))
+ }
</tbody>
</table>
</div>
toc.map(({ children, slug, text }) => {
return (
<li>
- <a class="nav-link" href={`#${slug}`}>{text}</a>
+ <a class="nav-link" href={`#${slug}`}>
+ {text}
+ </a>
{children.length > 0 && <Astro.self entries={children} />}
</li>
)
---
-import { readFileSync } from 'node:fs';
-import { join } from 'node:path';
+import { readFileSync } from 'node:fs'
+import { join } from 'node:path'
interface Props {
- utility: string | string[]; // The utility key(s) from the metadata (e.g., "font-size" or ["font-size", "text-size"])
- className?: string;
+ utility: string | string[] // The utility key(s) from the metadata (e.g., "font-size" or ["font-size", "text-size"])
+ className?: string
}
-const {
- utility,
- className = "table reference-table"
-} = Astro.props;
+const { utility, className = 'table reference-table' } = Astro.props
// Normalize to array
-const utilities = Array.isArray(utility) ? utility : [utility];
+const utilities = Array.isArray(utility) ? utility : [utility]
// Parse CSS variables from _root.scss at build time
function parseCSSVariables(): Record<string, string> {
try {
- const projectRoot = process.cwd();
- const rootScssPath = join(projectRoot, 'scss/_root.scss');
- const scssContent = readFileSync(rootScssPath, 'utf-8');
+ const projectRoot = process.cwd()
+ const rootScssPath = join(projectRoot, 'scss/_root.scss')
+ const scssContent = readFileSync(rootScssPath, 'utf-8')
- const cssVarValues: Record<string, string> = {};
- const varRegex = /--#\{\$prefix\}([a-z0-9-]+):\s*([^;]+);/gi;
- let match;
+ const cssVarValues: Record<string, string> = {}
+ const varRegex = /--#\{\$prefix\}([a-z0-9-]+):\s*([^;]+);/gi
+ let match
while ((match = varRegex.exec(scssContent)) !== null) {
- const varName = `--bs-${match[1]}`;
- let value = match[2].trim();
- value = value.replace(/#\{[^}]+\}/g, '').trim();
- value = value.replace(/\/\/.*$/gm, '').trim();
+ const varName = `--bs-${match[1]}`
+ let value = match[2].trim()
+ value = value.replace(/#\{[^}]+\}/g, '').trim()
+ value = value.replace(/\/\/.*$/gm, '').trim()
if (value) {
- cssVarValues[varName] = value;
+ cssVarValues[varName] = value
}
}
- return cssVarValues;
+ return cssVarValues
} catch (error) {
- console.warn('Could not parse CSS variables from _root.scss:', error);
- return {};
+ console.warn('Could not parse CSS variables from _root.scss:', error)
+ return {}
}
}
// Load utilities metadata
function loadUtilitiesMetadata(): any {
try {
- const projectRoot = process.cwd();
- const metadataPath = join(projectRoot, 'dist/css/bootstrap-utilities.metadata.json');
- const metadataContent = readFileSync(metadataPath, 'utf-8');
- return JSON.parse(metadataContent);
+ const projectRoot = process.cwd()
+ const metadataPath = join(projectRoot, 'dist/css/bootstrap-utilities.metadata.json')
+ const metadataContent = readFileSync(metadataPath, 'utf-8')
+ return JSON.parse(metadataContent)
} catch (error) {
- console.warn('Could not load utilities metadata:', error);
- return { utilities: {} };
+ console.warn('Could not load utilities metadata:', error)
+ return { utilities: {} }
}
}
// Parse compiled CSS to extract styles for given class selectors
function parseCompiledCSS(classNames: string[]): Record<string, string[]> {
try {
- const projectRoot = process.cwd();
- const bootstrapCssPath = join(projectRoot, 'dist/css/bootstrap.css');
- const cssContent = readFileSync(bootstrapCssPath, 'utf-8');
+ const projectRoot = process.cwd()
+ const bootstrapCssPath = join(projectRoot, 'dist/css/bootstrap.css')
+ const cssContent = readFileSync(bootstrapCssPath, 'utf-8')
- const classStyles: Record<string, string[]> = {};
+ const classStyles: Record<string, string[]> = {}
- classNames.forEach(className => {
+ classNames.forEach((className) => {
// Match class selectors, including :where() wrapped child-selector utilities
- const escapedClass = className.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ const escapedClass = className.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
const selectorRegex = new RegExp(
`(?:^|\\n)\\s*(?::where\\()?\\s*\\.${escapedClass}(?:\\s[^{]*)?\\)?\\s*\\{([^}]+)\\}`,
'gm'
- );
+ )
- let match;
- let foundDeclarations: string[] = [];
+ let match
+ let foundDeclarations: string[] = []
while ((match = selectorRegex.exec(cssContent)) !== null) {
const declarations = match[1]
.split(';')
- .map(decl => decl.trim())
- .filter(decl => decl.length > 0)
- .map(decl => `${decl};`);
+ .map((decl) => decl.trim())
+ .filter((decl) => decl.length > 0)
+ .map((decl) => `${decl};`)
if (declarations.length > 0) {
- foundDeclarations = declarations;
- break;
+ foundDeclarations = declarations
+ break
}
}
- classStyles[className] = foundDeclarations;
- });
+ classStyles[className] = foundDeclarations
+ })
- return classStyles;
+ return classStyles
} catch (error) {
- console.warn('Could not parse compiled CSS:', error);
- return {};
+ console.warn('Could not parse compiled CSS:', error)
+ return {}
}
}
-const cssVarValues = parseCSSVariables();
-const metadata = loadUtilitiesMetadata();
+const cssVarValues = parseCSSVariables()
+const metadata = loadUtilitiesMetadata()
// Collect classes from all specified utilities
-let allClasses: string[] = [];
+let allClasses: string[] = []
-utilities.forEach(util => {
- const utilityMeta = metadata.utilities[util];
+utilities.forEach((util) => {
+ const utilityMeta = metadata.utilities[util]
if (!utilityMeta) {
- console.warn(`Utility "${util}" not found in metadata. Available utilities: ${Object.keys(metadata.utilities).join(', ')}`);
- return;
+ console.warn(
+ `Utility "${util}" not found in metadata. Available utilities: ${Object.keys(metadata.utilities).join(', ')}`
+ )
+ return
}
- const classes = utilityMeta.classes || [];
- allClasses = allClasses.concat(classes);
-});
+ const classes = utilityMeta.classes || []
+ allClasses = allClasses.concat(classes)
+})
if (allClasses.length === 0) {
- throw new Error(`No classes found for utilities: ${utilities.join(', ')}`);
+ throw new Error(`No classes found for utilities: ${utilities.join(', ')}`)
}
-const classStyles = parseCompiledCSS(allClasses);
+const classStyles = parseCompiledCSS(allClasses)
// Function to add CSS variable value comments
function addVarComments(cssValue: string): string {
- const comments: string[] = [];
+ const comments: string[] = []
cssValue.replace(/var\((--[a-z0-9-]+)\)/gi, (match, varName) => {
- const resolvedValue = cssVarValues[varName];
+ const resolvedValue = cssVarValues[varName]
if (resolvedValue) {
- comments.push(`<span class="fg-3">/* ${resolvedValue} */</span>`);
+ comments.push(`<span class="fg-3">/* ${resolvedValue} */</span>`)
}
- return match;
- });
+ return match
+ })
if (comments.length > 0) {
- const hasSemicolon = cssValue.trimEnd().endsWith(';');
- return `${cssValue}${hasSemicolon ? '' : ';'} ${comments.join(' ')}`;
+ const hasSemicolon = cssValue.trimEnd().endsWith(';')
+ return `${cssValue}${hasSemicolon ? '' : ';'} ${comments.join(' ')}`
}
- return cssValue;
+ return cssValue
}
// Build table data
-const tableData = allClasses.map(cls => {
- const styles = classStyles[cls] || [];
- const formattedStyles = styles.map(style => addVarComments(style)).join('<br/>');
+const tableData = allClasses.map((cls) => {
+ const styles = classStyles[cls] || []
+ const formattedStyles = styles.map((style) => addVarComments(style)).join('<br/>')
return {
class: `.${cls}`,
styles: formattedStyles || '<em>Not found</em>'
- };
-});
+ }
+})
---
<div class="table-responsive bd-reference-table">
</tr>
</thead>
<tbody>
- {tableData.map((row) => (
- <tr>
- <td>{row.class}</td>
- <td><Fragment set:html={row.styles} /></td>
- </tr>
- ))}
+ {
+ tableData.map((row) => (
+ <tr>
+ <td>{row.class}</td>
+ <td>
+ <Fragment set:html={row.styles} />
+ </td>
+ </tr>
+ ))
+ }
</tbody>
</table>
</div>
<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">
+<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} />}
+{/* Loaded as `is:inline` (not via Vite) so the theme is applied before paint. */}
+{/* The script lives in `site/static/docs/[version]/assets/js/` so it can */}
+{/* render-block at this exact spot in the head without going through the */}
+{/* bundler (which would defer it and cause a flash of the wrong theme). */}
<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 layout={layout} />
- <ScssProd.default />
-)}
-
-{!import.meta.env.PROD && Scss && (
- <Scss.default />
-)}
+{
+ import.meta.env.PROD && ScssProd && (
+ <>
+ <Stylesheet />
+ <ScssProd.default />
+ </>
+ )
+}
+
+{!import.meta.env.PROD && Scss && <Scss.default />}
<Favicons />
<Social description={description} layout={layout} thumbnail={thumbnail} title={title} />
---
+
---
<link rel="stylesheet" href="/src/scss/docs-dev.scss" />
---
+
---
<style is:global lang="scss">
---
import { getVersionedBsCssProps } from '@libs/bootstrap'
-import type { Layout } from '@libs/layout'
-
-interface Props {
- layout: Layout
-}
---
<link {...getVersionedBsCssProps()} />
---
<li class="nav-item">
- <a
- aria-current={active ? true : undefined}
- class:list={['nav-link px-2', className, { active }]}
- {...props}
- >
+ <a aria-current={active ? true : undefined} class:list={['nav-link px-2', className, { active }]} {...props}>
<slot />
</a>
</li>
<div class="menu">
<a class:list={['menu-item', { active: !layout }]} href="/">
Home
- {!layout && <svg class="bi ms-auto" aria-hidden="true"><use href="#check2"></use></svg>}
+ {
+ !layout && (
+ <svg class="bi ms-auto" aria-hidden="true">
+ <use href="#check2" />
+ </svg>
+ )
+ }
</a>
<a
class:list={['menu-item', { active: layout === 'docs' }]}
href={getVersionedDocsPath('getting-started/install/')}
>
Docs
- {layout === 'docs' && <svg class="bi ms-auto" aria-hidden="true"><use href="#check2"></use></svg>}
+ {
+ layout === 'docs' && (
+ <svg class="bi ms-auto" aria-hidden="true">
+ <use href="#check2" />
+ </svg>
+ )
+ }
</a>
- <a
- class:list={['menu-item', { active: title === 'Examples' }]}
- href={getVersionedDocsPath('examples/')}
- >
+ <a class:list={['menu-item', { active: title === 'Examples' }]} href={getVersionedDocsPath('examples/')}>
Examples
- {title === 'Examples' && <svg class="bi ms-auto" aria-hidden="true"><use href="#check2"></use></svg>}
+ {
+ title === 'Examples' && (
+ <svg class="bi ms-auto" aria-hidden="true">
+ <use href="#check2" />
+ </svg>
+ )
+ }
</a>
<a class="menu-item" href={getConfig().icons} target="_blank" rel="noopener">
Icons
<ul class="nav navbar-nav flex-row ms-auto">
<li class="nav-item nav-link px-1" id="docsearch" data-bd-docs-version={getConfig().docs_version}></li>
- <Versions layout={layout} addedIn={addedIn} />
-
<LinkItem class="px-1 d-none lg:d-flex" href={getConfig().github_org} target="_blank" rel="noopener">
<GitHubIcon class="navbar-nav-svg" height={16} width={16} />
</LinkItem>
+ <Versions layout={layout} addedIn={addedIn} />
+
<li class="nav-item">
<ThemeToggler layout={layout} />
</li>
be modified.
</p>
<p class="d-flex flex-column lead fw-normal mb-0">
- <a href={getVersionedDocsPath('getting-started/css-variables')} class="icon-link icon-link-hover fw-semibold mb-3">
+ <a
+ href={getVersionedDocsPath('getting-started/css-variables')}
+ class="icon-link icon-link-hover fw-semibold mb-3"
+ >
Learn more about CSS variables
<svg class="bi" aria-hidden="true"><use href="#arrow-right"></use></svg>
</a>
<p class="lg:pe-5">
When you only need to include Bootstrap’s compiled CSS or JS, you can use <a
href="https://www.jsdelivr.com/package/npm/bootstrap">jsDelivr</a
- >. See it in action with our simple <a href={getVersionedDocsPath('guides/quickstart/')}
- >quick start</a
- >, or <a href={getVersionedDocsPath('examples')}>browse the examples</a> to jumpstart your next project.
+ >. See it in action with our simple <a href={getVersionedDocsPath('guides/quickstart/')}>quick start</a>, or <a
+ href={getVersionedDocsPath('examples')}>browse the examples</a
+ > to jumpstart your next project.
</p>
<Code
code={`<link href="${getConfig().cdn.css}" rel="stylesheet" integrity="${
rel="noopener"
target="_blank"
>
- <span class="sm:d-inline-flex align-items-center gap-1 py-2 px-3 me-2 mb-2 lg:mb-0 rounded-5 masthead-notice theme-subtle theme-warning ">
+ <span
+ class="sm:d-inline-flex align-items-center gap-1 py-2 px-3 me-2 mb-2 lg:mb-0 rounded-5 masthead-notice theme-subtle theme-warning"
+ >
Get Security Updates for Bootstrap 3 & 4
<svg class="bi" style="width: 20px; height: 20px; margin-block: -2px;" aria-hidden="true"
><use href="#arrow-right-short"></use></svg
width={width}
>
<title>CSS-Tricks</title>
- <path d="M156.58,239l-88.3,64.75c-10.59,7.06-18.84,11.77-29.43,11.77-21.19,0-38.85-18.84-38.85-40C0,257.83,14.13,244.88,27.08,239l103.6-44.74L27.08,148.34C13,142.46,0,129.51,0,111.85,0,90.66,18.84,73,40,73c10.6,0,17.66,3.53,28.25,11.77l88.3,64.75L144.81,44.74C141.28,20,157.76,0,181.31,0s40,18.84,36.5,43.56L206,149.52l88.3-64.75C304.93,76.53,313.17,73,323.77,73a39.2,39.2,0,0,1,38.85,38.85c0,18.84-12.95,30.61-27.08,36.5L231.93,194.26,335.54,239c14.13,5.88,27.08,18.83,27.08,37.67,0,21.19-18.84,38.85-40,38.85-9.42,0-17.66-4.71-28.26-11.77L206,239l11.77,104.78c3.53,24.72-12.95,44.74-36.5,44.74s-40-18.84-36.5-43.56Z"></path>
+ <path
+ d="M156.58,239l-88.3,64.75c-10.59,7.06-18.84,11.77-29.43,11.77-21.19,0-38.85-18.84-38.85-40C0,257.83,14.13,244.88,27.08,239l103.6-44.74L27.08,148.34C13,142.46,0,129.51,0,111.85,0,90.66,18.84,73,40,73c10.6,0,17.66,3.53,28.25,11.77l88.3,64.75L144.81,44.74C141.28,20,157.76,0,181.31,0s40,18.84,36.5,43.56L206,149.52l88.3-64.75C304.93,76.53,313.17,73,323.77,73a39.2,39.2,0,0,1,38.85,38.85c0,18.84-12.95,30.61-27.08,36.5L231.93,194.26,335.54,239c14.13,5.88,27.08,18.83,27.08,37.67,0,21.19-18.84,38.85-40,38.85-9.42,0-17.66-4.71-28.26-11.77L206,239l11.77,104.78c3.53,24.72-12.95,44.74-36.5,44.74s-40-18.84-36.5-43.56Z"
+ ></path>
</svg>
stroke-width="1"
stroke-linecap="round"
>
- <path d="M1 3.5h14M1 8h14M1 12.5h14" />
+ <path d="M1 3.5h14M1 8h14M1 12.5h14"></path>
</svg>
const { class: className, height, width } = Astro.props
---
-<svg
- xmlns="http://www.w3.org/2000/svg"
- viewBox="0 0 16 16"
- role="img"
- class={className}
- height={height}
- width={width}
->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" role="img" class={className} height={height} width={width}>
<title>MDN</title>
- <path d="M6.359.734 1.846 15.266H0L4.497.734h1.862ZM8 .734v14.532H6.359V.734H8ZM16 .734v14.532h-1.641V.734H16ZM14.359.734 9.862 15.266H8.016L12.513.734h1.846Z"></path>
+ <path
+ d="M6.359.734 1.846 15.266H0L4.497.734h1.862ZM8 .734v14.532H6.359V.734H8ZM16 .734v14.532h-1.641V.734H16ZM14.359.734 9.862 15.266H8.016L12.513.734h1.846Z"
+ ></path>
</svg>
---
+
---
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
></path>
</symbol>
<symbol id="aspect-ratio" viewBox="0 0 16 16">
- <path d="M0 3.5A1.5 1.5 0 0 1 1.5 2h13A1.5 1.5 0 0 1 16 3.5v9a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 12.5zM1.5 3a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h13a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5z"></path>
- <path d="M2 4.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1H3v2.5a.5.5 0 0 1-1 0zm12 7a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1 0-1H13V8.5a.5.5 0 0 1 1 0z"></path>
+ <path
+ d="M0 3.5A1.5 1.5 0 0 1 1.5 2h13A1.5 1.5 0 0 1 16 3.5v9a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 12.5zM1.5 3a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h13a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5z"
+ ></path>
+ <path
+ d="M2 4.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1H3v2.5a.5.5 0 0 1-1 0zm12 7a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1 0-1H13V8.5a.5.5 0 0 1 1 0z"
+ ></path>
</symbol>
<symbol id="aspect-ratio-fill" viewBox="0 0 16 16">
- <path fill="currentcolor" fill-rule="evenodd" d="M13.5 1A2.5 2.5 0 0 1 16 3.5v9a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5v-9A2.5 2.5 0 0 1 2.5 1zm0 8a.5.5 0 0 0-.5.5V12h-2.5a.5.5 0 0 0 0 1h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5m-11-6a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 1 0V4h2.5a.5.5 0 0 0 0-1z" clip-rule="evenodd"/>
+ <path
+ fill="currentcolor"
+ fill-rule="evenodd"
+ d="M13.5 1A2.5 2.5 0 0 1 16 3.5v9a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5v-9A2.5 2.5 0 0 1 2.5 1zm0 8a.5.5 0 0 0-.5.5V12h-2.5a.5.5 0 0 0 0 1h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5m-11-6a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 1 0V4h2.5a.5.5 0 0 0 0-1z"
+ clip-rule="evenodd"></path>
</symbol>
<symbol id="book-half" viewBox="0 0 16 16">
<path
></path>
</symbol>
<symbol id="bounding-box" viewBox="0 0 16 16">
- <path d="M5 2V0H0v5h2v6H0v5h5v-2h6v2h5v-5h-2V5h2V0h-5v2zm6 1v2h2v6h-2v2H5v-2H3V5h2V3zm1-2h3v3h-3zm3 11v3h-3v-3zM4 15H1v-3h3zM1 4V1h3v3z"></path>
+ <path
+ d="M5 2V0H0v5h2v6H0v5h5v-2h6v2h5v-5h-2V5h2V0h-5v2zm6 1v2h2v6h-2v2H5v-2H3V5h2V3zm1-2h3v3h-3zm3 11v3h-3v-3zM4 15H1v-3h3zM1 4V1h3v3z"
+ ></path>
</symbol>
<symbol id="box-arrow-up-right" viewBox="0 0 16 16">
- <path fill-rule="evenodd" d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5"/>
- <path fill-rule="evenodd" d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0z"/>
+ <path
+ fill-rule="evenodd"
+ d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5"
+ ></path>
+ <path
+ fill-rule="evenodd"
+ d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0z"
+ ></path>
</symbol>
<symbol id="box-seam" viewBox="0 0 16 16">
<path
></path>
</symbol>
<symbol id="box-seam-fill" viewBox="0 0 16 16">
- <path fill-rule="evenodd" d="M15.528 2.973a.75.75 0 0 1 .472.696v8.662a.75.75 0 0 1-.472.696l-7.25 2.9a.75.75 0 0 1-.557 0l-7.25-2.9A.75.75 0 0 1 0 12.331V3.669a.75.75 0 0 1 .471-.696L7.443.184l.01-.003.268-.108a.75.75 0 0 1 .558 0l.269.108.01.003zM10.404 2 4.25 4.461 1.846 3.5 1 3.839v.4l6.5 2.6v7.922l.5.2.5-.2V6.84l6.5-2.6v-.4l-.846-.339L8 5.961 5.596 5l6.154-2.461z"/>
+ <path
+ fill-rule="evenodd"
+ d="M15.528 2.973a.75.75 0 0 1 .472.696v8.662a.75.75 0 0 1-.472.696l-7.25 2.9a.75.75 0 0 1-.557 0l-7.25-2.9A.75.75 0 0 1 0 12.331V3.669a.75.75 0 0 1 .471-.696L7.443.184l.01-.003.268-.108a.75.75 0 0 1 .558 0l.269.108.01.003zM10.404 2 4.25 4.461 1.846 3.5 1 3.839v.4l6.5 2.6v7.922l.5.2.5-.2V6.84l6.5-2.6v-.4l-.846-.339L8 5.961 5.596 5l6.154-2.461z"
+ ></path>
</symbol>
<symbol id="braces" viewBox="0 0 16 16">
<path
></path>
</symbol>
<symbol id="calendar-week" viewBox="0 0 16 16">
- <path d="M11 6.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm-3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm-5 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5z"/>
- <path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5M1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4z"/>
+ <path
+ d="M11 6.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm-3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm-5 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5z"
+ ></path>
+ <path
+ d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5M1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4z"
+ ></path>
</symbol>
<symbol id="check2" viewBox="0 0 16 16">
<path
></path>
</symbol>
<symbol id="copy" viewBox="0 0 16 16">
- <path fill-rule="evenodd" d="M4 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1h1v1a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h1v1z"/>
+ <path
+ fill-rule="evenodd"
+ d="M4 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1h1v1a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h1v1z"
+ ></path>
</symbol>
<symbol id="css" viewBox="0 0 16 16">
- <path fill="currentcolor" fill-rule="evenodd" d="M13 0a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V0zM4.002 6.498q-.908 0-1.455.508Q2 7.513 2 8.49v3.106q0 .986.527 1.484t1.407.498a2.2 2.2 0 0 0 1.015-.224q.45-.225.703-.674.255-.449.254-1.114v-.185h-1.22v.176q0 .45-.186.683t-.527.235q-.371-.01-.557-.264-.186-.255-.186-.752V8.686q0-.547.166-.811.177-.264.577-.264.322 0 .517.225.196.224.196.693v.205h1.23V8.52q0-.674-.244-1.124a1.55 1.55 0 0 0-.664-.673q-.42-.225-1.006-.225m4.214-.01q-.585 0-1.006.244a1.67 1.67 0 0 0-.635.674 2.1 2.1 0 0 0-.224.996q0 .753.293 1.182.302.42.966.732l.47.215q.438.186.624.43t.186.635q0 .478-.166.703-.157.224-.528.224-.36 0-.547-.244-.185-.243-.205-.752H6.282q.02.996.498 1.524.48.527 1.387.527.908 0 1.416-.518.508-.517.508-1.484 0-.81-.332-1.289-.333-.479-1.045-.79l-.45-.196q-.39-.166-.556-.381-.165-.214-.166-.576 0-.4.166-.596.176-.195.508-.195.36 0 .508.234.156.234.175.703h1.124q-.03-.976-.499-1.484-.468-.518-1.308-.518m4.057 0q-.586 0-1.005.244a1.67 1.67 0 0 0-.635.674 2.1 2.1 0 0 0-.225.996q0 .753.293 1.182.303.42.967.732l.469.215q.44.186.625.43t.185.635q0 .478-.166.703-.156.224-.527.224-.36 0-.547-.244-.186-.243-.205-.752H10.34q.02.996.498 1.524.478.527 1.387.527.908 0 1.416-.518.507-.517.507-1.484 0-.81-.332-1.289t-1.045-.79l-.449-.196q-.39-.166-.556-.381-.166-.214-.166-.576 0-.4.166-.596.176-.195.507-.195.361 0 .508.234.156.234.176.703h1.123q-.03-.976-.498-1.484-.469-.518-1.309-.518" clip-rule="evenodd"/>
+ <path
+ fill="currentcolor"
+ fill-rule="evenodd"
+ d="M13 0a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V0zM4.002 6.498q-.908 0-1.455.508Q2 7.513 2 8.49v3.106q0 .986.527 1.484t1.407.498a2.2 2.2 0 0 0 1.015-.224q.45-.225.703-.674.255-.449.254-1.114v-.185h-1.22v.176q0 .45-.186.683t-.527.235q-.371-.01-.557-.264-.186-.255-.186-.752V8.686q0-.547.166-.811.177-.264.577-.264.322 0 .517.225.196.224.196.693v.205h1.23V8.52q0-.674-.244-1.124a1.55 1.55 0 0 0-.664-.673q-.42-.225-1.006-.225m4.214-.01q-.585 0-1.006.244a1.67 1.67 0 0 0-.635.674 2.1 2.1 0 0 0-.224.996q0 .753.293 1.182.302.42.966.732l.47.215q.438.186.624.43t.186.635q0 .478-.166.703-.157.224-.528.224-.36 0-.547-.244-.185-.243-.205-.752H6.282q.02.996.498 1.524.48.527 1.387.527.908 0 1.416-.518.508-.517.508-1.484 0-.81-.332-1.289-.333-.479-1.045-.79l-.45-.196q-.39-.166-.556-.381-.165-.214-.166-.576 0-.4.166-.596.176-.195.508-.195.36 0 .508.234.156.234.175.703h1.124q-.03-.976-.499-1.484-.468-.518-1.308-.518m4.057 0q-.586 0-1.005.244a1.67 1.67 0 0 0-.635.674 2.1 2.1 0 0 0-.225.996q0 .753.293 1.182.303.42.967.732l.469.215q.44.186.625.43t.185.635q0 .478-.166.703-.156.224-.527.224-.36 0-.547-.244-.186-.243-.205-.752H10.34q.02.996.498 1.524.478.527 1.387.527.908 0 1.416-.518.507-.517.507-1.484 0-.81-.332-1.289t-1.045-.79l-.449-.196q-.39-.166-.556-.381-.166-.214-.166-.576 0-.4.166-.596.176-.195.507-.195.361 0 .508.234.156.234.176.703h1.123q-.03-.976-.498-1.484-.469-.518-1.309-.518"
+ clip-rule="evenodd"></path>
</symbol>
<symbol id="envelope" viewBox="0 0 16 16">
- <path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1zm13 2.383-4.708 2.825L15 11.105zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741M1 11.105l4.708-2.897L1 5.383z"/>
+ <path
+ d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1zm13 2.383-4.708 2.825L15 11.105zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741M1 11.105l4.708-2.897L1 5.383z"
+ ></path>
</symbol>
<symbol id="exclamation-triangle-fill" viewBox="0 0 16 16">
- <path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/>
+ <path
+ d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2"
+ ></path>
</symbol>
<symbol id="file-earmark-richtext" viewBox="0 0 16 16">
<path
></path>
</symbol>
<symbol id="github" viewBox="0 0 16 16">
- <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8"/>
+ <path
+ d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8"
+ ></path>
</symbol>
<symbol id="globe2" viewBox="0 0 16 16">
<path
></path>
</symbol>
<symbol id="filetype-js" viewBox="0 0 16 16">
- <path fill-rule="evenodd" d="M13 0a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zM8.053 6.596v3.127l-.007 1.752q0 .498-.186.752t-.556.263q-.342 0-.528-.234-.185-.234-.185-.684v-.175H5.37v.185q0 .665.253 1.113.255.45.703.674.44.225 1.016.225.88 0 1.406-.498.527-.498.527-1.485l.007-1.752V6.596zm3.808-.108q-.585 0-1.006.244a1.67 1.67 0 0 0-.634.674 2.1 2.1 0 0 0-.225.996q0 .753.293 1.182.303.42.967.732l.469.215q.438.186.625.43.185.244.185.635 0 .478-.166.703-.156.224-.527.224-.361.001-.547-.244-.186-.243-.205-.752H9.928q.02.996.498 1.524.479.527 1.386.527.909 0 1.417-.518.507-.517.507-1.484 0-.81-.332-1.289t-1.045-.79L11.91 9.3q-.39-.166-.556-.381-.166-.214-.166-.576 0-.4.165-.596.177-.195.508-.195.361 0 .508.234.156.234.176.703h1.123q-.03-.976-.498-1.484-.47-.518-1.309-.518" clip-rule="evenodd"></path>
+ <path
+ fill-rule="evenodd"
+ d="M13 0a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zM8.053 6.596v3.127l-.007 1.752q0 .498-.186.752t-.556.263q-.342 0-.528-.234-.185-.234-.185-.684v-.175H5.37v.185q0 .665.253 1.113.255.45.703.674.44.225 1.016.225.88 0 1.406-.498.527-.498.527-1.485l.007-1.752V6.596zm3.808-.108q-.585 0-1.006.244a1.67 1.67 0 0 0-.634.674 2.1 2.1 0 0 0-.225.996q0 .753.293 1.182.303.42.967.732l.469.215q.438.186.625.43.185.244.185.635 0 .478-.166.703-.156.224-.527.224-.361.001-.547-.244-.186-.243-.205-.752H9.928q.02.996.498 1.524.479.527 1.386.527.909 0 1.417-.518.507-.517.507-1.484 0-.81-.332-1.289t-1.045-.79L11.91 9.3q-.39-.166-.556-.381-.166-.214-.166-.576 0-.4.165-.596.177-.195.508-.195.361 0 .508.234.156.234.176.703h1.123q-.03-.976-.498-1.484-.47-.518-1.309-.518"
+ clip-rule="evenodd"></path>
</symbol>
<symbol id="grid-fill" viewBox="0 0 16 16">
<path
></path>
</symbol>
<symbol id="layers" viewBox="0 0 16 16">
- <path d="M15.106 11.554a.5.5 0 0 1 0 .894l-6.659 3.33-.107.045a1 1 0 0 1-.68 0l-.107-.046-6.658-3.329a.5.5 0 0 1 0-.895l1.988-.994 1.677.838-1.206.604L8 14.323l4.646-2.322-1.207-.604 1.678-.838z" opacity=".5"></path>
- <path fill-rule="evenodd" d="M7.553.223a1 1 0 0 1 .894 0l6.659 3.33a.5.5 0 0 1 0 .894l-6.659 3.33-.107.045a1 1 0 0 1-.68 0l-.107-.046L.895 4.447a.5.5 0 0 1 0-.894zM3.354 4 8 6.322 12.646 4 8 1.677z" clip-rule="evenodd" opacity=".5"></path>
- <path d="M15.106 7.554a.5.5 0 0 1 0 .894l-6.659 3.33c-.281.14-.613.14-.894 0L.895 8.447a.5.5 0 0 1 0-.894l1.988-.995 4.222 2.112a2 2 0 0 0 1.79 0l4.222-2.112z"></path>
+ <path
+ d="M15.106 11.554a.5.5 0 0 1 0 .894l-6.659 3.33-.107.045a1 1 0 0 1-.68 0l-.107-.046-6.658-3.329a.5.5 0 0 1 0-.895l1.988-.994 1.677.838-1.206.604L8 14.323l4.646-2.322-1.207-.604 1.678-.838z"
+ opacity=".5"></path>
+ <path
+ fill-rule="evenodd"
+ d="M7.553.223a1 1 0 0 1 .894 0l6.659 3.33a.5.5 0 0 1 0 .894l-6.659 3.33-.107.045a1 1 0 0 1-.68 0l-.107-.046L.895 4.447a.5.5 0 0 1 0-.894zM3.354 4 8 6.322 12.646 4 8 1.677z"
+ clip-rule="evenodd"
+ opacity=".5"></path>
+ <path
+ d="M15.106 7.554a.5.5 0 0 1 0 .894l-6.659 3.33c-.281.14-.613.14-.894 0L.895 8.447a.5.5 0 0 1 0-.894l1.988-.995 4.222 2.112a2 2 0 0 0 1.79 0l4.222-2.112z"
+ ></path>
</symbol>
<symbol id="list" viewBox="0 0 16 16">
<path
></path>
</symbol>
<symbol id="map" viewBox="0 0 16 16">
- <path fill-rule="evenodd" d="M15.817.113A.5.5 0 0 1 16 .5v14a.5.5 0 0 1-.402.49l-5 1a.5.5 0 0 1-.196 0L5.5 15.01l-4.902.98A.5.5 0 0 1 0 15.5v-14a.5.5 0 0 1 .402-.49l5-1a.5.5 0 0 1 .196 0L10.5.99l4.902-.98a.5.5 0 0 1 .415.103M10 1.91l-4-.8v12.98l4 .8zm1 12.98 4-.8V1.11l-4 .8zm-6-.8V1.11l-4 .8v12.98z"/>
+ <path
+ fill-rule="evenodd"
+ d="M15.817.113A.5.5 0 0 1 16 .5v14a.5.5 0 0 1-.402.49l-5 1a.5.5 0 0 1-.196 0L5.5 15.01l-4.902.98A.5.5 0 0 1 0 15.5v-14a.5.5 0 0 1 .402-.49l5-1a.5.5 0 0 1 .196 0L10.5.99l4.902-.98a.5.5 0 0 1 .415.103M10 1.91l-4-.8v12.98l4 .8zm1 12.98 4-.8V1.11l-4 .8zm-6-.8V1.11l-4 .8v12.98z"
+ ></path>
</symbol>
<symbol id="menu-button-wide-fill" viewBox="0 0 16 16">
<path
></path>
</symbol>
<symbol id="search" viewBox="0 0 16 16">
- <path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"/>
+ <path
+ d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"
+ ></path>
</symbol>
<symbol id="stack" viewBox="0 0 16 16">
- <path d="m14.12 10.163 1.715.858c.22.11.22.424 0 .534L8.267 15.34a.6.6 0 0 1-.534 0L.165 11.555a.299.299 0 0 1 0-.534l1.716-.858 5.317 2.659c.505.252 1.1.252 1.604 0l5.317-2.66zM7.733.063a.6.6 0 0 1 .534 0l7.568 3.784a.3.3 0 0 1 0 .535L8.267 8.165a.6.6 0 0 1-.534 0L.165 4.382a.299.299 0 0 1 0-.535z"/>
- <path d="m14.12 6.576 1.715.858c.22.11.22.424 0 .534l-7.568 3.784a.6.6 0 0 1-.534 0L.165 7.968a.299.299 0 0 1 0-.534l1.716-.858 5.317 2.659c.505.252 1.1.252 1.604 0z"/>
+ <path
+ d="m14.12 10.163 1.715.858c.22.11.22.424 0 .534L8.267 15.34a.6.6 0 0 1-.534 0L.165 11.555a.299.299 0 0 1 0-.534l1.716-.858 5.317 2.659c.505.252 1.1.252 1.604 0l5.317-2.66zM7.733.063a.6.6 0 0 1 .534 0l7.568 3.784a.3.3 0 0 1 0 .535L8.267 8.165a.6.6 0 0 1-.534 0L.165 4.382a.299.299 0 0 1 0-.535z"
+ ></path>
+ <path
+ d="m14.12 6.576 1.715.858c.22.11.22.424 0 .534l-7.568 3.784a.6.6 0 0 1-.534 0L.165 7.968a.299.299 0 0 1 0-.534l1.716-.858 5.317 2.659c.505.252 1.1.252 1.604 0z"
+ ></path>
</symbol>
<symbol id="sun-fill" viewBox="0 0 16 16">
<path
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
<symbol id="arrow-left" viewBox="0 0 16 16">
- <path fill-rule="evenodd" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8"/>
+ <path
+ fill-rule="evenodd"
+ d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8"
+ ></path>
</symbol>
<symbol id="arrow-right" viewBox="0 0 16 16">
- <path fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8"/>
+ <path
+ fill-rule="evenodd"
+ d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8"
+ ></path>
</symbol>
<symbol id="plus-lg" viewBox="0 0 16 16">
- <path fill-rule="evenodd" d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2Z"/>
+ <path
+ fill-rule="evenodd"
+ d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2Z"></path>
</symbol>
</svg>
>
<span>Primary</span>
<svg class="bi ms-1" width="16" height="16" aria-hidden="true">
- <use href="#chevron-expand" />
+ <use href="#chevron-expand"></use>
</svg>
</button>
<div class="menu">
- {themeColors.map((themeColor) => (
- <a
- class:list={['menu-item', { 'active': themeColor.name === 'primary' }]}
- href="#"
- data-color={themeColor.name}
- data-selected={themeColor.name === 'primary'}
- >
- {themeColor.title}
- </a>
- ))}
+ {
+ themeColors.map((themeColor) => (
+ <a
+ class:list={['menu-item', { active: themeColor.name === 'primary' }]}
+ href="#"
+ data-color={themeColor.name}
+ data-selected={themeColor.name === 'primary'}
+ >
+ {themeColor.title}
+ </a>
+ ))
+ }
</div>
</div>
</div>
<div class="vstack gap-1 flex-grow-0">
<label class="form-label fw-semibold mb-0">Style</label>
<div class="btn-group text-capitalize btn-group-sm" role="group" aria-label="Button style">
- {styles.map((style) => (
- <label class="btn-check btn-outline theme-secondary">
- <input
- type="radio"
- name="btn-style"
- value={style}
- checked={style === 'styled'}
- data-style={style}
- />
- {style || 'default'}
- </label>
- ))}
+ {
+ styles.map((style) => (
+ <label class="btn-check btn-outline theme-secondary">
+ <input type="radio" name="btn-style" value={style} checked={style === 'styled'} data-style={style} />
+ {style || 'default'}
+ </label>
+ ))
+ }
</div>
</div>
>
<span>Medium</span>
<svg class="bi ms-1" width="16" height="16" aria-hidden="true">
- <use href="#chevron-expand" />
+ <use href="#chevron-expand"></use>
</svg>
</button>
<div class="menu">
- {sizes.map((size) => (
- <a
- class:list={['menu-item', { 'active': size.value === '' }]}
- href="#"
- data-size={size.value}
- data-selected={size.value === ''}
- >
- {size.label}
- </a>
- ))}
+ {
+ sizes.map((size) => (
+ <a
+ class:list={['menu-item', { active: size.value === '' }]}
+ href="#"
+ data-size={size.value}
+ data-selected={size.value === ''}
+ >
+ {size.label}
+ </a>
+ ))
+ }
</div>
</div>
</div>
<div class="vstack gap-1 flex-grow-0">
<label class="form-label fw-semibold mb-0">Rounded</label>
<div class="btn-group text-capitalize btn-group-sm" role="group" aria-label="Button rounded">
- {rounded.map((round) => (
- <label class="btn-check btn-outline theme-secondary">
- <input
- type="radio"
- name="btn-rounded"
- value={round}
- checked={round === 'default'}
- data-rounded={round}
- />
- {round}
- </label>
- ))}
+ {
+ rounded.map((round) => (
+ <label class="btn-check btn-outline theme-secondary">
+ <input type="radio" name="btn-rounded" value={round} checked={round === 'default'} data-rounded={round} />
+ {round}
+ </label>
+ ))
+ }
</div>
</div>
</div>
/>
<script>
- import { Menu } from '../../../../dist/js/bootstrap.bundle.js'
+ import { Menu } from '@bootstrap'
const colorMenuButton = document.querySelector('#btn-color-menu') as HTMLButtonElement
const colorMenuItems = document.querySelectorAll('#btn-color-menu + .menu .menu-item')
const roundedInputs = document.querySelectorAll('input[name="btn-rounded"]')
const previewButtons = document.querySelectorAll('#button-preview button')
// Find the code snippet inside the Example component
- const codeSnippet = document.querySelector('#button-preview')?.closest('.bd-example-snippet')?.querySelector('.highlight code') as HTMLElement
+ const codeSnippet = document
+ .querySelector('#button-preview')
+ ?.closest('.bd-example-snippet')
+ ?.querySelector('.highlight code') as HTMLElement
// Get all theme color names for removal
- const themeColorNames = Array.from(colorMenuItems).map(item => (item as HTMLElement).dataset.color || '')
+ const themeColorNames = Array.from(colorMenuItems).map((item) => (item as HTMLElement).dataset.color || '')
function generateHTML(button: HTMLElement): string {
- const classes = Array.from(button.classList).filter(cls =>
- cls === 'btn' || cls.startsWith('btn-') || cls.startsWith('theme-') || cls.startsWith('rounded')
- ).join(' ')
+ const classes = Array.from(button.classList)
+ .filter((cls) => cls === 'btn' || cls.startsWith('btn-') || cls.startsWith('theme-') || cls.startsWith('rounded'))
+ .join(' ')
const buttonType = button.getAttribute('data-button-type')
const ariaLabel = button.getAttribute('aria-label')
if (buttonType === 'text') {
html += 'Click me'
} else if (buttonType === 'left-icon') {
- html += '\n <svg class="bi me-1" width="16" height="16" aria-hidden="true">\n <use href="#arrow-left" />\n </svg>\n With left icon'
+ html +=
+ '\n <svg class="bi me-1" width="16" height="16" aria-hidden="true">\n <use href="#arrow-left" />\n </svg>\n With left icon'
} else if (buttonType === 'right-icon') {
- html += 'With right icon\n <svg class="bi ms-1" width="16" height="16" aria-hidden="true">\n <use href="#arrow-right" />\n </svg>'
+ html +=
+ 'With right icon\n <svg class="bi ms-1" width="16" height="16" aria-hidden="true">\n <use href="#arrow-right" />\n </svg>'
} else if (buttonType === 'icon-only') {
html += '\n <svg class="bi" width="16" height="16" aria-hidden="true">\n <use href="#plus-lg" />\n </svg>'
}
function updateCodeSnippet() {
if (!codeSnippet) return
- const htmlSnippets = Array.from(previewButtons).map(button => generateHTML(button as HTMLElement))
+ const htmlSnippets = Array.from(previewButtons).map((button) => generateHTML(button as HTMLElement))
const htmlCode = htmlSnippets.join('\n\n')
// Update the code content
function updateButtons() {
const selectedColor = colorMenuButton?.dataset.color || 'primary'
- const selectedStyle = (document.querySelector('input[name="btn-style"]:checked') as HTMLInputElement)?.value || 'solid'
+ const selectedStyle =
+ (document.querySelector('input[name="btn-style"]:checked') as HTMLInputElement)?.value || 'solid'
const selectedSize = sizeMenuButton?.dataset.size || ''
- const selectedRounded = (document.querySelector('input[name="btn-rounded"]:checked') as HTMLInputElement)?.value || 'default'
+ const selectedRounded =
+ (document.querySelector('input[name="btn-rounded"]:checked') as HTMLInputElement)?.value || 'default'
previewButtons.forEach((button) => {
// Remove all theme color classes
- themeColorNames.forEach(color => button.classList.remove(`theme-${color}`))
+ themeColorNames.forEach((color) => button.classList.remove(`theme-${color}`))
// Add selected theme color
button.classList.add(`theme-${selectedColor}`)
// Handle rounded - remove all rounded classes first
const roundedClasses = ['rounded-pill', 'rounded-0', 'rounded']
- roundedClasses.forEach(cls => button.classList.remove(cls))
+ roundedClasses.forEach((cls) => button.classList.remove(cls))
if (selectedRounded === 'pill') {
button.classList.add('rounded-pill')
---
<p>
- {component} use local CSS variables on <code>.{className}</code> for real-time customization.
- Values for the CSS variables are generated from Sass maps unique to each component and applied to the aforementioned class.
+ {component} use local CSS variables on <code>.{className}</code> for real-time customization. Values for the CSS variables
+ are generated from Sass maps unique to each component and applied to the aforementioned class.
</p>
let Content: MarkdownInstance<{}>['Content'] | undefined
if (name) {
- const callout = await getCalloutByName(name) as any
+ const callout = (await getCalloutByName(name)) as any
if (!callout) {
throw new Error(`Could not find callout with name '${name}'.`)
}
- const namedCallout = await render(callout) as any
+ const namedCallout = (await render(callout)) as any
Content = namedCallout.Content
}
---
const { class: className, dismiss, target } = Astro.props
---
-<button type="button" class:list={["btn-close", className]} data-bs-dismiss={dismiss} aria-label="Close" data-bs-target={target}>
+<button
+ type="button"
+ class:list={['btn-close', className]}
+ data-bs-dismiss={dismiss}
+ aria-label="Close"
+ data-bs-target={target}
+>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="20" height="20" fill="none">
- <path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm3.354 4.646L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 1 1 .708-.708"/>
+ <path
+ d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm3.354 4.646L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 1 1 .708-.708"
+ ></path>
</svg>
</button>
tabs
} = Astro.props
-const fileUrl = file
- ? `${getConfig().repo}/blob/v${getConfig().current_version}/${file}`.replaceAll('\\', '/')
- : null
+const fileUrl = file ? `${getConfig().repo}/blob/v${getConfig().current_version}/${file}`.replaceAll('\\', '/') : null
// Extract language from multiple possible sources (for markdown code blocks)
// Priority: lang prop > data-language attribute > className pattern
// 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()
+ ? 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
throw new Error(`The file at ${filePath} does not contain a match for the regex '${fileMatch}'.`)
}
- codeToDisplay = matches.map(m => m[0]).join('\n\n')
+ codeToDisplay = matches.map((m) => m[0]).join('\n\n')
}
-const diffTransformers = [
- transformerNotationDiff(),
- transformerNotationHighlight()
-]
+const diffTransformers = [transformerNotationDiff(), transformerNotationHighlight()]
// Process tabs if provided
let highlightedTabs: Array<{ label: string; code: string }> | null = null
highlightedTabs = await Promise.all(
tabs.map(async (tab) => ({
label: tab.label,
- code: await highlightCode(
- replaceConfigInText(tab.code),
- tab.lang || detectedLang || 'bash',
- diffTransformers
- )
+ code: await highlightCode(replaceConfigInText(tab.code), tab.lang || detectedLang || 'bash', diffTransformers)
}))
)
}
-const highlightedCode = codeToDisplay && detectedLang
- ? await highlightCode(codeToDisplay, detectedLang, diffTransformers)
- : null
+const highlightedCode =
+ codeToDisplay && detectedLang ? await highlightCode(codeToDisplay, detectedLang, diffTransformers) : null
---
<script>
- import { Tooltip } from '../../../../dist/js/bootstrap.bundle.js'
+ import { Tooltip } from '@bootstrap'
import { initCopyButtons } from '@libs/clipboard'
document.querySelectorAll('.btn-edit').forEach((btn) => {
// Handle tab switching
document.querySelectorAll('.code-tabs').forEach((tabContainer) => {
const buttons = tabContainer.querySelectorAll('.code-tab-btn')
- const parentContainer = tabContainer.closest('.bd-code-snippet') ||
- tabContainer.closest('.bd-example-snippet') ||
- tabContainer.parentElement?.parentElement
+ 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) => {
})
</script>
-{nestedInExample ? (
- <>
- {!noToolbar && (
- <div class="hstack highlight-toolbar align-items-center">
- {highlightedTabs ? (
- <div class="code-tabs">
- {highlightedTabs.map((tab, index) => (
- <button
- type="button"
- class:list={['code-tab-btn', { active: index === 0 }]}
- >
- {tab.label}
+{
+ nestedInExample ? (
+ <>
+ {!noToolbar && (
+ <div class="hstack highlight-toolbar align-items-center">
+ {highlightedTabs ? (
+ <div class="code-tabs">
+ {highlightedTabs.map((tab, index) => (
+ <button type="button" class:list={['code-tab-btn', { active: index === 0 }]}>
+ {tab.label}
+ </button>
+ ))}
+ </div>
+ ) : file ? (
+ <a
+ class="text-decoration-none font-monospace fs-xs fg-3"
+ href={fileUrl}
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ {file}
+ </a>
+ ) : (
+ <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 href="#lightning-charge-fill" />
+ </svg>
</button>
- ))}
- </div>
- ) : file ? (
- <a class="text-decoration-none font-monospace fs-xs fg-3" href={fileUrl} target="_blank" rel="noopener noreferrer">{file}</a>
- ) : (
- <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 href="#lightning-charge-fill" />
+ )}
+ <button type="button" class="btn btn-xs btn-icon bg-transparent" title="Copy" data-bd-clipboard>
+ <svg class="bi fs-md" aria-hidden="true">
+ <use href="#copy" />
</svg>
</button>
- )}
- <button type="button" class="btn btn-xs btn-icon bg-transparent" title="Copy" data-bd-clipboard>
- <svg class="bi fs-md" aria-hidden="true">
- <use href="#copy" />
- </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 class:list={["bd-code-snippet", containerClass]}>
- {!noToolbar && (
- <div class="hstack highlight-toolbar align-items-center">
- {highlightedTabs ? (
- <div class="code-tabs">
- {highlightedTabs.map((tab, index) => (
- <button
- type="button"
- class:list={['code-tab-btn', { active: index === 0 }]}
- >
- {tab.label}
- </button>
- ))}
+ </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 class:list={['bd-code-snippet', containerClass]}>
+ {!noToolbar && (
+ <div class="hstack highlight-toolbar align-items-center">
+ {highlightedTabs ? (
+ <div class="code-tabs">
+ {highlightedTabs.map((tab, index) => (
+ <button type="button" class:list={['code-tab-btn', { active: index === 0 }]}>
+ {tab.label}
+ </button>
+ ))}
+ </div>
+ ) : file ? (
+ <a
+ class="text-decoration-none font-monospace fs-xs fg-3"
+ href={fileUrl}
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ {file}
+ </a>
+ ) : (
+ <div class="font-monospace text-uppercase fs-xs fg-3">{displayLang}</div>
+ )}
+ <div class="d-flex ms-auto">
+ <button type="button" class="btn btn-xs btn-icon bg-transparent" title="Copy" data-bd-clipboard>
+ <svg class="bi fs-md" aria-hidden="true">
+ <use href="#copy" />
+ </svg>
+ </button>
</div>
- ) : file ? (
- <a class="text-decoration-none font-monospace fs-xs fg-3" href={fileUrl} target="_blank" rel="noopener noreferrer">{file}</a>
- ) : (
- <div class="font-monospace text-uppercase fs-xs fg-3">{displayLang}</div>
- )}
- <div class="d-flex ms-auto">
- <button type="button" class="btn btn-xs btn-icon bg-transparent" title="Copy" data-bd-clipboard>
- <svg class="bi fs-md" aria-hidden="true">
- <use href="#copy" />
- </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>
-)}
+ {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>
+ )
+}
<Fragment set:html={highlightedCode} />
<button type="button" class="btn btn-xs btn-icon bg-transparent" title="Copy" data-bd-clipboard>
<svg class="bi fs-md" aria-hidden="true">
- <use href="#copy" />
+ <use href="#copy"></use>
</svg>
</button>
</div>
display: inline-flex;
align-items: center;
justify-content: center;
- gap: .5rem;
+ gap: 0.5rem;
min-height: 2.5rem;
margin: 0;
- padding: .75rem 1rem;
+ padding: 0.75rem 1rem;
font-size: var(--font-size-md);
background-color: var(--bg-1);
border: 1px solid var(--border-color-translucent);
height: 1.5rem;
flex-shrink: 0;
padding: 0;
- margin: -.5rem -.25rem -.5rem 1rem;
+ margin: -0.5rem -0.25rem -0.5rem 1rem;
}
</style>
* The name of an existing details content to display located in `src/content/details`.
* This will override any content passed in via the default slot.
*/
- name?:
- | 'danger-example'
- | 'info-example'
- | 'warning-color-assistive-technologies'
- | 'warning-example'
+ name?: 'danger-example' | 'info-example' | 'warning-color-assistive-technologies' | 'warning-example'
/**
* The summary text displayed before the details are expanded.
* If not provided and `name` is set, will use the `title` from the markdown frontmatter.
let Content: MarkdownInstance<{}>['Content'] | undefined
if (name) {
- const details = await getDetailsByName(name) as any
+ const details = (await getDetailsByName(name)) as any
if (!details) {
throw new Error(`Could not find details with name '${name}'.`)
summary = details.data.title
}
- const namedDetails = await render(details) as any
+ const namedDetails = (await render(details)) as any
Content = namedDetails.Content
}
<details class="bd-details">
<summary class="bd-details-summary">
- <svg class="bd-details-icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
- <path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708"/>
+ <svg
+ class="bd-details-icon"
+ xmlns="http://www.w3.org/2000/svg"
+ width="16"
+ height="16"
+ fill="currentColor"
+ viewBox="0 0 16 16"
+ >
+ <path
+ fill-rule="evenodd"
+ d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708"
+ ></path>
</svg>
{summary}
</summary>
markup = replacePlaceholdersInHtml(markup)
// Use customMarkup for source code display if provided, otherwise use the processed markup
-let sourceMarkup = customMarkup
- ? (Array.isArray(customMarkup) ? customMarkup.join('\n') : customMarkup)
- : markup
+let sourceMarkup = customMarkup ? (Array.isArray(customMarkup) ? customMarkup.join('\n') : customMarkup) : markup
const simplifiedMarkup = sourceMarkup
.replace(
---
<div class="bd-example-snippet bd-code-snippet not-prose">
- {showPreview && (
- <div id={id} class:list={['bd-example', className]}>
- <Fragment set:html={markup} />
- </div>
- )}
- {showMarkup && (
- <Code code={simplifiedMarkup} lang={lang} file={file} nestedInExample={true} addStackblitzJs={addStackblitzJs} />
- )}
+ {
+ showPreview && (
+ <div id={id} class:list={['bd-example', className]}>
+ <Fragment set:html={markup} />
+ </div>
+ )
+ }
+ {
+ showMarkup && (
+ <Code code={simplifiedMarkup} lang={lang} file={file} nestedInExample={true} addStackblitzJs={addStackblitzJs} />
+ )
+ }
</div>
<label class="form-label fw-semibold mb-0">Placement type</label>
<div class="btn-group btn-group-sm" role="group" aria-label="Placement type">
<label class="btn-check btn-outline theme-secondary">
- <input
- type="radio"
- name="menu-placement-type"
- value="physical"
- checked
- data-placement-type="physical"
- />
+ <input type="radio" name="menu-placement-type" value="physical" checked data-placement-type="physical" />
Physical
</label>
<label class="btn-check btn-outline theme-secondary">
- <input
- type="radio"
- name="menu-placement-type"
- value="logical"
- data-placement-type="logical"
- />
+ <input type="radio" name="menu-placement-type" value="logical" data-placement-type="logical" />
Logical
</label>
</div>
>
<span>bottom-start</span>
<svg class="bi ms-1" width="16" height="16" aria-hidden="true">
- <use href="#chevron-expand" />
+ <use href="#chevron-expand"></use>
</svg>
</button>
<div class="menu" id="menu-placement-selector-menu">
- {physicalPlacements.map((p) => (
- <button
- class:list={['menu-item', { 'active': p.value === 'bottom-start' }]}
- type="button"
- data-placement-group="physical"
- data-placement={p.value}
- >
- {p.label}
- </button>
- ))}
- {logicalPlacements.map((p) => (
- <button
- class="menu-item d-none"
- type="button"
- data-placement-group="logical"
- data-placement={p.value}
- >
- {p.label}
- </button>
- ))}
+ {
+ physicalPlacements.map((p) => (
+ <button
+ class:list={['menu-item', { active: p.value === 'bottom-start' }]}
+ type="button"
+ data-placement-group="physical"
+ data-placement={p.value}
+ >
+ {p.label}
+ </button>
+ ))
+ }
+ {
+ logicalPlacements.map((p) => (
+ <button class="menu-item d-none" type="button" data-placement-group="logical" data-placement={p.value}>
+ {p.label}
+ </button>
+ ))
+ }
</div>
</div>
</div>
/>
<script>
- import { Menu } from '../../../../dist/js/bootstrap.bundle.js'
+ import { Menu } from '@bootstrap'
const selectorButton = document.querySelector('#menu-placement-selector') as HTMLButtonElement
const selectorItems = document.querySelectorAll('#menu-placement-selector-menu .menu-item')
const codeSnippet = previewContainer?.closest('.bd-example-snippet')?.querySelector('.highlight code') as HTMLElement
function updateSelectorItems() {
- const selectedType = (document.querySelector('input[name="menu-placement-type"]:checked') as HTMLInputElement)?.value || 'physical'
+ const selectedType =
+ (document.querySelector('input[name="menu-placement-type"]:checked') as HTMLInputElement)?.value || 'physical'
- document.querySelectorAll('#menu-placement-selector-menu [data-placement-group="physical"]').forEach(el => {
+ document.querySelectorAll('#menu-placement-selector-menu [data-placement-group="physical"]').forEach((el) => {
el.classList.toggle('d-none', selectedType !== 'physical')
})
- document.querySelectorAll('#menu-placement-selector-menu [data-placement-group="logical"]').forEach(el => {
+ document.querySelectorAll('#menu-placement-selector-menu [data-placement-group="logical"]').forEach((el) => {
el.classList.toggle('d-none', selectedType !== 'logical')
})
if (labelSpan) labelSpan.textContent = placement
selectorButton.dataset.placement = placement
- selectorItems.forEach(item => {
+ selectorItems.forEach((item) => {
const itemPlacement = (item as HTMLElement).dataset.placement
item.classList.toggle('active', itemPlacement === placement)
})
if (selectorButton) {
const selectorMenu = Menu.getOrCreateInstance(selectorButton)
- selectorItems.forEach(item => {
+ selectorItems.forEach((item) => {
item.addEventListener('click', (e) => {
e.preventDefault()
const placement = (item as HTMLElement).dataset.placement || 'bottom-start'
})
}
- placementTypeInputs.forEach(input => {
+ placementTypeInputs.forEach((input) => {
input.addEventListener('change', updateSelectorItems)
})
>
<span>Default (static)</span>
<svg class="bi ms-1" width="16" height="16" aria-hidden="true">
- <use href="#chevron-expand" />
+ <use href="#chevron-expand"></use>
</svg>
</button>
<div class="menu">
- {placements.map((p) => (
- <a
- class:list={['menu-item', { 'active': p.value === '' }]}
- href="#"
- data-placement={p.value}
- >
- {p.label}
- </a>
- ))}
+ {
+ placements.map((p) => (
+ <a class:list={['menu-item', { active: p.value === '' }]} href="#" data-placement={p.value}>
+ {p.label}
+ </a>
+ ))
+ }
</div>
</div>
</div>
/>
<script>
- import { Menu } from '../../../../dist/js/bootstrap.bundle.js'
+ import { Menu } from '@bootstrap'
const placementMenuButton = document.querySelector('#navbar-placement-menu') as HTMLButtonElement
const placementMenuItems = document.querySelectorAll('#navbar-placement-menu + .menu .menu-item')
const labelSpan = placementMenuButton.querySelector('span')
if (labelSpan) {
- labelSpan.textContent = placementLabels[placement] ? `${placementLabels[placement]}${placement ? '' : ' (static)'}` : 'Default (static)'
+ labelSpan.textContent = placementLabels[placement]
+ ? `${placementLabels[placement]}${placement ? '' : ' (static)'}`
+ : 'Default (static)'
}
placementMenuButton.dataset.placement = placement
- placementMenuItems.forEach(item => {
+ placementMenuItems.forEach((item) => {
const itemPlacement = (item as HTMLElement).dataset.placement
item.classList.toggle('active', itemPlacement === placement)
})
// Remove all placement classes
- placementClasses.forEach(cls => {
+ placementClasses.forEach((cls) => {
previewNavbar.classList.remove(cls)
})
if (placementMenuButton) {
const placementMenu = Menu.getOrCreateInstance(placementMenuButton)
- placementMenuItems.forEach(item => {
+ placementMenuItems.forEach((item) => {
item.addEventListener('click', (e) => {
e.preventDefault()
const placement = (item as HTMLElement).dataset.placement || ''
let markup = Array.isArray(code) ? code.join('\n') : code
markup = replacePlaceholdersInHtml(markup)
-const simplifiedMarkup = markup
- .replace(
- /<svg.*class="bd-placeholder-img(?:-lg)?(?: *?bd-placeholder-img-lg)? ?(.*?)".*?<\/svg>/g,
- (match, classes) => `<img src="..."${classes ? ` class="${classes}"` : ''} alt="...">`
- )
+const simplifiedMarkup = markup.replace(
+ /<svg.*class="bd-placeholder-img(?:-lg)?(?: *?bd-placeholder-img-lg)? ?(.*?)".*?<\/svg>/g,
+ (match, classes) => `<img src="..."${classes ? ` class="${classes}"` : ''} alt="...">`
+)
---
<div class="bd-example-snippet bd-code-snippet">
<Fragment set:html={markup} />
</div>
</div>
- {showMarkup && (
- <Code code={simplifiedMarkup} lang="html" nestedInExample={true} />
- )}
+ {showMarkup && <Code code={simplifiedMarkup} lang="html" nestedInExample={true} />}
</div>
const contrastStyles = {
marginLeft: 'auto',
- opacity: .5,
+ opacity: 0.5,
fontSize: '.75rem',
fontFamily: 'var(--bs-font-monospace)',
color: isLowContrast ? 'red' : 'inherit'
}
/* Show dark contrast in dark mode */
- :global([data-bs-theme="dark"]) .contrast-light {
+ :global([data-bs-theme='dark']) .contrast-light {
display: none;
}
- :global([data-bs-theme="dark"]) .contrast-dark {
+ :global([data-bs-theme='dark']) .contrast-dark {
display: inline;
}
}
/* Show dark value in dark mode */
- :global([data-bs-theme="dark"]) .css-var-light {
+ :global([data-bs-theme='dark']) .css-var-light {
display: none;
}
- :global([data-bs-theme="dark"]) .css-var-dark {
+ :global([data-bs-theme='dark']) .css-var-dark {
display: inline;
}
</style>
-{size === 'inline' ? (
- <>
+{
+ size === 'inline' ? (
+ <>
+ <span style={combinedStyles}>
+ <slot />
+ {contrast && (
+ <span style={contrastStyles} class="contrast-light">
+ {contrast}
+ </span>
+ )}
+ {contrastDark && (
+ <span style={{ ...contrastStyles, color: isLowContrastDark ? 'red' : 'inherit' }} class="contrast-dark">
+ {contrastDark}
+ </span>
+ )}
+ </span>
+ {showVar && displayCssVar && (
+ <code class="css-var" data-css-var={`--bs-${displayCssVar}`}>
+ <span class="css-var-light">Loading...</span>
+ <span class="css-var-dark">Loading...</span>
+ </code>
+ )}
+ </>
+ ) : size === 'large' ? (
<span style={combinedStyles}>
<slot />
- {contrast && <span style={contrastStyles} class="contrast-light">{contrast}</span>}
- {contrastDark && <span style={{...contrastStyles, color: isLowContrastDark ? 'red' : 'inherit'}} class="contrast-dark">{contrastDark}</span>}
+ {contrast && (
+ <span style={contrastStyles} class="contrast-light">
+ {contrast}
+ </span>
+ )}
+ {contrastDark && (
+ <span style={{ ...contrastStyles, color: isLowContrastDark ? 'red' : 'inherit' }} class="contrast-dark">
+ {contrastDark}
+ </span>
+ )}
</span>
- {showVar && displayCssVar && (
- <code class="css-var" data-css-var={`--bs-${displayCssVar}`}>
- <span class="css-var-light">Loading...</span>
- <span class="css-var-dark">Loading...</span>
- </code>
- )}
- </>
-) : size === 'large' ? (
- <span style={combinedStyles}>
- <slot />
- {contrast && <span style={contrastStyles} class="contrast-light">{contrast}</span>}
- {contrastDark && <span style={{...contrastStyles, color: isLowContrastDark ? 'red' : 'inherit'}} class="contrast-dark">{contrastDark}</span>}
- </span>
-) : (
- <div style={combinedStyles}>
- <slot />
- {contrast && <span style={contrastStyles} class="contrast-light">{contrast}</span>}
- {contrastDark && <span style={{...contrastStyles, color: isLowContrastDark ? 'red' : 'inherit'}} class="contrast-dark">{contrastDark}</span>}
- </div>
-)}
+ ) : (
+ <div style={combinedStyles}>
+ <slot />
+ {contrast && (
+ <span style={contrastStyles} class="contrast-light">
+ {contrast}
+ </span>
+ )}
+ {contrastDark && (
+ <span style={{ ...contrastStyles, color: isLowContrastDark ? 'red' : 'inherit' }} class="contrast-dark">
+ {contrastDark}
+ </span>
+ )}
+ </div>
+ )
+}
<script>
// Handle dynamic CSS variable display with light-dark() function support
document.addEventListener('DOMContentLoaded', () => {
const cssVarElements = document.querySelectorAll('.css-var[data-css-var]')
- cssVarElements.forEach(element => {
+ cssVarElements.forEach((element) => {
const cssVarName = element.getAttribute('data-css-var')
if (cssVarName) {
const mainProps = overrides?.main ?? {}
---
-<!DOCTYPE html>
+<!doctype html>
<html lang="en" data-bs-theme="auto">
<head>
- <Head
- description={description}
- layout={layout}
- robots={robots}
- thumbnail={thumbnail}
- title={title}
- />
+ <Head description={description} layout={layout} robots={robots} thumbnail={thumbnail} title={title} />
</head>
<body {...bodyProps}>
<div class="navbar-translucent sticky-top bd-sticky-navbar">
import ReferenceTable from '@components/ReferenceTable.astro'
import UtilityReferenceTable from '@components/UtilityReferenceTable.astro'
import HelperReferenceTable from '@components/HelperReferenceTable.astro'
-import { getData } from '@libs/data'
+import { getData, type SidebarItem, type SidebarSubItem } from '@libs/data'
import CloseButton from '@components/shortcodes/CloseButton.astro'
import PageMeta from '@components/PageMeta.astro'
// Create a flat array of all pages with their groups, handling nested groups
const allPages: NavigationPage[] = sidebar.flatMap((group) => {
if (!group.pages) return []
- return group.pages.flatMap((item: any) => {
+ return group.pages.flatMap((item: SidebarItem) => {
// Handle nested groups (e.g., Utilities > Borders > Border)
if (item.group && item.pages) {
- return item.pages.map((page: any) => ({
+ const subgroupTitle = item.group
+ return item.pages.map((page: SidebarSubItem) => ({
title: page.title,
url: `/docs/${getConfig().docs_version}/${getSlug(group.title)}/${getSlug(page.title)}/`,
- groupTitle: item.group
+ groupTitle: subgroupTitle
}))
}
// Handle direct pages
if (item.title) {
- return [{
- title: item.title,
- url: `/docs/${getConfig().docs_version}/${getSlug(group.title)}/${getSlug(item.title)}/`,
- groupTitle: group.title
- }]
+ return [
+ {
+ title: item.title,
+ url: `/docs/${getConfig().docs_version}/${getSlug(group.title)}/${getSlug(item.title)}/`,
+ groupTitle: group.title
+ }
+ ]
}
return []
})
})
// Find the current page index
-const currentPageIndex = allPages.findIndex(
- (page) => page.url === `/docs/${getConfig().docs_version}/${slug}/`
-)
+const currentPageIndex = allPages.findIndex((page) => page.url === `/docs/${getConfig().docs_version}/${slug}/`)
if (currentPageIndex > 0) {
prevPage = allPages[currentPageIndex - 1]
)
}
- {
- frontmatter.reference && (
- <ReferenceTable reference={frontmatter.reference} />
- )
- }
+ {frontmatter.reference && <ReferenceTable reference={frontmatter.reference} />}
- {
- frontmatter.utility && (
- <UtilityReferenceTable utility={frontmatter.utility} />
- )
- }
+ {frontmatter.utility && <UtilityReferenceTable utility={frontmatter.utility} />}
- {
- frontmatter.classes && (
- <HelperReferenceTable classes={frontmatter.classes} />
- )
- }
+ {frontmatter.classes && <HelperReferenceTable classes={frontmatter.classes} />}
<slot />
const canonicalUrl = new URL(Astro.url.pathname, Astro.site)
---
-<!DOCTYPE html>
+<!doctype html>
<html lang="en" class:list={html_class} data-bs-theme="auto">
<head>
<meta charset="utf-8" />
<link rel="canonical" href={canonicalUrl} />
+ {/* Loaded as `is:inline` (not via Vite) so the theme is applied before paint. */}
+ {/* The script lives in `site/static/docs/[version]/assets/js/` so it can */}
+ {/* render-block at this exact spot in the head without going through the */}
+ {/* bundler (which would defer it and cause a flash of the wrong theme). */}
<script is:inline src={getVersionedDocsPath('assets/js/color-modes.js')}></script>
- <Stylesheet layout="examples" />
+ <Stylesheet />
<Favicons />
<style is:global>
background-color: rgba(0, 0, 0, 0.1);
border: solid rgba(0, 0, 0, 0.15);
border-width: 1px 0;
- box-shadow: inset 0 0.5em 1.5em rgba(0, 0, 0, 0.1), inset 0 0.125em 0.5em rgba(0, 0, 0, 0.15);
+ box-shadow:
+ inset 0 0.5em 1.5em rgba(0, 0, 0, 0.1),
+ inset 0 0.125em 0.5em rgba(0, 0, 0, 0.15);
}
.b-example-vr {
<svg class="bi bi-sm" aria-hidden="true"><use href="#chevron-expand"></use></svg>
</button>
<div class="menu" id="bd-theme-menu" aria-labelledby="bd-theme" style="--bs-menu-min-width: 8rem;">
- <button
- type="button"
- class="menu-item"
- data-bs-theme-value="light"
- aria-pressed="false"
- >
+ <button type="button" class="menu-item" data-bs-theme-value="light" aria-pressed="false">
<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>
- <button
- type="button"
- class="menu-item"
- data-bs-theme-value="dark"
- aria-pressed="false"
- >
+ <button type="button" class="menu-item" data-bs-theme-value="dark" aria-pressed="false">
<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>
- <button
- type="button"
- class="menu-item active"
- data-bs-theme-value="auto"
- aria-pressed="true"
- >
+ <button type="button" class="menu-item active" data-bs-theme-value="auto" aria-pressed="true">
<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>
import ClipboardJS from 'clipboard'
-import { Tooltip } from '../../../dist/js/bootstrap.bundle.js'
+import { Tooltip } from '@bootstrap'
const btnTitle = 'Copy'
.array()
} satisfies Record<string, DataSchema>
+// Inferred types for individual data files. Exported so consumers can avoid
+// re-deriving them via `ReturnType<typeof getData<'sidebar'>>` and don't have
+// to fall back to `any` when iterating nested arrays.
+export type SidebarGroup = z.infer<typeof dataDefinitions.sidebar>[number]
+export type SidebarItem = NonNullable<SidebarGroup['pages']>[number]
+export type SidebarSubItem = NonNullable<SidebarItem['pages']>[number]
+
let data = new Map<DataType, z.infer<DataSchema>>()
// A helper to get data loaded fom a yml file in the `./site/data/` directory. If the data does not match its associated
import { slug } from 'github-slugger'
-import fromMarkdown from 'mdast-util-from-markdown'
-import toString from 'mdast-util-to-string'
import { remark } from 'remark'
import remarkHtml from 'remark-html'
+import type { Node, Parent } from 'unist'
export function capitalizeFirstLetter(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1)
return str.replace(/^\/+|\/+$/g, '')
}
+// Single shared `remark` pipeline for every markdown helper below — keeps the
+// dependency list to just `remark` + `remark-html` (no transitively-resolved
+// `mdast-util-*` imports) and avoids paying parser setup cost per call.
+const markdownParser = remark()
+const htmlProcessor = remark().use(remarkHtml)
+
+function nodeToText(node: Node): string {
+ if ('value' in node && typeof node.value === 'string') {
+ return node.value
+ }
+
+ if ('children' in node) {
+ return (node as Parent).children.map((child) => nodeToText(child)).join('')
+ }
+
+ return ''
+}
+
export function stripMarkdown(str: string) {
- return toString(fromMarkdown(str))
+ return nodeToText(markdownParser.parse(str))
}
export function processMarkdownToHtml(markdown: string): string {
- // Use remark to process markdown to HTML
- const result = remark().use(remarkHtml).processSync(markdown)
- return result.toString()
+ return htmlProcessor.processSync(markdown).toString()
}
"compilerOptions": {
"baseUrl": ".",
"paths": {
- "@assets/*": ["src/assets/*"],
+ "@bootstrap": ["../dist/js/bootstrap.bundle.js"],
"@components/*": ["src/components/*"],
"@layouts/*": ["src/layouts/*"],
"@libs/*": ["src/libs/*"],