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
+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>