Revamped the select docs

This commit is contained in:
Ronan Berder
2025-06-13 14:14:38 +08:00
parent 056c025628
commit 7e3773276c
22 changed files with 321 additions and 492 deletions
+14 -2
View File
@@ -12,10 +12,10 @@
.content {
> h2 {
@apply mt-16 mb-6 scroll-m-22 border-b pb-4 text-xl font-semibold tracking-tight first:mt-0;
@apply mt-12 lg:mt-20 mb-6 scroll-m-22 text-2xl font-semibold tracking-tight first:mt-0;
}
> h3 {
@apply mt-8 mb-6 scroll-m-22 text-lg font-semibold tracking-tight;
@apply mt-8 mb-6 scroll-m-22 text-xl font-semibold tracking-tight;
}
> h4 {
@apply my-6 scroll-m-22 font-semibold tracking-tight;
@@ -44,6 +44,18 @@
@apply mt-2;
}
}
dl {
@apply border-b pb-3 my-6;
dt {
@apply border-t pt-3 mt-3 first:mt-0;
}
dd {
@apply mt-2;
}
dl {
@apply mt-4 border-b-0 pb-0 pl-8 my-0;
}
}
b,
strong {
@apply font-semibold;
+8 -4
View File
@@ -65,12 +65,11 @@
<link rel="stylesheet" href="/assets/styles.css">
<!-- JS -->
<script src="/assets/js/toast.js" defer></script>
{# <script src="/assets/js/dropdown-menu.js" defer></script> #}
<script src="/assets/js/select.js" defer></script>
<script src="/assets/js/dropdown-menu.js" defer></script>
<script src="/assets/js/sidebar.js" defer></script>
<script src="/assets/js/tabs.js" defer></script>
<script src="/assets/js/toast.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
@@ -78,7 +77,12 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/django.min.js"></script>
<script>
const applyHighlighting = () => { if (window.hljs) hljs.highlightAll(); };
const applyHighlighting = () => { if (window.hljs) {
hljs.highlightAll();
document.querySelectorAll('code.highlight').forEach((block) => {
hljs.highlightElement(block);
});
}};
if (!window._hljsInitialized) {
window._hljsInitialized = true;
+3 -3
View File
@@ -3,10 +3,10 @@ layout: layouts/layout.njk
---
<main class="mx-auto relative flex w-full max-w-screen-lg gap-10">
<div class="mx-auto w-full max-w-screen-md">
<div class="mx-auto w-full flex-1 max-w-screen-md">
<header class="space-y-2">
<h1 class="text-3xl font-bold tracking-tight">{{ title }}</h1>
<p class="text-muted-foreground">{{ description }}</p>
<h1 class="text-2xl font-semibold tracking-tight sm:text-3xl xl:text-4xl">{{ title }}</h1>
<p class="text-muted-foreground text-[1.05rem] sm:text-base">{{ description }}</p>
</header>
<article class="pb-12 mt-8 content">
{{ content | safe }}
+4 -5
View File
@@ -15,15 +15,14 @@
}
}"
>
<pre x-ref="code" class="text-sm max-h-[650px] overflow-y-auto rounded-xl scrollbar whitespace-pre-wrap
"><code class="language-{{ language }} !bg-neutral-900 p-0">{{- code | escape -}}</code></pre>
<pre x-ref="code" class="grid text-sm max-h-[650px] overflow-y-auto rounded-xl scrollbar"><code class="language-{{ language }} !bg-neutral-900 p-0">{{- code | escape -}}</code></pre>
<button
@click="copy()"
class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50 [&_svg]:h-3 [&_svg]:w-3 absolute right-3 top-3"
class="btn-icon-ghost size-8 absolute right-2.5 top-2 text-muted-foreground hover:text-foreground"
>
{% lucide "clipboard", { "class": "size-4", "x-show": "!copied" } %}
{% lucide "check", { "class": "size-4", "x-show": "copied" } %}
{% lucide "clipboard", {"x-show": "!copied" } %}
{% lucide "check", {"x-show": "copied" } %}
</button>
</div>
{% endmacro %}
+2 -2
View File
@@ -20,7 +20,7 @@
<option value="supabase">Supabase</option>
</select>
<script>
document.addEventListener('DOMContentLoaded', () => {
(() => {
const themeSelect = document.getElementById('theme-select');
const storedTheme = localStorage.getItem('themeVariant');
if (themeSelect && storedTheme) themeSelect.value = storedTheme;
@@ -36,7 +36,7 @@
localStorage.removeItem('themeVariant');
}
});
});
})();
</script>
<button
+1 -19
View File
@@ -119,25 +119,7 @@
header=header,
footer=footer,
content_attrs={
"x-data": "{
updateCurrentPage() {
$el.querySelectorAll('a').forEach(link => {
normalizedCurrentPathname = window.location.pathname.endsWith('/') ?
window.location.pathname.slice(0, -1) :
window.location.pathname;
normalizedLinkPathname = link.pathname.endsWith('/') ?
link.pathname.slice(0, -1) :
link.pathname;
if (normalizedCurrentPathname === normalizedLinkPathname) {
link.setAttribute('aria-current', 'page');
} else {
link.setAttribute('aria-current', 'false');
}
});
}
}",
"class": "scrollbar",
"x-init": "updateCurrentPage()",
"class": "scrollbar",
"@htmx:pushed-into-history.window": "updateCurrentPage()"
},
menu=menu
+57 -181
View File
@@ -21,8 +21,6 @@
--color-blue-700: oklch(48.8% 0.243 264.376);
--color-blue-900: oklch(37.9% 0.146 265.522);
--color-blue-950: oklch(28.2% 0.091 267.935);
--color-zinc-50: oklch(98.5% 0 0);
--color-zinc-700: oklch(37% 0.013 285.805);
--color-neutral-900: oklch(20.5% 0 0);
--color-black: #000;
--color-white: #fff;
@@ -268,9 +266,6 @@
.absolute {
position: absolute;
}
.fixed {
position: fixed;
}
.relative {
position: relative;
}
@@ -283,26 +278,23 @@
.inset-x-0 {
inset-inline: calc(var(--spacing) * 0);
}
.inset-y-0 {
inset-block: calc(var(--spacing) * 0);
}
.top-0 {
top: calc(var(--spacing) * 0);
}
.top-1\/2 {
top: calc(1/2 * 100%);
}
.top-2 {
top: calc(var(--spacing) * 2);
}
.top-3 {
top: calc(var(--spacing) * 3);
}
.top-4 {
top: calc(var(--spacing) * 4);
}
.top-22 {
top: calc(var(--spacing) * 22);
}
.top-\[50\%\] {
top: 50%;
.right-2\.5 {
right: calc(var(--spacing) * 2.5);
}
.right-3 {
right: calc(var(--spacing) * 3);
@@ -313,18 +305,12 @@
.left-2 {
left: calc(var(--spacing) * 2);
}
.left-\[50\%\] {
left: 50%;
}
.isolate {
isolation: isolate;
}
.z-10 {
z-index: 10;
}
.z-50 {
z-index: 50;
}
.col-span-2 {
grid-column: span 2 / span 2;
}
@@ -346,9 +332,6 @@
max-width: 96rem;
}
}
.-mx-6 {
margin-inline: calc(var(--spacing) * -6);
}
.mx-auto {
margin-inline: auto;
}
@@ -499,9 +482,6 @@
.w-4 {
width: calc(var(--spacing) * 4);
}
.w-6 {
width: calc(var(--spacing) * 6);
}
.w-10 {
width: calc(var(--spacing) * 10);
}
@@ -541,9 +521,6 @@
.max-w-\[300px\] {
max-width: 300px;
}
.max-w-\[calc\(100\%-2rem\)\] {
max-width: calc(100% - 2rem);
}
.max-w-lg {
max-width: var(--container-lg);
}
@@ -625,9 +602,6 @@
.flex-col {
flex-direction: column;
}
.flex-col-reverse {
flex-direction: column-reverse;
}
.flex-row {
flex-direction: row;
}
@@ -774,9 +748,6 @@
.rounded-xl {
border-radius: var(--radius-xl);
}
.rounded-xs {
border-radius: var(--radius-xs);
}
.border {
border-style: var(--tw-border-style);
border-width: 1px;
@@ -817,12 +788,6 @@
.bg-background {
background-color: var(--color-background);
}
.bg-black\/50 {
background-color: color-mix(in srgb, #000 50%, transparent);
@supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--color-black) 50%, transparent);
}
}
.bg-card {
background-color: var(--color-card);
}
@@ -856,9 +821,6 @@
.p-4 {
padding: calc(var(--spacing) * 4);
}
.p-6 {
padding: calc(var(--spacing) * 6);
}
.p-10 {
padding: calc(var(--spacing) * 10);
}
@@ -949,6 +911,9 @@
font-size: var(--text-xs);
line-height: var(--tw-leading, var(--text-xs--line-height));
}
.text-\[1\.05rem\] {
font-size: 1.05rem;
}
.leading-none {
--tw-leading: 1;
line-height: 1;
@@ -992,9 +957,6 @@
.break-words {
overflow-wrap: break-word;
}
.whitespace-nowrap {
white-space: nowrap;
}
.whitespace-pre-wrap {
white-space: pre-wrap;
}
@@ -1016,9 +978,6 @@
.text-sidebar-primary-foreground {
color: var(--color-sidebar-primary-foreground);
}
.text-zinc-50 {
color: var(--color-zinc-50);
}
.uppercase {
text-transform: uppercase;
}
@@ -1041,16 +1000,9 @@
.opacity-60 {
opacity: 60%;
}
.opacity-70 {
opacity: 70%;
}
.opacity-100 {
opacity: 100%;
}
.shadow-lg {
--tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.shadow-none {
--tw-shadow: 0 0 #0000;
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -1083,11 +1035,6 @@
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.transition-opacity {
transition-property: opacity;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
transition-duration: var(--tw-duration, var(--default-transition-duration));
}
.transition-transform {
transition-property: transform, translate, scale, rotate;
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
@@ -1209,13 +1156,6 @@
}
}
}
.hover\:bg-zinc-700 {
&:hover {
@media (hover: hover) {
background-color: var(--color-zinc-700);
}
}
}
.hover\:text-foreground {
&:hover {
@media (hover: hover) {
@@ -1223,13 +1163,6 @@
}
}
}
.hover\:text-zinc-50 {
&:hover {
@media (hover: hover) {
color: var(--color-zinc-50);
}
}
}
.hover\:underline {
&:hover {
@media (hover: hover) {
@@ -1237,13 +1170,6 @@
}
}
}
.hover\:opacity-100 {
&:hover {
@media (hover: hover) {
opacity: 100%;
}
}
}
.focus\:bg-destructive\/10 {
&:focus {
background-color: var(--color-destructive);
@@ -1257,44 +1183,11 @@
color: var(--color-destructive);
}
}
.focus\:ring-2 {
&:focus {
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
}
.focus\:ring-ring {
&:focus {
--tw-ring-color: var(--color-ring);
}
}
.focus\:ring-offset-2 {
&:focus {
--tw-ring-offset-width: 2px;
--tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
}
}
.focus\:outline-hidden {
&:focus {
--tw-outline-style: none;
outline-style: none;
@media (forced-colors: active) {
outline: 2px solid transparent;
outline-offset: 2px;
}
}
}
.focus-visible\:border-ring {
&:focus-visible {
border-color: var(--color-ring);
}
}
.focus-visible\:ring-1 {
&:focus-visible {
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
}
.focus-visible\:ring-2 {
&:focus-visible {
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
@@ -1332,16 +1225,6 @@
outline-style: none;
}
}
.disabled\:pointer-events-none {
&:disabled {
pointer-events: none;
}
}
.disabled\:opacity-50 {
&:disabled {
opacity: 50%;
}
}
.has-\[input\[type\=\'checkbox\'\]\:checked\]\:border-blue-600 {
&:has(*:is(input[type='checkbox']:checked)) {
border-color: var(--color-blue-600);
@@ -1362,16 +1245,6 @@
background-color: var(--color-green-50);
}
}
.data-\[state\=open\]\:bg-accent {
&[data-state="open"] {
background-color: var(--color-accent);
}
}
.data-\[state\=open\]\:text-muted-foreground {
&[data-state="open"] {
color: var(--color-muted-foreground);
}
}
.max-sm\:flex-col {
@media (width < 40rem) {
flex-direction: column;
@@ -1382,16 +1255,6 @@
max-width: 425px;
}
}
.sm\:max-w-lg {
@media (width >= 40rem) {
max-width: var(--container-lg);
}
}
.sm\:flex-row {
@media (width >= 40rem) {
flex-direction: row;
}
}
.sm\:gap-2\.5 {
@media (width >= 40rem) {
gap: calc(var(--spacing) * 2.5);
@@ -1403,6 +1266,12 @@
line-height: var(--tw-leading, var(--text-3xl--line-height));
}
}
.sm\:text-base {
@media (width >= 40rem) {
font-size: var(--text-base);
line-height: var(--tw-leading, var(--text-base--line-height));
}
}
.sm\:text-lg {
@media (width >= 40rem) {
font-size: var(--text-lg);
@@ -1461,6 +1330,12 @@
padding: calc(var(--spacing) * 12);
}
}
.xl\:text-4xl {
@media (width >= 80rem) {
font-size: var(--text-4xl);
line-height: var(--tw-leading, var(--text-4xl--line-height));
}
}
.\@md\:w-auto {
@container (width >= 28rem) {
width: auto;
@@ -1696,32 +1571,6 @@
padding-top: calc(var(--spacing) * 2);
}
}
.\[\&_svg\]\:pointer-events-none {
& svg {
pointer-events: none;
}
}
.\[\&_svg\]\:size-4 {
& svg {
width: calc(var(--spacing) * 4);
height: calc(var(--spacing) * 4);
}
}
.\[\&_svg\]\:h-3 {
& svg {
height: calc(var(--spacing) * 3);
}
}
.\[\&_svg\]\:w-3 {
& svg {
width: calc(var(--spacing) * 3);
}
}
.\[\&_svg\]\:shrink-0 {
& svg {
flex-shrink: 0;
}
}
.\[\&_svg\]\:\!text-destructive {
& svg {
color: var(--color-destructive) !important;
@@ -3313,7 +3162,7 @@
color: var(--color-popover-foreground);
}
}
div.select {
*:not(select).select {
[popover].popover {
min-width: anchor-size(width);
padding: calc(var(--spacing) * 1);
@@ -3328,8 +3177,9 @@
text-overflow: ellipsis;
white-space: nowrap;
border-radius: var(--radius-sm);
padding-inline: calc(var(--spacing) * 2);
padding-block: calc(var(--spacing) * 1.5);
padding-right: calc(var(--spacing) * 7.5);
padding-left: calc(var(--spacing) * 2);
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
--tw-outline-style: none;
@@ -4464,14 +4314,11 @@
}
.content {
> h2 {
margin-top: calc(var(--spacing) * 16);
margin-top: calc(var(--spacing) * 12);
margin-bottom: calc(var(--spacing) * 6);
scroll-margin: calc(var(--spacing) * 22);
border-bottom-style: var(--tw-border-style);
border-bottom-width: 1px;
padding-bottom: calc(var(--spacing) * 4);
font-size: var(--text-xl);
line-height: var(--tw-leading, var(--text-xl--line-height));
font-size: var(--text-2xl);
line-height: var(--tw-leading, var(--text-2xl--line-height));
--tw-font-weight: var(--font-weight-semibold);
font-weight: var(--font-weight-semibold);
--tw-tracking: var(--tracking-tight);
@@ -4479,13 +4326,16 @@
&:first-child {
margin-top: calc(var(--spacing) * 0);
}
@media (width >= 64rem) {
margin-top: calc(var(--spacing) * 20);
}
}
> h3 {
margin-top: calc(var(--spacing) * 8);
margin-bottom: calc(var(--spacing) * 6);
scroll-margin: calc(var(--spacing) * 22);
font-size: var(--text-lg);
line-height: var(--tw-leading, var(--text-lg--line-height));
font-size: var(--text-xl);
line-height: var(--tw-leading, var(--text-xl--line-height));
--tw-font-weight: var(--font-weight-semibold);
font-weight: var(--font-weight-semibold);
--tw-tracking: var(--tracking-tight);
@@ -4536,6 +4386,32 @@
margin-top: calc(var(--spacing) * 2);
}
}
dl {
margin-block: calc(var(--spacing) * 6);
border-bottom-style: var(--tw-border-style);
border-bottom-width: 1px;
padding-bottom: calc(var(--spacing) * 3);
dt {
margin-top: calc(var(--spacing) * 3);
border-top-style: var(--tw-border-style);
border-top-width: 1px;
padding-top: calc(var(--spacing) * 3);
&:first-child {
margin-top: calc(var(--spacing) * 0);
}
}
dd {
margin-top: calc(var(--spacing) * 2);
}
dl {
margin-block: calc(var(--spacing) * 0);
margin-top: calc(var(--spacing) * 4);
border-bottom-style: var(--tw-border-style);
border-bottom-width: 0px;
padding-bottom: calc(var(--spacing) * 0);
padding-left: calc(var(--spacing) * 8);
}
}
b, strong {
--tw-font-weight: var(--font-weight-semibold);
font-weight: var(--font-weight-semibold);
+8 -46
View File
@@ -6,8 +6,8 @@ toc:
- label: Usage
id: usage
children:
- label: HTML + Javascript
id: usage-default
- label: HTML
id: usage-html
- label: Jinja and Nunjucks
id: usage-macro
---
@@ -29,64 +29,26 @@ toc:
trigger_attrs={"class": "btn-outline"},
footer=footer,
close_button=false,
close_on_overlay_click=false,
register_on=["alpine:init", "htmx:afterSwap"]
close_on_overlay_click=false
) }}
{% endset %}
{% set code_script %}
<script>
{% fetchCode "src/js/dialog.js" %}
document.addEventListener('alpine:init', () => {
window.basecoat.registerDialog(Alpine);
});
</script>
{% endset %}
{{ code_preview("alert-dialog", code_script | prettyHtml ~ code_html | prettyHtml) }}
{{ code_preview("alert-dialog", code_html | prettyHtml) }}
<h2 id="usage"><a href="#usage">Usage</a></h2>
<h3 id="usage-default"><a href="#usage-default">HTML + Javascript</a></h3>
<div class="alert my-6">
{% lucide "circle-alert" %}
<h2>This component requires Javascript.</h2>
</div>
<h3 id="usage-default"><a href="#usage-html">HTML</a></h3>
<section class="prose">
<p>The Alert Dialog component uses the same Alpine.js code as the <a href="/components/dialog" class="font-medium underline underline-offset-4">Dialog</a> component with one small difference: we set <code>initialCloseOnOverlayClick</code> to <code>false</code> to prevent the dialog from closing when the overlay is clicked.</p>
<p>You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
<p>The Alert Dialog component is identical to <a href="/components/dialog" class="font-medium underline underline-offset-4">Dialog</a> except for two small differences: we do not display a close button and we do not let the user close the dialog when clicking on the backdrop (no <code>onClick</code> attribute on the <code>&lt;dialog&gt;</code> or the <code>&lt;article&gt;</code> elements).</p>
</section>
<div class="flex flex-wrap gap-2 my-6">
<a class="badge-outline" href="/installation/#install-js">
Components with Javascript
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="/installation/#install-cli">
Use the CLI
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/js/dialog.js" target="_blank">
Individual JS file (dialog.js)
{% lucide "arrow-right" %}
</a>
</div>
{% from "tabs.njk" import tabs %}
{% set tabsets = [
{ tab: "HTML", panel: code_block(code_html | prettyHtml, class="") },
{ tab: "Javascipt (Alpine.js)", panel: code_block(code_script | prettyHtml, class="") }
] %}
{{ tabs(id="alert-dialog", tabsets=tabsets) }}
{{ code_block(code_html | prettyHtml, class="") }}
<h3 id="usage-macro"><a href="#usage-macro">Jinja and Nunjucks</a></h3>
<div class="prose">
<p>You can use the <code class="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs">dialog()</code> Nunjucks or Jinja macro for this component. If you use one of the macros, make sure to set <code>close_on_overlay_click</code> and <code>close_button</code> to <code>False</code> (or <code>false</code> for Nunjucks).</p>
<p>You can use the <code class="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs">dialog()</code> Nunjucks or Jinja macro for this component. Make sure to set <code>close_on_overlay_click</code> and <code>close_button</code> to <code>False</code> (or <code>false</code> for Nunjucks).</p>
</div>
<div class="flex flex-wrap gap-2 my-6">
+4 -41
View File
@@ -16,16 +16,6 @@ toc:
<h2>There is no dedicated breadcrumb component in Basecoat.</h2>
</div>
{% set code_script %}
<script>
{% fetchCode "src/js/dropdown-menu.js" %}
document.addEventListener('alpine:init', () => {
window.basecoat.registerDropdownMenu(Alpine);
});
</script>
{% endset %}
{% set code_html %}
<ol class="text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5">
<li class="inline-flex items-center gap-1.5">
@@ -43,7 +33,7 @@ document.addEventListener('alpine:init', () => {
register_on=["alpine:init", "htmx:afterSwap"]
) %}
<nav role="menu">
<button type="button" role="menuitem">Documentation</button>
<button type="button" role="menuitem" onClick="console.log('Documentation')">Documentation</button>
<button type="button" role="menuitem">Themes</button>
<button type="button" role="menuitem">GitHub</button>
</nav>
@@ -60,39 +50,12 @@ document.addEventListener('alpine:init', () => {
</ol>
{% endset %}
{{ code_preview("dropdown-menu", code_script | prettyHtml ~ code_html | prettyHtml) }}
{{ code_preview("dropdown-menu", code_html | prettyHtml) }}
<h2 id="usage"><a href="#usage">Usage</a></h2>
<div class="alert my-6">
{% lucide "circle-alert" %}
<h2>This component requires Javascript.</h2>
</div>
<section class="prose">
<p>This example uses some simple Tailwind CSS styles along with the <a href="/components/dropdown-menu">Dropdown Menu</a>.</p>
<p>You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
<p>This example uses some simple Tailwind CSS styles along with the <a href="/components/dropdown-menu">Dropdown Menu</a> component (which may require JavaScript).</p>
</section>
<div class="flex flex-wrap gap-2 my-6">
<a class="badge-outline" href="/installation/#install-js">
Components with Javascript
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="/installation/#install-cli">
Use the CLI
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/js/select.js" target="_blank">
Individual JS file (select.js)
{% lucide "arrow-right" %}
</a>
</div>
{% from "tabs.njk" import tabs %}
{% set tabsets = [
{ tab: "HTML", panel: code_block(code_html | prettyHtml, class="") },
{ tab: "Javascipt (Alpine.js)", panel: code_block(code_script | prettyHtml, class="") }
] %}
{{ tabs(id="alert-dialog", tabsets=tabsets) }}
{{ code_block(code_html | prettyHtml, class="") }}
+5 -5
View File
@@ -6,7 +6,7 @@ toc:
- label: Usage
id: usage
children:
- label: HTML + Javascript
- label: HTML + JavaScript
id: usage-default
- label: Jinja and Nunjucks
id: usage-macro
@@ -52,11 +52,11 @@ document.addEventListener('alpine:init', () => {
<h2 id="usage"><a href="#usage">Usage</a></h2>
<h3 id="usage-default"><a href="#usage-default">HTML + Javascript</a></h3>
<h3 id="usage-default"><a href="#usage-default">HTML + JavaScript</a></h3>
<div class="alert my-6">
{% lucide "circle-alert" %}
<h2>This component requires Javascript.</h2>
<h2>This component requires JavaScript.</h2>
</div>
<div class="prose">
@@ -68,12 +68,12 @@ document.addEventListener('alpine:init', () => {
<li><code>data-empty</code> to set up the message when the search is empty.</li>
</ul>
<p>You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
<p>You can include the JavaScript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
</div>
<div class="flex flex-wrap gap-2 my-6">
<a class="badge-outline" href="/installation/#install-js">
Components with Javascript
Components with JavaScript
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="/installation/#install-cli">
+37 -73
View File
@@ -6,8 +6,10 @@ toc:
- label: Usage
id: usage
children:
- label: HTML + Javascript
id: usage-default
- label: HTML
id: usage-html
- label: JavasScript
id: usage-html
- label: Jinja and Nunjucks
id: usage-macro
- label: Examples
@@ -30,12 +32,10 @@ toc:
id="demo-dialog-edit-profile",
title="Edit profile",
description="Make changes to your profile here. Click save when you're done.",
main_attrs={"x-ref": "myDialog"},
trigger="Edit Profile",
trigger_attrs={"class": "btn-outline"},
content_attrs={"class": "w-full sm:max-w-[425px] max-h-[612px]"},
footer=footer,
register_on=["alpine:init", "htmx:afterSwap"]
footer=footer
) %}
<form class="form grid gap-4">
<div class="grid gap-3">
@@ -50,75 +50,40 @@ toc:
{% endcall %}
{% endset %}
{% set code_script %}
<script>
{% fetchCode "src/js/dialog.js" %}
document.addEventListener('alpine:init', () => {
window.basecoat.registerDialog(Alpine);
});
</script>
{% endset %}
{{ code_preview("dialog", code_script | prettyHtml ~ code_html | prettyHtml) }}
{{ code_preview("dialog", code_html | prettyHtml) }}
<h2 id="usage"><a href="#usage">Usage</a></h2>
<h3 id="usage-default"><a href="#usage-default">HTML + Javascript</a></h3>
<div class="alert my-6">
{% lucide "circle-alert" %}
<h2>This component requires Javascript.</h2>
</div>
<h3 id="usage-html"><a href="#usage-html">HTML</a></h3>
<section class="prose">
<p>The component is structured as such:</p>
<ul>
<li>A <code>&lt;div class="dialog"&gt;</code> which wraps around the entire component and holds it state (e.g. open/close).</li>
<li>A <code>&lt;button&gt;</code> that acts as the trigger to open or close the popover.</li>
<li>A <code>&lt;div role="dialog"&gt;</code> that holds the dialog itself. It also acts as the backdrop for the dialog.</li>
<li>Inside of it, we have:
<ul>
<li>A <code>&lt;header&gt;</code> that contains the title (<code>&lt;h2&gt;</code>) and description (<code>&lt;p&gt;</code>) of the dialog.</li>
<li>A <code>&lt;section&gt;</code> for the content.</li>
<li>A <code>&lt;footer&gt;</code> that usually contains actions.</li>
<li>A close <code>&lt;button&gt;</code>.</li>
</ul>
</li>
</ul>
<p>You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
<p>The component has the following structure:</p>
<dl>
<dt><code class="highlight language-html">&lt;dialog class="dialog"&gt;</code></dt>
<dd>Wraps around the entire component and acts as its backdrop.
<dl>
<dt><code class="highlight language-html">&lt;header&gt;</code></dt>
<dd>Contains the header for the dialog:
<dl>
<dt><code class="highlight language-html">&lt;h2&gt;</code></dt>
<dd>The title of the dialog.</dd>
<dt><code class="highlight language-html">&lt;p&gt;</code> <span class="badge-secondary">Optional</span></dt>
<dd>The description of the dialog.</dd>
</dl>
</dd>
<dt><code class="highlight language-html">&lt;section&gt;</code> <span class="badge-secondary">Optional</span></dt>
<dd>For the content.</dd>
<dt><code class="highlight language-html">&lt;footer&gt;</code> <span class="badge-secondary">Optional</span></dt>
<dd>Usually contains actions.</dd>
<dt><code class="highlight language-html">&lt;form method="dialog"&gt;</code> <span class="badge-secondary">Optional</span></dt>
<dd>A form that contains a single button to close the dialog.</dd>
</dl>
</dd>
</dl>
<p>The example below also include a <code>&lt;button type="button&gt;</code> to show how we can open the dialog using <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal" target="_blank">the <code>showModal()</code> method</a> as well as aria-related attributes recommended for accessiblity:</p>.</p>
</section>
<div class="flex flex-wrap gap-2 my-6">
<a class="badge-outline" href="/installation/#install-js">
Components with Javascript
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="/installation/#install-cli">
Use the CLI
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/js/dialog.js" target="_blank">
Individual JS file (dialog.js)
{% lucide "arrow-right" %}
</a>
</div>
{% from "tabs.njk" import tabs %}
{% set tabsets = [
{ tab: "HTML", panel: code_block(code_html | prettyHtml, class="") },
{ tab: "Javascipt (Alpine.js)", panel: code_block(code_script | prettyHtml, class="") }
] %}
{{ tabs(id="combobox", tabsets=tabsets) }}
<section class="prose">
<p>The component will dispatch <code>dialog:opened</code> and <code>dialog:closed</code> events when the dialog is opened or closed. You can also dispatch <code>dialog:open</code> and <code>dialog:close</code> events to open or close the dialog programmatically. You will need the element to have an <code>id</code> to be able to target it (e.g. <code>&lt;div id="my-dialog" class="dialog"&gt;</code>):</p>
</section>
{% set code_dispatch %}<button type="button" x-init @click="$dispatch('dialog:open', { id: 'my-dialog' })">
Open #my-dialog
</button>{% endset %}
{{ code_block(code_dispatch | prettyHtml, "html") }}
{{ code_block(code_html | prettyHtml, class="") }}
<h3 id="usage-macro"><a href="#usage-macro">Jinja and Nunjucks</a></h3>
@@ -141,20 +106,19 @@ document.addEventListener('alpine:init', () => {
</a>
</div>
{% set raw_code %}{% raw %}{% set footer %}
<button class="btn-outline" @click="open = false">Cancel</button>
<button class="btn" @click="open = false">Save changes</button>
{% set raw_code %}{% raw %}{% set code_html %}
{% set footer %}
<button class="btn-outline" onClick="this.closest('dialog').close()">Cancel</button>
<button class="btn" onClick="this.closest('dialog').close()">Save changes</button>
{% endset %}
{% call dialog(
id="demo-dialog-edit-profile",
title="Edit profile",
description="Make changes to your profile here. Click save when you're done.",
main_attrs={"x-ref": "myDialog"},
trigger="Edit Profile",
trigger_attrs={"class": "btn-outline"},
content_attrs={"class": "w-full sm:max-w-[425px] max-h-[612px]"},
footer=footer,
register_on=["alpine:init", "htmx:afterSwap"]
footer=footer
) %}
<form class="form grid gap-4">
<div class="grid gap-3">
+5 -5
View File
@@ -6,7 +6,7 @@ toc:
- label: Usage
id: usage
children:
- label: HTML + Javascript
- label: HTML + JavaScript
id: usage-default
- label: Jinja and Nunjucks
id: usage-macro
@@ -76,11 +76,11 @@ toc:
<h2 id="usage">Usage</h2>
<h3 id="usage-default"><a href="#usage-default">HTML + Javascript</a></h3>
<h3 id="usage-default"><a href="#usage-default">HTML + JavaScript</a></h3>
<div class="alert my-6">
{% lucide "circle-alert" %}
<h2>This component requires Javascript.</h2>
<h2>This component requires JavaScript.</h2>
</div>
<section class="prose">
@@ -105,12 +105,12 @@ toc:
<li><code>data-align</code> can be set to <code>start</code>, <code>center</code>, or <code>end</code> to change the alignment of the popover.</li>
</ul>
<p>You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
<p>You can include the JavaScript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
</section>
<div class="flex flex-wrap gap-2 my-6">
<a class="badge-outline" href="/installation/#install-js">
Components with Javascript
Components with JavaScript
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="/installation/#install-cli">
+11 -5
View File
@@ -6,7 +6,7 @@ toc:
- label: Usage
id: usage
children:
- label: HTML + Javascript
- label: HTML + JavaScript
id: usage-default
- label: Jinja and Nunjucks
id: usage-macro
@@ -64,11 +64,17 @@ document.addEventListener('alpine:init', () => {
<h2 id="usage"><a href="#usage">Usage</a></h2>
<h3 id="usage-default"><a href="#usage-default">HTML + Javascript</a></h3>
<h3 id="usage-default"><a href="#usage-default">HTML</a></h3>
<div class="alert my-6">
{% lucide "circle-alert" %}
<h2>This component requires Javascript.</h2>
<h2>This component requires JavaScript.</h2>
<section><p>By default, you can use the component without JavaScript. However, you may want to <a href="#usage-javascript" class="underline underline-offset-4">include some JavaScript</a> to support older browsers and accessiblity features, including keyboard navigation.</p></section>
</div>
<div class="alert my-6">
{% lucide "circle-alert" %}
<h2>This component requires JavaScript.</h2>
</div>
<section class="prose">
@@ -87,12 +93,12 @@ document.addEventListener('alpine:init', () => {
<li><code>data-align</code> can be set to <code>start</code>, <code>center</code>, or <code>end</code> to change the alignment of the popover.</li>
</ul>
<p>You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
<p>You can include the JavaScript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
</section>
<div class="flex flex-wrap gap-2 my-6">
<a class="badge-outline" href="/installation/#install-js">
Components with Javascript
Components with JavaScript
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="/installation/#install-cli">
+71 -55
View File
@@ -8,7 +8,7 @@ toc:
children:
- label: HTML
id: usage-html
- label: HTML + Javascript
- label: HTML + JavaScript
id: usage-js
- label: Jinja and Nunjucks
id: usage-macro
@@ -73,39 +73,66 @@ toc:
{{ code_block(code_native) }}
<h3 id="usage-js"><a href="#usage-js">HTML + Javascript</a></h3>
<h3 id="usage-js"><a href="#usage-js">HTML + JavaScript</a></h3>
<section class="prose">
<p>If you need to do more than what <code>&lt;select&gt;</code> allows (e.g. HTML options), you can use this component. It is structured as such:</p>
<ul>
<li>A <code>&lt;div class="popover"&gt;</code> which wraps around the entire component and holds it state (e.g. open/close).</li>
<li>A <code>&lt;button&gt;</code> that acts as the trigger to open or close the popover.</li>
<li>A <code>&lt;div data-popover&gt;</code> that holds the content of the popover.</li>
<li>Inside of the <code>&lt;div data-popover&gt;</code> is a <code>&lt;nav role="listbox"&gt;</code> that contains a combination of:
<ul>
<li><code>&lt;div role="option"&gt;</code> for the options with a <code>data-value</code> attribute.</li>
<li><code>&lt;hr role="separator"&gt;</code> to display a horizontal separator.</li>
<li><code>&lt;div role="group"&gt;</code> to group options.</li>
<li><code>&lt;span role="heading"&gt;</code> for group headings.</li>
</ul>
</li>
<li>An <code>&lt;input type="hidden"&gt;</code> to hold the value of the field (if needed).</li>
</ul>
<p>As with the <a href="/components/popover">Popover</a> component, you can set up a few additional options on the <code>&lt;div data-popover&gt;</code> element:</p>
<ul>
<li><code>data-side</code> can be set to <code>top</code>, <code>bottom</code>, <code>left</code>, or <code>right</code> to change the side of the popover.</li>
<li><code>data-align</code> can be set to <code>start</code>, <code>center</code>, or <code>end</code> to change the alignment of the popover.</li>
</ul>
<p>You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
<p>The component has the following structure:</p>
<dl>
<dt><code class="highlight language-html">&lt;div class="select"&gt;</code></dt>
<dd>Wraps around the entire component.
<dl>
<dt><code class="highlight language-html">&lt;button type="button" popovertarget="{ POPOVER_ID }"&gt;</code></dt>
<dd>
<p>The trigger to open the popover, can also have the following attributes:</p>
<ul>
<li><code>id="{BUTTON_ID}"</code>: linked to by the <code>aria-labelledby</code> attribute of the listbox.</li>
<li><code>aria-haspopup="listbox"</code>: indicates that the button opens a listbox.</li>
<li><code>aria-controls="{ LISTBOX_ID }"</code>: points to the listbox's id.</li>
<li><code>aria-expanded="false"</code>: tracks the popover's state.</li>
<li><code>aria-activedescendant="{ OPTION_ID }"</code>: points to the active option's id.</li>
</ul>
</dd>
<dt><code class="highlight language-html">&lt;div popover class="popover" id="{ POPOVER_ID }"&gt;</code></dt>
<dd>As with the <a href="/components/popover">Popover</a> component, you can set up the side and alignment of the popover using the <code>data-side</code> and <code>data-align</code> attributes.
<dl>
<dt><code class="highlight language-html">&lt;div role="listbox"&gt;</code></dt>
<dd>The listbox containing the options. Should have the following attributes:
<ul>
<li><code>id="{ LISTBOX_ID }"</code>: refered to by the <code>aria-controls</code> attribute of the trigger.</li>
<li><code>aria-labelledby="{ BUTTON_ID }"</code>: linked to by the button's <code>id</code> attribute.</li>
</ul>
<dl>
<dt><code class="highlight language-html">&lt;div role="option" data-value="{ VALUE }"&gt;</code></dt>
<dd>Option that can be selected.Should have a unique id if you use the <code>aria-activedescendant</code> attribute on the trigger.</dd>
<dt><code class="highlight language-html">&lt;hr role="separator"&gt;</code> <span class="badge-secondary">Optional</span></dt>
<dd>Separator between groups/options.</dd>
<dt><code class="highlight language-html">&lt;div role="group"&gt;</code> <span class="badge-secondary">Optional</span></dt>
<dd>Group of options.</dd>
<dt><code class="highlight language-html">&lt;span role="heading"&gt;</code></dt>
<dd>Group heading.</dd>
</dl>
</dd>
</dl>
</dl>
</dd>
<dt><code class="highlight language-html">&lt;input type="hidden" name="{ NAME }" value="{ VALUE }"&gt;</code> <span class="badge-secondary">Optional</span></dt>
<dd>The hidden input that holds the value of the field (if needed).</dd>
</dl>
<p>More aria-related attributes are also recommended as illustrated in the example below:</p>
</section>
{{ code_block(code_html | prettyHtml, "html") }}
<section class="prose">
<p>This component requires you to include the <a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/js/select.js" target="_blank"><code>select.js</code></a> file and recommends you include the <a href="https://github.com/oddbird/css-anchor-positioning" target="_blank">CSS anchor positioning polyfill</a> to support older browsers. You can either include the code shown below in your <code>&lt;head&gt;</code> or <a class="badge-outline" href="/installation/#install-js">use one of the alternative methods</a>.</p>
</section>
{% set code_script %}{% raw %}<script src="https://unpkg.com/css-anchor-positioning"></script>{% endraw %}{% endset %}
{{ code_block(code_script | prettyHtml, "html") }}
<div class="flex flex-wrap gap-2 my-6">
<a class="badge-outline" href="/installation/#install-js">
Components with Javascript
Components with JavaScript
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="/installation/#install-cli">
@@ -118,36 +145,10 @@ toc:
</a>
</div>
{% from "tabs.njk" import tabs %}
{% set tabsets = [
{ tab: "HTML", panel: code_block(code_html | prettyHtml, class="") },
{ tab: "Javascipt (Alpine.js)", panel: code_block(code_script | prettyHtml, class="") }
] %}
{{ tabs(id="select", tabsets=tabsets) }}
<section class="prose">
<p>The component will dispatch a <code>select:change</code> event when a value is selected, along with the value and label of the selected option in the <code>detail</code> object.</p>
</section>
<h3 id="usage-macro"><a href="#usage-macro">Jinja and Nunjucks</a></h3>
<div class="prose">
<p>You can use the <code class="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs">dropdown_menu()</code> Nunjucks or Jinja macro for this component.</p>
</div>
<div class="flex flex-wrap gap-2 my-6">
<a class="badge-outline" href="/installation/#install-macros" target="_blank">
Use Nunjucks or Jinja macros
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/jinja/dialog.html.jinja" target="_blank">
Jinja macro
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/dialog.njk" target="_blank">
Nunjucks macro
{% lucide "arrow-right" %}
</a>
<p>You can use the <code class="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs">select()</code> Nunjucks or Jinja macro for this component.</p>
</div>
{% set raw_code %}{% raw %}{{ select(
@@ -167,6 +168,21 @@ toc:
) }}{% endraw %}{% endset %}
{{ code_block(raw_code, "jinja") }}
<div class="flex flex-wrap gap-2 my-6">
<a class="badge-outline" href="/installation/#install-macros" target="_blank">
Use Nunjucks or Jinja macros
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/jinja/dialog.html.jinja" target="_blank">
Jinja macro
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/dialog.njk" target="_blank">
Nunjucks macro
{% lucide "arrow-right" %}
</a>
</div>
<h2 id="examples"><a href="#examples">Examples</a></h2>
<h3 id="example-scrollable"><a href="#example-scrollable">Scrollable</a></h3>
+5 -5
View File
@@ -6,7 +6,7 @@ toc:
- label: Usage
id: usage
children:
- label: HTML + Javascript
- label: HTML + JavaScript
id: usage-default
- label: Jinja and Nunjucks
id: usage-macro
@@ -18,11 +18,11 @@ toc:
<h2 id="usage"><a href="#usage">Usage</a></h2>
<h3 id="usage-default"><a href="#usage-default">HTML + Javascript</a></h3>
<h3 id="usage-default"><a href="#usage-default">HTML + JavaScript</a></h3>
<div class="alert my-6">
{% lucide "circle-alert" %}
<h2>This component requires Javascript.</h2>
<h2>This component requires JavaScript.</h2>
</div>
<section class="prose">
@@ -40,12 +40,12 @@ toc:
<p>Any elememt on the page can toggle, open or close the sidebar by dispatching the <code>sidebar:toggle</code>, <code>sidebar:open</code> or <code>sidebar:close</code> events.</p>
<p>You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
<p>You can include the JavaScript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
</section>
<div class="flex flex-wrap gap-2 my-6">
<a class="badge-outline" href="/installation/#install-js">
Components with Javascript
Components with JavaScript
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="/installation/#install-cli">
+4 -2
View File
@@ -10,7 +10,8 @@ toc:
{% from "macros/code_preview.njk" import code_preview %}
{% from "macros/code_block.njk" import code_block %}
{% set code %}<table class="table">
{% set code %}<div class="overflow-x-auto">
<table class="table">
<caption>A list of your recent invoices.</caption>
<thead>
<tr>
@@ -70,7 +71,8 @@ toc:
<td class="text-right">$2,500.00</td>
</tr>
</tfoot>
</table>{% endset %}
</table>
</div>{% endset %}
{{ code_preview("table", code, "w-full") }}
<h2 id="usage"><a href="#usage">Usage</a></h2>
+5 -5
View File
@@ -6,7 +6,7 @@ toc:
- label: Usage
id: usage
children:
- label: HTML + Javascript
- label: HTML + JavaScript
id: usage-default
- label: Jinja and Nunjucks
id: usage-macro
@@ -90,11 +90,11 @@ document.addEventListener('alpine:init', () => {
<h2 id="usage"><a href="#usage">Usage</a></h2>
<h3 id="usage-default"><a href="#usage-default">HTML + Javascript</a></h3>
<h3 id="usage-default"><a href="#usage-default">HTML + JavaScript</a></h3>
<div class="alert my-6">
{% lucide "circle-alert" %}
<h2>This component requires Javascript.</h2>
<h2>This component requires JavaScript.</h2>
</div>
<section class="prose">
@@ -106,12 +106,12 @@ document.addEventListener('alpine:init', () => {
<li>A series of <code>&lt;div role="tabpanel"&gt;</code> that holds the tab panels corresponding to each tab button.</li>
</ul>
<p>You can include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
<p>You can include the JavaScript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
</section>
<div class="flex flex-wrap gap-2 my-6">
<a class="badge-outline" href="/installation/#install-js">
Components with Javascript
Components with JavaScript
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="/installation/#install-cli">
+6 -6
View File
@@ -6,7 +6,7 @@ toc:
- label: Usage
id: usage
children:
- label: HTML + Javascript
- label: HTML + JavaScript
id: usage-default
children:
- label: Append a toast
@@ -52,11 +52,11 @@ toc:
<h2 id="usage"><a href="#usage">Usage</a></h2>
<h3 id="usage-default"><a href="#usage-default">HTML + Javascript</a></h3>
<h3 id="usage-default"><a href="#usage-default">HTML + JavaScript</a></h3>
<div class="alert my-6">
{% lucide "circle-alert" %}
<h2>This component requires Javascript.</h2>
<h2>This component requires JavaScript.</h2>
</div>
<section class="prose">
@@ -64,12 +64,12 @@ toc:
<p>The <code>&lt;template id="toast-template"&gt;</code> bit is only required if you plan on using the <code>$toast()</code> method (see below).</p>
<p>You will also need to include the Javascript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
<p>You will also need to include the JavaScript code provided below, load it as an individual file or use the CLI. Some Alpine.js properties are also required on certain elements (e.g. <code class="text-xs">x-bind</code>, <code class="text-xs">x-data</code>, <code class="text-xs">@click</code>).</p>
</section>
<div class="flex flex-wrap gap-2 my-6">
<a class="badge-outline" href="/installation/#install-js">
Components with Javascript
Components with JavaScript
{% lucide "arrow-right" %}
</a>
<a class="badge-outline" href="/installation/#install-cli">
@@ -191,7 +191,7 @@ document.addEventListener('alpine:init', () => {
<li><code>action</code> (optional): an object to define an action button:
<ul>
<li><code>label</code>: the label of the action button.</li>
<li><code>click</code> (optional): a string of Javascript to execute when the button is clicked.</li>
<li><code>click</code> (optional): a string of JavaScript to execute when the button is clicked.</li>
<li><code>url</code> (optional): a URL to link to when the button is clicked.</li>
</ul>
</li>
+10 -10
View File
@@ -14,7 +14,7 @@ toc:
id: install-css-import
- label: That's it
id: install-css-done
- label: Components with Javascript
- label: Components with JavaScript
id: install-js
- label: Use the CLI
id: install-cli
@@ -67,20 +67,20 @@ toc:
<h3 class="mb-4 scroll-m-20 font-semibold tracking-tight" id="install-css-done"><a href="#install-css-done">That's it</a></h3>
<div class="prose">
<p>You can now use any of the Basecoat classes in your project (e.g. <code>btn</code>, <code>card</code>, <code>input</code>, etc). Read the documentation about each component to get started (e.g. <a href="/components/button">button</a>, <a href="/components/card">card</a>, <a href="/components/input">input</a>, etc).</p>
<p>Some components (e.g. <a href="/components/select">Select</a>) require <a href="#install-js">Javascript code to work</a>.</p>
<p>Some components (e.g. <a href="/components/select">Select</a>) require <a href="#install-js">JavaScript code to work</a>.</p>
</div>
</li>
</ol>
<h2 id="install-js"><a href="#install-js">Components with Javascript</a></h2>
<h2 id="install-js"><a href="#install-js">Components with JavaScript</a></h2>
<div class="prose">
<p><b>Some components require Javascript code to work.</b></p>
<p><b>Some components require JavaScript code to work.</b></p>
<p>The documentation provides Alpine.js code snippets to cover that. <a href="https://alpinejs.dev/docs/installation" target="_blank">Make sure to install Alpine.js if you decide to use them.</a></p>
<p>For simple components like <a href="/components/accordion">Accordion</a> or <a href="/components/slider">Slider</a>, you will notice some inline Alpine.js code that you can just copy along with the rest of the HTML.</p>
<p>For more complex components (<a href="/components/dialog">Dialog</a>, <a href="/components/dropdown-menu">Dropdown Menu</a>, <a href="/components/popover">Popover</a>, <a href="/components/select">Select</a>, <a href="/components/tabs">Tabs</a> and <a href="/components/tooltip">Toast</a>), you have one of 2 options:
<ul>
<li>Copy the Javascript snippet provided in the documentation page into your project.</li>
<li>Copy the JavaScript snippet provided in the documentation page into your project.</li>
<li>
<p><a href="https://github.com/hunvreus/basecoat/tree/main/src/js" target="_blank">Download the script as a separate file</a> and include it in your project (<a href="#install-cli">or let the CLI do it for you</a>). <b>If you use this approach, make sure to load the file before the Alpine.js core JS file and register the component on the <code>alpine:init</code> event.</b> For example:</p>
{% set code %}<script src="/assets/js/dialog.js" defer></script>
@@ -93,7 +93,7 @@ toc:
{{ code_block(code, "html") }}
</li>
</ul>
<p>You are also welcome to write your own Javascript.</p>
<p>You are also welcome to write your own JavaScript.</p>
<p>All of the components try and implement WAI-ARIA standards to make them accessible.</p>
</div>
@@ -117,15 +117,15 @@ toc:
{{ tabs(id="cli-execution", tabsets=exec_tabsets, label="Execute CLI commands to add components:") }}
<div class="prose my-6">
<p>You will be asked to confirm the template engine and destination directory for the Javascript and macros.</p>
<p>You will be asked to confirm the template engine and destination directory for the JavaScript and macros.</p>
</div>
<h2 id="install-macros"><a href="#install-macros">Use Nunjucks or Jinja macros</a></h2>
<div class="prose">
<p>For more complex components, you can use <a href="https://mozilla.github.io/nunjucks/" target="_blank">Nunjucks</a> or <a href="https://jinja.palletsprojects.com" target="_blank">Jinja</a> macros to help you generate the HTML and Javascript code.</p>
<p>For more complex components, you can use <a href="https://mozilla.github.io/nunjucks/" target="_blank">Nunjucks</a> or <a href="https://jinja.palletsprojects.com" target="_blank">Jinja</a> macros to help you generate the HTML and JavaScript code.</p>
<p>To install them, either <a href="https://github.com/hunvreus/basecoat/tree/main/src" target="_blank">copy them directly from the GitHub repository</a> or use <a href="#install-cli">the CLI</a> to do the work for you.</p>
<p>For example, here's how you could use the <code>select()</code> macro to generate the HTML and Javascript code for a <a href="/components/select">Select</a> component:</p>
<p>For example, here's how you could use the <code>select()</code> macro to generate the HTML and JavaScript code for a <a href="/components/select">Select</a> component:</p>
{% set code %}{% raw %}{% call select() %}
<div role="group" aria-labelledby="fruit-options-label">
<span id="fruit-options-label" role="heading">Fruits</span>
@@ -137,7 +137,7 @@ toc:
</div>
{% endcall %}{% endraw %}{% endset %}
{{ code_block(code, "jinja") }}
<p>Make sure to include the Javascript code <b>before</b> Alpine.js core JS file:</p>
<p>Make sure to include the JavaScript code <b>before</b> Alpine.js core JS file:</p>
{% set code %}<script src="/assets/js/select.js" defer></script>
<script>
document.addEventListener('alpine:init', () => {
+2 -2
View File
@@ -731,12 +731,12 @@
@apply bg-popover text-popover-foreground;
}
}
div.select {
*:not(select).select {
[popover].popover {
@apply p-1 min-w-[anchor-size(width)];
[role='option'] {
@apply aria-hidden:hidden [&_svg]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none [&_svg]:shrink-0 [&_svg]:size-4 aria-disabled:opacity-50 aria-disabled:pointer-events-none disabled:opacity-50 disabled:pointer-events-none w-full truncate;
@apply aria-hidden:hidden [&_svg]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm pl-2 py-1.5 pr-7.5 text-sm outline-hidden select-none [&_svg]:shrink-0 [&_svg]:size-4 aria-disabled:opacity-50 aria-disabled:pointer-events-none disabled:opacity-50 disabled:pointer-events-none w-full truncate;
&[aria-selected='true'] {
@apply bg-[image:var(--check-icon)] bg-no-repeat bg-position-[center_right_0.5rem] bg-size-[0.875rem];
+45 -3
View File
@@ -1,4 +1,21 @@
(() => {
// Monkey patching the history API to detect client-side navigation
if (!window.history.__basecoatPatched) {
const originalPushState = window.history.pushState;
window.history.pushState = function(...args) {
originalPushState.apply(this, args);
window.dispatchEvent(new Event('basecoat:locationchange'));
};
const originalReplaceState = window.history.replaceState;
window.history.replaceState = function(...args) {
originalReplaceState.apply(this, args);
window.dispatchEvent(new Event('basecoat:locationchange'));
};
window.history.__basecoatPatched = true;
}
const initSidebar = (sidebarComponent) => {
const initialOpen = sidebarComponent.dataset.initialOpen !== 'false';
const initialMobileOpen = sidebarComponent.dataset.initialMobileOpen === 'true';
@@ -8,6 +25,20 @@
? (window.innerWidth >= breakpoint ? initialOpen : initialMobileOpen)
: initialOpen;
const updateCurrentPageLinks = () => {
const currentPath = window.location.pathname.replace(/\/$/, '');
sidebarComponent.querySelectorAll('a').forEach(link => {
if (link.hasAttribute('data-ignore-current')) return;
const linkPath = new URL(link.href).pathname.replace(/\/$/, '');
if (linkPath === currentPath) {
link.setAttribute('aria-current', 'page');
} else {
link.removeAttribute('aria-current');
}
});
};
const updateState = () => {
sidebarComponent.setAttribute('aria-hidden', !open);
if (open) {
@@ -35,17 +66,28 @@
});
sidebarComponent.addEventListener('click', (e) => {
const target = e.target;
const nav = sidebarComponent.querySelector('nav');
const closeTarget = e.target.closest('[data-sidebar-close-on-mobile]');
if (closeTarget && window.innerWidth < breakpoint) {
const isMobile = window.innerWidth < breakpoint;
if (isMobile && (target.closest('[data-close-mobile-sidebar]') || (target.closest('a, button') && !target.closest('[data-keep-mobile-sidebar-open]')))) {
if (document.activeElement) document.activeElement.blur();
setState(false);
} else if (e.target === sidebarComponent || (nav && !nav.contains(e.target))) {
return;
}
if (target === sidebarComponent || (nav && !nav.contains(target))) {
if (document.activeElement) document.activeElement.blur();
setState(false);
}
});
window.addEventListener('popstate', updateCurrentPageLinks);
window.addEventListener('basecoat:locationchange', updateCurrentPageLinks);
updateState();
updateCurrentPageLinks();
sidebarComponent.dataset.sidebarInitialized = true;
};
+14 -13
View File
@@ -22,12 +22,11 @@
title=None,
description=None,
footer=None,
main_attrs={},
dialog_attrs={},
trigger_attrs={},
content_attrs={},
content_header_attrs={},
content_body_attrs={},
content_footer_attrs={},
header_attrs={},
body_attrs={},
footer_attrs={},
open=false,
close_button=true,
close_on_overlay_click=true
@@ -47,25 +46,27 @@
<dialog
id="{{ id }}"
class="dialog"
onClick="this.close()"
{% for key, value in main_attrs %}
aria-labelledby="{{ id }}-title"
{% if description %}aria-describedby="{{ id }}-description"{% endif %}
{% if close_on_overlay_click %}onClick="this.close()"{% endif %}
{% for key, value in dialog_attrs %}
{{ key }}="{{ value }}"
{% endfor %}
>
<article onClick="event.stopPropagation()">
<article {% if close_on_overlay_click %}onClick="event.stopPropagation()"{% endif %}>
{% if title or description %}
<header
{% for key, value in content_header_attrs %}
{% for key, value in header_attrs %}
{{ key }}="{{ value }}"
{% endfor %}
>
<h2 id="{{ id }}-title">{{ title | safe }}</h2>
{% if description %}<p>{{ description | safe }}</p>{% endif %}
{% if description %}<p id="{{ id }}-description">{{ description | safe }}</p>{% endif %}
</header>
{% endif %}
{% if caller %}
<section
{% for key, value in content_body_attrs %}
{% for key, value in body_attrs %}
{{ key }}="{{ value }}"
{% endfor %}
>
@@ -74,7 +75,7 @@
{% endif %}
{% if footer %}
<footer
{% for key, value in content_footer_attrs %}
{% for key, value in footer_attrs %}
{{ key }}="{{ value }}"
{% endfor %}
>
@@ -85,7 +86,7 @@
<form method="dialog">
<button aria-label="Close dialog">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
</button>
</button>
</form>
{% endif %}
</article>