Fix sidebar nested item styling

This commit is contained in:
Ronan Berder
2026-06-06 16:41:18 +08:00
parent 1509cdadef
commit 2e8daaef03
45 changed files with 55860 additions and 1338 deletions
+21
View File
@@ -36,6 +36,27 @@
"type": "group",
"label": "Components",
"items": [
{
"type": "submenu",
"label": "Parent item",
"icon": "folder",
"items": [
{
"type": "item",
"label": "Item 1",
"icon": "file",
"url": "#",
"external": true
},
{
"type": "item",
"label": "Item 2",
"icon": "file",
"url": "#",
"external": true
}
]
},
"components/accordion",
"components/alert",
"components/alert-dialog",
+21 -34
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
{# Dark mode #}
<script>
@@ -12,17 +12,6 @@
}
} catch (_) {}
const apply = dark => {
document.documentElement.classList.toggle('dark', dark);
try { localStorage.setItem('themeMode', dark ? 'dark' : 'light'); } catch (_) {}
};
document.addEventListener('basecoat:theme', (event) => {
const mode = event.detail?.mode;
apply(mode === 'dark' ? true
: mode === 'light' ? false
: !document.documentElement.classList.contains('dark'));
});
})();
</script>
{# Theme variant #}
@@ -36,17 +25,6 @@
}
})();
</script>
{# Style variant #}
<script>
(function() {
try {
const storedStyle = localStorage.getItem('styleVariant') || 'nova';
document.documentElement.dataset.styleVariant = storedStyle;
} catch (event) {
console.error('Could not apply style variant from localStorage', event);
}
})();
</script>
{# Meta #}
<meta charset="utf-8">
<meta http-equiv="Content-Language" content="en">
@@ -58,34 +36,43 @@
<meta name="description" content="{{ meta_description }}"/>
<meta name="title" content="{{ meta_title }}">
{% if site.keywords %}<meta name="keywords" content="{{ site.keywords | join(',') }}" />{% endif %}
{% if siteUrl %}<link rel="canonical" href="{{ siteUrl }}{{ page.url }}">{% endif %}
{# Favicon #}
<link rel="icon" type="image/svg+xml" href="/assets/favicon.svg">
<link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon.png">
<link rel="icon" type="image/svg+xml" href="{{ siteUrl }}/assets/favicon.svg">
<link rel="apple-touch-icon" sizes="180x180" href="{{ siteUrl }}/assets/apple-touch-icon.png">
{# Open Graph / Facebook #}
<meta property="og:type" content="website">
<meta property="og:url" content="{{ site.url }}{{ page.url }}">
<meta property="og:url" content="{{ siteUrl }}{{ page.url }}">
<meta property="og:title" content="{{ meta_title }}">
<meta property="og:description" content="{{ meta_description }}">
<meta property="og:image" content="{{ site.url }}/assets/social-screenshot.png">
<meta property="og:image" content="{{ siteUrl }}/assets/social.png">
<meta property="og:site_name" content="{{ site.title }}">
<meta property="og:locale" content="en_US">
<meta property="og:author" content="{{ site.author.name }}">
{# X/Twitter #}
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:url" content="{{ site.url }}{{ page.url }}">
<meta name="twitter:title" content="{{ title if title else site.title }}">
<meta name="twitter:url" content="{{ siteUrl }}{{ page.url }}">
<meta name="twitter:title" content="{{ meta_title }}">
<meta name="twitter:description" content="{{ meta_description }}">
<meta name="twitter:image" content="{{ site.url }}/assets/social-screenshot.png">
<meta name="twitter:image" content="{{ siteUrl }}/assets/social.png">
<meta name="twitter:creator" content="{{ site.author.x }}">
{# CSS #}
<link rel="stylesheet" id="style-variant-stylesheet" href="/assets/styles-nova.css">
<script>
(() => {
const styleLink = document.getElementById('style-variant-stylesheet');
const styleVariant = document.documentElement.dataset.styleVariant || 'nova';
styleLink.href = `/assets/styles-${styleVariant}.css`;
const styles = new Set(['vega', 'nova', 'maia', 'lyra', 'mira', 'luma', 'sera', 'rhea']);
let styleVariant = 'nova';
try {
const storedStyle = localStorage.getItem('styleVariant');
if (styles.has(storedStyle)) styleVariant = storedStyle;
} catch (event) {
console.error('Could not apply style variant from localStorage', event);
}
document.documentElement.dataset.styleVariant = styleVariant;
document.write(`<link rel="stylesheet" id="style-variant-stylesheet" href="/assets/styles-${styleVariant}.css">`);
})();
</script>
<noscript><link rel="stylesheet" id="style-variant-stylesheet" href="/assets/styles-nova.css"></noscript>
{# JS #}
<script src="/assets/js/basecoat.js?v={{ pkg.version }}" defer></script>
<script src="/assets/js/command.js?v={{ pkg.version }}" defer></script>
+64 -59
View File
@@ -4,69 +4,74 @@ layout: layouts/layout.njk
{% set docsMenu = docs.menu if docs and docs.menu else menu %}
{% set navigation = docsMenu | getNavigation(collections) %}
<main class="mx-auto relative flex w-full max-w-screen-lg gap-10">
<div class="mx-auto w-full flex-1 max-w-screen-md">
<header class="space-y-2">
<div class="flex items-start gap-x-2">
<h1 class="text-2xl font-semibold tracking-tight sm:text-3xl xl:text-4xl mr-auto">{{ title }}</h1>
<div class="flex items-center gap-x-2 pt-1">
<button
type="button"
class="btn-sm-secondary h-7 px-2.5 text-[13px] max-sm:size-7"
data-md-url="{{ page.url | markdownUrl }}"
onclick="copyPageMarkdown(this)"
>
{% lucide "copy" %}
<span class="hidden sm:block">Copy page</span>
</button>
<div role="group" class="button-group">
{% if navigation.prev %}
<a href="{{ navigation.prev.url }}" class="btn-sm-icon-secondary hidden sm:flex size-7">
<span class="sr-only">{{ navigation.prev.label }}</span>
{% lucide "arrow-left" %}
</a>
{% endif %}
{% if navigation.prev and navigation.next %}
<hr role="separator">
{% endif %}
{% if navigation.next %}
<a href="{{ navigation.next.url }}" class="btn-sm-icon-secondary hidden sm:flex size-7">
<span class="sr-only">{{ navigation.next.label }}</span>
{% lucide "arrow-right" %}
</a>
{% endif %}
<div class="mx-auto flex w-full max-w-screen-lg gap-10">
<article class="min-w-0 flex-1 wrap-break-word">
<div class="mx-auto w-full max-w-screen-md">
<header class="space-y-2">
<div class="flex items-start gap-x-2">
<h1 class="text-2xl font-semibold tracking-tight sm:text-3xl xl:text-4xl mr-auto">{{ title }}</h1>
<div class="flex items-center gap-x-2 pt-1">
<button
type="button"
class="btn-sm-secondary h-7 px-2.5 text-[13px] max-sm:size-7"
data-md-url="{{ page.url | markdownUrl }}"
onclick="copyPageMarkdown(this)"
>
{% lucide "copy" %}
<span class="hidden sm:block">Copy page</span>
</button>
<div role="group" class="button-group">
{% if navigation.prev %}
<a href="{{ navigation.prev.url }}" class="btn-sm-icon-secondary hidden sm:flex size-7">
<span class="sr-only">{{ navigation.prev.label }}</span>
{% lucide "arrow-left" %}
</a>
{% endif %}
{% if navigation.prev and navigation.next %}
<hr role="separator">
{% endif %}
{% if navigation.next %}
<a href="{{ navigation.next.url }}" class="btn-sm-icon-secondary hidden sm:flex size-7">
<span class="sr-only">{{ navigation.next.label }}</span>
{% lucide "arrow-right" %}
</a>
{% endif %}
</div>
</div>
</div>
<p class="text-muted-foreground text-[1.05rem] sm:text-base">{{ description }}</p>
</header>
<div class="pb-12 mt-8 content">
{{ content | safe }}
</div>
<p class="text-muted-foreground text-[1.05rem] sm:text-base">{{ description }}</p>
</header>
<article class="pb-12 mt-8 content">
{{ content | safe }}
</article>
{% if navigation.prev or navigation.next %}
<footer class="flex items-center gap-2 border-t pt-6 mt-12">
{% if navigation.prev %}
<a href="{{ navigation.prev.url }}" class="btn-sm-secondary mr-auto">
{% lucide "arrow-left" %}
{{ navigation.prev.label }}
</a>
{% endif %}
{% if navigation.next %}
<a href="{{ navigation.next.url }}" class="btn-sm-secondary ml-auto">
{{ navigation.next.label }}
{% lucide "arrow-right" %}
</a>
{% endif %}
</footer>
{% endif %}
</div>
{% if toc %}
<div class="hidden text-sm xl:block w-full max-w-[300px]">
{% from "macros/toc.njk" import toc_nav %}
{{ toc_nav(toc) }}
{% if navigation.prev or navigation.next %}
<footer class="flex items-center gap-2 border-t pt-6 mt-12">
{% if navigation.prev %}
<a href="{{ navigation.prev.url }}" class="btn-sm-secondary mr-auto">
{% lucide "arrow-left" %}
{{ navigation.prev.label }}
</a>
{% endif %}
{% if navigation.next %}
<a href="{{ navigation.next.url }}" class="btn-sm-secondary ml-auto">
{{ navigation.next.label }}
{% lucide "arrow-right" %}
</a>
{% endif %}
</footer>
{% endif %}
</div>
{% endif %}
</main>
</article>
<aside class="hidden xl:block w-56 shrink-0">
<div class="sticky top-20 text-sm [&_a]:text-muted-foreground [&_a]:hover:text-primary space-y-2 [&_ol]:space-y-2 [&_ol_ol]:pl-3 [&_ol_ol]:mt-2">
<h2 class="font-medium flex items-center gap-1">
{% lucide "table-of-contents", { class: "size-4" } %}
On this page
</h2>
{{ content | toc | safe }}
</div>
</aside>
</div>
<script>
async function copyPageMarkdown(button) {
+6 -3
View File
@@ -21,7 +21,7 @@
<div class="flex h-14 w-full items-center gap-2 px-4">
<button
type="button"
onclick="document.dispatchEvent(new CustomEvent('basecoat:sidebar'))"
onclick="document.getElementById('sidebar')?.toggle()"
aria-label="Toggle sidebar"
data-tooltip="Toggle sidebar"
data-side="bottom"
@@ -83,8 +83,11 @@
const styleLink = document.getElementById('style-variant-stylesheet');
const applyStyle = (styleName) => {
const nextStyle = styleName || 'nova';
const nextHref = `/assets/styles-${nextStyle}.css`;
document.documentElement.dataset.styleVariant = nextStyle;
if (styleLink) styleLink.href = `/assets/styles-${nextStyle}.css`;
if (styleLink && styleLink.getAttribute('href') !== nextHref) {
styleLink.href = nextHref;
}
};
const storedStyle = localStorage.getItem('styleVariant') || 'nova';
@@ -104,7 +107,7 @@
aria-label="Toggle dark mode"
data-tooltip="Toggle dark mode"
data-side="bottom"
onclick="document.dispatchEvent(new CustomEvent('basecoat:theme'))"
onclick="window.basecoat?.theme.toggle()"
class="btn-icon-outline size-8"
>
<span class="hidden dark:block">{% lucide "sun" %}</span>
+2 -2
View File
@@ -44,8 +44,8 @@
{% endif %}
</div>
<div class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-medium">{{ site.title }}</span>
<span class="truncate text-xs">v{{ pkg.version }}</span>
<span class="truncate font-semibold">{{ site.title }}</span>
<span class="truncate text-xs text-muted-foreground">v{{ pkg.version }}</span>
</div>
</a>
{% endset %}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+4
View File
@@ -147,6 +147,10 @@ toc:
<dd>Removes a selected value.</dd>
<dt><code>toggle(value)</code> <span class="badge-secondary">Multiple only</span></dt>
<dd>Toggles a selected value.</dd>
<dt><code>refresh()</code></dt>
<dd>Rescans options after changing children inside the existing <code>role="listbox"</code> element.</dd>
<dt><code>window.basecoat.refresh(combobox)</code></dt>
<dd>Calls the component refresh method through the global dispatcher.</dd>
</dl>
</section>
+6 -2
View File
@@ -18,7 +18,7 @@ toc:
id: usage-html-js-2
- label: HTML structure
id: usage-html-js-3
- label: JavaScript events
- label: JavaScript API
id: usage-html-js-4
- label: Jinja and Nunjucks
id: usage-macro
@@ -183,12 +183,16 @@ toc:
</dl>
</section>
<h4 id="usage-html-js-4"><a href="#usage-html-js-4">JavaScript events</a></h4>
<h4 id="usage-html-js-4"><a href="#usage-html-js-4">JavaScript API</a></h4>
<section class="prose">
<dl>
<dt><code>basecoat:initialized</code></dt>
<dd>Once the component is initialized, it dispatches a custom non-bubbling <code>basecoat:initialized</code> event on itself.</dd>
<dt><code>command.refresh()</code></dt>
<dd>Rescans command items after changing children inside the existing <code>role="menu"</code> list.</dd>
<dt><code>window.basecoat.refresh(command)</code></dt>
<dd>Calls the component refresh method through the global dispatcher.</dd>
</dl>
</section>
+12 -2
View File
@@ -16,7 +16,7 @@ toc:
id: usage-html-js-2
- label: HTML structure
id: usage-html-js-3
- label: JavaScript events
- label: JavaScript API
id: usage-html-js-4
- label: Jinja and Nunjucks
id: usage-macro
@@ -151,7 +151,7 @@ toc:
</dl>
</section>
<h4 id="usage-html-js-4"><a href="#usage-html-js-4">JavaScript events</a></h4>
<h4 id="usage-html-js-4"><a href="#usage-html-js-4">JavaScript API</a></h4>
<section class="prose">
<dl>
@@ -159,6 +159,16 @@ toc:
<dd>Once the component is initialized, it dispatches a custom non-bubbling <code>basecoat:initialized</code> event on itself.</dd>
<dt><code>basecoat:popover</code></dt>
<dd>When the menu opens, the component dispatches a custom event on <code>document</code>. Other popover-based components listen for this to close any open popovers.</dd>
<dt><code>dropdown.open()</code></dt>
<dd>Opens the menu.</dd>
<dt><code>dropdown.close()</code></dt>
<dd>Closes the menu.</dd>
<dt><code>dropdown.toggle()</code></dt>
<dd>Toggles the menu.</dd>
<dt><code>dropdown.refresh()</code></dt>
<dd>Rescans menu items after changing children inside the existing <code>role="menu"</code> element.</dd>
<dt><code>window.basecoat.refresh(dropdown)</code></dt>
<dd>Calls the component refresh method through the global dispatcher.</dd>
</dl>
</section>
+1 -1
View File
@@ -123,6 +123,6 @@ toc:
<section class="prose">
<ul>
<li>Use <strong>Native Select</strong> for native browser behavior, better performance, form integration, and mobile-optimized pickers.</li>
<li>Use <strong>Select</strong> for a custom popover, richer option content, multi-select behavior, or custom JavaScript events.</li>
<li>Use <strong>Select</strong> for a custom popover, richer option content, multi-select behavior, or custom JavaScript APIs.</li>
</ul>
</section>
+4
View File
@@ -283,6 +283,10 @@ toc:
<dd>
<p><strong>Deprecated:</strong> Alias for <code>select(value)</code>. Kept for backward compatibility.</p>
</dd>
<dt><code>refresh()</code></dt>
<dd>Rescans options after changing children inside the existing <code>role="listbox"</code> element.</dd>
<dt><code>window.basecoat.refresh(selectComponent)</code></dt>
<dd>Calls the component refresh method through the global dispatcher.</dd>
</dl>
</section>
+20 -17
View File
@@ -18,7 +18,7 @@ toc:
id: usage-html-js-3
- label: Basecoat differences
id: usage-html-js-4
- label: JavaScript events
- label: JavaScript API
id: usage-html-js-5
- label: Jinja and Nunjucks
id: usage-macro
@@ -82,7 +82,7 @@ toc:
menu=menu
) }}
<main>
<button type="button" onclick="document.dispatchEvent(new CustomEvent('basecoat:sidebar'))">Toggle sidebar</button>
<button type="button" onclick="document.getElementById('sidebar')?.toggle()">Toggle sidebar</button>
<h1>Content</h1>
</main>
{% endset %}
@@ -95,7 +95,7 @@ toc:
<dt><code class="highlight language-html">&lt;aside class="sidebar" aria-hidden="false"&gt;</code></dt>
<dd>Wraps the sidebar component. Supported attributes:
<ul>
<li><code>id</code>: target a specific sidebar from the <code>basecoat:sidebar</code> event.</li>
<li><code>id</code>: target a specific sidebar from JavaScript methods.</li>
<li><code>aria-hidden</code>: current open state. <code>false</code> means visible, <code>true</code> means hidden.</li>
<li><code>data-side</code>: physical side of the viewport. Use <code>left</code> or <code>right</code>. Defaults to <code>left</code>.</li>
<li><code>data-initial-open</code>: set to <code>false</code> to start closed on desktop.</li>
@@ -123,8 +123,8 @@ toc:
<dd>Bottom area for account controls, settings, or secondary actions.</dd>
<dt><code class="highlight language-html">&lt;main&gt;</code></dt>
<dd>The sibling content wrapper. Basecoat applies the desktop margin to the sibling that follows the sidebar.</dd>
<dt><code class="highlight language-html">&lt;button type="button" onclick="document.dispatchEvent(new CustomEvent('basecoat:sidebar'))"&gt;</code></dt>
<dd>Dispatches the event used to toggle, open, or close the sidebar.</dd>
<dt><code class="highlight language-html">&lt;button type="button" onclick="document.getElementById('sidebar')?.toggle()"&gt;</code></dt>
<dd>Calls the sidebar method used to toggle, open, or close the sidebar.</dd>
</dl>
</section>
@@ -146,26 +146,29 @@ toc:
<p>RTL content is supported through logical spacing and borders. The <code>data-side</code> value remains physical: <code>left</code> means the left side of the viewport and <code>right</code> means the right side.</p>
</section>
<h4 id="usage-html-js-5"><a href="#usage-html-js-5">JavaScript events</a></h4>
<h4 id="usage-html-js-5"><a href="#usage-html-js-5">JavaScript API</a></h4>
<section class="prose">
<dl>
<dt><code>basecoat:initialized</code></dt>
<dd>Once the component is fully initialized, it dispatches a custom (non-bubbling) <code>basecoat:initialized</code> event on itself.</dd>
<dt><code>basecoat:sidebar</code></dt>
<dd>
<p>Sidebars listen for this event on <code>document</code> to open, close or toggle themselves. By default, the event will toggle the sidebar, but can be used to open or close if you add an <code>action</code> to the detail. Additionally, if you have multiple sidebars on the page, you can target a specific sidebar by adding its <code>id</code> to the detail:</p>
{% set code_trigger %}<!-- Toggles the sidebar -->
<button type="button" onclick="document.dispatchEvent(new CustomEvent('basecoat:sidebar'));">Toggle sidebar</button>
<!-- Opens the `#main-navigation` sidebar -->
<button type="button" onclick="document.dispatchEvent(new CustomEvent('basecoat:sidebar', { detail: { id: 'main-navigation', action: 'open' } }));">Open sidebar</button>
<!-- Closes the sidebar -->
<button type="button" onclick="document.dispatchEvent(new CustomEvent('basecoat:sidebar', { detail: { action: 'close' } }));">Close sidebar</button>{% endset %}
{{ code_block(code_trigger | prettyHtml, "html") }}
</dd>
<dt><code>sidebar.open()</code></dt>
<dd>Opens the specific sidebar element.</dd>
<dt><code>sidebar.close()</code></dt>
<dd>Closes the specific sidebar element.</dd>
<dt><code>sidebar.toggle()</code></dt>
<dd>Toggles the specific sidebar element.</dd>
</dl>
</section>
{% set code_trigger %}<!-- Toggles the sidebar -->
<button type="button" onclick="document.getElementById('sidebar')?.toggle()">Toggle sidebar</button>
<!-- Opens the `#main-navigation` sidebar -->
<button type="button" onclick="document.getElementById('main-navigation')?.open()">Open sidebar</button>
<!-- Closes the sidebar -->
<button type="button" onclick="document.getElementById('sidebar')?.close()">Close sidebar</button>{% endset %}
{{ code_block(code_trigger | prettyHtml, "html") }}
<h3 id="usage-macro"><a href="#usage-macro">Jinja and Nunjucks</a></h3>
<div class="prose">
+8 -2
View File
@@ -16,7 +16,7 @@ toc:
id: usage-html-js-2
- label: HTML structure
id: usage-html-js-3
- label: JavaScript events
- label: JavaScript API
id: usage-html-js-4
- label: Jinja and Nunjucks
id: usage-macro
@@ -159,12 +159,18 @@ toc:
</dl>
</section>
<h4 id="usage-html-js-4"><a href="#usage-html-js-4">JavaScript events</a></h4>
<h4 id="usage-html-js-4"><a href="#usage-html-js-4">JavaScript API</a></h4>
<section class="prose">
<dl>
<dt><code>basecoat:initialized</code></dt>
<dd>Once the component is initialized, it dispatches a custom non-bubbling <code>basecoat:initialized</code> event on itself.</dd>
<dt><code>tabs.select(tab)</code></dt>
<dd>Selects a tab button in the tablist.</dd>
<dt><code>tabs.refresh()</code></dt>
<dd>Rescans tabs and panels after changing children inside the existing tablist/panel structure.</dd>
<dt><code>window.basecoat.refresh(tabs)</code></dt>
<dd>Calls the component refresh method through the global dispatcher.</dd>
</dl>
</section>
+28 -51
View File
@@ -2,7 +2,7 @@
templateEngineOverride: njk
layout: layouts/page.njk
title: Theme Switcher
description: A component that allows the user to switch between light and dark mode.
description: A simple button pattern for switching between light and dark mode.
toc:
- label: Usage
id: usage
@@ -10,48 +10,47 @@ toc:
- label: HTML + JavaScript
id: usage-html-js
children:
- label: "Step 1: Include the JavaScript script"
- label: "Step 1: Avoid the initial flash"
id: usage-html-js-1
- label: "Step 2: Add your switcher"
id: usage-html-js-2
- label: JavaScript events
- label: JavaScript API
id: usage-html-js-3
---
<div class="alert mb-6">
{% lucide "circle-alert" %}
<h2>There is no dedicated theme switcher component in Basecoat.</h2>
<section><p>Instead, we offer a short JavaScript <code>&lt;script&gt;</code> block you can insert in the <code>&lt;head&gt;</code> of your page.</p></section>
<section><p>Theme switching is handled by <code>window.basecoat.theme</code> and a small inline script that applies the stored mode before styles render.</p></section>
</div>
{% from "macros/code_preview.njk" import code_preview %}
{% from "macros/code_block.njk" import code_block %}
{% set code %}<button
{% set code_switcher %}<button
type="button"
aria-label="Toggle dark mode"
data-tooltip="Toggle dark mode"
data-side="bottom"
onclick="document.dispatchEvent(new CustomEvent('basecoat:theme'))"
onclick="window.basecoat.theme.toggle()"
class="btn-icon-outline size-8"
>
<span class="hidden dark:block">{% lucide "sun" %}</span>
<span class="block dark:hidden">{% lucide "moon" %}</span>
</button>{% endset %}
{{ code_preview("button", code) }}
{{ code_preview("button", code_switcher) }}
<h2 id="usage"><a href="#usage">Usage</a></h2>
<h3 id="usage-html-js"><a href="#usage-html-js">HTML + JavaScript</a></h3>
<h4 id="usage-html-js-1"><a href="#usage-html-js-1">Step 1: Include the JavaScript script</a></h4>
<h4 id="usage-html-js-1"><a href="#usage-html-js-1">Step 1: Avoid the initial flash</a></h4>
<section class="prose">
<p>Insert the following JavaScript block right at the beginning of the <code>&lt;head&gt;</code> your page. We do this instead of using a separate file to ensure the theme is defined before the page loads, avoiding a flash of unstyled or mist-styled content.</p>
<p>Add this inline script before loading your styles. It applies the stored mode before the page renders.</p>
</section>
{% set code %}<script>
{% set code_head %}<script>
(() => {
try {
const stored = localStorage.getItem('themeMode');
@@ -60,53 +59,31 @@ toc:
document.documentElement.classList.add('dark');
}
} catch (_) {}
const apply = dark => {
document.documentElement.classList.toggle('dark', dark);
try { localStorage.setItem('themeMode', dark ? 'dark' : 'light'); } catch (_) {}
};
document.addEventListener('basecoat:theme', (event) => {
const mode = event.detail?.mode;
apply(mode === 'dark' ? true
: mode === 'light' ? false
: !document.documentElement.classList.contains('dark'));
});
})();
</script>{% endset %}
{{ code_block(code) }}
{{ code_block(code_head | prettyHtml, "html") }}
<section class="prose">
<p>Load <code>basecoat.js</code> or the bundled JavaScript before calling <code>window.basecoat.theme</code>.</p>
</section>
<h4 id="usage-html-js-2"><a href="#usage-html-js-2">Step 2: Add your switcher</a></h4>
<section class="prose">
<p>To change the theme, you need to dispatch a <code>basecoat:theme</code> event:</p>
</section>
{{ code_block(code_switcher | prettyHtml, "html") }}
{% set code %}<button
type="button"
aria-label="Toggle dark mode"
data-tooltip="Toggle dark mode"
data-side="bottom"
onclick="document.dispatchEvent(new CustomEvent('basecoat:theme'))"
class="btn-icon-outline size-8"
>
<span class="hidden dark:block">{% lucide "sun" %}</span>
<span class="block dark:hidden">{% lucide "moon" %}</span>
</button>{% endset %}
{{ code_block(code) }}
<h4 id="usage-html-js-3"><a href="#usage-html-js-3">JavaScript events</a></h4>
<h4 id="usage-html-js-3"><a href="#usage-html-js-3">JavaScript API</a></h4>
<section class="prose">
<dl>
<dt><code>basecoat:theme</code></dt>
<dd>
<p>By default, the event will toggle the theme, but you can also add a <code>mode</code> to the detail to set the theme explicitly:</p>
{% set code_trigger %}<!-- Toggles the theme -->
<button type="button" onclick="document.dispatchEvent(new CustomEvent('basecoat:theme'));">Toggle theme</button>
<!-- Sets the theme to dark -->
<button type="button" onclick="document.dispatchEvent(new CustomEvent('basecoat:theme', { detail: { mode: 'dark' } }));">Set dark theme</button>{% endset %}
{{ code_block(code_trigger | prettyHtml, "html") }}
</dd>
<dt><code>window.basecoat.theme.get()</code></dt>
<dd>Returns <code>"dark"</code> or <code>"light"</code>.</dd>
<dt><code>window.basecoat.theme.set(mode)</code></dt>
<dd>Sets the mode to <code>"dark"</code> or <code>"light"</code> and stores it in <code>localStorage.themeMode</code>.</dd>
<dt><code>window.basecoat.theme.toggle()</code></dt>
<dd>Toggles between dark and light mode.</dd>
</dl>
</section>
</section>
{% set code_methods %}<button type="button" onclick="window.basecoat.theme.toggle()">Toggle theme</button>
<button type="button" onclick="window.basecoat.theme.set('dark')">Set dark theme</button>{% endset %}
{{ code_block(code_methods | prettyHtml, "html") }}
+18 -20
View File
@@ -18,7 +18,7 @@ toc:
id: usage-html-js-3
- label: HTML structure
id: usage-html-js-4
- label: JavaScript events
- label: JavaScript API
id: usage-html-js-5
- label: Toast config object
id: usage-html-js-6
@@ -44,18 +44,14 @@ toc:
{% set code_client_side %}<button
class="btn-outline"
onclick="document.dispatchEvent(new CustomEvent('basecoat:toast', {
detail: {
config: {
category: 'success',
title: 'Success',
description: 'A success toast called from the front-end.',
cancel: {
label: 'Dismiss'
}
}
onclick="document.getElementById('toaster').toast({
category: 'success',
title: 'Success',
description: 'A success toast called from the front-end.',
cancel: {
label: 'Dismiss'
}
}))"
})"
>
Toast from front-end
</button>{% endset %}
@@ -106,7 +102,7 @@ toc:
<h4 id="usage-html-js-3"><a href="#usage-html-js-3">Step 3: Add your toasts</a></h4>
<section class="prose">
<p>If you decide to server-render your toasts, or load them using asynchronoulsy with something like HTMX, you can just add the toast's markup to the toaster:</p>
<p>If you decide to server-render your toasts, or load them asynchronously with something like HTMX, you can just add the toast's markup to the toaster:</p>
</section>
{% set code_toast_html %}<div id="toaster" class="toaster">
@@ -123,14 +119,14 @@ toc:
{{ code_block(code_toast_html | prettyHtml, "html") }}
<div class="flex flex-wrap gap-2 my-6">
<a class="badge-outline" href="#usage-html-js-5">
<a class="badge-outline" href="#usage-html-js-4">
HTML structure
{% lucide "arrow-right" %}
</a>
</div>
<section class="prose">
<p>If you need to create a toast from the front-end, you can dispatch a custom <code>basecoat:toast</code> event as such:</p>
<p>If you need to create a toast from the front-end, call the toaster element's <code>toast(config)</code> method:</p>
</section>
{{ code_block(code_client_side | prettyHtml, "html") }}
@@ -181,16 +177,18 @@ toc:
</dl>
</section>
<h4 id="usage-html-js-5"><a href="#usage-html-js-5">JavaScript events</a></h4>
<h4 id="usage-html-js-5"><a href="#usage-html-js-5">JavaScript API</a></h4>
<section class="prose">
<dl>
<dt><code>basecoat:initialized</code></dt>
<dd>Once the component is fully initialized, it dispatches a custom (non-bubbling) <code>basecoat:initialized</code> event on the <code>toaster</code> element.</dd>
<dt><code>basecoat:toast</code></dt>
<dd>
<p>The <code>toaster</code> listens for <code>basecoat:toast</code> events on <code>document</code> to create toasts. The event's <code>detail</code> object must contain a <code>config</code> object (see <a href="#usage-html-js-6">JavaScript config object</a> below).</p>
</dd>
<dt><code>toaster.toast(config)</code></dt>
<dd>Creates a toast from a config object and returns the inserted <code>.toast</code> element. See <a href="#usage-html-js-6">Toast config object</a>.</dd>
<dt><code>toaster.closeAll()</code></dt>
<dd>Closes every toast inside the toaster.</dd>
<dt><code>toast.close()</code></dt>
<dd>Closes one initialized toast element.</dd>
</dl>
</section>
+28 -15
View File
@@ -33,20 +33,33 @@ description: A collection of all the components available in Basecoat.
{ label: "Tooltip", id: "tooltip" }
] %}
<div class="flex gap-x-10 justify-center">
<div>
<header class="space-y-2 mb-8">
<h1 class="text-3xl font-semibold tracking-tight">{{ title }}</h1>
<p class="text-muted-foreground">{{ description }}</p>
</header>
<div class="grid gap-4 flex-1">
{% for component in components %}
{% include "partials/kitchen-sink/" ~ component.id ~ ".njk" %}
{% endfor %}
<div class="mx-auto flex w-full max-w-screen-lg gap-10">
<article class="min-w-0 flex-1 wrap-break-word">
<div class="mx-auto w-full max-w-screen-md">
<header class="space-y-2 mb-8">
<h1 class="text-3xl font-semibold tracking-tight">{{ title }}</h1>
<p class="text-muted-foreground">{{ description }}</p>
</header>
<div class="grid gap-4 flex-1">
{% for component in components %}
{% include "partials/kitchen-sink/" ~ component.id ~ ".njk" %}
{% endfor %}
</div>
</div>
</div>
<div class="hidden text-sm xl:block w-full max-w-[200px]">
{% from "macros/toc.njk" import toc_nav %}
{{ toc_nav(components) }}
</div>
</article>
<aside class="hidden xl:block w-56 shrink-0">
<div class="sticky top-20 text-sm [&_a]:text-muted-foreground [&_a]:hover:text-primary space-y-2 [&_ol]:space-y-2">
<h2 class="font-medium flex items-center gap-1">
{% lucide "table-of-contents", { class: "size-4" } %}
On this page
</h2>
<nav class="toc">
<ol>
{% for component in components %}
<li><a href="#{{ component.id }}">{{ component.label }}</a></li>
{% endfor %}
</ol>
</nav>
</div>
</aside>
</div>
+2 -2
View File
@@ -2,6 +2,6 @@
permalink: /robots.txt
eleventyExcludeFromCollections: true
---
Sitemap: {{ site.url }}/sitemap.xml
Sitemap: {{ siteUrl }}/sitemap.xml
User-agent: *
Disallow:
Disallow:
+3 -3
View File
@@ -5,11 +5,11 @@ eleventyExcludeFromCollections: true
<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{% for page in collections.all %}
{% if not page.url.startsWith('/fragments/') %}
{% if page.url and not page.url.startsWith('/fragments/') %}
<url>
<loc>{{ site.url }}{{ page.url | url }}</loc>
<loc>{{ siteUrl }}{{ page.url | url }}</loc>
<lastmod>{{ page.date.toISOString() }}</lastmod>
</url>
{% endif %}
{% endfor %}
</urlset>
</urlset>
+2 -1
View File
@@ -1,5 +1,6 @@
---
layout: layouts/layout.njk
eleventyExcludeFromCollections: true
---
{% from "dialog.njk" import dialog %}
@@ -36,4 +37,4 @@ footer=footer
]
) }}
</form>
{% endcall %}
{% endcall %}