mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-14 14:08:18 +00:00
Migrate empty and item docs to Basecoat APIs
This commit is contained in:
+18
-1
@@ -1,3 +1,10 @@
|
||||
/* Basecoat docs font override. This is intentionally not part of the package default. */
|
||||
:root {
|
||||
--font-sans: "Geist", ui-sans-serif, system-ui, sans-serif;
|
||||
--font-heading: "Geist", ui-sans-serif, system-ui, sans-serif;
|
||||
--font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
}
|
||||
|
||||
/* HTMX */
|
||||
.htmx-indicator {
|
||||
@apply invisible opacity-0 transition-discrete transition-all;
|
||||
@@ -11,6 +18,16 @@
|
||||
}
|
||||
|
||||
.content {
|
||||
pre {
|
||||
@apply grid max-h-[650px] overflow-y-auto rounded-lg bg-muted/40 text-sm;
|
||||
}
|
||||
pre > code {
|
||||
@apply block rounded-[inherit] bg-transparent p-3.5 font-mono;
|
||||
}
|
||||
pre > code.hljs {
|
||||
@apply !bg-transparent;
|
||||
}
|
||||
|
||||
> h2 {
|
||||
@apply mt-12 lg:mt-20 mb-6 scroll-m-22 text-2xl font-semibold tracking-tight first:mt-0;
|
||||
}
|
||||
@@ -89,4 +106,4 @@
|
||||
@apply max-md:ml-10 min-h-8 flex items-center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "../../src/css/base/base.css";
|
||||
@import "../../src/css/styles/style-luma.css";
|
||||
@@ -1,3 +0,0 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "../../src/css/base/base.css";
|
||||
@import "../../src/css/styles/style-lyra.css";
|
||||
@@ -1,3 +0,0 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "../../src/css/base/base.css";
|
||||
@import "../../src/css/styles/style-maia.css";
|
||||
@@ -1,3 +0,0 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "../../src/css/base/base.css";
|
||||
@import "../../src/css/styles/style-mira.css";
|
||||
@@ -1,3 +0,0 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "../../src/css/base/base.css";
|
||||
@import "../../src/css/styles/style-nova.css";
|
||||
@@ -1,3 +0,0 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "../../src/css/base/base.css";
|
||||
@import "../../src/css/styles/style-rhea.css";
|
||||
@@ -1,3 +0,0 @@
|
||||
@import "tailwindcss" source(none);
|
||||
@import "../../src/css/base/base.css";
|
||||
@import "../../src/css/styles/style-sera.css";
|
||||
@@ -0,0 +1,9 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@400&display=swap");
|
||||
@import "tailwindcss";
|
||||
@import "../../src/css/basecoat-luma.css";
|
||||
@import "./custom.css";
|
||||
@import "./highlight.css";
|
||||
|
||||
@import "./themes/claude.css";
|
||||
@import "./themes/doom-64.css";
|
||||
@import "./themes/supabase.css";
|
||||
@@ -0,0 +1,9 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@400&display=swap");
|
||||
@import "tailwindcss";
|
||||
@import "../../src/css/basecoat-lyra.css";
|
||||
@import "./custom.css";
|
||||
@import "./highlight.css";
|
||||
|
||||
@import "./themes/claude.css";
|
||||
@import "./themes/doom-64.css";
|
||||
@import "./themes/supabase.css";
|
||||
@@ -0,0 +1,9 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@400&display=swap");
|
||||
@import "tailwindcss";
|
||||
@import "../../src/css/basecoat-maia.css";
|
||||
@import "./custom.css";
|
||||
@import "./highlight.css";
|
||||
|
||||
@import "./themes/claude.css";
|
||||
@import "./themes/doom-64.css";
|
||||
@import "./themes/supabase.css";
|
||||
@@ -0,0 +1,9 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@400&display=swap");
|
||||
@import "tailwindcss";
|
||||
@import "../../src/css/basecoat-mira.css";
|
||||
@import "./custom.css";
|
||||
@import "./highlight.css";
|
||||
|
||||
@import "./themes/claude.css";
|
||||
@import "./themes/doom-64.css";
|
||||
@import "./themes/supabase.css";
|
||||
@@ -0,0 +1,9 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@400&display=swap");
|
||||
@import "tailwindcss";
|
||||
@import "../../src/css/basecoat-nova.css";
|
||||
@import "./custom.css";
|
||||
@import "./highlight.css";
|
||||
|
||||
@import "./themes/claude.css";
|
||||
@import "./themes/doom-64.css";
|
||||
@import "./themes/supabase.css";
|
||||
@@ -0,0 +1,9 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@400&display=swap");
|
||||
@import "tailwindcss";
|
||||
@import "../../src/css/basecoat-rhea.css";
|
||||
@import "./custom.css";
|
||||
@import "./highlight.css";
|
||||
|
||||
@import "./themes/claude.css";
|
||||
@import "./themes/doom-64.css";
|
||||
@import "./themes/supabase.css";
|
||||
@@ -0,0 +1,9 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@400&display=swap");
|
||||
@import "tailwindcss";
|
||||
@import "../../src/css/basecoat-sera.css";
|
||||
@import "./custom.css";
|
||||
@import "./highlight.css";
|
||||
|
||||
@import "./themes/claude.css";
|
||||
@import "./themes/doom-64.css";
|
||||
@import "./themes/supabase.css";
|
||||
@@ -0,0 +1,9 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@400&display=swap");
|
||||
@import "tailwindcss";
|
||||
@import "../../src/css/basecoat-vega.css";
|
||||
@import "./custom.css";
|
||||
@import "./highlight.css";
|
||||
|
||||
@import "./themes/claude.css";
|
||||
@import "./themes/doom-64.css";
|
||||
@import "./themes/supabase.css";
|
||||
+3
-2
@@ -1,8 +1,9 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@400&display=swap");
|
||||
@import "tailwindcss";
|
||||
@import "../../src/css/basecoat.css";
|
||||
@import "../../src/css/basecoat-vega.css";
|
||||
@import "./custom.css";
|
||||
@import "./highlight.css";
|
||||
|
||||
@import "./themes/claude.css";
|
||||
@import "./themes/doom-64.css";
|
||||
@import "./themes/supabase.css";
|
||||
@import "./themes/supabase.css";
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<script>
|
||||
(function() {
|
||||
try {
|
||||
const storedStyle = localStorage.getItem('styleVariant') || 'vega';
|
||||
const storedStyle = localStorage.getItem('styleVariant') || 'nova';
|
||||
document.documentElement.dataset.styleVariant = storedStyle;
|
||||
} catch (event) {
|
||||
console.error('Could not apply style variant from localStorage', event);
|
||||
@@ -78,24 +78,21 @@
|
||||
<meta name="twitter:image" content="{{ site.url }}/assets/social-screenshot.png">
|
||||
<meta name="twitter:creator" content="{{ site.author.x }}">
|
||||
{# CSS #}
|
||||
<link rel="stylesheet" href="/assets/styles.css">
|
||||
<link rel="stylesheet" id="style-variant-stylesheet">
|
||||
<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 || 'vega';
|
||||
if (styleVariant === 'vega') {
|
||||
styleLink.removeAttribute('href');
|
||||
} else {
|
||||
styleLink.href = `/assets/style-${styleVariant}.css`;
|
||||
}
|
||||
const styleVariant = document.documentElement.dataset.styleVariant || 'nova';
|
||||
styleLink.href = `/assets/styles-${styleVariant}.css`;
|
||||
})();
|
||||
</script>
|
||||
{# JS #}
|
||||
<script src="/assets/js/basecoat.js?v={{ pkg.version }}" defer></script>
|
||||
<script src="/assets/js/command.js?v={{ pkg.version }}" defer></script>
|
||||
<script src="/assets/js/combobox.js?v={{ pkg.version }}" defer></script>
|
||||
<script src="/assets/js/dropdown-menu.js?v={{ pkg.version }}" defer></script>
|
||||
<script src="/assets/js/popover.js?v={{ pkg.version }}" defer></script>
|
||||
<script src="/assets/js/range.js?v={{ pkg.version }}" defer></script>
|
||||
<script src="/assets/js/select.js?v={{ pkg.version }}" defer></script>
|
||||
<script src="/assets/js/sidebar.js?v={{ pkg.version }}" defer></script>
|
||||
<script src="/assets/js/tabs.js?v={{ pkg.version }}" defer></script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% macro code_block(code, language="html", class="my-4") %}
|
||||
<div class="relative {{ class }}">
|
||||
<pre class="grid text-sm max-h-[650px] overflow-y-auto rounded-xl scrollbar"><code class="language-{{ language }} !bg-muted/40 !p-3.5">{{- code | escape -}}</code></pre>
|
||||
<pre class="scrollbar"><code class="language-{{ language }}">{{- code | escape -}}</code></pre>
|
||||
|
||||
<button
|
||||
onclick="
|
||||
@@ -20,4 +20,4 @@
|
||||
{% lucide "check", {"class": "hidden group-[.copied]:block" } %}
|
||||
</button>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
||||
@@ -56,21 +56,17 @@
|
||||
const styleSelect = document.getElementById('style-select');
|
||||
const styleLink = document.getElementById('style-variant-stylesheet');
|
||||
const applyStyle = (styleName) => {
|
||||
const nextStyle = styleName || 'vega';
|
||||
const nextStyle = styleName || 'nova';
|
||||
document.documentElement.dataset.styleVariant = nextStyle;
|
||||
if (nextStyle === 'vega') {
|
||||
styleLink.removeAttribute('href');
|
||||
} else {
|
||||
styleLink.href = `/assets/style-${nextStyle}.css`;
|
||||
}
|
||||
styleLink.href = `/assets/styles-${nextStyle}.css`;
|
||||
};
|
||||
|
||||
const storedStyle = localStorage.getItem('styleVariant') || 'vega';
|
||||
const storedStyle = localStorage.getItem('styleVariant') || 'nova';
|
||||
if (styleSelect) styleSelect.value = storedStyle;
|
||||
applyStyle(storedStyle);
|
||||
|
||||
styleSelect.addEventListener('change', () => {
|
||||
const newStyle = styleSelect.value || 'vega';
|
||||
const newStyle = styleSelect.value || 'nova';
|
||||
applyStyle(newStyle);
|
||||
localStorage.setItem('styleVariant', newStyle);
|
||||
});
|
||||
|
||||
@@ -13,17 +13,17 @@
|
||||
<p>Enter your details below to login to your account</p>
|
||||
</header>
|
||||
<section>
|
||||
<form class="form grid gap-6">
|
||||
<form class="grid gap-6">
|
||||
<div class="grid gap-2">
|
||||
<label for="demo-card-form-email">Email</label>
|
||||
<input type="email" id="demo-card-form-email">
|
||||
<label class="label" for="demo-card-form-email">Email</label>
|
||||
<input class="input" type="email" id="demo-card-form-email">
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<label for="demo-card-form-password">Password</label>
|
||||
<label class="label" for="demo-card-form-password">Password</label>
|
||||
<a href="#" class="ml-auto inline-block text-sm underline-offset-4 hover:underline">Forgot your password?</a>
|
||||
</div>
|
||||
<input type="password" id="demo-card-form-password">
|
||||
<input class="input" type="password" id="demo-card-form-password">
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
@@ -22,14 +22,14 @@
|
||||
dialog_attrs={"class": "w-full sm:max-w-[425px] max-h-[612px]"},
|
||||
footer=footer
|
||||
) %}
|
||||
<form class="form grid gap-4">
|
||||
<form class="grid gap-4">
|
||||
<div class="grid gap-3">
|
||||
<label for="demo-dialog-edit-profile-name">Name</label>
|
||||
<input type="text" value="Pedro Duarte" id="demo-dialog-edit-profile-name" />
|
||||
<label class="label" for="demo-dialog-edit-profile-name">Name</label>
|
||||
<input class="input" type="text" value="Pedro Duarte" id="demo-dialog-edit-profile-name" />
|
||||
</div>
|
||||
<div class="grid gap-3">
|
||||
<label for="demo-dialog-edit-profile-username">Username</label>
|
||||
<input type="text" value="@peduarte" id="demo-dialog-edit-profile-username" />
|
||||
<label class="label" for="demo-dialog-edit-profile-username">Username</label>
|
||||
<input class="input" type="text" value="@peduarte" id="demo-dialog-edit-profile-username" />
|
||||
</div>
|
||||
</form>
|
||||
{% endcall %}
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
<h2 class="text-sm font-medium">Form</h2>
|
||||
</header>
|
||||
<div class="p-4">
|
||||
<form class="form grid w-full max-w-sm gap-6">
|
||||
<form class="grid w-full max-w-sm gap-6">
|
||||
<div class="grid gap-2">
|
||||
<label for="demo-form-text">Username</label>
|
||||
<input type="text" id="demo-form-text" placeholder="hunvreus">
|
||||
<label class="label" for="demo-form-text">Username</label>
|
||||
<input class="input" type="text" id="demo-form-text" placeholder="hunvreus">
|
||||
<p class="text-muted-foreground text-sm">This is your public display name.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<label for="demo-form-select">Email</label>
|
||||
<select id="demo-form-select">
|
||||
<label class="label" for="demo-form-select">Email</label>
|
||||
<select class="select" id="demo-form-select">
|
||||
<option value="bob@example.com">m@example.com</option>
|
||||
<option value="alice@example.com">m@google.com</option>
|
||||
<option value="john@example.com">m@support.com</option>
|
||||
@@ -21,64 +21,64 @@
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<label for="demo-form-text">Bio</label>
|
||||
<textarea id="demo-form-textarea" placeholder="I like to..." rows="3"></textarea>
|
||||
<label class="label" for="demo-form-text">Bio</label>
|
||||
<textarea class="textarea" id="demo-form-textarea" placeholder="I like to..." rows="3"></textarea>
|
||||
<p class="text-muted-foreground text-sm">You can @mention other users and organizations.</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-3">
|
||||
<label for="demo-form-radio">Notify me about...</label>
|
||||
<label class="label" for="demo-form-radio">Notify me about...</label>
|
||||
<fieldset id="demo-form-radio" class="grid gap-3">
|
||||
<label class="font-normal"><input type="radio" name="demo-form-radio" value="1">All new messages</label>
|
||||
<label class="font-normal"><input type="radio" name="demo-form-radio" value="2">Direct messages and mentions</label>
|
||||
<label class="font-normal"><input type="radio" name="demo-form-radio" value="3" >Nothing</label>
|
||||
<label class="label font-normal"><input class="input" type="radio" name="demo-form-radio" value="1">All new messages</label>
|
||||
<label class="label font-normal"><input class="input" type="radio" name="demo-form-radio" value="2">Direct messages and mentions</label>
|
||||
<label class="label font-normal"><input class="input" type="radio" name="demo-form-radio" value="3" >Nothing</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-start gap-3 rounded-md border p-4 shadow-xs">
|
||||
<input type="checkbox" id="demo-form-checkbox">
|
||||
<input class="input" type="checkbox" id="demo-form-checkbox">
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="demo-form-checkbox" class="leading-snug">Use different settings for my mobile devices</label>
|
||||
<label for="demo-form-checkbox" class="label text-sm font-normal leading-snug normal-case tracking-normal">Use different settings for my mobile devices</label>
|
||||
<p class="text-muted-foreground text-sm leading-snug">You can manage your mobile notifications in the mobile settings page.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
<header>
|
||||
<label for="demo-form-checkboxes" class="text-base leading-normal">Sidebar</label>
|
||||
<label for="demo-form-checkboxes" class="label text-base leading-normal">Sidebar</label>
|
||||
<p class="text-muted-foreground text-sm">Select the items you want to display in the sidebar.</p>
|
||||
</header>
|
||||
<fieldset id="demo-form-checkboxes" class="flex flex-col gap-2">
|
||||
<label class="font-normal leading-tight">
|
||||
<input type="checkbox" name="demo-form-checkboxes" value="1" checked>
|
||||
<label class="label font-normal leading-tight">
|
||||
<input class="input" type="checkbox" name="demo-form-checkboxes" value="1" checked>
|
||||
Recents
|
||||
</label>
|
||||
<label class="font-normal leading-tight">
|
||||
<input type="checkbox" name="demo-form-checkboxes" value="2" checked>
|
||||
<label class="label font-normal leading-tight">
|
||||
<input class="input" type="checkbox" name="demo-form-checkboxes" value="2" checked>
|
||||
Home
|
||||
</label>
|
||||
<label class="font-normal leading-tight">
|
||||
<input type="checkbox" name="demo-form-checkboxes" value="3">
|
||||
<label class="label font-normal leading-tight">
|
||||
<input class="input" type="checkbox" name="demo-form-checkboxes" value="3">
|
||||
Applications
|
||||
</label>
|
||||
<label class="font-normal leading-tight">
|
||||
<input type="checkbox" name="demo-form-checkboxes" value="4">
|
||||
<label class="label font-normal leading-tight">
|
||||
<input class="input" type="checkbox" name="demo-form-checkboxes" value="4">
|
||||
Desktop
|
||||
</label>
|
||||
<label class="font-normal leading-tight">
|
||||
<input type="checkbox" name="demo-form-checkboxes" value="5">
|
||||
<label class="label font-normal leading-tight">
|
||||
<input class="input" type="checkbox" name="demo-form-checkboxes" value="5">
|
||||
Download
|
||||
</label>
|
||||
<label class="font-normal leading-tight">
|
||||
<input type="checkbox" name="demo-form-checkboxes" value="6">
|
||||
<label class="label font-normal leading-tight">
|
||||
<input class="input" type="checkbox" name="demo-form-checkboxes" value="6">
|
||||
Documents
|
||||
</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<label for="demo-form-date">Date of birth</label>
|
||||
<input type="date" id="demo-form-date">
|
||||
<label class="label" for="demo-form-date">Date of birth</label>
|
||||
<input class="input" type="date" id="demo-form-date">
|
||||
<p class="text-muted-foreground text-sm">Your date of birth is used to calculate your age.</p>
|
||||
</div>
|
||||
|
||||
@@ -86,17 +86,17 @@
|
||||
<h3 class="text-lg font-medium">Email Notifications</h3>
|
||||
<div class="gap-2 flex flex-row items-start justify-between rounded-lg border p-4 shadow-xs">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<label for="demo-form-switch" class="leading-normal">Marketing emails</label>
|
||||
<label for="demo-form-switch" class="label text-sm font-normal leading-normal normal-case tracking-normal">Marketing emails</label>
|
||||
<p class="text-muted-foreground text-sm">Receive emails about new products, features, and more.</p>
|
||||
</div>
|
||||
<input type="checkbox" id="demo-form-switch" role="switch">
|
||||
<input class="input" type="checkbox" id="demo-form-switch" role="switch">
|
||||
</div>
|
||||
<div class="gap-2 flex flex-row items-start justify-between rounded-lg border p-4 shadow-xs">
|
||||
<div class="flex flex-col gap-0.5 opacity-60">
|
||||
<label for="demo-form-switch-disabled" class="leading-normal">Marketing emails</label>
|
||||
<label for="demo-form-switch-disabled" class="label text-sm font-normal leading-normal normal-case tracking-normal">Marketing emails</label>
|
||||
<p class="text-muted-foreground text-sm">Receive emails about new products, features, and more.</p>
|
||||
</div>
|
||||
<input type="checkbox" id="demo-form-switch-disabled" role="switch" disabled>
|
||||
<input class="input" type="checkbox" id="demo-form-switch-disabled" role="switch" disabled>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -19,22 +19,22 @@
|
||||
<h4 class="leading-none font-medium">Dimensions</h4>
|
||||
<p class="text-muted-foreground text-sm">Set the dimensions for the layer.</p>
|
||||
</header>
|
||||
<form class="form grid gap-2">
|
||||
<form class="grid gap-2">
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-width">Width</label>
|
||||
<input type="text" id="demo-popover-width" value="100%" class="col-span-2 h-8"/>
|
||||
<label class="label" for="demo-popover-width">Width</label>
|
||||
<input type="text" id="demo-popover-width" value="100%" class="input col-span-2 h-8"/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-max-width">Max. width</label>
|
||||
<input type="text" id="demo-popover-max-width" value="300px" class="col-span-2 h-8"/>
|
||||
<label class="label" for="demo-popover-max-width">Max. width</label>
|
||||
<input type="text" id="demo-popover-max-width" value="300px" class="input col-span-2 h-8"/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-height">Height</label>
|
||||
<input type="text" id="demo-popover-height" value="25px" class="col-span-2 h-8"/>
|
||||
<label class="label" for="demo-popover-height">Height</label>
|
||||
<input type="text" id="demo-popover-height" value="25px" class="input col-span-2 h-8"/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-max-height">Max. height</label>
|
||||
<input type="text" id="demo-popover-max-height" value="none" class="col-span-2 h-8"/>
|
||||
<label class="label" for="demo-popover-max-height">Max. height</label>
|
||||
<input type="text" id="demo-popover-max-height" value="none" class="input col-span-2 h-8"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
<p>Make changes to your account here. Click save when you're done.</p>
|
||||
</header>
|
||||
<section>
|
||||
<form class="form grid gap-6">
|
||||
<form class="grid gap-6">
|
||||
<div class="grid gap-3">
|
||||
<label for="demo-tabs-account-name">Name</label>
|
||||
<input type="text" id="demo-tabs-account-name" value="Pedro Duarte" />
|
||||
<label class="label" for="demo-tabs-account-name">Name</label>
|
||||
<input class="input" type="text" id="demo-tabs-account-name" value="Pedro Duarte" />
|
||||
</div>
|
||||
<div class="grid gap-3">
|
||||
<label for="demo-tabs-account-username">Username</label>
|
||||
<input type="text" id="demo-tabs-account-username" value="@peduarte" />
|
||||
<label class="label" for="demo-tabs-account-username">Username</label>
|
||||
<input class="input" type="text" id="demo-tabs-account-username" value="@peduarte" />
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
@@ -39,14 +39,14 @@
|
||||
<p>Change your password here. After saving, you'll be logged out.</p>
|
||||
</header>
|
||||
<section>
|
||||
<form class="form grid gap-6">
|
||||
<form class="grid gap-6">
|
||||
<div class="grid gap-3">
|
||||
<label for="demo-tabs-password-current">Current password</label>
|
||||
<input type="password" id="demo-tabs-password-current" />
|
||||
<label class="label" for="demo-tabs-password-current">Current password</label>
|
||||
<input class="input" type="password" id="demo-tabs-password-current" />
|
||||
</div>
|
||||
<div class="grid gap-3">
|
||||
<label for="demo-tabs-password-new">New password</label>
|
||||
<input type="password" id="demo-tabs-password-new"/>
|
||||
<label class="label" for="demo-tabs-password-new">New password</label>
|
||||
<input class="input" type="password" id="demo-tabs-password-new"/>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
</a>
|
||||
|
||||
{# <div class="flex flex-col gap-2 px-2 text-sm">
|
||||
<form class="form relative">
|
||||
<form class="relative">
|
||||
<label for="search" class="sr-only">Search</label>
|
||||
<input type="text" placeholder="Search the docs..." class="h-8 w-full shadow-none pl-8">
|
||||
{% lucide "search", { "class": "pointer-events-none absolute top-1/2 left-2 size-4 -translate-y-1/2 opacity-50 select-none" } %}
|
||||
@@ -98,17 +98,12 @@
|
||||
"hx-swap": "outerHTML"
|
||||
} %}
|
||||
|
||||
{% set new_link_attrs = {
|
||||
"hx-boost": "true",
|
||||
"hx-select": "#content",
|
||||
"hx-target": "#content",
|
||||
"hx-swap": "outerHTML",
|
||||
"data-new-link": "true"
|
||||
} %}
|
||||
|
||||
{% set icon_home %}{% lucide "house" %}{% endset %}
|
||||
{% set icon_info %}{% lucide "info" %}{% endset %}
|
||||
{% set icon_download %}{% lucide "download" %}{% endset %}
|
||||
{% set icon_square_terminal %}{% lucide "square-terminal" %}{% endset %}
|
||||
{% set icon_settings_2 %}{% lucide "settings-2" %}{% endset %}
|
||||
{% set icon_layout_dashboard %}{% lucide "layout-dashboard" %}{% endset %}
|
||||
{% set icon_github %}<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>{% endset %}
|
||||
{% set icon_discord %}<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Discord</title><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/></svg>{% endset %}
|
||||
@@ -116,9 +111,11 @@
|
||||
{% set icon_discord %}<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Discord</title><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/></svg>{% endset %}
|
||||
|
||||
{% set menu = [
|
||||
{ type: "group", label: "Getting started", items: [
|
||||
{ type: "group", label: "Getting started", items: [
|
||||
{ label: "Introduction", url: "/introduction", icon: icon_info, attrs: link_attrs },
|
||||
{ label: "Installation", url: "/installation", icon: icon_square_terminal, attrs: link_attrs },
|
||||
{ label: "Installation", url: "/installation", icon: icon_download, attrs: link_attrs },
|
||||
{ label: "Customization", url: "/customization", icon: icon_settings_2, attrs: link_attrs },
|
||||
{ label: "CLI", url: "/cli", icon: icon_square_terminal, attrs: link_attrs },
|
||||
{ label: "Kitchen sink", url: "/kitchen-sink", icon: icon_layout_dashboard, attrs: link_attrs },
|
||||
{ label: "GitHub", url: "https://github.com/hunvreus/basecoat", icon: icon_github, attrs: { "target": "_blank" } },
|
||||
{ label: "Discord", url: "https://basecoatui.com/chat", icon: icon_discord, attrs: { "target": "_blank" } }
|
||||
@@ -131,30 +128,30 @@
|
||||
{ label: "Badge", url: "/components/badge", attrs: link_attrs },
|
||||
{ label: "Breadcrumb", url: "/components/breadcrumb", attrs: link_attrs },
|
||||
{ label: "Button", url: "/components/button", attrs: link_attrs },
|
||||
{ label: "Button Group", url: "/components/button-group", attrs: new_link_attrs },
|
||||
{ label: "Button Group", url: "/components/button-group", attrs: link_attrs },
|
||||
{ label: "Card", url: "/components/card", attrs: link_attrs },
|
||||
{ label: "Checkbox", url: "/components/checkbox", attrs: link_attrs },
|
||||
{ label: "Command", url: "/components/command", attrs: new_link_attrs },
|
||||
{ label: "Command", url: "/components/command", attrs: link_attrs },
|
||||
{ label: "Combobox", url: "/components/combobox", attrs: link_attrs },
|
||||
{ label: "Dialog", url: "/components/dialog", attrs: link_attrs },
|
||||
{ label: "Dropdown Menu", url: "/components/dropdown-menu", attrs: link_attrs },
|
||||
{ label: "Empty", url: "/components/empty", attrs: new_link_attrs },
|
||||
{ label: "Field", url: "/components/field", attrs: new_link_attrs },
|
||||
{ label: "Form", url: "/components/form", attrs: link_attrs },
|
||||
{ label: "Empty", url: "/components/empty", attrs: link_attrs },
|
||||
{ label: "Field", url: "/components/field", attrs: link_attrs },
|
||||
{ label: "Input", url: "/components/input", attrs: link_attrs },
|
||||
{ label: "Input Group", url: "/components/input-group", attrs: new_link_attrs },
|
||||
{ label: "Item", url: "/components/item", attrs: new_link_attrs },
|
||||
{ label: "Kbd", url: "/components/kbd", attrs: new_link_attrs },
|
||||
{ label: "Input Group", url: "/components/input-group", attrs: link_attrs },
|
||||
{ label: "Item", url: "/components/item", attrs: link_attrs },
|
||||
{ label: "Kbd", url: "/components/kbd", attrs: link_attrs },
|
||||
{ label: "Label", url: "/components/label", attrs: link_attrs },
|
||||
{ label: "Native Select", url: "/components/native-select", attrs: link_attrs },
|
||||
{ label: "Pagination", url: "/components/pagination", attrs: link_attrs },
|
||||
{ label: "Popover", url: "/components/popover", attrs: link_attrs },
|
||||
{ label: "Progress", url: "/components/progress", attrs: new_link_attrs },
|
||||
{ label: "Progress", url: "/components/progress", attrs: link_attrs },
|
||||
{ label: "Radio Group", url: "/components/radio-group", attrs: link_attrs },
|
||||
{ label: "Select", url: "/components/select", attrs: link_attrs },
|
||||
{ label: "Sidebar", url: "/components/sidebar", attrs: link_attrs },
|
||||
{ label: "Skeleton", url: "/components/skeleton", attrs: link_attrs },
|
||||
{ label: "Slider", url: "/components/slider", attrs: link_attrs },
|
||||
{ label: "Spinner", url: "/components/spinner", attrs: new_link_attrs },
|
||||
{ label: "Spinner", url: "/components/spinner", attrs: link_attrs },
|
||||
{ label: "Switch", url: "/components/switch", attrs: link_attrs },
|
||||
{ label: "Table", url: "/components/table", attrs: link_attrs },
|
||||
{ label: "Tabs", url: "/components/tabs", attrs: link_attrs },
|
||||
@@ -171,6 +168,6 @@
|
||||
label="Sidebar navigation",
|
||||
header=header,
|
||||
footer=footer,
|
||||
content_attrs={ "class": "scrollbar [&_[data-new-link]::after]:content-['New'] [&_[data-new-link]::after]:ml-auto [&_[data-new-link]::after]:text-xs [&_[data-new-link]::after]:font-medium [&_[data-new-link]::after]:bg-sidebar-primary [&_[data-new-link]::after]:text-sidebar-primary-foreground [&_[data-new-link]::after]:px-2 [&_[data-new-link]::after]:py-0.5 [&_[data-new-link]::after]:rounded-md" },
|
||||
content_attrs={ "class": "scrollbar" },
|
||||
menu=menu
|
||||
) }}
|
||||
|
||||
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
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
@@ -0,0 +1,56 @@
|
||||
---
|
||||
layout: layouts/page.njk
|
||||
title: CLI
|
||||
description: Use the Basecoat CLI and template macros.
|
||||
toc:
|
||||
- label: Use the CLI
|
||||
id: cli
|
||||
- label: Use Nunjucks or Jinja macros
|
||||
id: macros
|
||||
---
|
||||
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
{% from "tabs.njk" import tabs %}
|
||||
|
||||
<h2 id="cli"><a href="#cli">Use the CLI</a></h2>
|
||||
|
||||
<div class="prose">
|
||||
<p>If you're using Nunjucks or Jinja, you can use the CLI to install the CSS and macros for complex components (e.g. <a href="/components/dialog">Dialog</a>). It will copy the JS and <code>.html.jinja</code> or <code>.njk</code> macros into your code.</p>
|
||||
<p>For example, to add the Dialog component, run one of the following commands:</p>
|
||||
</div>
|
||||
|
||||
{% set code_npm_exec %}npx basecoat-cli add dialog{% endset %}
|
||||
{% set code_pnpm_exec %}pnpm dlx basecoat-cli add dialog{% endset %}
|
||||
{% set code_bun_exec %}bunx basecoat-cli add dialog{% endset %}
|
||||
{% set code_yarn_exec %}yarn dlx basecoat-cli add dialog{% endset %}
|
||||
{% set exec_tabsets = [
|
||||
{ tab: "npm", panel: code_block(code_npm_exec, class="") },
|
||||
{ tab: "pnpm", panel: code_block(code_pnpm_exec, class="") },
|
||||
{ tab: "bun", panel: code_block(code_bun_exec, class="") },
|
||||
{ tab: "yarn", panel: code_block(code_yarn_exec, class="") }
|
||||
] %}
|
||||
{{ 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>
|
||||
</div>
|
||||
|
||||
<h2 id="macros"><a href="#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 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="#cli">the CLI</a>.</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>
|
||||
</div>
|
||||
|
||||
{% set code %}{% raw %}{% call select() %}
|
||||
<div role="group" aria-labelledby="fruit-options-label">
|
||||
<span id="fruit-options-label" role="heading">Fruits</span>
|
||||
<div role="option" data-value="apple">Apple</div>
|
||||
<div role="option" data-value="banana">Banana</div>
|
||||
<div role="option" data-value="blueberry">Blueberry</div>
|
||||
<div role="option" data-value="pineapple">Grapes</div>
|
||||
<div role="option" data-value="pineapple">Pineapple</div>
|
||||
</div>
|
||||
{% endcall %}{% endraw %}{% endset %}
|
||||
{{ code_block(code, "jinja") }}
|
||||
@@ -87,7 +87,7 @@ toc:
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
<a class="badge-outline" href="/installation/#install-macros" target="_blank">
|
||||
<a class="badge-outline" href="/cli#macros" target="_blank">
|
||||
Use Nunjucks or Jinja macros
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
|
||||
@@ -14,6 +14,12 @@ toc:
|
||||
id: example-no-description
|
||||
- label: No icon
|
||||
id: example-no-icon
|
||||
- label: Action
|
||||
id: example-action
|
||||
- label: Custom Colors
|
||||
id: example-custom-colors
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
@@ -47,7 +53,7 @@ toc:
|
||||
{% endset %}
|
||||
{{ code_preview("alert", code | prettyHtml) }}
|
||||
|
||||
<h2 id="usage">Usage</h2>
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Use the <code>alert</code> or <code>alert-destructive</code>.</p>
|
||||
@@ -74,6 +80,8 @@ toc:
|
||||
<dd>The title.</dd>
|
||||
<dt><code class="highlight language-html"><section></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>The description.</dd>
|
||||
<dt><code class="highlight language-html"><div data-action></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>An action aligned to the inline end of the alert.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
@@ -105,4 +113,41 @@ toc:
|
||||
<h2>Success! Your changes have been saved</h2>
|
||||
<section>This is an alert with icon, title and description.</section>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("alert-no-icon", code) }}
|
||||
{{ code_preview("alert-no-icon", code) }}
|
||||
|
||||
<h3 id="example-action"><a href="#example-action">Action</a></h3>
|
||||
|
||||
{% set code %}<div class="alert">
|
||||
{% lucide "info" %}
|
||||
<h2>Enable notifications</h2>
|
||||
<section>Receive updates when there is activity in your workspace.</section>
|
||||
<div data-action>
|
||||
<button type="button" class="btn-xs-outline">Enable</button>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("alert-action", code) }}
|
||||
|
||||
<h3 id="example-custom-colors"><a href="#example-custom-colors">Custom Colors</a></h3>
|
||||
|
||||
{% set code %}<div class="alert bg-amber-50 text-amber-900 dark:bg-amber-950 dark:text-amber-200">
|
||||
{% lucide "triangle-alert" %}
|
||||
<h2>Storage almost full</h2>
|
||||
<section class="text-amber-800 dark:text-amber-300">Upgrade your plan or delete unused files.</section>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("alert-custom-colors", code) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Alerts support right-to-left layouts through logical spacing and native text flow. Add <code>dir="rtl"</code> to the alert or an ancestor.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<div class="alert" dir="rtl">
|
||||
{% lucide "info" %}
|
||||
<h2>تم حفظ التغييرات</h2>
|
||||
<section>تم تحديث إعداداتك بنجاح.</section>
|
||||
<div data-action>
|
||||
<button type="button" class="btn-xs-outline">عرض</button>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("alert-rtl", code) }}
|
||||
|
||||
@@ -20,6 +20,10 @@ toc:
|
||||
id: example-with-icon
|
||||
- label: Link
|
||||
id: example-link
|
||||
- label: Custom Colors
|
||||
id: example-custom-colors
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
@@ -49,7 +53,8 @@ toc:
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Use one of the badge classes: <code>badge</code>, <code>badge-primary</code>, <code>badge-secondary</code>, <code>badge-destructive</code> or <code>badge-outline</code>.
|
||||
<p>Use one of the badge classes: <code>badge</code>, <code>badge-primary</code>, <code>badge-secondary</code>, <code>badge-destructive</code>, <code>badge-outline</code>, <code>badge-ghost</code>, or <code>badge-link</code>.</p>
|
||||
<p>When an icon needs directional spacing, add <code>data-icon="inline-start"</code> or <code>data-icon="inline-end"</code> to the icon.</p>
|
||||
</section>
|
||||
|
||||
{{ code_block(code) }}
|
||||
@@ -88,6 +93,28 @@ toc:
|
||||
|
||||
{% set code %}<a href="#" class="badge-outline">
|
||||
Link
|
||||
{% lucide "arrow-right" %}
|
||||
{% lucide "arrow-right", { "data-icon": "inline-end", "class": "rtl:rotate-180" } %}
|
||||
</a>{% endset %}
|
||||
{{ code_preview("badge-link", code) }}
|
||||
|
||||
<h3 id="example-custom-colors"><a href="#example-custom-colors">Custom Colors</a></h3>
|
||||
|
||||
{% set code %}<span class="badge bg-green-50 text-green-700 dark:bg-green-950 dark:text-green-300">
|
||||
Custom
|
||||
</span>{% endset %}
|
||||
{{ code_preview("badge-custom-colors", code) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Badges support right-to-left layouts through native text flow and logical icon spacing. Directional icons should be flipped explicitly.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<div dir="rtl" class="flex flex-wrap gap-2">
|
||||
<span class="badge">نشط</span>
|
||||
<a href="#" class="badge-outline">
|
||||
التفاصيل
|
||||
{% lucide "arrow-right", { "data-icon": "inline-end", "class": "rtl:rotate-180" } %}
|
||||
</a>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("badge-rtl", code) }}
|
||||
|
||||
@@ -5,23 +5,35 @@ description: A container that groups related buttons together with consistent st
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
- label: Accessibility
|
||||
id: accessibility
|
||||
- label: Button Group vs Toggle Group
|
||||
id: button-group-vs-toggle-group
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Orientation
|
||||
id: example-orientation
|
||||
- label: Size
|
||||
id: example-size
|
||||
- label: Nested
|
||||
id: example-nested
|
||||
- label: Separator
|
||||
id: example-separator
|
||||
- label: Split
|
||||
id: example-split
|
||||
- label: Input
|
||||
id: example-input
|
||||
- label: Input Group
|
||||
id: example-input-group
|
||||
- label: Dropdown Menu
|
||||
id: example-dropdown-menu
|
||||
- label: Select
|
||||
id: example-select
|
||||
- label: Popover
|
||||
id: example-popover
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "dropdown-menu.njk" import dropdown_menu %}
|
||||
@@ -77,22 +89,45 @@ toc:
|
||||
</div>
|
||||
{% endcall %}
|
||||
</div>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("button-group-default", code_default | prettyHtml) }}
|
||||
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<p class="prose">Simply wrap buttons you want to group together in a <code><div role="group" class="button-group"></code> element. You can also use the <code>data-orientation="vertical"</code> attribute to stack buttons vertically. You can also group buttons with <a href="#example-dropdown-menu">dropdown menus</a> or <a href="#example-popover">popovers</a> (provided the trigger is a button). Finally, you can use a <a href="#example-separator">separator</a> to visually divide buttons within a group (for secondary buttons for example).</p>
|
||||
<section class="prose">
|
||||
<p>Wrap related buttons or inputs in a <code><div role="group" class="button-group"></code> element. Add <code>aria-label</code> or <code>aria-labelledby</code> when the group purpose is not already clear from nearby text.</p>
|
||||
<p>Use <code>data-orientation="vertical"</code> to stack items vertically. Use <code><hr role="separator"></code> to visually divide non-outline buttons. Use <code>button-group-text</code> for static text inside a group.</p>
|
||||
</section>
|
||||
|
||||
{% set code_simple %}
|
||||
<div role="group" class="button-group">
|
||||
<div role="group" aria-label="Message actions" class="button-group">
|
||||
<button type="button" class="btn-outline">Archive</button>
|
||||
<button type="button" class="btn-outline">Report</button>
|
||||
</div>
|
||||
{% endset %}
|
||||
{{ code_block(code_simple | prettyHtml, "html") }}
|
||||
|
||||
<h2 id="accessibility"><a href="#accessibility">Accessibility</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<ul>
|
||||
<li>Use <code>role="group"</code> on the <code>button-group</code> container.</li>
|
||||
<li>Use <code>aria-label</code> or <code>aria-labelledby</code> to label groups whose purpose is not obvious.</li>
|
||||
<li>Keyboard navigation remains native: users tab through each button, input, select, dropdown trigger, or popover trigger.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<h2 id="button-group-vs-toggle-group"><a href="#button-group-vs-toggle-group">Button Group vs Toggle Group</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<ul>
|
||||
<li>Use Button Group for related actions, such as archive/report/snooze.</li>
|
||||
<li>Use Toggle Group when each item controls selected/on state.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-orientation"><a href="#example-orientation">Orientation</a></h3>
|
||||
@@ -144,7 +179,7 @@ toc:
|
||||
<h3 id="example-nested"><a href="#example-nested">Nested</a></h3>
|
||||
|
||||
{% set code_nested %}
|
||||
<div role="group" aria-label="Pagination controls" class="flex w-fit items-stretch gap-2">
|
||||
<div role="group" aria-label="Pagination controls" class="button-group">
|
||||
<div role="group" class="button-group">
|
||||
<button type="button" class="btn-sm-outline">1</button>
|
||||
<button type="button" class="btn-sm-outline">2</button>
|
||||
@@ -181,18 +216,55 @@ toc:
|
||||
|
||||
{{ code_preview("button-group-separator", code_separator | prettyHtml) }}
|
||||
|
||||
<h3 id="example-split"><a href="#example-split">Split</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Use a separator to create a split action with a primary action and a secondary menu trigger.</p>
|
||||
</section>
|
||||
|
||||
{% set code_split %}
|
||||
<div role="group" aria-label="Split save actions" class="button-group">
|
||||
<button type="button" class="btn-secondary">Save</button>
|
||||
<hr role="separator">
|
||||
{% set trigger_split %}
|
||||
{% lucide "chevron-down" %}
|
||||
{% endset %}
|
||||
{% call dropdown_menu(
|
||||
trigger=trigger_split,
|
||||
trigger_attrs={"class": "btn-icon-secondary", "aria-label": "More save options"},
|
||||
popover_attrs={"data-align": "end"}
|
||||
) %}
|
||||
<div role="menuitem">Save as draft</div>
|
||||
<div role="menuitem">Save and publish</div>
|
||||
<div role="menuitem">Save as template</div>
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("button-group-split", code_split | prettyHtml) }}
|
||||
|
||||
<h3 id="example-input"><a href="#example-input">Input</a></h3>
|
||||
|
||||
{% set code_input %}
|
||||
<div class="button-group">
|
||||
<div role="group" aria-label="Search" class="button-group w-full max-w-sm">
|
||||
<input type="text" class="input" placeholder="Search...">
|
||||
<button type="button" class="btn-icon-outline">{% lucide "search" %}</button>
|
||||
<button type="button" class="btn-icon-outline" aria-label="Search">{% lucide "search" %}</button>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("button-group-input", code_input | prettyHtml) }}
|
||||
|
||||
<h3 id="example-input-group"><a href="#example-input-group">Input Group</a></h3>
|
||||
|
||||
{% set code_input_group %}
|
||||
<div role="group" aria-label="Amount" class="button-group w-full max-w-sm">
|
||||
<div class="button-group-text">$</div>
|
||||
<input type="text" class="input" placeholder="0.00">
|
||||
<div class="button-group-text">USD</div>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("button-group-input-group", code_input_group | prettyHtml) }}
|
||||
|
||||
<h3 id="example-dropdown-menu"><a href="#example-dropdown-menu">Dropdown Menu</a></h3>
|
||||
|
||||
@@ -246,7 +318,7 @@ toc:
|
||||
|
||||
{% set code_select %}
|
||||
<div class="flex w-fit items-stretch gap-2">
|
||||
<div class="button-group">
|
||||
<div role="group" aria-label="Currency amount" class="button-group">
|
||||
{% call select(trigger_attrs={"class": "[&_[data-name]]:hidden"}) %}
|
||||
<div role="option" data-value="$">$ <span class="text-muted-foreground" data-name>US Dollar</span></div>
|
||||
<div role="option" data-value="€">€ <span class="text-muted-foreground" data-name>Euro</span></div>
|
||||
@@ -269,14 +341,17 @@ toc:
|
||||
{% lucide "bot" %}
|
||||
Copilot
|
||||
</button>
|
||||
{% set trigger_popover %}
|
||||
{% lucide "chevron-down" %}
|
||||
{% endset %}
|
||||
{% call popover(
|
||||
trigger=trigger,
|
||||
trigger=trigger_popover,
|
||||
trigger_attrs={"class": "btn-icon-outline"},
|
||||
popover_attrs={"class": "w-72 p-0", "data-align": "end"}
|
||||
) %}
|
||||
<header class="px-4 py-3 text-sm font-medium border-b">Agent Tasks</header>
|
||||
<section class="p-4 text-sm space-y-2">
|
||||
<textarea class="textarea w-full mb-4" placeholder="Describe your taks in natural language"></textarea>
|
||||
<textarea class="textarea w-full mb-4" placeholder="Describe your task in natural language"></textarea>
|
||||
<p class="font-medium">Start a new task with Copilot</p>
|
||||
<p class="text-muted-foreground">
|
||||
Describe your task in natural language. Copilot will work in the
|
||||
@@ -288,3 +363,24 @@ toc:
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("button-group-popover", code_popover | prettyHtml) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Button groups support right-to-left layouts through logical border and radius rules. Add <code>dir="rtl"</code> to the group or an ancestor. Directional icons should be flipped explicitly.</p>
|
||||
</section>
|
||||
|
||||
{% set code_rtl %}
|
||||
<div dir="rtl">
|
||||
<div role="group" aria-label="إجراءات الرسالة" class="button-group">
|
||||
<button type="button" class="btn-outline">أرشفة</button>
|
||||
<button type="button" class="btn-outline">تقرير</button>
|
||||
<button type="button" class="btn-outline">
|
||||
إرسال
|
||||
{% lucide "arrow-right", { "class": "rtl:rotate-180" } %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("button-group-rtl", code_rtl | prettyHtml) }}
|
||||
|
||||
+127
-66
@@ -8,57 +8,55 @@ toc:
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Primary
|
||||
id: example-primary
|
||||
- label: Secondary
|
||||
id: example-secondary
|
||||
- label: Destructive
|
||||
id: example-destructive
|
||||
- label: Size
|
||||
id: example-size
|
||||
- label: Default
|
||||
id: example-default
|
||||
- label: Outline
|
||||
id: example-outline
|
||||
- label: Secondary
|
||||
id: example-secondary
|
||||
- label: Ghost
|
||||
id: example-ghost
|
||||
- label: Destructive
|
||||
id: example-destructive
|
||||
- label: Link
|
||||
id: example-link
|
||||
- label: Icon
|
||||
id: example-icon
|
||||
- label: With Icon
|
||||
id: example-with-icon
|
||||
- label: Loading
|
||||
id: example-loading
|
||||
- label: Large
|
||||
id: example-large
|
||||
- label: Extra Small
|
||||
id: example-extra-small
|
||||
- label: Extra Small Icon
|
||||
id: example-extra-small-icon
|
||||
- label: Small
|
||||
id: example-small
|
||||
- label: Rounded
|
||||
id: example-rounded
|
||||
- label: Spinner
|
||||
id: example-spinner
|
||||
- label: Button Group
|
||||
id: example-button-group
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
|
||||
{% set code %}<button class="btn">Button</button>{% endset %}
|
||||
{% set code %}<button type="button" class="btn">Button</button>{% endset %}
|
||||
{{ code_preview("button", code) }}
|
||||
|
||||
<h2 id="usage">Usage</h2>
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>You can use one of the <code>btn</code> classes to style any element as a button:</p>
|
||||
<p>You can use one of the <code>btn</code> classes to style a button element:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<b>Variants</b>:
|
||||
<ul>
|
||||
<li><code>btn</code> or <code>btn-primary</code> for primary
|
||||
buttons,</li>
|
||||
<li><code>btn</code> or <code>btn-primary</code> for default/primary buttons.</li>
|
||||
<li><code>btn-secondary</code> for secondary buttons.</li>
|
||||
<li><code>btn-destructive</code> for destructive buttons.</li>
|
||||
<li><code>btn-outline</code> for outline buttons.</li>
|
||||
<li><code>btn-ghost</code> for ghost buttons.</li>
|
||||
<li><code>btn-link</code> for link buttons.</li>
|
||||
<li><code>btn-icon</code> for icon buttons. This can be combined with other variants, for example <code>btn-icon-destructive</code>.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
@@ -67,94 +65,157 @@ toc:
|
||||
<li><code>btn-xs</code> for extra small buttons.</li>
|
||||
<li><code>btn-sm</code> for small buttons.</li>
|
||||
<li><code>btn-lg</code> for large buttons.</li>
|
||||
<li>You can combine sizes with any variant, for example <code>btn-lg-destructive</code>, <code>btn-xs-outline</code> or <code>btn-sm-icon-outline</code>.</li>
|
||||
<li><code>btn-icon</code> for icon buttons. This can be combined with sizes and variants, for example <code>btn-sm-icon-outline</code> or <code>btn-xs-icon-destructive</code>.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Button classes are combined into a single class name. Use <code>btn-{variant}</code>, <code>btn-{size}</code>, <code>btn-{size}-{variant}</code>, <code>btn-icon-{variant}</code>, or <code>btn-{size}-icon-{variant}</code>. Do not stack classes like <code>btn-sm btn-outline</code>; use <code>btn-sm-outline</code> instead.</p>
|
||||
|
||||
<p>Basecoat follows Tailwind v4's default button cursor behavior and does not force <code>cursor: pointer</code>.</p>
|
||||
</section>
|
||||
|
||||
{{ code_block(code) }}
|
||||
|
||||
{% set code_composition %}<button type="button" class="btn">Default</button>
|
||||
<button type="button" class="btn-outline">Outline</button>
|
||||
<button type="button" class="btn-sm">Small</button>
|
||||
<button type="button" class="btn-sm-outline">Small outline</button>
|
||||
<button type="button" class="btn-icon-outline" aria-label="Settings">
|
||||
{% lucide "settings" %}
|
||||
</button>
|
||||
<button type="button" class="btn-sm-icon-outline" aria-label="Settings">
|
||||
{% lucide "settings" %}
|
||||
</button>{% endset %}
|
||||
{{ code_block(code_composition) }}
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-primary"><a href="#example-primary">Primary</a></h3>
|
||||
<h3 id="example-size"><a href="#example-size">Size</a></h3>
|
||||
|
||||
{% set code %}<button class="btn">Primary</button>{% endset %}
|
||||
{{ code_preview("button-primary", code) }}
|
||||
<section class="prose">
|
||||
<p>Use size-specific classes to change the size of the button.</p>
|
||||
</section>
|
||||
|
||||
<h3 id="example-secondary"><a href="#example-secondary">Secondary</a></h3>
|
||||
{% set code_size %}<div class="flex flex-wrap items-center gap-2">
|
||||
<button type="button" class="btn-xs">Extra Small</button>
|
||||
<button type="button" class="btn-sm">Small</button>
|
||||
<button type="button" class="btn">Default</button>
|
||||
<button type="button" class="btn-lg">Large</button>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("button-size", code_size) }}
|
||||
|
||||
{% set code %}<button class="btn-secondary">Button</button>{% endset %}
|
||||
{{ code_preview("button-secondary", code) }}
|
||||
<h3 id="example-default"><a href="#example-default">Default</a></h3>
|
||||
|
||||
<h3 id="example-destructive"><a href="#example-destructive">Destructive</a></h3>
|
||||
|
||||
{% set code %}<button class="btn-destructive">Destructive</button>{% endset %}
|
||||
{{ code_preview("button-destructive", code) }}
|
||||
{% set code %}<button type="button" class="btn">Button</button>{% endset %}
|
||||
{{ code_preview("button-default", code) }}
|
||||
|
||||
<h3 id="example-outline"><a href="#example-outline">Outline</a></h3>
|
||||
|
||||
{% set code %}<button class="btn-outline">Outline</button>{% endset %}
|
||||
{% set code %}<button type="button" class="btn-outline">Outline</button>{% endset %}
|
||||
{{ code_preview("button-outline", code) }}
|
||||
|
||||
<h3 id="example-secondary"><a href="#example-secondary">Secondary</a></h3>
|
||||
|
||||
{% set code %}<button type="button" class="btn-secondary">Secondary</button>{% endset %}
|
||||
{{ code_preview("button-secondary", code) }}
|
||||
|
||||
<h3 id="example-ghost"><a href="#example-ghost">Ghost</a></h3>
|
||||
|
||||
{% set code %}<button class="btn-ghost">Ghost</button>{% endset %}
|
||||
{% set code %}<button type="button" class="btn-ghost">Ghost</button>{% endset %}
|
||||
{{ code_preview("button-ghost", code) }}
|
||||
|
||||
<h3 id="example-destructive"><a href="#example-destructive">Destructive</a></h3>
|
||||
|
||||
{% set code %}<button type="button" class="btn-destructive">Destructive</button>{% endset %}
|
||||
{{ code_preview("button-destructive", code) }}
|
||||
|
||||
<h3 id="example-link"><a href="#example-link">Link</a></h3>
|
||||
|
||||
{% set code %}<button class="btn-link">Link</button>{% endset %}
|
||||
{% set code %}<button type="button" class="btn-link">Link</button>{% endset %}
|
||||
{{ code_preview("button-link", code) }}
|
||||
|
||||
<h3 id="example-icon"><a href="#example-icon">Icon</a></h3>
|
||||
|
||||
{% set code %}<button class="btn-icon-outline">
|
||||
{% set code %}<button type="button" class="btn-icon-outline" aria-label="Open">
|
||||
{% lucide "chevron-right" %}
|
||||
</button>{% endset %}
|
||||
{{ code_preview("button-icon", code) }}
|
||||
|
||||
<h3 id="example-with-icon"><a href="#example-with-icon">With Icon</a></h3>
|
||||
|
||||
{% set code %}<button class="btn">
|
||||
{% lucide "send" %}
|
||||
Send email
|
||||
<section class="prose">
|
||||
<p>Mark inline icons with <code>data-icon="inline-start"</code> or <code>data-icon="inline-end"</code> when the button should use icon-aware spacing.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<button type="button" class="btn">
|
||||
{% lucide "git-branch", { "data-icon": "inline-start" } %}
|
||||
New Branch
|
||||
</button>{% endset %}
|
||||
{{ code_preview("button-with-icon", code) }}
|
||||
|
||||
<h3 id="example-loading"><a href="#example-loading">Loading</a></h3>
|
||||
<h3 id="example-rounded"><a href="#example-rounded">Rounded</a></h3>
|
||||
|
||||
{% set code %}<button class="btn-outline" disabled>
|
||||
{% lucide "loader", { "class": "animate-spin" } %}
|
||||
Loading
|
||||
<section class="prose">
|
||||
<p>Use standard Tailwind classes to override the button radius.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<button type="button" class="btn rounded-full">
|
||||
{% lucide "arrow-up" %}
|
||||
Rounded
|
||||
</button>{% endset %}
|
||||
{{ code_preview("button-loading", code) }}
|
||||
{{ code_preview("button-rounded", code) }}
|
||||
|
||||
<h3 id="example-large"><a href="#example-large">Large</a></h3>
|
||||
<h3 id="example-spinner"><a href="#example-spinner">Spinner</a></h3>
|
||||
|
||||
{% set code %}<button class="btn-lg">
|
||||
{% lucide "shopping-cart" %}
|
||||
Buy
|
||||
</button>{% endset %}
|
||||
{{ code_preview("button-large", code) }}
|
||||
{% set code_spinner %}<div class="flex flex-wrap items-center gap-2">
|
||||
<button type="button" class="btn" disabled>
|
||||
{% lucide "loader", { "class": "animate-spin" } %}
|
||||
Generating
|
||||
</button>
|
||||
<button type="button" class="btn-outline">
|
||||
Downloading
|
||||
{% lucide "loader", { "class": "animate-spin" } %}
|
||||
</button>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("button-spinner", code_spinner) }}
|
||||
|
||||
<h3 id="example-extra-small"><a href="#example-extra-small">Extra Small</a></h3>
|
||||
<h3 id="example-button-group"><a href="#example-button-group">Button Group</a></h3>
|
||||
|
||||
{% set code %}<button class="btn-xs-outline">
|
||||
XS outline
|
||||
</button>{% endset %}
|
||||
{{ code_preview("button-extra-small", code) }}
|
||||
<section class="prose">
|
||||
<p>To group related buttons, use the <a href="/components/button-group/">Button Group</a> component.</p>
|
||||
</section>
|
||||
|
||||
<h3 id="example-extra-small-icon"><a href="#example-extra-small-icon">Extra Small Icon</a></h3>
|
||||
{% set code_button_group %}<div class="flex w-fit items-stretch gap-2">
|
||||
<div role="group" class="button-group">
|
||||
<button type="button" class="btn-outline">Archive</button>
|
||||
<button type="button" class="btn-outline">Report</button>
|
||||
</div>
|
||||
<div role="group" class="button-group">
|
||||
<button type="button" class="btn-outline">Snooze</button>
|
||||
<button type="button" class="btn-icon-outline" aria-label="More options">
|
||||
{% lucide "ellipsis" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("button-group-demo", code_button_group) }}
|
||||
|
||||
{% set code %}<button class="btn-xs-icon-outline">
|
||||
{% lucide "plus" %}
|
||||
</button>{% endset %}
|
||||
{{ code_preview("button-extra-small-icon", code) }}
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<h3 id="example-small"><a href="#example-small">Small</a></h3>
|
||||
<section class="prose">
|
||||
<p>Buttons support right-to-left layouts through logical spacing. Add <code>dir="rtl"</code> to the button or an ancestor. Directional icons should be flipped explicitly.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<button class="btn-sm-icon-destructive">
|
||||
{% lucide "trash" %}
|
||||
</button>{% endset %}
|
||||
{{ code_preview("button-small", code) }}
|
||||
{% set code_rtl %}<div dir="rtl" class="flex flex-wrap items-center gap-2">
|
||||
<button type="button" class="btn-outline">زر</button>
|
||||
<button type="button" class="btn-destructive">حذف</button>
|
||||
<button type="button" class="btn-outline">
|
||||
إرسال
|
||||
{% lucide "arrow-right", { "data-icon": "inline-end", "class": "rtl:rotate-180" } %}
|
||||
</button>
|
||||
<button type="button" class="btn-secondary" disabled>
|
||||
{% lucide "loader", { "class": "animate-spin" } %}
|
||||
جاري التحميل
|
||||
</button>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("button-rtl", code_rtl) }}
|
||||
|
||||
@@ -8,10 +8,14 @@ toc:
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Size
|
||||
id: example-size
|
||||
- label: With content and footer
|
||||
id: example-with-content-and-footer
|
||||
- label: With image
|
||||
id: example-with-image
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
@@ -23,17 +27,17 @@ toc:
|
||||
<p>Enter your details below to login to your account</p>
|
||||
</header>
|
||||
<section>
|
||||
<form class="form grid gap-6">
|
||||
<form class="grid gap-6">
|
||||
<div class="grid gap-2">
|
||||
<label for="demo-card-form-email">Email</label>
|
||||
<input type="email" id="demo-card-form-email">
|
||||
<label class="label" for="demo-card-form-email">Email</label>
|
||||
<input class="input" type="email" id="demo-card-form-email">
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<label for="demo-card-form-password">Password</label>
|
||||
<a href="#" class="ml-auto inline-block text-sm underline-offset-4 hover:underline">Forgot your password?</a>
|
||||
<label class="label" for="demo-card-form-password">Password</label>
|
||||
<a href="#" class="ms-auto inline-block text-sm underline-offset-4 hover:underline">Forgot your password?</a>
|
||||
</div>
|
||||
<input type="password" id="demo-card-form-password">
|
||||
<input class="input" type="password" id="demo-card-form-password">
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
@@ -45,7 +49,7 @@ toc:
|
||||
</div>{% endset %}
|
||||
{{ code_preview("card", code, "w-full max-w-sm") }}
|
||||
|
||||
<h2 id="usage">Usage</h2>
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
{% set code %}<div class="card">
|
||||
<header>
|
||||
@@ -87,6 +91,19 @@ toc:
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-size"><a href="#example-size">Size</a></h3>
|
||||
|
||||
{% set code %}<div class="card" data-size="sm">
|
||||
<header>
|
||||
<h2>Small Card</h2>
|
||||
<p>A compact card with reduced spacing.</p>
|
||||
</header>
|
||||
<section>
|
||||
<p class="text-sm">Use <code>data-size="sm"</code> for denser layouts.</p>
|
||||
</section>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("card-size", code) }}
|
||||
|
||||
<h3 id="example-with-content-and-footer"><a href="#example-with-content-and-footer">With content and footer</a></h3>
|
||||
|
||||
{% set code %}<div class="card">
|
||||
@@ -96,7 +113,7 @@ toc:
|
||||
</header>
|
||||
<section class="text-sm">
|
||||
<p>Client requested dashboard redesign with focus on mobile responsiveness.</p>
|
||||
<ol class="mt-4 flex list-decimal flex-col gap-2 pl-6">
|
||||
<ol class="mt-4 flex list-decimal flex-col gap-2 ps-6">
|
||||
<li>New analytics widgets for daily/weekly metrics</li>
|
||||
<li>Simplified navigation menu</li>
|
||||
<li>Dark mode support</li>
|
||||
@@ -148,9 +165,29 @@ toc:
|
||||
{% lucide "land-plot" %}
|
||||
350m²
|
||||
</span>
|
||||
<span class="ml-auto font-medium tabular-nums">
|
||||
<span class="ms-auto font-medium tabular-nums">
|
||||
$135,000
|
||||
</span>
|
||||
</footer>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("card-with-image", code) }}
|
||||
{{ code_preview("card-with-image", code) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Cards support right-to-left layouts through native text flow. Add <code>dir="rtl"</code> to the card or an ancestor.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<div class="card" dir="rtl">
|
||||
<header>
|
||||
<h2>إعدادات الحساب</h2>
|
||||
<p>راجع إعدادات الأمان والإشعارات.</p>
|
||||
</header>
|
||||
<section>
|
||||
<p class="text-sm">يمكنك تعديل هذه الإعدادات في أي وقت.</p>
|
||||
</section>
|
||||
<footer>
|
||||
<button type="button" class="btn">حفظ</button>
|
||||
</footer>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("card-rtl", code) }}
|
||||
|
||||
@@ -5,97 +5,162 @@ description: A control that allows the user to toggle between checked and not ch
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
- label: Checked state
|
||||
id: checked-state
|
||||
- label: Invalid state
|
||||
id: invalid-state
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Basic
|
||||
id: example-basic
|
||||
- label: Description
|
||||
id: example-description
|
||||
- label: Disabled
|
||||
id: example-disabled
|
||||
- label: With text
|
||||
id: example-with-text
|
||||
- label: Form
|
||||
id: example-form
|
||||
- label: Group
|
||||
id: example-group
|
||||
- label: Table
|
||||
id: example-table
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
|
||||
{% set code %}<label class="label gap-3">
|
||||
<input type="checkbox" class="input">
|
||||
Accept terms and conditions
|
||||
</label>{% endset %}
|
||||
{% set code %}<div role="group" class="field flex-row items-center gap-3">
|
||||
<input type="checkbox" id="terms" class="input">
|
||||
<label for="terms">Accept terms and conditions</label>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("checkbox", code) }}
|
||||
|
||||
<h2 id="usage">Usage</h2>
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Add the <code>input</code> class to your <code><input type="radio"></code> elements or have a parent with the <code>form</code> class (<a href="/components/form">read more about form</a>). You can also set the <code>aria-invalid</code> attribute to <code>true</code> to make the input invalid.</p>
|
||||
<p>Add the <code>input</code> class to your <code><input type="checkbox"></code> elements. Pair it with <a href="/components/field">Field</a> and a native <code><label></code> for proper layout and labeling.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<input type="checkbox" class="input">{% endset %}
|
||||
{{ code_block(code) }}
|
||||
|
||||
<h2 id="checked-state"><a href="#checked-state">Checked state</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Use native HTML attributes for state. Add <code>checked</code> for an initially checked checkbox, or read and update the element's <code>checked</code> property in JavaScript.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<input type="checkbox" class="input" checked>{% endset %}
|
||||
{{ code_block(code) }}
|
||||
|
||||
<h2 id="invalid-state"><a href="#invalid-state">Invalid state</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Set <code>aria-invalid="true"</code> on the checkbox and, when using <code>field</code>, set <code>data-invalid="true"</code> on the field wrapper.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<div role="group" class="field flex-row items-start gap-3" data-invalid="true">
|
||||
<input type="checkbox" id="invalid-checkbox" class="input" aria-invalid="true">
|
||||
<div class="grid gap-1.5">
|
||||
<label for="invalid-checkbox">Accept terms and conditions</label>
|
||||
<p class="field-error">You must accept the terms to continue.</p>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("checkbox-invalid", code) }}
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-basic"><a href="#example-basic">Basic</a></h3>
|
||||
|
||||
{% set code %}<div role="group" class="field flex-row items-center gap-3">
|
||||
<input type="checkbox" id="checkbox-basic" class="input">
|
||||
<label for="checkbox-basic">Accept terms and conditions</label>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("checkbox-basic", code) }}
|
||||
|
||||
<h3 id="example-description"><a href="#example-description">Description</a></h3>
|
||||
|
||||
{% set code %}<div role="group" class="field flex-row items-start gap-3">
|
||||
<input type="checkbox" id="checkbox-description" class="input" aria-describedby="checkbox-description-text">
|
||||
<div class="grid gap-1.5">
|
||||
<label for="checkbox-description">Accept terms and conditions</label>
|
||||
<p id="checkbox-description-text" class="field-description">By clicking this checkbox, you agree to the terms and conditions.</p>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("checkbox-description", code) }}
|
||||
|
||||
<h3 id="example-disabled"><a href="#example-disabled">Disabled</a></h3>
|
||||
|
||||
{% set code %}<label class="label gap-3">
|
||||
<input type="checkbox" class="input" disabled>
|
||||
Accept terms and conditions
|
||||
</label>{% endset %}
|
||||
{% set code %}<div role="group" class="field flex-row items-center gap-3" data-disabled="true">
|
||||
<input type="checkbox" id="checkbox-disabled" class="input" disabled>
|
||||
<label for="checkbox-disabled">Accept terms and conditions</label>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("checkbox-disabled", code) }}
|
||||
|
||||
<h3 id="example-with-text"><a href="#example-with-text">With text</a></h3>
|
||||
<h3 id="example-group"><a href="#example-group">Group</a></h3>
|
||||
|
||||
{% set code %}<div class="flex items-start gap-3">
|
||||
<input type="checkbox" id="checkbox-with-text" class="input">
|
||||
<div class="grid gap-2">
|
||||
<label for="checkbox-with-text" class="label">Accept terms and conditions</label>
|
||||
<p class="text-muted-foreground text-sm">By clicking this checkbox, you agree to the terms and conditions.</p>
|
||||
{% set code %}<fieldset class="fieldset">
|
||||
<legend>Sidebar</legend>
|
||||
<p>Select the items you want to display in the sidebar.</p>
|
||||
<div role="group" aria-label="Sidebar items" data-slot="checkbox-group">
|
||||
<div role="group" class="field flex-row items-center gap-3">
|
||||
<input class="input" type="checkbox" id="checkbox-recents" checked>
|
||||
<label for="checkbox-recents">Recents</label>
|
||||
</div>
|
||||
<div role="group" class="field flex-row items-center gap-3">
|
||||
<input class="input" type="checkbox" id="checkbox-home" checked>
|
||||
<label for="checkbox-home">Home</label>
|
||||
</div>
|
||||
<div role="group" class="field flex-row items-center gap-3">
|
||||
<input class="input" type="checkbox" id="checkbox-applications">
|
||||
<label for="checkbox-applications">Applications</label>
|
||||
</div>
|
||||
<div role="group" class="field flex-row items-center gap-3">
|
||||
<input class="input" type="checkbox" id="checkbox-desktop">
|
||||
<label for="checkbox-desktop">Desktop</label>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>{% endset %}
|
||||
{{ code_preview("checkbox-group", code) }}
|
||||
|
||||
<h3 id="example-table"><a href="#example-table">Table</a></h3>
|
||||
|
||||
{% set code %}<div class="table-container">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" class="input" aria-label="Select all"></th>
|
||||
<th>Status</th>
|
||||
<th>Email</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><input type="checkbox" class="input" aria-label="Select row 1" checked></td>
|
||||
<td>Active</td>
|
||||
<td>olivia@example.com</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" class="input" aria-label="Select row 2"></td>
|
||||
<td>Inactive</td>
|
||||
<td>noah@example.com</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("checkbox-table", code) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Checkbox layout uses document direction and logical spacing. Set <code>dir="rtl"</code> on a parent to render right-to-left.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<div dir="rtl" class="field flex-row items-start gap-3">
|
||||
<input type="checkbox" id="checkbox-rtl" class="input" aria-describedby="checkbox-rtl-description" checked>
|
||||
<div class="grid gap-1.5">
|
||||
<label for="checkbox-rtl">قبول الشروط والأحكام</label>
|
||||
<p id="checkbox-rtl-description" class="field-description">يمكنك تغيير هذا الخيار في أي وقت.</p>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("checkbox-with-text", code) }}
|
||||
|
||||
<h3 id="example-form"><a href="#example-form">Form</a></h3>
|
||||
|
||||
{% set code %}<form class="form flex flex-row items-start gap-3 rounded-md border p-4 shadow-xs">
|
||||
<input type="checkbox" id="checkbox-form-1">
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="checkbox-form-1" class="leading-snug">Use different settings for my mobile devices</label>
|
||||
<p class="text-muted-foreground text-sm leading-snug">You can manage your mobile notifications in the mobile settings page.</p>
|
||||
</div>
|
||||
</form>{% endset %}
|
||||
{{ code_preview("input-form", code) }}
|
||||
|
||||
{% set code %}<form class="form flex flex-col gap-4">
|
||||
<header>
|
||||
<label for="demo-form-checkboxes" class="text-base leading-normal">Sidebar</label>
|
||||
<p class="text-muted-foreground text-sm">Select the items you want to display in the sidebar.</p>
|
||||
</header>
|
||||
<fieldset id="demo-form-checkboxes" class="flex flex-col gap-2">
|
||||
<label class="font-normal leading-tight">
|
||||
<input type="checkbox" name="demo-form-checkboxes" value="1" checked>
|
||||
Recents
|
||||
</label>
|
||||
<label class="font-normal leading-tight">
|
||||
<input type="checkbox" name="demo-form-checkboxes" value="2" checked>
|
||||
Home
|
||||
</label>
|
||||
<label class="font-normal leading-tight">
|
||||
<input type="checkbox" name="demo-form-checkboxes" value="3">
|
||||
Applications
|
||||
</label>
|
||||
<label class="font-normal leading-tight">
|
||||
<input type="checkbox" name="demo-form-checkboxes" value="4">
|
||||
Desktop
|
||||
</label>
|
||||
<label class="font-normal leading-tight">
|
||||
<input type="checkbox" name="demo-form-checkboxes" value="5">
|
||||
Download
|
||||
</label>
|
||||
<label class="font-normal leading-tight">
|
||||
<input type="checkbox" name="demo-form-checkboxes" value="6">
|
||||
Documents
|
||||
</label>
|
||||
</fieldset>
|
||||
</form>{% endset %}
|
||||
{{ code_preview("input-form", code) }}
|
||||
{{ code_preview("checkbox-rtl", code) }}
|
||||
|
||||
+207
-162
@@ -1,7 +1,7 @@
|
||||
---
|
||||
layout: layouts/page.njk
|
||||
title: Combobox
|
||||
description: Autocomplete input and command palette with a list of suggestions.
|
||||
description: Autocomplete input with a list of suggestions.
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
@@ -24,26 +24,39 @@ toc:
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Scrollable
|
||||
id: example-scrollable
|
||||
- label: Top side
|
||||
- label: Multiple
|
||||
id: example-multiple
|
||||
- label: Groups
|
||||
id: example-groups
|
||||
- label: Custom Items
|
||||
id: example-custom
|
||||
- label: Invalid
|
||||
id: example-invalid
|
||||
- label: Disabled
|
||||
id: example-disabled
|
||||
- label: Auto Highlight
|
||||
id: example-auto-highlight
|
||||
- label: Top Side
|
||||
id: example-top
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
{% from "select.njk" import select %}
|
||||
{% from "combobox.njk" import combobox %}
|
||||
|
||||
{% set options_frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"] %}
|
||||
{% set frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"] %}
|
||||
{% set code_html %}
|
||||
{% call select(
|
||||
trigger_attrs={"class": "w-[200px]"},
|
||||
listbox_attrs={"data-empty": "No framework found."},
|
||||
is_combobox=true,
|
||||
search_placeholder="Search framework..."
|
||||
{% call combobox(
|
||||
id="framework-combobox",
|
||||
name="framework",
|
||||
placeholder="Select a framework",
|
||||
input_attrs={"class": "w-[240px]"},
|
||||
listbox_attrs={"data-empty": "No framework found."}
|
||||
) %}
|
||||
{% for framework in options_frameworks %}
|
||||
<div role="option" data-value="{{ framework }}">{{ framework }}</div>
|
||||
{% for framework in frameworks %}
|
||||
<div role="option" data-value="{{ framework }}">{{ framework }}</div>
|
||||
{% endfor %}
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
@@ -57,11 +70,11 @@ toc:
|
||||
<h4 id="usage-html-js-1"><a href="#usage-html-js-1">Step 1: Include the JavaScript files</a></h4>
|
||||
|
||||
<section class="prose">
|
||||
<p>You can either <a href="/installation/#install-cdn-all">include the JavaScript file for all the components</a>, or just the one for this component by adding this to the <code><head></code> of your page:</p>
|
||||
<p>You can either <a href="/installation/#install-cdn-all">include the JavaScript file for all the components</a>, or just the one for this component:</p>
|
||||
</section>
|
||||
|
||||
{% set code_script %}<script src="https://cdn.jsdelivr.net/npm/basecoat-css@{{ pkg.version }}/dist/js/basecoat.min.js" defer></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/basecoat-css@{{ pkg.version }}/dist/js/select.min.js" defer></script>{% endset %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/basecoat-css@{{ pkg.version }}/dist/js/combobox.min.js" defer></script>{% endset %}
|
||||
{{ code_block(code_script | prettyHtml, "html") }}
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
@@ -69,12 +82,12 @@ toc:
|
||||
Components with JavaScript
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="/installation/#install-cli">
|
||||
<a class="badge-outline" href="/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">
|
||||
select.js
|
||||
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/js/combobox.js" target="_blank">
|
||||
combobox.js
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
</div>
|
||||
@@ -82,7 +95,7 @@ toc:
|
||||
<h4 id="usage-html-js-2"><a href="#usage-html-js-2">Step 2: Add your combobox HTML</a></h4>
|
||||
|
||||
<section class="prose">
|
||||
<p>Combobox uses the same markup and JavaScript as the <a href="/components/select#usage-html-js">Select component</a>, the only difference being the search box at the top of the listbox:</p>
|
||||
<p>Combobox is input-first. The visible input filters the list; the hidden input stores the submitted value.</p>
|
||||
</section>
|
||||
|
||||
{{ code_block(code_html | prettyHtml, "html") }}
|
||||
@@ -91,54 +104,20 @@ toc:
|
||||
|
||||
<section class="prose">
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><div class="select"></code></dt>
|
||||
<dd>Wraps around the entire component.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><button type="button" popovertarget="{ POPOVER_ID }"></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"><div popover class="popover" id="{ POPOVER_ID }"></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"><header></code></dt>
|
||||
<dd>Contains the search input to filter the options in the listbox.</dd>
|
||||
<dt><code class="highlight language-html"><div role="listbox"></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"><div role="option" data-value="{ VALUE }"></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. Optional attributes:
|
||||
<ul>
|
||||
<li><code>data-filter</code>: custom text used for filtering (if different from displayed content, e.g., <code>data-filter="united states"</code>)</li>
|
||||
<li><code>data-keywords</code>: additional keywords for enhanced filtering (e.g., <code>data-keywords="bread fruit"</code>)</li>
|
||||
<li><code>data-force</code>: forces the option to always display, regardless of filter (e.g., <code>data-force</code>)</li>
|
||||
<li><code>aria-disabled="true"</code>: marks option as disabled (excluded from selection and keyboard navigation)</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt><code class="highlight language-html"><hr role="separator"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Separator between groups/options.</dd>
|
||||
<dt><code class="highlight language-html"><div role="group"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Group of options, can have a <code>aria-labelledby</code> attribute to link to a heading.</dd>
|
||||
<dt><code class="highlight language-html"><span role="heading"></code></dt>
|
||||
<dd>Group heading, must have an <code>id</code> attribute if you use the <code>aria-labelledby</code> attribute on the group.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code class="highlight language-html"><input type="hidden" name="{ NAME }" value="{ VALUE }"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>The hidden input that holds the value of the field (if needed).</dd>
|
||||
<dt><code class="highlight language-html"><div class="combobox"></code></dt>
|
||||
<dd>Root element. Add <code>data-multiple="true"</code> for multiple selection and <code>data-auto-highlight="true"</code> to highlight the first match while filtering.</dd>
|
||||
<dt><code class="highlight language-html"><input type="text" role="combobox"></code></dt>
|
||||
<dd>The editable control. It should include <code>aria-expanded</code>, <code>aria-controls</code>, and <code>aria-autocomplete="list"</code>.</dd>
|
||||
<dt><code class="highlight language-html"><div class="combobox-chips"></code> <span class="badge-secondary">Multiple only</span></dt>
|
||||
<dd>Wraps selected chips and the editable input in multiple mode.</dd>
|
||||
<dt><code class="highlight language-html"><div data-popover></code></dt>
|
||||
<dd>The suggestions popup. Use <code>data-side</code> and <code>data-align</code> like Popover.</dd>
|
||||
<dt><code class="highlight language-html"><div role="listbox"></code></dt>
|
||||
<dd>The suggestions list. Add <code>aria-multiselectable="true"</code> in multiple mode and <code>data-empty</code> for the empty message.</dd>
|
||||
<dt><code class="highlight language-html"><div role="option" data-value="..."></code></dt>
|
||||
<dd>Selectable option. Options can contain custom HTML; use <code>data-label</code> when the visible input should use different text than the rendered content.</dd>
|
||||
<dt><code class="highlight language-html"><input type="hidden"></code></dt>
|
||||
<dd>Submitted value. Single mode stores a string; multiple mode stores a JSON array.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
@@ -147,147 +126,213 @@ toc:
|
||||
<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>
|
||||
<dd>Dispatched on the component after initialization.</dd>
|
||||
<dt><code>basecoat:popover</code></dt>
|
||||
<dd>When the popover opens, the component dispatches a custom (non-bubbling) <code>basecoat:popover</code> event on <code>document</code>. Other popover components (Combobox, Dropdown Menu, Popover and Select) listen for this to close any open popovers.</dd>
|
||||
<dd>Dispatched on <code>document</code> when the popup opens so other popup components can close.</dd>
|
||||
<dt><code>change</code></dt>
|
||||
<dd>When the selected value changes, the component dispatches a custom (bubbling) <code>change</code> event on itself, with the selected value in <code>event.detail</code> (e.g. <code>{detail: {value: "something"}}</code>).</dd>
|
||||
<dd>Dispatched on selection changes with <code>event.detail.value</code>.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<h4 id="usage-html-js-5"><a href="#usage-html-js-4">JavaScript methods</a></h4>
|
||||
<h4 id="usage-html-js-5"><a href="#usage-html-js-5">JavaScript methods</a></h4>
|
||||
|
||||
<section class="prose">
|
||||
<dl>
|
||||
<dt><code>selectByValue</code></dt>
|
||||
<dd>
|
||||
<p>You can call this method on the component after it is initialized to select an option by value (i.e. the option with the matching <code>data-value</code> attribute):</p>
|
||||
|
||||
{% set code_script %}<script>
|
||||
const comboboxComponent = document.querySelector('#my-combobox');
|
||||
comboboxComponent.addEventListener('basecoat:initialized', () => {
|
||||
comboboxComponent.selectByValue('apple');
|
||||
});
|
||||
</script>{% endset %}
|
||||
{{ code_block(code_script | prettyHtml, "html") }}
|
||||
</dd>
|
||||
<dt><code>value</code></dt>
|
||||
<dd>Gets or sets the selected value. Multiple mode uses an array.</dd>
|
||||
<dt><code>select(value)</code></dt>
|
||||
<dd>Selects an option by value.</dd>
|
||||
<dt><code>deselect(value)</code> <span class="badge-secondary">Multiple only</span></dt>
|
||||
<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>
|
||||
</dl>
|
||||
</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">select()</code> Nunjucks or Jinja macro for this component. If you use one of the macros, make sure to set <code>is_combobox</code> to <code>True</code> (or <code>true</code> for Nunjucks).</p>
|
||||
</div>
|
||||
<section class="prose">
|
||||
<p>Use the <code>combobox()</code> macro for Nunjucks or Jinja.</p>
|
||||
</section>
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
<a class="badge-outline" href="/installation/#install-macros" target="_blank">
|
||||
<a class="badge-outline" href="/cli#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/select.html.jinja" target="_blank">
|
||||
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/jinja/combobox.html.jinja" target="_blank">
|
||||
Jinja macro
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/select.njk" target="_blank">
|
||||
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/combobox.njk" target="_blank">
|
||||
Nunjucks macro
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% set raw_code %}{% raw %}{% call select(
|
||||
listbox_attrs={"data-empty": "No framework found."},
|
||||
is_combobox=true
|
||||
{% set raw_code %}{% raw %}{% call combobox(
|
||||
placeholder="Select a framework",
|
||||
listbox_attrs={"data-empty": "No framework found."}
|
||||
) %}
|
||||
<div role="option" data-value="nextjs">Next.js</div>
|
||||
<div role="option" data-value="sveltekit">SvelteKit</div>
|
||||
<div role="option" data-value="nuxtjs">Nuxt.js</div>
|
||||
<div role="option" data-value="remix">Remix</div>
|
||||
<div role="option" data-value="astro">Astro</div>
|
||||
{% endcall %}{% endraw %}{% endset %}
|
||||
{{ code_block(raw_code, "jinja") }}
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-scrollable"><a href="#example-scrollable">Scrollable</a></h3>
|
||||
<h3 id="example-multiple"><a href="#example-multiple">Multiple</a></h3>
|
||||
|
||||
{% set options_timezones = [
|
||||
{
|
||||
label: "Americas",
|
||||
timezones: [
|
||||
{ value: "America/New_York", label: "(GMT-5) New York" },
|
||||
{ value: "America/Los_Angeles", label: "(GMT-8) Los Angeles" },
|
||||
{ value: "America/Chicago", label: "(GMT-6) Chicago" },
|
||||
{ value: "America/Toronto", label: "(GMT-5) Toronto" },
|
||||
{ value: "America/Vancouver", label: "(GMT-8) Vancouver" },
|
||||
{ value: "America/Sao_Paulo", label: "(GMT-3) São Paulo" }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Europe",
|
||||
timezones: [
|
||||
{ value: "Europe/London", label: "(GMT+0) London" },
|
||||
{ value: "Europe/Paris", label: "(GMT+1) Paris" },
|
||||
{ value: "Europe/Berlin", label: "(GMT+1) Berlin" },
|
||||
{ value: "Europe/Rome", label: "(GMT+1) Rome" },
|
||||
{ value: "Europe/Madrid", label: "(GMT+1) Madrid" },
|
||||
{ value: "Europe/Amsterdam", label: "(GMT+1) Amsterdam" }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Asia/Pacific",
|
||||
timezones: [
|
||||
{ value: "Asia/Tokyo", label: "(GMT+9) Tokyo" },
|
||||
{ value: "Asia/Shanghai", label: "(GMT+8) Shanghai" },
|
||||
{ value: "Asia/Singapore", label: "(GMT+8) Singapore" },
|
||||
{ value: "Asia/Dubai", label: "(GMT+4) Dubai" },
|
||||
{ value: "Australia/Sydney", label: "(GMT+11) Sydney" },
|
||||
{ value: "Asia/Seoul", label: "(GMT+9) Seoul" }
|
||||
]
|
||||
}
|
||||
] %}
|
||||
{% set code %}
|
||||
{% call select(
|
||||
trigger_attrs={"class": "w-[200px]"},
|
||||
popover_attrs={"class": "w-72"},
|
||||
listbox_attrs={"data-empty": "No timezone found."},
|
||||
is_combobox=true,
|
||||
search_placeholder="Search timezone..."
|
||||
{% call combobox(
|
||||
id="frameworks-combobox",
|
||||
name="frameworks",
|
||||
multiple=true,
|
||||
selected=["Next.js"],
|
||||
placeholder="Add framework",
|
||||
input_attrs={"class": "w-[320px]"},
|
||||
listbox_attrs={"data-empty": "No framework found."},
|
||||
auto_highlight=true
|
||||
) %}
|
||||
<div class="max-h-64 overflow-y-auto scrollbar">
|
||||
{% for group in options_timezones %}
|
||||
<div role="group" aria-labelledby="demo-combobox-timezones-group-{{ loop.index0 }}">
|
||||
<div role="heading" id="demo-combobox-timezones-group-{{ loop.index0 }}">{{ group.label }}</div>
|
||||
{% for timezone in group.timezones %}
|
||||
<div role="option" data-value="{{ timezone.value}}">
|
||||
{{ timezone.label }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% for framework in frameworks %}
|
||||
<div role="option" data-value="{{ framework }}">{{ framework }}</div>
|
||||
{% endfor %}
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("combobox-multiple", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-groups"><a href="#example-groups">Groups</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{% call combobox(
|
||||
id="timezone-combobox",
|
||||
placeholder="Search timezone",
|
||||
input_attrs={"class": "w-[280px]"},
|
||||
listbox_attrs={"data-empty": "No timezone found."}
|
||||
) %}
|
||||
<div role="group" aria-labelledby="timezone-americas">
|
||||
<div role="heading" id="timezone-americas">Americas</div>
|
||||
<div role="option" data-value="America/New_York">(GMT-5) New York</div>
|
||||
<div role="option" data-value="America/Los_Angeles">(GMT-8) Los Angeles</div>
|
||||
<div role="option" data-value="America/Sao_Paulo">(GMT-3) São Paulo</div>
|
||||
</div>
|
||||
<hr role="separator">
|
||||
<div role="option">
|
||||
{% lucide "circle-plus" %}
|
||||
Create timezone
|
||||
<div role="group" aria-labelledby="timezone-europe">
|
||||
<div role="heading" id="timezone-europe">Europe</div>
|
||||
<div role="option" data-value="Europe/London">(GMT+0) London</div>
|
||||
<div role="option" data-value="Europe/Paris">(GMT+1) Paris</div>
|
||||
<div role="option" data-value="Europe/Berlin">(GMT+1) Berlin</div>
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("combobox-with-groups", code | prettyHtml) }}
|
||||
{{ code_preview("combobox-groups", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-top"><a href="#example-top">Top side</a></h3>
|
||||
<h3 id="example-custom"><a href="#example-custom">Custom Items</a></h3>
|
||||
|
||||
{% set options_frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"] %}
|
||||
{% set code %}
|
||||
{% call select(
|
||||
trigger_attrs={"class": "w-[200px]"},
|
||||
listbox_attrs={"data-empty": "No framework found."},
|
||||
is_combobox=true,
|
||||
search_placeholder="Search framework...",
|
||||
{% call combobox(
|
||||
id="country-combobox",
|
||||
placeholder="Search countries",
|
||||
input_attrs={"class": "w-[280px]"},
|
||||
listbox_attrs={"data-empty": "No country found."}
|
||||
) %}
|
||||
<div role="option" data-value="argentina" data-label="Argentina" data-filter="Argentina South America">
|
||||
<span class="flex flex-col">
|
||||
<span>Argentina</span>
|
||||
<span class="text-muted-foreground text-xs">South America (ar)</span>
|
||||
</span>
|
||||
</div>
|
||||
<div role="option" data-value="japan" data-label="Japan" data-filter="Japan Asia">
|
||||
<span class="flex flex-col">
|
||||
<span>Japan</span>
|
||||
<span class="text-muted-foreground text-xs">Asia (jp)</span>
|
||||
</span>
|
||||
</div>
|
||||
<div role="option" data-value="united-states" data-label="United States" data-filter="United States North America USA">
|
||||
<span class="flex flex-col">
|
||||
<span>United States</span>
|
||||
<span class="text-muted-foreground text-xs">North America (us)</span>
|
||||
</span>
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("combobox-custom", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-invalid"><a href="#example-invalid">Invalid</a></h3>
|
||||
|
||||
{% set code %}
|
||||
<div role="group" class="field" data-invalid="true">
|
||||
<label for="invalid-combobox-input">Framework</label>
|
||||
{% call combobox(
|
||||
id="invalid-combobox",
|
||||
placeholder="Select a framework",
|
||||
input_attrs={"id": "invalid-combobox-input", "aria-invalid": "true", "class": "w-[240px]"},
|
||||
listbox_attrs={"data-empty": "No framework found."}
|
||||
) %}
|
||||
{% for framework in frameworks %}
|
||||
<div role="option" data-value="{{ framework }}">{{ framework }}</div>
|
||||
{% endfor %}
|
||||
{% endcall %}
|
||||
<p class="field-error" role="alert">Select a framework to continue.</p>
|
||||
</div>
|
||||
{% endset %}
|
||||
{{ code_preview("combobox-invalid", code | prettyHtml, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-disabled"><a href="#example-disabled">Disabled</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{% call combobox(
|
||||
placeholder="Select a framework",
|
||||
input_attrs={"disabled": "disabled", "class": "w-[240px]"}
|
||||
) %}
|
||||
<div role="option" data-value="nextjs">Next.js</div>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("combobox-disabled", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-auto-highlight"><a href="#example-auto-highlight">Auto Highlight</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{% call combobox(
|
||||
placeholder="Select a framework",
|
||||
input_attrs={"class": "w-[240px]"},
|
||||
auto_highlight=true
|
||||
) %}
|
||||
{% for framework in frameworks %}
|
||||
<div role="option" data-value="{{ framework }}">{{ framework }}</div>
|
||||
{% endfor %}
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("combobox-auto-highlight", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-top"><a href="#example-top">Top Side</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{% call combobox(
|
||||
placeholder="Select a framework",
|
||||
input_attrs={"class": "w-[240px]"},
|
||||
popover_attrs={"data-side": "top"}
|
||||
) %}
|
||||
{% for framework in options_frameworks %}
|
||||
<div role="option" data-value="{{ framework }}">{{ framework }}</div>
|
||||
{% for framework in frameworks %}
|
||||
<div role="option" data-value="{{ framework }}">{{ framework }}</div>
|
||||
{% endfor %}
|
||||
{% endcall %}{% endset %}
|
||||
{{ code_preview("combobox", code | prettyHtml) }}
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("combobox-top", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
{% set code %}
|
||||
<div dir="rtl">
|
||||
{% call combobox(
|
||||
placeholder="اختر إطار عمل",
|
||||
input_attrs={"class": "w-[240px]"}
|
||||
) %}
|
||||
<div role="option" data-value="nextjs">Next.js</div>
|
||||
<div role="option" data-value="sveltekit">SvelteKit</div>
|
||||
<div role="option" data-value="astro">Astro</div>
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endset %}
|
||||
{{ code_preview("combobox-rtl", code | prettyHtml) }}
|
||||
|
||||
+156
-109
@@ -1,8 +1,10 @@
|
||||
---
|
||||
layout: layouts/page.njk
|
||||
title: Command
|
||||
description: Fast, composable command menu for quick navigation and actions.
|
||||
description: Command menu for search and quick actions.
|
||||
toc:
|
||||
- label: About
|
||||
id: about
|
||||
- label: Usage
|
||||
id: usage
|
||||
children:
|
||||
@@ -22,61 +24,76 @@ toc:
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Dialog
|
||||
id: example-dialog
|
||||
- label: Basic
|
||||
id: example-basic
|
||||
- label: Shortcuts
|
||||
id: example-shortcuts
|
||||
- label: Groups
|
||||
id: example-groups
|
||||
- label: Scrollable
|
||||
id: example-scrollable
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
- label: API Reference
|
||||
id: api-reference
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
{% from "command.njk" import command, command_dialog %}
|
||||
|
||||
{% macro command_items(suggestions_id="suggestions", settings_id="settings") %}
|
||||
{% macro command_items(suggestions_id="suggestions", settings_id="settings", shortcuts=false) %}
|
||||
<div role="group" aria-labelledby="{{ suggestions_id }}">
|
||||
<span role="heading" id="{{ suggestions_id }}">Suggestions</span>
|
||||
<div role="menuitem" onclick="console.log('Open calendar')">
|
||||
<div role="menuitem" data-filter="Calendar" data-keywords="date event schedule">
|
||||
{% lucide "calendar" %}
|
||||
<span>Calendar</span>
|
||||
{% if shortcuts %}<span data-shortcut>⌘K</span>{% endif %}
|
||||
</div>
|
||||
<div role="menuitem" onclick="console.log('Search emoji')">
|
||||
<div role="menuitem" data-filter="Search Emoji" data-keywords="emoji smile reaction">
|
||||
{% lucide "smile" %}
|
||||
<span>Search Emoji</span>
|
||||
{% if shortcuts %}<span data-shortcut>⌘E</span>{% endif %}
|
||||
</div>
|
||||
<div role="menuitem" aria-disabled="true">
|
||||
{% lucide "calculator" %}
|
||||
<span>Calculator</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr role="separator" />
|
||||
<hr role="separator">
|
||||
<div role="group" aria-labelledby="{{ settings_id }}">
|
||||
<span role="heading" id="{{ settings_id }}">Settings</span>
|
||||
<div role="menuitem" onclick="console.log('Open profile')" data-filter="Profile">
|
||||
<div role="menuitem" data-filter="Profile" data-keywords="user account">
|
||||
{% lucide "user" %}
|
||||
<span>Profile</span>
|
||||
<kbd class="ml-auto text-muted-foreground bg-transparent tracking-widest">⌘P</kbd>
|
||||
{% if shortcuts %}<span data-shortcut>⌘P</span>{% endif %}
|
||||
</div>
|
||||
<div role="menuitem" onclick="console.log('Open billing')" data-filter="Billing">
|
||||
<div role="menuitem" data-filter="Billing" data-keywords="invoice payment">
|
||||
{% lucide "credit-card" %}
|
||||
<span>Billing</span>
|
||||
<kbd class="ml-auto text-muted-foreground bg-transparent tracking-widest">⌘B</kbd>
|
||||
{% if shortcuts %}<span data-shortcut>⌘B</span>{% endif %}
|
||||
</div>
|
||||
<div role="menuitem" onclick="console.log('Open settings')" data-filter="Settings">
|
||||
<div role="menuitem" data-filter="Settings" data-keywords="config preferences">
|
||||
{% lucide "settings" %}
|
||||
<span>Settings</span>
|
||||
<kbd class="ml-auto text-muted-foreground bg-transparent tracking-widest">⌘S</kbd>
|
||||
{% if shortcuts %}<span data-shortcut>⌘S</span>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% set code_standalone %}
|
||||
{% call command(
|
||||
id="demo-command-standalone",
|
||||
main_attrs={ "class": "rounded-lg border shadow-md" }
|
||||
) %}
|
||||
{% call command(id="demo-command-standalone") %}
|
||||
{{ command_items("suggestions", "settings") }}
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("command-standalone", code_standalone | prettyHtml, class=" w-full max-w-[450px]") }}
|
||||
{{ code_preview("command-standalone", code_standalone | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h2 id="about"><a href="#about">About</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>shadcn/ui's Command component is built on <code>cmdk</code>. Basecoat implements the same command-menu pattern with vanilla HTML, CSS, and a small JavaScript controller for filtering, active item navigation, and dialog closing.</p>
|
||||
</section>
|
||||
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
@@ -97,7 +114,7 @@ toc:
|
||||
Components with JavaScript
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="/installation/#install-cli">
|
||||
<a class="badge-outline" href="/cli">
|
||||
Use the CLI
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
@@ -109,68 +126,53 @@ toc:
|
||||
|
||||
<h4 id="usage-html-js-2"><a href="#usage-html-js-2">Step 2: Add your command HTML</a></h4>
|
||||
|
||||
<section class="prose">
|
||||
<p>You can use the command menu standalone:</p>
|
||||
</section>
|
||||
|
||||
{{ code_block(code_standalone | prettyHtml, "html") }}
|
||||
|
||||
<section class="prose">
|
||||
<p>Wrap the command menu in a <code><dialog class="command-dialog"></code> to display in a dialog:</p>
|
||||
<p>Use <code><dialog class="command-dialog"></code> for the command palette variant.</p>
|
||||
</section>
|
||||
|
||||
{% set code_dialog %}
|
||||
<dialog class="command-dialog" aria-label="Command menu">
|
||||
{% set code_dialog_usage %}<button class="btn-outline" onclick="document.getElementById('command-palette').showModal()">Open</button>
|
||||
<dialog id="command-palette" class="command-dialog" aria-label="Command menu">
|
||||
<div class="command">
|
||||
<!-- Regular command content: header, menu, etc. -->
|
||||
<!-- Command content -->
|
||||
</div>
|
||||
</dialog>
|
||||
{% endset %}
|
||||
|
||||
{{ code_block(code_dialog | prettyHtml, "html") }}
|
||||
</dialog>{% endset %}
|
||||
{{ code_block(code_dialog_usage | prettyHtml, "html") }}
|
||||
|
||||
<h4 id="usage-html-js-3"><a href="#usage-html-js-3">HTML structure</a></h4>
|
||||
|
||||
<section class="prose">
|
||||
<p>The command menu can be used standalone or inside a native dialog. The structure is:</p>
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><dialog class="command-dialog"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>For dialog variant, wraps a <code><div class="command"></code> to display as a modal. Uses native <code><dialog></code> behavior and requires <code>aria-label</code> or <code>aria-labelledby</code> for accessibility.
|
||||
<dd>Dialog variant. Wraps one <code><div class="command"></code>. Add <code>aria-label</code> or <code>aria-labelledby</code> for an accessible name.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><div class="command"></code></dt>
|
||||
<dd>Wraps around the entire command component. Can be used standalone or inside a dialog.
|
||||
<dd>Root command menu. Can also be used standalone without the dialog wrapper.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><header></code></dt>
|
||||
<dd>Contains the search input to filter command options.
|
||||
<dd>Search input wrapper. The macro includes a search icon and input.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><input type="text"></code></dt>
|
||||
<dd>The search input. Should have:
|
||||
<ul>
|
||||
<li><code>role="combobox"</code>: indicates the input controls a menu with filtering</li>
|
||||
<li><code>aria-expanded="true"</code>: the menu is always expanded</li>
|
||||
<li><code>aria-controls="{ MENU_ID }"</code>: points to the menu's id</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt><code class="highlight language-html"><input type="text" role="combobox"></code></dt>
|
||||
<dd>Filter input. Use <code>aria-expanded="true"</code> and <code>aria-controls="{ MENU_ID }"</code> to point to the command list.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code class="highlight language-html"><div role="menu"></code></dt>
|
||||
<dd>The menu containing the command items. Should have <code>id="{ MENU_ID }"</code> refered to by the input's <code>aria-controls</code> attribute.
|
||||
<dt><code class="highlight language-html"><div role="menu" id="{ MENU_ID }"></code></dt>
|
||||
<dd>Command list. Set <code>data-empty</code> to customize the empty state.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><div role="group" aria-labelledby="{ HEADING_ID }"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Groups related command items. Hidden automatically when every item in the group is filtered out.</dd>
|
||||
<dt><code class="highlight language-html"><span role="heading" id="{ HEADING_ID }"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Group heading. Use an <code>id</code> when the parent group uses <code>aria-labelledby</code>.</dd>
|
||||
<dt><code class="highlight language-html"><div role="menuitem"></code> or <code class="highlight language-html"><a role="menuitem"></code></dt>
|
||||
<dd>Command item that triggers an action when clicked. Use <code><a></code> for navigation links, or <code><div></code> for actions. In a command-dialog, clicking a menuitem automatically closes the dialog unless the <code>data-keep-command-open</code> attribute is present. Optional attributes:
|
||||
<ul>
|
||||
<li><code>href</code>: If present on an <code><a></code>, the item navigates to that URL</li>
|
||||
<li><code>data-filter</code>: custom text used for filtering (if different from displayed content, e.g., <code>data-filter="calendar event"</code>)</li>
|
||||
<li><code>data-keywords</code>: additional keywords for enhanced filtering (e.g., <code>data-keywords="calendar event date"</code>)</li>
|
||||
<li><code>data-force</code>: forces the item to always display, regardless of filter (e.g., <code>data-force</code>)</li>
|
||||
<li><code>aria-disabled="true"</code>: marks item as disabled (excluded from keyboard navigation and filtering)</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dd>Selectable command item. Use <code><a></code> for navigation and <code><div></code> for actions. Supports <code>data-filter</code>, <code>data-keywords</code>, <code>data-force</code>, <code>data-keep-command-open</code>, and <code>aria-disabled="true"</code>.</dd>
|
||||
<dt><code class="highlight language-html"><span data-shortcut></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Shortcut hint aligned to the inline end of the item.</dd>
|
||||
<dt><code class="highlight language-html"><span data-indicator></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Item indicator. It becomes visible with <code>data-checked="true"</code> or <code>aria-selected="true"</code>.</dd>
|
||||
<dt><code class="highlight language-html"><hr role="separator"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Separator between groups/items. Hidden when filtering is active.</dd>
|
||||
<dt><code class="highlight language-html"><div role="group"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Group of items, can have <code>aria-labelledby</code> attribute to link to a heading.</dd>
|
||||
<dt><code class="highlight language-html"><span role="heading"></code></dt>
|
||||
<dd>Group heading, must have an <code>id</code> attribute if you use the <code>aria-labelledby</code> attribute on the group.</dd>
|
||||
<dd>Separator between groups/items. Hidden while filtering.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
@@ -185,7 +187,7 @@ toc:
|
||||
<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>
|
||||
<dd>Once the component is initialized, it dispatches a custom non-bubbling <code>basecoat:initialized</code> event on itself.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
@@ -196,7 +198,7 @@ toc:
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
<a class="badge-outline" href="/installation/#install-macros" target="_blank">
|
||||
<a class="badge-outline" href="/cli#macros" target="_blank">
|
||||
Use Nunjucks or Jinja macros
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
@@ -211,7 +213,7 @@ toc:
|
||||
</div>
|
||||
|
||||
{% set raw_code %}{% raw %}{% call command() %}
|
||||
<div role="menuitem">
|
||||
<div role="menuitem" data-keywords="date event">
|
||||
<svg>...</svg>
|
||||
<span>Calendar</span>
|
||||
</div>
|
||||
@@ -222,67 +224,112 @@ toc:
|
||||
{% endcall %}{% endraw %}{% endset %}
|
||||
{{ code_block(raw_code, "jinja") }}
|
||||
|
||||
<section class="prose">
|
||||
<p>You can also use the <code>items</code> parameter to pass an array of items:</p>
|
||||
</section>
|
||||
|
||||
{% set icon_calendar %}{% lucide "calendar" %}{% endset %}
|
||||
{% set icon_settings %}{% lucide "settings" %}{% endset %}
|
||||
{% set raw_code_items %}{% raw %}{% set icon_calendar %}{% lucide "calendar" %}{% endset %}
|
||||
{% set icon_settings %}{% lucide "settings" %}{% endset %}
|
||||
{% set items = [
|
||||
{ type: "group", label: "Suggestions", items: [
|
||||
{ label: "Calendar", icon: icon_calendar, keywords: "date event" },
|
||||
{ label: "Search Emoji", icon: icon_settings },
|
||||
{ label: "Calculator", disabled: true }
|
||||
]},
|
||||
{ type: "separator" },
|
||||
{ type: "group", label: "Settings", items: [
|
||||
{ label: "Profile", url: "/profile", icon: icon_settings, keywords: "user account" },
|
||||
{ label: "Billing", url: "/billing", icon: icon_settings },
|
||||
{ label: "Settings", url: "/settings", icon: icon_settings }
|
||||
]}
|
||||
] %}
|
||||
|
||||
{% call command(items=items) %}{% endcall %}{% endraw %}{% endset %}
|
||||
{{ code_block(raw_code_items, "jinja") }}
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-dialog"><a href="#example-dialog">Dialog</a></h3>
|
||||
<h3 id="example-basic"><a href="#example-basic">Basic</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>You can use some simple JavaScript to open the command menu with a keyboard shortcut (in this case <kbd class="kbd">⌘J</kbd>).</p>
|
||||
<p>A simple command menu in a dialog. The button also shows the keyboard shortcut used in this example.</p>
|
||||
</section>
|
||||
|
||||
{% set code_dialog %}
|
||||
<button
|
||||
type="button"
|
||||
class="btn-outline"
|
||||
onclick="document.getElementById('demo-command-dialog').showModal()"
|
||||
>
|
||||
{% set code_basic %}
|
||||
<button type="button" class="btn-outline" onclick="document.getElementById('command-basic').showModal()">
|
||||
Open command menu
|
||||
<kbd class="kbd">⌘J</kbd>
|
||||
</button>
|
||||
|
||||
{% call command_dialog(id="demo-command-dialog") %}
|
||||
{{ command_items("cmd-suggestions", "cmd-settings") }}
|
||||
{% call command_dialog(id="command-basic") %}
|
||||
{{ command_items("command-basic-suggestions", "command-basic-settings") }}
|
||||
{% endcall %}
|
||||
|
||||
<script>
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 'j') {
|
||||
e.preventDefault();
|
||||
const dialog = document.getElementById('demo-command-dialog');
|
||||
if (dialog.open) {
|
||||
dialog.close();
|
||||
} else {
|
||||
dialog.showModal();
|
||||
dialog.querySelector('header input')?.focus();
|
||||
}
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if ((event.metaKey || event.ctrlKey) && event.key === 'j') {
|
||||
event.preventDefault();
|
||||
const dialog = document.getElementById('command-basic');
|
||||
dialog.open ? dialog.close() : dialog.showModal();
|
||||
dialog.querySelector('header input')?.focus();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endset %}
|
||||
{{ code_preview("command-basic", code_basic | prettyHtml) }}
|
||||
|
||||
{{ code_preview("command-dialog", code_dialog | prettyHtml) }}
|
||||
<h3 id="example-shortcuts"><a href="#example-shortcuts">Shortcuts</a></h3>
|
||||
|
||||
{% set code_shortcuts %}
|
||||
{% call command(id="command-shortcuts") %}
|
||||
{{ command_items("command-shortcuts-suggestions", "command-shortcuts-settings", true) }}
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("command-shortcuts", code_shortcuts | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-groups"><a href="#example-groups">Groups</a></h3>
|
||||
|
||||
{% set code_groups %}
|
||||
{% call command(id="command-groups") %}
|
||||
{{ command_items("command-groups-suggestions", "command-groups-settings") }}
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("command-groups", code_groups | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-scrollable"><a href="#example-scrollable">Scrollable</a></h3>
|
||||
|
||||
{% set code_scrollable %}
|
||||
{% call command(id="command-scrollable") %}
|
||||
<div role="group" aria-labelledby="command-scrollable-heading">
|
||||
<span role="heading" id="command-scrollable-heading">Pages</span>
|
||||
{% for i in range(0, 18) %}
|
||||
<div role="menuitem" data-filter="Page {{ i + 1 }}">
|
||||
{% lucide "file" %}
|
||||
<span>Page {{ i + 1 }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("command-scrollable", code_scrollable | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Command supports document direction. Set <code>dir="rtl"</code> on the command root or a parent element.</p>
|
||||
</section>
|
||||
|
||||
{% set code_rtl %}
|
||||
<div dir="rtl">
|
||||
{% call command(id="command-rtl", placeholder="اكتب أمراً أو ابحث...") %}
|
||||
<div role="group" aria-labelledby="command-rtl-suggestions">
|
||||
<span role="heading" id="command-rtl-suggestions">اقتراحات</span>
|
||||
<div role="menuitem" data-filter="Calendar">
|
||||
{% lucide "calendar" %}
|
||||
<span>التقويم</span>
|
||||
<span data-shortcut>⌘K</span>
|
||||
</div>
|
||||
<div role="menuitem" data-filter="Settings">
|
||||
{% lucide "settings" %}
|
||||
<span>الإعدادات</span>
|
||||
<span data-shortcut>⌘S</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endset %}
|
||||
{{ code_preview("command-rtl", code_rtl | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h2 id="api-reference"><a href="#api-reference">API Reference</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<dl>
|
||||
<dt><code class="highlight language-html">.command</code></dt>
|
||||
<dd>Root command menu.</dd>
|
||||
<dt><code class="highlight language-html">.command-dialog</code></dt>
|
||||
<dd>Native dialog variant. Closes automatically when an item is clicked unless that item has <code>data-keep-command-open</code>.</dd>
|
||||
<dt><code class="highlight language-html">data-filter</code></dt>
|
||||
<dd>Overrides item text used by the filter.</dd>
|
||||
<dt><code class="highlight language-html">data-keywords</code></dt>
|
||||
<dd>Additional whitespace- or comma-separated search terms.</dd>
|
||||
<dt><code class="highlight language-html">data-force</code></dt>
|
||||
<dd>Always keeps an item visible while filtering.</dd>
|
||||
<dt><code class="highlight language-html">data-empty</code></dt>
|
||||
<dd>Custom empty-state text on the menu element.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
+168
-58
@@ -8,13 +8,25 @@ toc:
|
||||
children:
|
||||
- label: HTML
|
||||
id: usage-html
|
||||
- label: HTML structure
|
||||
id: usage-html-structure
|
||||
- label: Jinja and Nunjucks
|
||||
id: usage-macro
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Scrollable content
|
||||
- label: Custom Close Button
|
||||
id: example-custom-close-button
|
||||
- label: No Close Button
|
||||
id: example-no-close-button
|
||||
- label: Sticky Footer
|
||||
id: example-sticky-footer
|
||||
- label: Scrollable Content
|
||||
id: example-scrollable-content
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
- label: API Reference
|
||||
id: api-reference
|
||||
---
|
||||
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
@@ -32,17 +44,16 @@ toc:
|
||||
description="Make changes to your profile here. Click save when you're done.",
|
||||
trigger="Edit Profile",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
dialog_attrs={"class": "w-full sm:max-w-[425px] max-h-[612px]"},
|
||||
footer=footer
|
||||
) %}
|
||||
<form class="form grid gap-4">
|
||||
<form class="grid gap-4">
|
||||
<div class="grid gap-3">
|
||||
<label for="demo-dialog-edit-profile-name">Name</label>
|
||||
<input type="text" value="Pedro Duarte" id="demo-dialog-edit-profile-name" autofocus/>
|
||||
<label class="label" for="demo-dialog-edit-profile-name">Name</label>
|
||||
<input class="input" type="text" value="Pedro Duarte" id="demo-dialog-edit-profile-name" autofocus>
|
||||
</div>
|
||||
<div class="grid gap-3">
|
||||
<label for="demo-dialog-edit-profile-username">Username</label>
|
||||
<input type="text" value="@peduarte" id="demo-dialog-edit-profile-username" />
|
||||
<label class="label" for="demo-dialog-edit-profile-username">Username</label>
|
||||
<input class="input" type="text" value="@peduarte" id="demo-dialog-edit-profile-username">
|
||||
</div>
|
||||
</form>
|
||||
{% endcall %}
|
||||
@@ -52,41 +63,42 @@ toc:
|
||||
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Basecoat uses the native <code><dialog></code> element and <code>showModal()</code>. This differs from shadcn/ui's portalled Base UI implementation, but preserves native modality, focus handling, and inert page content without component JavaScript.</p>
|
||||
</section>
|
||||
|
||||
<h3 id="usage-html"><a href="#usage-html">HTML</a></h3>
|
||||
|
||||
{{ code_block(code_html | prettyHtml, class="") }}
|
||||
|
||||
<h3 id="usage-html-structure"><a href="#usage-html-structure">HTML structure</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>The component has the following HTML structure:</p>
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><button type="button"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>The trigger button to open the dialog (using <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal" target="_blank">the native <code>showModal()</code> method</a>).</dd>
|
||||
<dt><code class="highlight language-html"><button type="button" onclick="dialog.showModal()"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Trigger button. Basecoat intentionally uses the native <code>HTMLDialogElement.showModal()</code> method.</dd>
|
||||
<dt><code class="highlight language-html"><dialog class="dialog" id="{ DIALOG_ID }"></code></dt>
|
||||
<dd>Wraps the whole component and acts as the backdrop. It can also have the following attributes:
|
||||
<ul>
|
||||
<li><code>aria-labelledby="{ TITLE_ID }"</code>: linked to by the <code>aria-labelledby</code> attribute of the <code><dialog></code>.</li>
|
||||
<li><code>aria-describedby="{ DESCRIPTION_ID }"</code>: linked to by the <code>aria-describedby</code> attribute of the <code><dialog></code>.</li>
|
||||
<li><code>onclick</code>: closes the dialog when the backdrop is clicked.</li>
|
||||
</ul>
|
||||
<dd>Native modal dialog. Add <code>aria-labelledby="{ TITLE_ID }"</code> and <code>aria-describedby="{ DESCRIPTION_ID }"</code> when title and description are present. The macro also adds backdrop-click close handling.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><div></code></dt>
|
||||
<dd>Wraps the content of the dialog.
|
||||
<dd>Dialog content surface.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><header></code></dt>
|
||||
<dd>Contains the header for the dialog:
|
||||
<dt><code class="highlight language-html"><header></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Dialog header.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><h2></code></dt>
|
||||
<dd>The title of the dialog, must have an <code>id</code> if you use the <code>aria-labelledby</code> attribute on the <code><dialog></code>.</dd>
|
||||
<dt><code class="highlight language-html"><p></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>The description of the dialog, must have an <code>id</code> if you use the <code>aria-describedby</code> attribute on the <code><dialog></code>.</dd>
|
||||
<dt><code class="highlight language-html"><h2 id="{ TITLE_ID }"></code></dt>
|
||||
<dd>Dialog title. Reference it from <code>aria-labelledby</code>.</dd>
|
||||
<dt><code class="highlight language-html"><p id="{ DESCRIPTION_ID }"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Dialog description. Reference it from <code>aria-describedby</code>.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code class="highlight language-html"><section></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>For the content.</dd>
|
||||
<dd>Dialog body/content area. Add overflow utilities when the body should scroll.</dd>
|
||||
<dt><code class="highlight language-html"><footer></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Usually contains actions.</dd>
|
||||
<dd>Action area. It stacks actions on small screens and aligns them to the end on larger screens.</dd>
|
||||
<dt><code class="highlight language-html"><button type="button" onclick="this.closest('dialog').close()"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>A button to close the dialog. Alternatively, you can wrap a button in a <code><form method="dialog"></code> element.</dd>
|
||||
<dd>Close button. You can also wrap a button in <code><form method="dialog"></code>.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
@@ -101,7 +113,7 @@ toc:
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
<a class="badge-outline" href="/installation/#install-macros" target="_blank">
|
||||
<a class="badge-outline" href="/cli#macros" target="_blank">
|
||||
Use Nunjucks or Jinja macros
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
@@ -115,63 +127,161 @@ toc:
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% set raw_code %}{% raw %}{% set code_html %}
|
||||
{% set footer %}
|
||||
{% set raw_code %}{% raw %}{% 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",
|
||||
id="dialog-edit-profile",
|
||||
title="Edit profile",
|
||||
description="Make changes to your profile here. Click save when you're done.",
|
||||
description="Make changes to your profile here.",
|
||||
trigger="Edit Profile",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
dialog_attrs={"class": "w-full sm:max-w-[425px] max-h-[612px]"},
|
||||
footer=footer
|
||||
) %}
|
||||
<form class="form grid gap-4">
|
||||
<div class="grid gap-3">
|
||||
<label for="demo-dialog-edit-profile-name">Name</label>
|
||||
<input type="text" value="Pedro Duarte" id="demo-dialog-edit-profile-name" />
|
||||
</div>
|
||||
<div class="grid gap-3">
|
||||
<label for="demo-dialog-edit-profile-username">Username</label>
|
||||
<input type="text" value="@peduarte" id="demo-dialog-edit-profile-username" />
|
||||
</div>
|
||||
</form>
|
||||
{% endcall %}{% endraw %}{% endset%}
|
||||
<form class="grid gap-4">...</form>
|
||||
{% endcall %}{% endraw %}{% endset %}
|
||||
{{ code_block(raw_code, "jinja") }}
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-scrollable-content"><a href="#example-scrollable-content">Scrollable content</a></h3>
|
||||
<h3 id="example-custom-close-button"><a href="#example-custom-close-button">Custom Close Button</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{% set footer %}
|
||||
<button class="btn-outline" onclick="this.closest('dialog').close()">Close</button>
|
||||
<button class="btn" onclick="this.closest('dialog').close()">Continue</button>
|
||||
{% endset %}
|
||||
{% call dialog(
|
||||
id="dialog-custom-close",
|
||||
title="Custom close button",
|
||||
description="The default close icon is hidden and the footer provides the close action.",
|
||||
trigger="Open",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
close_button=false,
|
||||
footer=footer
|
||||
) %}
|
||||
<p class="text-sm text-muted-foreground">Use <code>close_button=false</code> and add a close control where it fits your layout.</p>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("dialog-custom-close", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-no-close-button"><a href="#example-no-close-button">No Close Button</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{% set footer %}
|
||||
<button class="btn" onclick="this.closest('dialog').close()">I understand</button>
|
||||
{% endset %}
|
||||
{% call dialog(
|
||||
id="dialog-no-close",
|
||||
title="No close button",
|
||||
description="The dialog can still be closed with Escape or a custom action.",
|
||||
trigger="Open",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
close_button=false,
|
||||
footer=footer
|
||||
) %}
|
||||
<p class="text-sm text-muted-foreground">Native dialogs remain keyboard-accessible even without the default close icon.</p>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("dialog-no-close", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-sticky-footer"><a href="#example-sticky-footer">Sticky Footer</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{% set footer %}
|
||||
<button class="btn-outline" onclick="this.closest('dialog').close()">Cancel</button>
|
||||
<button class="btn" onclick="this.closest('dialog').close()">Save</button>
|
||||
{% endset %}
|
||||
{% call dialog(
|
||||
id="dialog-sticky-footer",
|
||||
title="Sticky footer",
|
||||
description="Keep actions visible while the body scrolls.",
|
||||
trigger="Sticky Footer",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
body_attrs={"class": "overflow-y-auto scrollbar"},
|
||||
footer_attrs={"class": "sticky bottom-0"},
|
||||
footer=footer
|
||||
) %}
|
||||
<div class="space-y-4 text-sm text-muted-foreground">
|
||||
{% for i in range(0, 8) %}
|
||||
<p>Dialog content row {{ i + 1 }}. Add enough content to make the body scroll while the footer remains available.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("dialog-sticky-footer", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-scrollable-content"><a href="#example-scrollable-content">Scrollable Content</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{% set footer %}
|
||||
<button class="btn-outline" onclick="this.closest('dialog').close()">Close</button>
|
||||
{% endset %}
|
||||
{% call dialog(
|
||||
id="dialog-example",
|
||||
id="dialog-scrollable",
|
||||
title="Scrollable Content",
|
||||
description="This is a dialog with scrollable content.",
|
||||
description="Long content can scroll inside the dialog body.",
|
||||
trigger="Scrollable Content",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
dialog_attrs={"class": "w-full sm:max-w-[425px] max-h-[612px]"},
|
||||
body_attrs={"class": "overflow-y-auto scrollbar"},
|
||||
footer=footer
|
||||
) %}
|
||||
<div class="space-y-4 text-sm">
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
||||
<div class="space-y-4 text-sm text-muted-foreground">
|
||||
{% for i in range(0, 10) %}
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer non nibh sit amet augue commodo ultrices. Row {{ i + 1 }}.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("dialog-scrollable-content", code | prettyHtml) }}
|
||||
{{ code_preview("dialog-scrollable-content", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Dialog positioning and close button placement use logical properties. Set <code>dir="rtl"</code> on the dialog or a parent element.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}
|
||||
<div dir="rtl">
|
||||
{% set footer %}
|
||||
<button class="btn-outline" onclick="this.closest('dialog').close()">إلغاء</button>
|
||||
<button class="btn" onclick="this.closest('dialog').close()">حفظ</button>
|
||||
{% endset %}
|
||||
{% call dialog(
|
||||
id="dialog-rtl",
|
||||
title="تعديل الملف الشخصي",
|
||||
description="قم بتحديث بياناتك ثم احفظ التغييرات.",
|
||||
trigger="فتح",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
footer=footer
|
||||
) %}
|
||||
<form class="grid gap-4">
|
||||
<div class="grid gap-3">
|
||||
<label class="label" for="dialog-rtl-name">الاسم</label>
|
||||
<input class="input" id="dialog-rtl-name" value="ليلى">
|
||||
</div>
|
||||
</form>
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endset %}
|
||||
{{ code_preview("dialog-rtl", code | prettyHtml) }}
|
||||
|
||||
<h2 id="api-reference"><a href="#api-reference">API Reference</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<dl>
|
||||
<dt><code class="highlight language-html">.dialog</code></dt>
|
||||
<dd>Native dialog root. It owns the backdrop through <code>::backdrop</code> and the open state through the native <code>open</code> attribute.</dd>
|
||||
<dt><code class="highlight language-html">.dialog > div</code></dt>
|
||||
<dd>Dialog content surface. This maps to shadcn/ui's <code>DialogContent</code>.</dd>
|
||||
<dt><code class="highlight language-html">.dialog > div > header</code></dt>
|
||||
<dd>Header area. Direct <code>h2</code> and <code>p</code> children are styled as title and description.</dd>
|
||||
<dt><code class="highlight language-html">.dialog > div > section</code></dt>
|
||||
<dd>Body/content area. Add utilities such as <code>overflow-y-auto</code> when you need scrolling.</dd>
|
||||
<dt><code class="highlight language-html">.dialog > div > footer</code></dt>
|
||||
<dd>Action area. Use Basecoat button classes for actions.</dd>
|
||||
<dt><code class="highlight language-html">close_button=false</code></dt>
|
||||
<dd>Macro option to hide the generated close icon.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
@@ -9,7 +9,7 @@ toc:
|
||||
- label: HTML + JavaScript
|
||||
id: usage-html-js
|
||||
children:
|
||||
- label: "Step 1: Include the JavaScript files"
|
||||
- label: "Step 1: Include the JavaScript file"
|
||||
id: usage-html-js-1
|
||||
- label: "Step 2: Add your dropdown menu HTML"
|
||||
id: usage-html-js-2
|
||||
@@ -22,10 +22,22 @@ toc:
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Basic
|
||||
id: example-basic
|
||||
- label: Shortcuts
|
||||
id: example-shortcuts
|
||||
- label: Icons
|
||||
id: example-icons
|
||||
- label: Checkboxes
|
||||
id: example-checkboxes
|
||||
- label: Radio Group
|
||||
id: example-radio-group
|
||||
- label: Destructive
|
||||
id: example-destructive
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
- label: API Reference
|
||||
id: api-reference
|
||||
---
|
||||
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
@@ -39,50 +51,39 @@ toc:
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
popover_attrs={"class": "min-w-56"}
|
||||
) %}
|
||||
<div role="group" aria-labelledby="account-options">
|
||||
<div role="heading" id="account-options">My Account</div>
|
||||
<div role="group" aria-labelledby="demo-dropdown-account">
|
||||
<div role="heading" id="demo-dropdown-account">My Account</div>
|
||||
<div role="menuitem">
|
||||
Profile
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⇧⌘P</span>
|
||||
<span data-shortcut>⇧⌘P</span>
|
||||
</div>
|
||||
<div role="menuitem">
|
||||
Billing
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⌘B</span>
|
||||
</div>
|
||||
<div role="menuitem">
|
||||
Settings
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⌘S</span>
|
||||
</div>
|
||||
<div role="menuitem">
|
||||
Keyboard shortcuts
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⌘K</span>
|
||||
<span data-shortcut>⌘B</span>
|
||||
</div>
|
||||
<div role="menuitem">Settings</div>
|
||||
<div role="menuitem">Keyboard shortcuts</div>
|
||||
</div>
|
||||
<hr role="separator">
|
||||
<div role="menuitem">
|
||||
GitHub
|
||||
</div>
|
||||
<div role="menuitem">
|
||||
Support
|
||||
</div>
|
||||
<div role="menuitem" aria-disabled="true">
|
||||
API
|
||||
</div>
|
||||
<div role="menuitem">GitHub</div>
|
||||
<div role="menuitem">Support</div>
|
||||
<div role="menuitem" aria-disabled="true">API</div>
|
||||
<hr role="separator">
|
||||
<div role="menuitem">
|
||||
Logout
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⇧⌘P</span>
|
||||
</div>
|
||||
<div role="menuitem">Log out</div>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("dropdown-menu", code_html | prettyHtml) }}
|
||||
|
||||
<h2 id="usage">Usage</h2>
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Basecoat dropdown menus are inline-positioned relative to the <code>.dropdown-menu</code> wrapper. This differs from shadcn/ui's portalled Base UI implementation, but keeps the markup dependency-free and matches Basecoat's current popover/select positioning model.</p>
|
||||
</section>
|
||||
|
||||
<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 files</a></h4>
|
||||
<h4 id="usage-html-js-1"><a href="#usage-html-js-1">Step 1: Include the JavaScript file</a></h4>
|
||||
|
||||
<section class="prose">
|
||||
<p>You can either <a href="/installation/#install-cdn-all">include the JavaScript file for all the components</a>, or just the one for this component by adding this to the <code><head></code> of your page:</p>
|
||||
@@ -97,7 +98,7 @@ toc:
|
||||
Components with JavaScript
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="/installation/#install-cli">
|
||||
<a class="badge-outline" href="/cli">
|
||||
Use the CLI
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
@@ -116,43 +117,34 @@ toc:
|
||||
<section class="prose">
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><div class="dropdown-menu"></code></dt>
|
||||
<dd>Wraps around the entire component.
|
||||
<dd>Relative wrapper for the trigger and inline menu content.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><button type="button" popovertarget="{ POPOVER_ID }"></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="menu"</code>: indicates that the button opens a menu.</li>
|
||||
<li><code>aria-controls="{ MENU_ID }"</code>: points to the menu's id.</li>
|
||||
<li><code>aria-expanded="false"</code>: tracks the popover's state.</li>
|
||||
</ul>
|
||||
<dt><code class="highlight language-html"><button aria-haspopup="menu" aria-expanded="false"></code></dt>
|
||||
<dd>Trigger button. The script toggles <code>aria-expanded</code> and manages keyboard navigation.</dd>
|
||||
<dt><code class="highlight language-html"><div data-popover aria-hidden="true"></code></dt>
|
||||
<dd>Menu content popover. Set <code>data-side="top|right|bottom|left"</code> and <code>data-align="start|center|end"</code> to control placement.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><div role="menu"></code></dt>
|
||||
<dd>Container for menu items, groups, labels, and separators.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><div role="group" aria-labelledby="{ HEADING_ID }"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Groups related menu items.</dd>
|
||||
<dt><code class="highlight language-html"><div role="heading" id="{ HEADING_ID }"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Group heading/label.</dd>
|
||||
<dt><code class="highlight language-html"><div role="menuitem"></code></dt>
|
||||
<dd>Standard action item. Use <code>aria-disabled="true"</code> for disabled items.</dd>
|
||||
<dt><code class="highlight language-html"><div role="menuitemcheckbox" aria-checked="true"></code></dt>
|
||||
<dd>Checkbox-style item. Add a child with <code>data-indicator</code> for the checked icon.</dd>
|
||||
<dt><code class="highlight language-html"><div role="menuitemradio" aria-checked="true"></code></dt>
|
||||
<dd>Radio-style item. Add a child with <code>data-indicator</code> for the selected icon.</dd>
|
||||
<dt><code class="highlight language-html"><span data-shortcut></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Shortcut hint aligned to the inline end of the item.</dd>
|
||||
<dt><code class="highlight language-html"><hr role="separator"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Separator between groups or options.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code class="highlight language-html"><div popover class="popover" id="{ POPOVER_ID }"></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"><div role="menu"></code></dt>
|
||||
<dd>The menu containing the options. Should have the following attributes:
|
||||
<ul>
|
||||
<li><code>id="{ MENU_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"><button role="menuitem"></code></dt>
|
||||
<dd>Menu item.</dd>
|
||||
<dt><code class="highlight language-html"><button role="menuitemcheckbox"></code></dt>
|
||||
<dd>Menu item with a checkbox.</dd>
|
||||
<dt><code class="highlight language-html"><button role="menuitemradio"></code></dt>
|
||||
<dd>Menu item with a radio button.</dd>
|
||||
<dt><code class="highlight language-html"><hr role="separator"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Separator between groups/options.</dd>
|
||||
<dt><code class="highlight language-html"><div role="group"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Group of options, can have a <code>aria-labelledby</code> attribute to link to a heading.</dd>
|
||||
<dt><code class="highlight language-html"><span role="heading"></code></dt>
|
||||
<dd>Group heading, must have an <code>id</code> attribute if you use the <code>aria-labelledby</code> attribute on the group.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
@@ -163,9 +155,9 @@ toc:
|
||||
<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>
|
||||
<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 popover opens, the component dispatches a custom (non-bubbling) <code>basecoat:popover</code> event on <code>document</code>. Other popover components (Combobox, Dropdown Menu, Popover and Select) listen for this to close any open popovers.</dd>
|
||||
<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>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
@@ -176,7 +168,7 @@ toc:
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
<a class="badge-outline" href="/installation/#install-macros" target="_blank">
|
||||
<a class="badge-outline" href="/cli#macros" target="_blank">
|
||||
Use Nunjucks or Jinja macros
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
@@ -198,128 +190,111 @@ toc:
|
||||
) %}
|
||||
<div role="group" aria-labelledby="account-options">
|
||||
<div role="heading" id="account-options">My Account</div>
|
||||
<div role="menuitem">
|
||||
Profile
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⇧⌘P</span>
|
||||
</div>
|
||||
<div role="menuitem">
|
||||
Billing
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⌘B</span>
|
||||
</div>
|
||||
<div role="menuitem">
|
||||
Settings
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⌘S</span>
|
||||
</div>
|
||||
<div role="menuitem">
|
||||
Keyboard shortcuts
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⌘K</span>
|
||||
</div>
|
||||
<div role="menuitem">Profile</div>
|
||||
<div role="menuitem">Billing</div>
|
||||
</div>
|
||||
<hr role="separator">
|
||||
<div role="menuitem">
|
||||
GitHub
|
||||
</div>
|
||||
<div role="menuitem">
|
||||
Support
|
||||
</div>
|
||||
<div role="menuitem" disabled>
|
||||
API
|
||||
</div>
|
||||
<hr role="separator">
|
||||
<div role="menuitem">
|
||||
Logout
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⇧⌘P</span>
|
||||
</div>
|
||||
{% endcall %}{% endraw %}{% endset%}
|
||||
<div role="menuitem">Team</div>
|
||||
<div role="menuitem">Subscription</div>
|
||||
{% endcall %}{% endraw %}{% endset %}
|
||||
{{ code_block(raw_code, "jinja") }}
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-checkboxes"><a href="#example-checkboxes">Checkboxes</a></h3>
|
||||
<h3 id="example-basic"><a href="#example-basic">Basic</a></h3>
|
||||
|
||||
{% set code %}{% call dropdown_menu(
|
||||
id="dropdown-menu-checkboxes",
|
||||
{% set code %}
|
||||
{% call dropdown_menu(
|
||||
id="dropdown-basic",
|
||||
trigger="Open",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
popover_attrs={"class": "min-w-56"}
|
||||
) %}
|
||||
<div role="group" aria-labelledby="account-options">
|
||||
<div role="heading" id="account-options">Account Options</div>
|
||||
<div role="menuitem">
|
||||
{% lucide "user" %}
|
||||
Profile
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⇧⌘P</span>
|
||||
</div>
|
||||
<div role="menuitem">
|
||||
{% lucide "credit-card" %}
|
||||
Billing
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⌘B</span>
|
||||
</div>
|
||||
<div role="menuitem">
|
||||
{% lucide "settings" %}
|
||||
Settings
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⌘S</span>
|
||||
</div>
|
||||
<div role="group" aria-labelledby="dropdown-basic-account">
|
||||
<div role="heading" id="dropdown-basic-account">My Account</div>
|
||||
<div role="menuitem">Profile</div>
|
||||
<div role="menuitem">Billing</div>
|
||||
<div role="menuitem">Settings</div>
|
||||
</div>
|
||||
<hr role="separator">
|
||||
<div role="group" aria-labelledby="appearance-options">
|
||||
<div role="heading" id="appearance-options">Appearance</div>
|
||||
<div
|
||||
role="menuitemcheckbox"
|
||||
aria-checked="true"
|
||||
class="group"
|
||||
>
|
||||
{% lucide "check", {
|
||||
"class": "invisible group-aria-checked:visible",
|
||||
"aria-hidden": "true",
|
||||
"focusable": "false"
|
||||
} %}
|
||||
<div role="menuitem">Team</div>
|
||||
<div role="menuitem" aria-disabled="true">API</div>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("dropdown-menu-basic", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-shortcuts"><a href="#example-shortcuts">Shortcuts</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{% call dropdown_menu(
|
||||
id="dropdown-shortcuts",
|
||||
trigger="Open",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
popover_attrs={"class": "min-w-56"}
|
||||
) %}
|
||||
<div role="menuitem">New Tab <span data-shortcut>⌘T</span></div>
|
||||
<div role="menuitem">New Window <span data-shortcut>⌘N</span></div>
|
||||
<div role="menuitem">New Private Window <span data-shortcut>⇧⌘N</span></div>
|
||||
<hr role="separator">
|
||||
<div role="menuitem">Print <span data-shortcut>⌘P</span></div>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("dropdown-menu-shortcuts", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-icons"><a href="#example-icons">Icons</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{% call dropdown_menu(
|
||||
id="dropdown-icons",
|
||||
trigger="Open",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
popover_attrs={"class": "min-w-56"}
|
||||
) %}
|
||||
<div role="menuitem">{% lucide "user" %} Profile</div>
|
||||
<div role="menuitem">{% lucide "credit-card" %} Billing</div>
|
||||
<div role="menuitem">{% lucide "settings" %} Settings</div>
|
||||
<hr role="separator">
|
||||
<div role="menuitem">{% lucide "log-out" %} Log out</div>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("dropdown-menu-icons", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-checkboxes"><a href="#example-checkboxes">Checkboxes</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{% call dropdown_menu(
|
||||
id="dropdown-checkboxes",
|
||||
trigger="Open",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
popover_attrs={"class": "min-w-56"}
|
||||
) %}
|
||||
<div role="group" aria-labelledby="dropdown-checkboxes-label">
|
||||
<div role="heading" id="dropdown-checkboxes-label">Appearance</div>
|
||||
<div role="menuitemcheckbox" aria-checked="true">
|
||||
<span data-indicator>{% lucide "check" %}</span>
|
||||
Status Bar
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⇧⌘P</span>
|
||||
<span data-shortcut>⌘S</span>
|
||||
</div>
|
||||
<div
|
||||
role="menuitemcheckbox"
|
||||
aria-checked="false"
|
||||
class="group"
|
||||
aria-disabled="true"
|
||||
>
|
||||
{% lucide "check", {
|
||||
"class": "invisible group-aria-checked:visible",
|
||||
"aria-hidden": "true",
|
||||
"focusable": "false"
|
||||
} %}
|
||||
<div role="menuitemcheckbox" aria-checked="false">
|
||||
<span data-indicator>{% lucide "check" %}</span>
|
||||
Activity Bar
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⌘B</span>
|
||||
<span data-shortcut>⌘A</span>
|
||||
</div>
|
||||
<div
|
||||
role="menuitemcheckbox"
|
||||
aria-checked="false"
|
||||
class="group"
|
||||
>
|
||||
{% lucide "check", {
|
||||
"class": "invisible group-aria-checked:visible",
|
||||
"aria-hidden": "true",
|
||||
"focusable": "false"
|
||||
} %}
|
||||
<div role="menuitemcheckbox" aria-checked="false" aria-disabled="true">
|
||||
<span data-indicator>{% lucide "check" %}</span>
|
||||
Panel
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⌘S</span>
|
||||
<span data-shortcut>⌘P</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr role="separator">
|
||||
<div role="menuitem">
|
||||
{% lucide "log-out" %}
|
||||
Logout
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⇧⌘P</span>
|
||||
</div>
|
||||
{% endcall %}
|
||||
<script>
|
||||
(() => {
|
||||
const dropdownMenu = document.querySelector('#dropdown-menu-checkboxes');
|
||||
const checkboxes = dropdownMenu.querySelectorAll('div[role="menuitemcheckbox"]');
|
||||
checkboxes.forEach(checkbox => {
|
||||
const dropdownMenu = document.querySelector('#dropdown-checkboxes');
|
||||
const checkboxes = dropdownMenu.querySelectorAll('[role="menuitemcheckbox"]');
|
||||
checkboxes.forEach((checkbox) => {
|
||||
checkbox.addEventListener('click', () => {
|
||||
const isChecked = checkbox.getAttribute('aria-checked') === 'true';
|
||||
checkbox.setAttribute('aria-checked', !isChecked);
|
||||
if (checkbox.getAttribute('aria-disabled') === 'true') return;
|
||||
checkbox.setAttribute('aria-checked', checkbox.getAttribute('aria-checked') !== 'true');
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -329,75 +304,111 @@ toc:
|
||||
|
||||
<h3 id="example-radio-group"><a href="#example-radio-group">Radio Group</a></h3>
|
||||
|
||||
{% set code %}{% call dropdown_menu(
|
||||
id="dropdown-menu-radio-group",
|
||||
{% set code %}
|
||||
{% call dropdown_menu(
|
||||
id="dropdown-radio-group",
|
||||
trigger="Open",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
popover_attrs={"class": "min-w-56"}
|
||||
) %}
|
||||
<div role="group" aria-labelledby="position-options">
|
||||
<span id="position-options" role="heading">Panel Position</span>
|
||||
<hr role="separator">
|
||||
<div
|
||||
role="menuitemradio"
|
||||
aria-checked="false"
|
||||
class="group"
|
||||
>
|
||||
<div class="size-4 flex items-center justify-center">
|
||||
<div
|
||||
class="size-2 rounded-full bg-foreground invisible group-aria-checked:visible"
|
||||
aria-hidden="true",
|
||||
focusable="false"
|
||||
></div>
|
||||
</div>
|
||||
Status Bar
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⇧⌘P</span>
|
||||
<div role="group" aria-labelledby="dropdown-radio-label">
|
||||
<div role="heading" id="dropdown-radio-label">Panel Position</div>
|
||||
<div role="menuitemradio" aria-checked="false">
|
||||
<span data-indicator>{% lucide "check" %}</span>
|
||||
Top
|
||||
</div>
|
||||
<div
|
||||
role="menuitemradio"
|
||||
aria-checked="true"
|
||||
class="group"
|
||||
>
|
||||
<div class="size-4 flex items-center justify-center">
|
||||
<div
|
||||
class="size-2 rounded-full bg-foreground invisible group-aria-checked:visible"
|
||||
aria-hidden="true",
|
||||
focusable="false"
|
||||
></div>
|
||||
</div>
|
||||
Activity Bar
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⌘B</span>
|
||||
<div role="menuitemradio" aria-checked="true">
|
||||
<span data-indicator>{% lucide "check" %}</span>
|
||||
Bottom
|
||||
</div>
|
||||
<div
|
||||
role="menuitemradio"
|
||||
aria-checked="false"
|
||||
class="group"
|
||||
>
|
||||
<div class="size-4 flex items-center justify-center">
|
||||
<div
|
||||
class="size-2 rounded-full bg-foreground invisible group-aria-checked:visible"
|
||||
aria-hidden="true",
|
||||
focusable="false"
|
||||
></div>
|
||||
</div>
|
||||
Panel
|
||||
<span class="text-muted-foreground ml-auto text-xs tracking-widest">⌘S</span>
|
||||
<div role="menuitemradio" aria-checked="false">
|
||||
<span data-indicator>{% lucide "check" %}</span>
|
||||
Right
|
||||
</div>
|
||||
</div>
|
||||
{% endcall %}
|
||||
<script>
|
||||
(() => {
|
||||
const dropdownMenu = document.querySelector('#dropdown-menu-radio-group');
|
||||
const radioButtons = dropdownMenu.querySelectorAll('div[role="menuitemradio"]');
|
||||
radioButtons.forEach(radioButton => {
|
||||
radioButton.addEventListener('click', () => {
|
||||
radioButtons.forEach(radioButton => {
|
||||
radioButton.setAttribute('aria-checked', 'false');
|
||||
});
|
||||
radioButton.setAttribute('aria-checked', 'true');
|
||||
const dropdownMenu = document.querySelector('#dropdown-radio-group');
|
||||
const radios = dropdownMenu.querySelectorAll('[role="menuitemradio"]');
|
||||
radios.forEach((radio) => {
|
||||
radio.addEventListener('click', () => {
|
||||
radios.forEach((item) => item.setAttribute('aria-checked', 'false'));
|
||||
radio.setAttribute('aria-checked', 'true');
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{% endset %}
|
||||
{{ code_preview("dropdown-menu", code | prettyHtml) }}
|
||||
{{ code_preview("dropdown-menu-radio-group", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-destructive"><a href="#example-destructive">Destructive</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{% call dropdown_menu(
|
||||
id="dropdown-destructive",
|
||||
trigger="Open",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
popover_attrs={"class": "min-w-56"}
|
||||
) %}
|
||||
<div role="menuitem">Edit</div>
|
||||
<div role="menuitem">Duplicate</div>
|
||||
<hr role="separator">
|
||||
<div role="menuitem" data-variant="destructive">
|
||||
{% lucide "trash-2" %}
|
||||
Delete
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("dropdown-menu-destructive", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Dropdown menus support document direction. Set <code>dir="rtl"</code> on the dropdown root or a parent element. Use logical alignment data attributes where possible.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}
|
||||
<div dir="rtl">
|
||||
{% call dropdown_menu(
|
||||
id="dropdown-rtl",
|
||||
trigger="فتح",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
popover_attrs={"class": "min-w-56", "data-align": "end"}
|
||||
) %}
|
||||
<div role="heading" id="dropdown-rtl-label">الحساب</div>
|
||||
<div role="menuitem">الملف الشخصي <span data-shortcut>⌘P</span></div>
|
||||
<div role="menuitem">الإعدادات <span data-shortcut>⌘S</span></div>
|
||||
<hr role="separator">
|
||||
<div role="menuitem" data-variant="destructive">حذف</div>
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endset %}
|
||||
{{ code_preview("dropdown-menu-rtl", code | prettyHtml) }}
|
||||
|
||||
<h2 id="api-reference"><a href="#api-reference">API Reference</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<dl>
|
||||
<dt><code class="highlight language-html">.dropdown-menu</code></dt>
|
||||
<dd>Root component class. Must wrap the trigger and the <code>[data-popover]</code> content.</dd>
|
||||
<dt><code class="highlight language-html">[data-popover]</code></dt>
|
||||
<dd>Inline menu popover. Supports <code>data-side</code> and <code>data-align</code> through the shared popover positioning rules.</dd>
|
||||
<dt><code class="highlight language-html">[role="menu"]</code></dt>
|
||||
<dd>Menu container referenced by the trigger's <code>aria-controls</code>.</dd>
|
||||
<dt><code class="highlight language-html">[role="menuitem"]</code></dt>
|
||||
<dd>Action item. Supports <code>aria-disabled="true"</code>, <code>data-inset</code>, and <code>data-variant="destructive"</code>.</dd>
|
||||
<dt><code class="highlight language-html">[role="menuitemcheckbox"]</code></dt>
|
||||
<dd>Checkbox item. Use <code>aria-checked="true|false"</code> and an optional <code>[data-indicator]</code> child.</dd>
|
||||
<dt><code class="highlight language-html">[role="menuitemradio"]</code></dt>
|
||||
<dd>Radio item. Use <code>aria-checked="true|false"</code> and an optional <code>[data-indicator]</code> child.</dd>
|
||||
<dt><code class="highlight language-html">[role="heading"]</code></dt>
|
||||
<dd>Group label. Supports <code>data-inset</code>.</dd>
|
||||
<dt><code class="highlight language-html">[role="separator"]</code></dt>
|
||||
<dd>Visual separator between groups or actions.</dd>
|
||||
<dt><code class="highlight language-html">[data-shortcut]</code></dt>
|
||||
<dd>Shortcut hint aligned to the inline end of a menu item.</dd>
|
||||
<dt><code class="highlight language-html">[data-indicator]</code></dt>
|
||||
<dd>Indicator slot for checkbox and radio menu items. It becomes visible when the item has <code>aria-checked="true"</code>.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
+115
-79
@@ -5,6 +5,11 @@ description: Display empty states with icons, titles, descriptions, and actions.
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
children:
|
||||
- label: HTML
|
||||
id: usage-html
|
||||
- label: HTML structure
|
||||
id: usage-html-structure
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
@@ -16,38 +21,33 @@ toc:
|
||||
id: example-avatar-group
|
||||
- label: Input Group
|
||||
id: example-input-group
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
|
||||
<div class="alert mb-6">
|
||||
{% lucide "circle-alert" %}
|
||||
<h2>There is no dedicated Empty component in Basecoat.</h2>
|
||||
</div>
|
||||
|
||||
{% set code_default %}
|
||||
<div class="flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12 text-neutral-800 dark:text-neutral-300">
|
||||
<header class="flex max-w-sm flex-col items-center gap-2 text-center">
|
||||
<div class="mb-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6">
|
||||
<section class="empty">
|
||||
<header>
|
||||
<figure>
|
||||
{% lucide "folder-code" %}
|
||||
</div>
|
||||
<h3 class="text-lg font-medium tracking-tight">No Projects Yet</h3>
|
||||
<p class="text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4">
|
||||
You haven't created any projects yet. Get started by creating your first project.
|
||||
</p>
|
||||
</figure>
|
||||
<h3>No Projects Yet</h3>
|
||||
<p>You haven't created any projects yet. Get started by creating your first project.</p>
|
||||
</header>
|
||||
<section class="flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance">
|
||||
<footer>
|
||||
<div class="flex gap-2">
|
||||
<button class="btn">Create Project</button>
|
||||
<button class="btn-outline">Import Project</button>
|
||||
</div>
|
||||
</section>
|
||||
<a href="#" class="inline-flex items-center justify-center whitespace-nowrap text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive underline-offset-4 hover:underline h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5 text-muted-foreground">
|
||||
Learn More
|
||||
{% lucide "arrow-up-right" %}
|
||||
</a>
|
||||
</div>
|
||||
<a href="#" class="btn-link text-muted-foreground">
|
||||
Learn More
|
||||
{% lucide "arrow-up-right" %}
|
||||
</a>
|
||||
</footer>
|
||||
</section>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("empty-default", code_default) }}
|
||||
@@ -55,8 +55,37 @@ toc:
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Empty states are pure HTML composition using Tailwind utility classes. Use flex layout utilities to center content, and compose with semantic elements like <code><header></code>, <code><h3></code>, <code><p></code>, and <code><button></code>.</p>
|
||||
<p>Browse the examples below and copy the pattern that fits your needs.</p>
|
||||
<p>Use <code>.empty</code> on a semantic container. The component styles common children like <code><header></code>, <code><figure></code>, headings, descriptions, and <code><footer></code>.</p>
|
||||
</section>
|
||||
|
||||
<h3 id="usage-html"><a href="#usage-html">HTML</a></h3>
|
||||
|
||||
{{ code_block(code_default) }}
|
||||
|
||||
<h3 id="usage-html-structure"><a href="#usage-html-structure">HTML structure</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>The component has the following HTML structure:</p>
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><section class="empty"></code></dt>
|
||||
<dd>The empty-state container.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><header></code></dt>
|
||||
<dd>Groups the media, title, and description.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><figure></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Icon, avatar, image, or avatar group. An icon-only figure is styled automatically with <code>:has(> svg:only-child)</code>.</dd>
|
||||
<dt><code class="highlight language-html"><h2></code>, <code class="highlight language-html"><h3></code>, or <code class="highlight language-html"><h4></code></dt>
|
||||
<dd>The empty-state title.</dd>
|
||||
<dt><code class="highlight language-html"><p></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>The empty-state description.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code class="highlight language-html"><section></code> or <code class="highlight language-html"><footer></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Actions and supporting content.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
@@ -64,20 +93,18 @@ toc:
|
||||
<h3 id="example-outline"><a href="#example-outline">Outline</a></h3>
|
||||
|
||||
{% set code_outline %}
|
||||
<div class="flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12 text-neutral-800 dark:text-neutral-300 border">
|
||||
<header class="flex max-w-sm flex-col items-center gap-3 text-center">
|
||||
<div class="mb-2 bg-muted text-foreground flex size-12 shrink-0 items-center justify-center rounded-lg [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-6">
|
||||
<section class="empty border">
|
||||
<header>
|
||||
<figure>
|
||||
{% lucide "cloud" %}
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold tracking-tight">Cloud Storage Empty</h3>
|
||||
<p class="text-muted-foreground text-sm/relaxed">
|
||||
Upload files to your cloud storage to access them anywhere.
|
||||
</p>
|
||||
</figure>
|
||||
<h3>Cloud Storage Empty</h3>
|
||||
<p>Upload files to your cloud storage to access them anywhere.</p>
|
||||
</header>
|
||||
<section class="flex w-full max-w-sm min-w-0 flex-col items-center gap-3">
|
||||
<footer>
|
||||
<button class="btn-sm-outline">Upload Files</button>
|
||||
</section>
|
||||
</div>
|
||||
</footer>
|
||||
</section>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("empty-outline", code_outline) }}
|
||||
@@ -85,20 +112,18 @@ toc:
|
||||
<h3 id="example-avatar"><a href="#example-avatar">Avatar</a></h3>
|
||||
|
||||
{% set code_avatar %}
|
||||
<div class="flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12 text-neutral-800 dark:text-neutral-300">
|
||||
<header class="flex max-w-sm flex-col items-center gap-3 text-center">
|
||||
<div class="mb-2 flex items-center justify-center">
|
||||
<img alt="@shadcn" src="https://github.com/shadcn.png" class="grayscale size-12 shrink-0 object-cover rounded-full">
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold tracking-tight">User Offline</h3>
|
||||
<p class="text-muted-foreground text-sm/relaxed">
|
||||
This user is currently offline. You can leave a message to notify them or try again later.
|
||||
</p>
|
||||
<section class="empty">
|
||||
<header>
|
||||
<figure class="size-12 rounded-full">
|
||||
<img alt="@shadcn" src="https://github.com/shadcn.png">
|
||||
</figure>
|
||||
<h3>User Offline</h3>
|
||||
<p>This user is currently offline. You can leave a message or try again later.</p>
|
||||
</header>
|
||||
<section class="flex w-full max-w-sm min-w-0 flex-col items-center gap-3">
|
||||
<footer>
|
||||
<button class="btn-sm">Leave Message</button>
|
||||
</section>
|
||||
</div>
|
||||
</footer>
|
||||
</section>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("empty-avatar", code_avatar) }}
|
||||
@@ -106,27 +131,23 @@ toc:
|
||||
<h3 id="example-avatar-group"><a href="#example-avatar-group">Avatar Group</a></h3>
|
||||
|
||||
{% set code_avatar_group %}
|
||||
<div class="flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12 text-neutral-800 dark:text-neutral-300">
|
||||
<header class="flex max-w-sm flex-col items-center gap-3 text-center">
|
||||
<div class="mb-2 flex items-center justify-center">
|
||||
<div class="flex -space-x-2 [&_img]:ring-background [&_img]:ring-2 [&_img]:grayscale [&_img]:size-12 [&_img]:shrink-0 [&_img]:object-cover [&_img]:rounded-full">
|
||||
<img alt="@hunvreus" src="https://github.com/hunvreus.png">
|
||||
<img alt="@shadcn" src="https://github.com/shadcn.png">
|
||||
<img alt="@adamwathan" src="https://github.com/adamwathan.png">
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold tracking-tight">No Team Members</h3>
|
||||
<p class="text-muted-foreground text-sm/relaxed">
|
||||
Invite your team to collaborate on this project.
|
||||
</p>
|
||||
<section class="empty">
|
||||
<header>
|
||||
<figure class="-space-x-2 [&_img]:ring-background [&_img]:ring-2 [&_img]:grayscale [&_img]:size-12 [&_img]:rounded-full">
|
||||
<img alt="@hunvreus" src="https://github.com/hunvreus.png">
|
||||
<img alt="@shadcn" src="https://github.com/shadcn.png">
|
||||
<img alt="@adamwathan" src="https://github.com/adamwathan.png">
|
||||
</figure>
|
||||
<h3>No Team Members</h3>
|
||||
<p>Invite your team to collaborate on this project.</p>
|
||||
</header>
|
||||
<section class="flex w-full max-w-sm min-w-0 flex-col items-center gap-3">
|
||||
<button class="btn-sm inline-flex items-center gap-2">
|
||||
<footer>
|
||||
<button class="btn-sm">
|
||||
{% lucide "plus" %}
|
||||
Invite Members
|
||||
</button>
|
||||
</section>
|
||||
</div>
|
||||
</footer>
|
||||
</section>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("empty-avatar-group", code_avatar_group) }}
|
||||
@@ -134,24 +155,39 @@ toc:
|
||||
<h3 id="example-input-group"><a href="#example-input-group">Input Group</a></h3>
|
||||
|
||||
{% set code_input_group %}
|
||||
<div class="flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg p-6 text-center text-balance md:p-12 text-neutral-800 dark:text-neutral-300">
|
||||
<header class="flex max-w-sm flex-col items-center gap-3 text-center">
|
||||
<h3 class="text-lg font-semibold tracking-tight">404 - Not Found</h3>
|
||||
<p class="text-muted-foreground text-sm/relaxed">
|
||||
The page you're looking for doesn't exist. Try searching for what you need below.
|
||||
</p>
|
||||
<section class="empty">
|
||||
<header>
|
||||
<h3>404 - Not Found</h3>
|
||||
<p>The page you're looking for doesn't exist. Try searching for what you need below.</p>
|
||||
</header>
|
||||
<section class="flex w-full max-w-md min-w-0 flex-col items-center gap-3">
|
||||
<div class="relative w-3/4">
|
||||
<input type="text" class="input pl-8.5 pr-10" placeholder="Try searching for pages...">
|
||||
{% lucide "search" , { "class": "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground pointer-events-none shrink-0 size-4" } %}
|
||||
<kbd class="kbd absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none">/</kbd>
|
||||
<footer>
|
||||
<div class="input-group w-full">
|
||||
{% lucide "search" %}
|
||||
<input type="search" placeholder="Try searching for pages...">
|
||||
<kbd class="kbd">/</kbd>
|
||||
</div>
|
||||
<p class="text-muted-foreground text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary">
|
||||
Need help? <a href="#">Contact support</a>
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
<p>Need help? <a href="#">Contact support</a></p>
|
||||
</footer>
|
||||
</section>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("empty-input-group", code_input_group) }}
|
||||
{{ code_preview("empty-input-group", code_input_group) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
{% set code_rtl %}
|
||||
<section class="empty" dir="rtl">
|
||||
<header>
|
||||
<figure>
|
||||
{% lucide "folder-code" %}
|
||||
</figure>
|
||||
<h3>لا توجد مشاريع بعد</h3>
|
||||
<p>ابدأ بإنشاء مشروعك الأول.</p>
|
||||
</header>
|
||||
<footer>
|
||||
<button class="btn-sm">إنشاء مشروع</button>
|
||||
</footer>
|
||||
</section>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("empty-rtl", code_rtl) }}
|
||||
|
||||
@@ -14,6 +14,8 @@ toc:
|
||||
id: example-textarea
|
||||
- label: Select
|
||||
id: example-select
|
||||
- label: Slider
|
||||
id: example-slider
|
||||
- label: Fieldset
|
||||
id: example-fieldset
|
||||
- label: Checkbox
|
||||
@@ -22,8 +24,14 @@ toc:
|
||||
id: example-radio
|
||||
- label: Switch
|
||||
id: example-switch
|
||||
- label: Choice Card
|
||||
id: example-choice-card
|
||||
- label: Field Group
|
||||
id: example-field-group
|
||||
- label: Responsive Layout
|
||||
id: example-responsive
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
@@ -292,25 +300,25 @@ toc:
|
||||
<p>Select the items you want to show on the desktop.</p>
|
||||
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="field">
|
||||
<div role="group" class="field">
|
||||
<label class="gap-3">
|
||||
<input type="checkbox">
|
||||
Hard disks
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div role="group" class="field">
|
||||
<label class="gap-3">
|
||||
<input type="checkbox">
|
||||
External disks
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div role="group" class="field">
|
||||
<label class="gap-3">
|
||||
<input type="checkbox">
|
||||
CDs, DVDs and iPods
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div role="group" class="field">
|
||||
<label class="gap-3">
|
||||
<input type="checkbox">
|
||||
Connected servers
|
||||
@@ -320,7 +328,7 @@ toc:
|
||||
|
||||
<hr role="separator">
|
||||
|
||||
<div class="field" data-orientation="horizontal">
|
||||
<div role="group" class="field" data-orientation="horizontal">
|
||||
{# <div class="flex w-full items-center gap-3"> #}
|
||||
<input id="sync-desktop-documents" type="checkbox" checked class="input self-start">
|
||||
<section>
|
||||
@@ -342,19 +350,19 @@ toc:
|
||||
<p>Yearly and lifetime plans offer significant savings.</p>
|
||||
|
||||
<div role="radiogroup" class="grid gap-3">
|
||||
<div class="field">
|
||||
<div role="group" class="field">
|
||||
<label class="gap-3">
|
||||
<input type="radio" name="subscription-plan" checked>
|
||||
Monthly ($9.99/month)
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div role="group" class="field">
|
||||
<label class="gap-3">
|
||||
<input type="radio" name="subscription-plan">
|
||||
Yearly ($99.99/year)
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div role="group" class="field">
|
||||
<label class="gap-3">
|
||||
<input type="radio" name="subscription-plan">
|
||||
Lifetime ($299.99)
|
||||
@@ -414,6 +422,34 @@ toc:
|
||||
|
||||
{{ code_preview("field-choice-card", code_choice_card, class="w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-field-group"><a href="#example-field-group">Field Group</a></h3>
|
||||
|
||||
{% set code_field_group %}
|
||||
<fieldset class="fieldset">
|
||||
<legend>Account</legend>
|
||||
<p>Use a field group to stack related fields with consistent spacing.</p>
|
||||
|
||||
<div role="group" aria-label="Field group">
|
||||
<div role="group" class="field">
|
||||
<label for="field-group-name">Name</label>
|
||||
<input id="field-group-name" type="text" placeholder="Evil Rabbit">
|
||||
</div>
|
||||
|
||||
<div class="field-separator">
|
||||
<hr role="separator">
|
||||
<span>Optional</span>
|
||||
</div>
|
||||
|
||||
<div role="group" class="field">
|
||||
<label for="field-group-bio">Bio</label>
|
||||
<textarea id="field-group-bio" rows="3" placeholder="Tell us about yourself"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("field-group", code_field_group, class="w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-responsive"><a href="#example-responsive">Responsive Layout</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
@@ -456,4 +492,27 @@ toc:
|
||||
</fieldset>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("field-responsive", code_responsive, class="w-full max-w-3xl") }}
|
||||
{{ code_preview("field-responsive", code_responsive, class="w-full max-w-3xl") }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Fields support right-to-left layouts through native form direction and logical spacing. Add <code>dir="rtl"</code> to the field, fieldset, or an ancestor.</p>
|
||||
</section>
|
||||
|
||||
{% set code_rtl %}
|
||||
<fieldset class="fieldset" dir="rtl">
|
||||
<legend>الملف الشخصي</legend>
|
||||
<p>تظهر هذه المعلومات في حسابك.</p>
|
||||
|
||||
<div role="group" aria-label="Field group">
|
||||
<div role="group" class="field">
|
||||
<label for="field-rtl-name">الاسم الكامل</label>
|
||||
<input id="field-rtl-name" type="text" placeholder="أرنب شرير">
|
||||
<p>اكتب اسمك الأول والأخير.</p>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("field-rtl", code_rtl, class="w-full max-w-md") }}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
---
|
||||
layout: layouts/page.njk
|
||||
title: Form
|
||||
description: Building forms with Basecoat components.
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
|
||||
{% set code %}<form class="form grid gap-6">
|
||||
<div class="grid gap-2">
|
||||
<label for="demo-form-text">Username</label>
|
||||
<input type="text" id="demo-form-text" placeholder="hunvreus">
|
||||
<p class="text-muted-foreground text-sm">This is your public display name.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<label for="demo-form-select">Email</label>
|
||||
<select id="demo-form-select">
|
||||
<option value="bob@example.com">m@example.com</option>
|
||||
<option value="alice@example.com">m@google.com</option>
|
||||
<option value="john@example.com">m@support.com</option>
|
||||
</select>
|
||||
<p class="text-muted-foreground text-sm">You can manage email addresses in your email settings.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<label for="demo-form-textarea">Bio</label>
|
||||
<textarea id="demo-form-textarea" placeholder="I like to..." rows="3"></textarea>
|
||||
<p class="text-muted-foreground text-sm">You can @mention other users and organizations.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<label for="demo-form-date">Date of birth</label>
|
||||
<input type="date" id="demo-form-date">
|
||||
<p class="text-muted-foreground text-sm">Your date of birth is used to calculate your age.</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-3">
|
||||
<label for="demo-form-radio">Notify me about...</label>
|
||||
<fieldset id="demo-form-radio" class="grid gap-3">
|
||||
<label class="font-normal"><input type="radio" name="demo-form-radio" value="1" checked>All new messages</label>
|
||||
<label class="font-normal"><input type="radio" name="demo-form-radio" value="2">Direct messages and mentions</label>
|
||||
<label class="font-normal"><input type="radio" name="demo-form-radio" value="3" >Nothing</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<section class="grid gap-4">
|
||||
<h3 class="text-lg font-medium">Email Notifications</h3>
|
||||
<div class="gap-2 flex flex-row items-start justify-between rounded-lg border p-4 shadow-xs">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<label for="demo-form-switch" class="leading-normal">Marketing emails</label>
|
||||
<p class="text-muted-foreground text-sm">Receive emails about new products, features, and more.</p>
|
||||
</div>
|
||||
<input type="checkbox" id="demo-form-switch" role="switch">
|
||||
</div>
|
||||
<div class="gap-2 flex flex-row items-start justify-between rounded-lg border p-4 shadow-xs">
|
||||
<div class="flex flex-col gap-0.5 opacity-60">
|
||||
<label for="demo-form-switch-disabled" class="leading-normal">Security emails</label>
|
||||
<p class="text-muted-foreground text-sm">Receive emails about your account security.</p>
|
||||
</div>
|
||||
<input type="checkbox" id="demo-form-switch-disabled" role="switch" disabled>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<button type="submit" class="btn">Submit</button>
|
||||
</form>{% endset %}
|
||||
{{ code_preview("input", code, "w-full max-w-sm") }}
|
||||
|
||||
<h2 id="usage">Usage</h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Adding the <code>form</code> class to the parent of any form element (<code><label></code>, <code><input></code>, <code><textarea></code>, <code><select></code>) will automatically apply the style you would get from adding <code>label</code>, <code>input</code>, <code>textarea</code>, or <code>select</code> classes. Read more about <a href="/components/label">Label</a>, <a href="/components/input">Input</a>, <a href="/components/textarea">Textarea</a>, and <a href="/components/select">Select</a>.</p>
|
||||
</section>
|
||||
|
||||
{{ code_block(code) }}
|
||||
+368
-241
@@ -1,329 +1,364 @@
|
||||
---
|
||||
layout: layouts/page.njk
|
||||
title: Input Group
|
||||
description: Display additional information or actions to an input or textarea.
|
||||
description: Add icons, text, buttons, and helper content to inputs and textareas.
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Align
|
||||
id: example-align
|
||||
children:
|
||||
- label: inline-start
|
||||
id: align-inline-start
|
||||
- label: inline-end
|
||||
id: align-inline-end
|
||||
- label: block-start
|
||||
id: align-block-start
|
||||
- label: block-end
|
||||
id: align-block-end
|
||||
- label: Icon
|
||||
id: example-icon
|
||||
- label: Text
|
||||
id: example-text
|
||||
- label: Button
|
||||
id: example-button
|
||||
- label: Tooltip
|
||||
id: example-tooltip
|
||||
- label: Textarea
|
||||
id: example-textarea
|
||||
- label: Spinner
|
||||
id: example-spinner
|
||||
- label: Kbd
|
||||
id: example-kbd
|
||||
- label: Dropdown
|
||||
id: example-dropdown
|
||||
- label: Group
|
||||
id: example-group
|
||||
- label: Spinner
|
||||
id: example-spinner
|
||||
- label: Textarea
|
||||
id: example-textarea
|
||||
- label: Custom Input
|
||||
id: example-custom-input
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "dropdown-menu.njk" import dropdown_menu %}
|
||||
{% from "popover.njk" import popover %}
|
||||
|
||||
<div class="alert mb-6">
|
||||
{% lucide "circle-alert" %}
|
||||
<h2>There is no dedicated Input Group component in Basecoat.</h2>
|
||||
</div>
|
||||
{% set spinner_icon %}{% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "animate-spin" } %}{% endset %}
|
||||
|
||||
{% set code_default %}
|
||||
<div class="grid gap-6">
|
||||
<div class="relative">
|
||||
<input type="text" class="input pl-9 pr-20" placeholder="Search...">
|
||||
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4">
|
||||
{% lucide "search" %}
|
||||
</div>
|
||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground text-sm">12 results</div>
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="Search...">
|
||||
<span data-align="start" aria-hidden="true">{% lucide "search" %}</span>
|
||||
<span data-align="end">12 results</span>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="text" class="input pl-15 pr-9" placeholder="example.com">
|
||||
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground text-sm">https://</div>
|
||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground [&>svg]:size-4" data-tooltip="This is content in a tooltip.">{% lucide "info" %}</div>
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="example.com">
|
||||
<span data-align="start">https://</span>
|
||||
<button type="button" class="btn-xs-icon-ghost rounded-full" data-align="end" aria-label="Info">
|
||||
{% lucide "info" %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<textarea class="textarea pr-10 min-h-27 pb-12" placeholder="Ask, Search or Chat..."></textarea>
|
||||
<footer role="group" class="absolute bottom-0 px-3 pb-3 pt-1.5 flex items-center w-full gap-2">
|
||||
<button type="button" class="btn-icon-outline rounded-full size-6 text-muted-foreground hover:text-accent-foreground">
|
||||
{% lucide "plus" %}
|
||||
</button>
|
||||
<div class="input-group min-h-32" data-orientation="vertical">
|
||||
<textarea placeholder="Ask, Search or Chat..."></textarea>
|
||||
<footer data-align="end" role="group" aria-label="Message actions">
|
||||
<button type="button" class="btn-xs-icon-outline rounded-full" aria-label="Add attachment">{% lucide "plus" %}</button>
|
||||
{% set trigger %}Auto{% endset %}
|
||||
{{ dropdown_menu(
|
||||
id="demo-dropdown-menu",
|
||||
trigger="Auto",
|
||||
trigger_attrs={"class": "btn-sm-ghost text-muted-foreground hover:text-accent-foreground h-6 p-2"},
|
||||
popover_attrs={"data-side": "top", "class": "min-w-32"},
|
||||
main_attrs={"class": "min-w-0"},
|
||||
trigger=trigger,
|
||||
trigger_attrs={"class": "btn-xs-ghost"},
|
||||
popover_attrs={"data-side": "top", "data-align": "start", "class": "min-w-28 [--radius:0.95rem]"},
|
||||
items=[
|
||||
{ type: "item", label: "Auto" },
|
||||
{ type: "item", label: "Agent" },
|
||||
{ type: "item", label: "Manual" }
|
||||
]
|
||||
) }}
|
||||
<div class="text-muted-foreground text-sm ml-auto">52% used</div>
|
||||
<hr class="w-0 h-4 border-r border-border shrink-0 m-0">
|
||||
<button type="button" class="btn-icon rounded-full size-6 bg-muted-foreground hover:bg-foreground" disabled>
|
||||
{% lucide "arrow-up" %}
|
||||
</button>
|
||||
<span class="ms-auto">52% used</span>
|
||||
<hr role="separator" class="h-4">
|
||||
<button type="button" class="btn-xs-icon rounded-full" disabled aria-label="Send">{% lucide "arrow-up" %}</button>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="text" class="input pr-9" placeholder="@shadcn">
|
||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none bg-primary text-primary-foreground flex size-4 items-center justify-center rounded-full [&>svg]:size-3">{% lucide "check" %}</div>
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="@shadcn">
|
||||
<span data-align="end" aria-hidden="true">
|
||||
<span class="flex size-4 items-center justify-center rounded-full bg-primary text-primary-foreground">
|
||||
{% lucide "check", { "class": "size-3" } %}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-default", code_default, class="w-full max-w-sm") }}
|
||||
{{ code_preview("input-group-default", code_default | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
{% set code_usage %}
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="Search...">
|
||||
<span data-align="start" aria-hidden="true">
|
||||
<!-- icon -->
|
||||
</span>
|
||||
<span data-align="end">12 results</span>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_block(code_usage | prettyHtml, "html") }}
|
||||
|
||||
<section class="prose">
|
||||
<p>Input groups are pure HTML composition using Tailwind utility classes. There is no dedicated component: use <code>relative</code> positioning on a wrapper, add your content with <code>absolute</code> positioning, and adjust input padding to make room.</p>
|
||||
<p>Browse the examples below and copy the pattern that fits your needs.</p>
|
||||
<p>Use <code>.input-group</code> on the root. The group owns the visible shell. Native controls stay semantic and become borderless children inside that shell.</p>
|
||||
<p>The component has the following HTML structure:</p>
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><div class="input-group"></code></dt>
|
||||
<dd>The root shell. Add <code>role="group"</code> and an accessible label only when the group itself needs to be named.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><input></code>, <code class="highlight language-html"><textarea></code>, <code class="highlight language-html"><select></code></dt>
|
||||
<dd>The native control. Use <code>aria-invalid="true"</code> for invalid state and <code>disabled</code> for disabled state.</dd>
|
||||
<dt><code class="highlight language-html"><span></code>, <code class="highlight language-html"><svg></code>, <code class="highlight language-html"><kbd></code></dt>
|
||||
<dd>Decorative or helper content. Use <code>aria-hidden="true"</code> for decorative icons.</dd>
|
||||
<dt><code class="highlight language-html"><button></code>, <code class="highlight language-html"><div role="group"></code></dt>
|
||||
<dd>Interactive actions. Use <code>role="group"</code> with an accessible label when multiple related actions share one side of the input.</dd>
|
||||
<dt><code class="highlight language-html"><header></code>, <code class="highlight language-html"><footer></code></dt>
|
||||
<dd>Header or footer addon rows for vertically oriented input groups.</dd>
|
||||
<dt><code class="highlight language-html">data-align="start|end"</code></dt>
|
||||
<dd>Optional visual placement. Values use logical direction, so start/end automatically flip in RTL. Use <code>data-orientation="vertical"</code> on the root for header/footer layouts.</dd>
|
||||
<dt><code class="highlight language-html">[data-control]</code></dt>
|
||||
<dd>Escape hatch for custom controls that are not native <code>input</code>, <code>textarea</code>, or <code>select</code>.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-align"><a href="#example-align">Align</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Use <code>data-align="start|end"</code> on addon wrappers to position them inline. Use <code>data-orientation="vertical"</code> with <code>header</code> or <code>footer</code> for block placement.</p>
|
||||
</section>
|
||||
|
||||
<h4 id="align-inline-start"><a href="#align-inline-start">inline-start</a></h4>
|
||||
|
||||
{% set code_align_inline_start %}
|
||||
<div class="field max-w-sm">
|
||||
<label for="inline-start-input">Input</label>
|
||||
<div class="input-group">
|
||||
<input id="inline-start-input" type="text" placeholder="Search...">
|
||||
<span role="group" data-align="start" aria-hidden="true">
|
||||
{% lucide "search", { "class": "text-muted-foreground" } %}
|
||||
</span>
|
||||
</div>
|
||||
<p>Icon positioned at the start.</p>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-inline-start", code_align_inline_start | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h4 id="align-inline-end"><a href="#align-inline-end">inline-end</a></h4>
|
||||
|
||||
{% set code_align_inline_end %}
|
||||
<div class="field max-w-sm">
|
||||
<label for="inline-end-input">Input</label>
|
||||
<div class="input-group">
|
||||
<input id="inline-end-input" type="password" placeholder="Enter password">
|
||||
<span role="group" data-align="end" aria-hidden="true">
|
||||
{% lucide "eye-off" %}
|
||||
</span>
|
||||
</div>
|
||||
<p>Icon positioned at the end.</p>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-inline-end", code_align_inline_end | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h4 id="align-block-start"><a href="#align-block-start">block-start</a></h4>
|
||||
|
||||
{% set code_align_block_start %}
|
||||
<div role="group" aria-label="Block start input group examples" class="fieldset max-w-sm">
|
||||
<div class="field">
|
||||
<label for="block-start-input">Input</label>
|
||||
<div class="input-group" data-orientation="vertical">
|
||||
<input id="block-start-input" type="text" placeholder="Enter your name">
|
||||
<header data-align="start">
|
||||
<span>Full Name</span>
|
||||
</header>
|
||||
</div>
|
||||
<p>Header positioned above the input.</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="block-start-textarea">Textarea</label>
|
||||
<div class="input-group" data-orientation="vertical">
|
||||
<textarea id="block-start-textarea" class="font-mono text-sm" placeholder="console.log('Hello, world!');"></textarea>
|
||||
<header data-align="start">
|
||||
{% lucide "file-code", { "class": "text-muted-foreground" } %}
|
||||
<span class="font-mono">script.js</span>
|
||||
<button type="button" class="btn-xs-icon-ghost ml-auto" aria-label="Copy">
|
||||
{% lucide "copy" %}
|
||||
</button>
|
||||
</header>
|
||||
</div>
|
||||
<p>Header positioned above the textarea.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-block-start", code_align_block_start | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h4 id="align-block-end"><a href="#align-block-end">block-end</a></h4>
|
||||
|
||||
{% set code_align_block_end %}
|
||||
<div role="group" aria-label="Block end input group examples" class="fieldset max-w-sm">
|
||||
<div class="field">
|
||||
<label for="block-end-input">Input</label>
|
||||
<div class="input-group" data-orientation="vertical">
|
||||
<input id="block-end-input" type="text" placeholder="Enter amount">
|
||||
<footer data-align="end">
|
||||
<span>USD</span>
|
||||
</footer>
|
||||
</div>
|
||||
<p>Footer positioned below the input.</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="block-end-textarea">Textarea</label>
|
||||
<div class="input-group" data-orientation="vertical">
|
||||
<textarea id="block-end-textarea" placeholder="Write a comment..."></textarea>
|
||||
<footer data-align="end">
|
||||
<span>0/280</span>
|
||||
<button type="button" class="btn-sm ml-auto">Post</button>
|
||||
</footer>
|
||||
</div>
|
||||
<p>Footer positioned below the textarea.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-block-end", code_align_block_end | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-icon"><a href="#example-icon">Icon</a></h3>
|
||||
|
||||
{% set code_icon %}
|
||||
<div class="grid gap-6">
|
||||
<div class="relative">
|
||||
<input type="text" class="input pl-9" placeholder="Search...">
|
||||
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4">
|
||||
{% lucide "search" %}
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="Search...">
|
||||
<span data-align="start" aria-hidden="true">{% lucide "search" %}</span>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="email" class="input pl-9" placeholder="Enter your email">
|
||||
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4">
|
||||
{% lucide "mail" %}
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="email" placeholder="Enter your email">
|
||||
<span data-align="start" aria-hidden="true">{% lucide "mail" %}</span>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="text" class="input px-9" placeholder="Card number">
|
||||
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4">
|
||||
{% lucide "credit-card" %}
|
||||
</div>
|
||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4">
|
||||
{% lucide "check" %}
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="Card number">
|
||||
<span data-align="start" aria-hidden="true">{% lucide "credit-card" %}</span>
|
||||
<span data-align="end" aria-hidden="true">{% lucide "check" %}</span>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="text" class="input pr-16" placeholder="Card number">
|
||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4 flex items-center gap-2">
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="Card number">
|
||||
<span data-align="end" aria-hidden="true">
|
||||
{% lucide "star" %}
|
||||
{% lucide "info" %}
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-icon-prefix", code_icon, class="w-full max-w-sm") }}
|
||||
{{ code_preview("input-group-icon", code_icon | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-text"><a href="#example-text">Text</a></h3>
|
||||
|
||||
{% set code_text %}
|
||||
<div class="grid gap-6">
|
||||
<div class="relative">
|
||||
<input type="text" class="input pl-7 pr-12" placeholder="0.00">
|
||||
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground text-sm">$</div>
|
||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground text-sm">USD</div>
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="0.00">
|
||||
<span data-align="start">$</span>
|
||||
<span data-align="end">USD</span>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="text" class="input pl-15 pr-12" placeholder="example.com">
|
||||
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground text-sm">https://</div>
|
||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground text-sm">.com</div>
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="example.com">
|
||||
<span data-align="start">https://</span>
|
||||
<span data-align="end">.com</span>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="text" class="input pr-30" placeholder="Enter your username">
|
||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground text-sm">@company.com</div>
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="Enter your username">
|
||||
<span data-align="end">@company.com</span>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<textarea class="textarea pr-10 min-h-25 pb-12" placeholder="Enter your message"></textarea>
|
||||
<footer role="group" class="absolute bottom-0 px-3 pb-3 pt-1.5 flex items-center w-full gap-2">
|
||||
<div class="text-muted-foreground text-sm">120 characters left</div>
|
||||
<div class="input-group" data-orientation="vertical">
|
||||
<textarea placeholder="Enter your message"></textarea>
|
||||
<footer data-align="end">
|
||||
<span class="text-xs text-muted-foreground">120 characters left</span>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-text", code_text, class="w-full max-w-sm") }}
|
||||
{{ code_preview("input-group-text", code_text | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-button"><a href="#example-button">Button</a></h3>
|
||||
|
||||
{% set code_button %}
|
||||
<div class="grid gap-6">
|
||||
<div class="relative">
|
||||
<input type="text" readonly class="input pr-9" value="https://x.com/hunvreus">
|
||||
<button
|
||||
data-copied="false"
|
||||
onclick="
|
||||
this.dataset.copied = 'true';
|
||||
setTimeout(() => this.dataset.copied = 'false', 2000);
|
||||
"
|
||||
class="group absolute right-1.5 top-1/2 -translate-y-1/2 btn-icon-ghost text-muted-foreground hover:text-accent-foreground size-6"
|
||||
>
|
||||
{% lucide "copy", { "class": "group-data-[copied=true]:hidden" } %}
|
||||
{% lucide "check", { "class": "hidden group-data-[copied=true]:block" } %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="relative">
|
||||
<input type="text" class="input pl-21 pr-9 rounded-full">
|
||||
<button
|
||||
aria-pressed="false"
|
||||
onclick="this.ariaPressed = this.ariaPressed === 'true' ? 'false' : 'true'"
|
||||
class="absolute right-1.5 top-1/2 -translate-y-1/2 btn-icon-ghost text-muted-foreground hover:text-accent-foreground size-6 rounded-full aria-pressed:bg-transparent aria-pressed:hover:bg-accent dark:aria-pressed:hover:bg-accent/50 aria-pressed:text-blue-600 aria-pressed:[&>svg]:fill-blue-600"
|
||||
>{% lucide "star" %}</button>
|
||||
<div class="absolute left-1.5 top-1/2 -translate-y-1/2 flex items-center gap-1 z-10">
|
||||
{% set trigger %}
|
||||
{% lucide "info" %}
|
||||
{% endset %}
|
||||
{% call popover(
|
||||
trigger=trigger,
|
||||
trigger_attrs={"class": "btn-sm-icon-ghost rounded-full size-6"},
|
||||
popover_attrs={"class": "max-w-72"}
|
||||
) %}
|
||||
<h3 class="font-medium mb-1">Your connection is not secure.</h3>
|
||||
<p>You should not enter any sensitive information on this site.</p>
|
||||
{% endcall %}
|
||||
<div class="text-muted-foreground text-sm pointer-events-none">https://</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="text" readonly class="input pr-9" placeholder="Type to search...">
|
||||
<button type="button" class="absolute right-1.5 top-1/2 -translate-y-1/2 btn-secondary h-6 p-2 rounded">Search</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-button", code_button, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-tooltip"><a href="#example-tooltip">Tooltip</a></h3>
|
||||
|
||||
{% set code_tooltip %}
|
||||
<div class="grid gap-6">
|
||||
<div class="relative">
|
||||
<input type="password" class="input pr-9" placeholder="Enter password">
|
||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground [&>svg]:size-4" data-tooltip="Password must be at least 8 characters long.">{% lucide "info" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="email" class="input pr-9" placeholder="Your email address">
|
||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground [&>svg]:size-4" data-tooltip="We'll use this to send your notifications.">{% lucide "circle-help" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="email" class="input pl-9" placeholder="Your email address">
|
||||
<div class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground [&>svg]:size-4" data-tooltip="Click for help with API keys." data-side="left">{% lucide "circle-help" %}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-tooltip", code_tooltip, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-textarea"><a href="#example-textarea">Textarea</a></h3>
|
||||
|
||||
{% set code_textarea %}
|
||||
<div class="relative">
|
||||
<textarea class="textarea pt-15 pb-17 min-h-77" placeholder="console.log('Hello, world!')."></textarea>
|
||||
<header class="absolute top-0 flex items-center w-full gap-2 p-3 border-b">
|
||||
<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="tabler-icon tabler-icon-brand-javascript size-4 text-muted-foreground"><path d="M20 4l-2 14.5l-6 2l-6 -2l-2 -14.5z"></path><path d="M7.5 8h3v8l-2 -1"></path><path d="M16.5 8h-2.5a.5 .5 0 0 0 -.5 .5v3a.5 .5 0 0 0 .5 .5h1.423a.5 .5 0 0 1 .495 .57l-.418 2.93l-2 .5"></path></svg>
|
||||
<span class="font-mono text-sm text-muted-foreground mr-auto">script.js</span>
|
||||
<button class="btn-sm-icon-ghost text-muted-foreground hover:text-accent-foreground size-6">
|
||||
{% lucide "refresh-ccw" %}
|
||||
</button>
|
||||
<button class="btn-sm-icon-ghost text-muted-foreground hover:text-accent-foreground size-6">
|
||||
<div class="input-group">
|
||||
<input type="text" readonly placeholder="https://x.com/shadcn">
|
||||
<button type="button" class="btn-xs-icon-ghost" data-align="end" aria-label="Copy" title="Copy">
|
||||
{% lucide "copy" %}
|
||||
</button>
|
||||
</header>
|
||||
<footer class="absolute bottom-0 flex items-center w-full gap-2 p-3 border-t">
|
||||
<span class="text-sm text-muted-foreground mr-auto">Line 1, Column 1</span>
|
||||
<button type="button" class="btn-sm">
|
||||
Run
|
||||
{% lucide "corner-down-left" %}
|
||||
</div>
|
||||
|
||||
<div class="input-group [--radius:9999px]">
|
||||
<button type="button" class="btn-xs-icon-secondary" data-align="start" aria-label="Connection information">
|
||||
{% lucide "info" %}
|
||||
</button>
|
||||
<span data-align="start" class="text-muted-foreground">https://</span>
|
||||
<input type="text" id="input-secure-19">
|
||||
<button type="button" class="btn-xs-icon-ghost" data-align="end" aria-label="Favorite">
|
||||
{% lucide "star" %}
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-textarea", code_textarea, class="w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-spinner"><a href="#example-spinner">Spinner</a></h3>
|
||||
|
||||
{% set code_spinner %}
|
||||
<div class="grid gap-6">
|
||||
<div class="relative">
|
||||
<input type="text" class="input pr-9" placeholder="Searching..." disabled>
|
||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4 opacity-50">
|
||||
{% lucide "loader-circle", { "class": "animate-spin" } %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="text" class="input pl-9" placeholder="Processing..." disabled>
|
||||
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4 opacity-50">
|
||||
{% lucide "loader-circle", { "class": "animate-spin" } %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="text" class="input pr-24" placeholder="Saving changes..." disabled>
|
||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-2 text-sm text-muted-foreground opacity-50">
|
||||
<div>Saving...</div>
|
||||
<div class="[&>svg]:size-4">
|
||||
{% lucide "loader-circle", { "class": "animate-spin" } %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="text" class="input pl-9 pr-26" placeholder="Refreshing data..." disabled>
|
||||
<div class="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4 opacity-50">
|
||||
{% lucide "loader", { "class": "animate-spin" } %}
|
||||
</div>
|
||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-2 text-sm text-muted-foreground opacity-50">
|
||||
Please wait...
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="Type to search...">
|
||||
<button type="button" class="btn-secondary" data-align="end">Search</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-spinner", code_spinner, class="w-full max-w-md") }}
|
||||
{{ code_preview("input-group-button", code_button | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-kbd"><a href="#example-kbd">Kbd</a></h3>
|
||||
|
||||
{% set code_kbd %}
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="Search...">
|
||||
<span data-align="start" aria-hidden="true">{% lucide "search" %}</span>
|
||||
<kbd data-align="end">⌘K</kbd>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-kbd", code_kbd | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-dropdown"><a href="#example-dropdown">Dropdown</a></h3>
|
||||
|
||||
{% set code_dropdown %}
|
||||
<div class="grid gap-6">
|
||||
<div class="relative">
|
||||
<input type="text" class="input pr-9" placeholder="Enter file name">
|
||||
{% set trigger %}
|
||||
{% lucide "ellipsis" %}
|
||||
{% endset %}
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="Enter file name">
|
||||
{% set trigger %}{% lucide "ellipsis" %}{% endset %}
|
||||
{{ dropdown_menu(
|
||||
main_attrs={"class": "absolute right-1.5 top-1/2 -translate-y-1/2 z-10"},
|
||||
main_attrs={"data-align": "end"},
|
||||
trigger=trigger,
|
||||
trigger_attrs={"class": "btn-sm-icon-ghost text-muted-foreground hover:text-accent-foreground size-6"},
|
||||
trigger_attrs={"class": "btn-xs-icon-ghost", "aria-label": "Open file actions"},
|
||||
popover_attrs={"data-align": "end", "class": "min-w-32"},
|
||||
items=[
|
||||
{ type: "item", label: "Settings" },
|
||||
@@ -333,17 +368,14 @@ toc:
|
||||
) }}
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<input type="text" class="input pr-30" placeholder="Enter search query">
|
||||
{% set trigger %}
|
||||
Search in...
|
||||
{% lucide "chevron-down", { "class": "size-3" } %}
|
||||
{% endset %}
|
||||
<div class="input-group [--radius:1rem]">
|
||||
<input type="text" placeholder="Enter search query">
|
||||
{% set trigger %}Search In... {% lucide "chevron-down", { "class": "size-3" } %}{% endset %}
|
||||
{{ dropdown_menu(
|
||||
main_attrs={"class": "absolute right-1.5 top-1/2 -translate-y-1/2"},
|
||||
main_attrs={"data-align": "end"},
|
||||
trigger=trigger,
|
||||
trigger_attrs={"class": "btn-sm-ghost text-muted-foreground hover:text-accent-foreground p-2 h-6"},
|
||||
popover_attrs={"data-align": "end", "class": "min-w-32"},
|
||||
trigger_attrs={"class": "btn-xs-ghost pr-1.5! text-xs"},
|
||||
popover_attrs={"data-align": "end", "class": "min-w-32 [--radius:0.95rem]"},
|
||||
items=[
|
||||
{ type: "item", label: "Documentation" },
|
||||
{ type: "item", label: "Blog Posts" },
|
||||
@@ -354,19 +386,114 @@ toc:
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-dropdown", code_dropdown, class="w-full max-w-sm") }}
|
||||
{{ code_preview("input-group-dropdown", code_dropdown | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-group"><a href="#example-group">Group</a></h3>
|
||||
<h3 id="example-spinner"><a href="#example-spinner">Spinner</a></h3>
|
||||
|
||||
{% set code_group %}
|
||||
<div class="flex w-fit items-stretch">
|
||||
<label for="url" class="text-sm flex items-center bg-muted border border-r-0 px-4 rounded-l-md shadow-xs">https://</label>
|
||||
<div class="relative">
|
||||
<input type="text" class="input pr-9 rounded-none">
|
||||
<div class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground [&>svg]:size-4">{% lucide "link-2" %}</div>
|
||||
{% set code_spinner %}
|
||||
<div class="grid gap-6">
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="Searching...">
|
||||
<span data-align="end">{{ spinner_icon | safe }}</span>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="Processing...">
|
||||
<span data-align="start">{{ spinner_icon | safe }}</span>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="Saving changes...">
|
||||
<span data-align="end">
|
||||
Saving...
|
||||
{{ spinner_icon | safe }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="Refreshing data...">
|
||||
<span data-align="start" aria-hidden="true">{% lucide "loader", { "class": "animate-spin" } %}</span>
|
||||
<span data-align="end" class="text-muted-foreground">Please wait...</span>
|
||||
</div>
|
||||
<div class="text-sm flex items-center bg-muted border border-l-0 px-4 rounded-r-md shadow-xs">.com</div>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-group", code_group, class="w-full max-w-sm") }}
|
||||
{{ code_preview("input-group-spinner", code_spinner | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-textarea"><a href="#example-textarea">Textarea</a></h3>
|
||||
|
||||
{% set code_textarea %}
|
||||
<div class="input-group min-h-[200px]" data-orientation="vertical">
|
||||
<textarea placeholder="console.log('Hello, world!');"></textarea>
|
||||
<footer data-align="end" class="border-t">
|
||||
<span>Line 1, Column 1</span>
|
||||
<button type="button" class="btn-sm ml-auto">Run {% lucide "corner-down-left" %}</button>
|
||||
</footer>
|
||||
<header data-align="start" class="border-b">
|
||||
{% lucide "file-code", { "class": "text-muted-foreground" } %}
|
||||
<span>script.js</span>
|
||||
<div role="group" aria-label="File actions" class="ml-auto flex gap-1">
|
||||
<button type="button" class="btn-xs-icon-ghost">{% lucide "refresh-ccw" %}</button>
|
||||
<button type="button" class="btn-xs-icon-ghost">{% lucide "copy" %}</button>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-textarea", code_textarea | prettyHtml, class="w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-custom-input"><a href="#example-custom-input">Custom Input</a></h3>
|
||||
|
||||
{% set code_custom %}
|
||||
<div class="input-group" data-orientation="vertical">
|
||||
<textarea
|
||||
data-control
|
||||
placeholder="Autoresize textarea..."
|
||||
class="field-sizing-content min-h-16 resize-none"
|
||||
></textarea>
|
||||
<footer data-align="end">
|
||||
<button type="button" class="btn-sm ml-auto">Submit</button>
|
||||
</footer>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-custom", code_custom | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
{% set code_rtl %}
|
||||
<div dir="rtl" class="grid gap-6">
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="بحث...">
|
||||
<span role="group" data-align="start" aria-hidden="true">{% lucide "search" %}</span>
|
||||
<span role="group" data-align="end">١٢ نتيجة</span>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="جاري البحث...">
|
||||
<span role="group" data-align="end">{{ spinner_icon | safe }}</span>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="جاري حفظ التغييرات...">
|
||||
<span role="group" data-align="end">
|
||||
جاري الحفظ...
|
||||
{{ spinner_icon | safe }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="rtl-textarea">منطقة النص</label>
|
||||
<div class="input-group" data-orientation="vertical">
|
||||
<textarea id="rtl-textarea" placeholder="اكتب تعليقًا..."></textarea>
|
||||
<footer data-align="end">
|
||||
<span>٠/٢٨٠</span>
|
||||
<button type="button" class="btn-sm ms-auto">نشر</button>
|
||||
</footer>
|
||||
</div>
|
||||
<p>تذييل موضع أسفل منطقة النص.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("input-group-rtl", code_rtl | prettyHtml, class="w-full max-w-sm") }}
|
||||
|
||||
+152
-42
@@ -1,90 +1,200 @@
|
||||
---
|
||||
layout: layouts/page.njk
|
||||
title: Input
|
||||
description: Displays a form input field or a component that looks like an input field.
|
||||
description: A text input component for forms and user data entry with built-in styling and accessibility features.
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Default
|
||||
id: example-default
|
||||
- label: Invalid
|
||||
id: example-invalid
|
||||
- label: Basic
|
||||
id: example-basic
|
||||
- label: Field
|
||||
id: example-field
|
||||
- label: Field Group
|
||||
id: example-field-group
|
||||
- label: Disabled
|
||||
id: example-disabled
|
||||
- label: With label
|
||||
id: example-with-label
|
||||
- label: With text
|
||||
id: example-with-text
|
||||
- label: With button
|
||||
id: example-with-button
|
||||
- label: Invalid
|
||||
id: example-invalid
|
||||
- label: File
|
||||
id: example-file
|
||||
- label: Inline
|
||||
id: example-inline
|
||||
- label: Grid
|
||||
id: example-grid
|
||||
- label: Required
|
||||
id: example-required
|
||||
- label: Badge
|
||||
id: example-badge
|
||||
- label: Input Group
|
||||
id: example-input-group
|
||||
- label: Button Group
|
||||
id: example-button-group
|
||||
- label: Form
|
||||
id: example-form
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
|
||||
{% set code %}<input class="input" type="email" placeholder="Email">{% endset %}
|
||||
{{ code_preview("input", code) }}
|
||||
{{ code_preview("input", code, class="w-full max-w-xs") }}
|
||||
|
||||
<h2 id="usage">Usage</h2>
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Simply add the <code>input</code> class to your <code><input></code> element or have a parent with the <code>form</code> class (<a href="/components/form">read more about form</a>). You can also set the <code>aria-invalid</code> attribute to <code>true</code> to make the input invalid.</p>
|
||||
<p>Add the <code>input</code> class to supported <code><input></code> elements. Inputs inside a <code>form</code> or <code>field</code> container also receive the same styling automatically for common text-like input types.</p>
|
||||
<p>Use native HTML attributes for behavior: <code>disabled</code> for disabled inputs, <code>required</code> for required fields, and <code>aria-invalid="true"</code> for invalid state styling.</p>
|
||||
</section>
|
||||
|
||||
{{ code_block(code) }}
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
{{ code_preview("input-default", code) }}
|
||||
<h3 id="example-basic"><a href="#example-basic">Basic</a></h3>
|
||||
|
||||
<h3 id="example-invalid"><a href="#example-invalid">Invalid</a></h3>
|
||||
{% set code_basic %}<input class="input" type="email" placeholder="Email">{% endset %}
|
||||
{{ code_preview("input-basic", code_basic, class="w-full max-w-xs") }}
|
||||
|
||||
{% set code %}<input class="input" type="email" placeholder="Email" aria-invalid="true">{% endset %}
|
||||
{{ code_preview("input-invalid", code) }}
|
||||
<h3 id="example-field"><a href="#example-field">Field</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Use <code>field</code> with a label and description to create an accessible field.</p>
|
||||
</section>
|
||||
|
||||
{% set code_field %}<div role="group" class="field w-full max-w-xs">
|
||||
<label for="input-field">Email</label>
|
||||
<input id="input-field" type="email" placeholder="m@example.com">
|
||||
<p>Enter your email address.</p>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("input-field", code_field, class="w-full max-w-xs") }}
|
||||
|
||||
<h3 id="example-field-group"><a href="#example-field-group">Field Group</a></h3>
|
||||
|
||||
{% set code_field_group %}<div class="grid w-full max-w-xs gap-4">
|
||||
<div role="group" class="field">
|
||||
<label for="input-first-name">First name</label>
|
||||
<input id="input-first-name" type="text" placeholder="Jane">
|
||||
</div>
|
||||
<div role="group" class="field">
|
||||
<label for="input-last-name">Last name</label>
|
||||
<input id="input-last-name" type="text" placeholder="Doe">
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("input-field-group", code_field_group, class="w-full max-w-xs") }}
|
||||
|
||||
<h3 id="example-disabled"><a href="#example-disabled">Disabled</a></h3>
|
||||
|
||||
{% set code %}<input class="input" type="email" placeholder="Email" disabled>{% endset %}
|
||||
{{ code_preview("input-disabled", code) }}
|
||||
{% set code_disabled %}<input class="input" type="email" placeholder="Email" disabled>{% endset %}
|
||||
{{ code_preview("input-disabled", code_disabled, class="w-full max-w-xs") }}
|
||||
|
||||
<h3 id="example-with-label"><a href="#example-with-label">With label</a></h3>
|
||||
<h3 id="example-invalid"><a href="#example-invalid">Invalid</a></h3>
|
||||
|
||||
{% set code %}<div class="grid gap-3">
|
||||
<label for="input-with-label" class="label">Label</label>
|
||||
<input class="input" id="input-with-label" type="email" placeholder="Email">
|
||||
{% set code_invalid %}<div role="group" class="field w-full max-w-xs">
|
||||
<label for="input-invalid">Email</label>
|
||||
<input class="input" id="input-invalid" type="email" placeholder="Email" aria-invalid="true">
|
||||
<p>Please enter a valid email address.</p>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("input-with-label", code) }}
|
||||
{{ code_preview("input-invalid", code_invalid, class="w-full max-w-xs") }}
|
||||
|
||||
<h3 id="example-with-text"><a href="#example-with-text">With text</a></h3>
|
||||
<h3 id="example-file"><a href="#example-file">File</a></h3>
|
||||
|
||||
{% set code %}<div class="grid gap-3">
|
||||
<label for="input-with-text" class="label">Label</label>
|
||||
<input class="input" id="input-with-text" type="email" placeholder="Email">
|
||||
<p class="text-muted-foreground text-sm">Fill in your email address.</p>
|
||||
{% set code_file %}<input class="input" type="file">{% endset %}
|
||||
{{ code_preview("input-file", code_file, class="w-full max-w-xs") }}
|
||||
|
||||
<h3 id="example-inline"><a href="#example-inline">Inline</a></h3>
|
||||
|
||||
{% set code_inline %}<div class="flex w-full max-w-xs items-center gap-2">
|
||||
<input class="input" type="search" placeholder="Search...">
|
||||
<button type="submit" class="btn">Search</button>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("input-with-text", code) }}
|
||||
{{ code_preview("input-inline", code_inline, class="w-full max-w-xs") }}
|
||||
|
||||
<h3 id="example-with-button"><a href="#example-with-button">With button</a></h3>
|
||||
<h3 id="example-grid"><a href="#example-grid">Grid</a></h3>
|
||||
|
||||
{% set code %}<div class="flex items-center space-x-2">
|
||||
<input class="input" type="email" placeholder="Email">
|
||||
<button type="submit" class="btn">Submit</button>
|
||||
{% set code_grid %}<div class="grid w-full gap-4 md:grid-cols-2">
|
||||
<input class="input" type="text" placeholder="First name">
|
||||
<input class="input" type="text" placeholder="Last name">
|
||||
<input class="input md:col-span-2" type="email" placeholder="Email">
|
||||
</div>{% endset %}
|
||||
{{ code_preview("textarea-form", code) }}
|
||||
{{ code_preview("input-grid", code_grid, class="w-full max-w-xl") }}
|
||||
|
||||
<h3 id="example-required"><a href="#example-required">Required</a></h3>
|
||||
|
||||
{% set code_required %}<div role="group" class="field w-full max-w-xs">
|
||||
<label for="input-required">Username <span class="text-destructive">*</span></label>
|
||||
<input id="input-required" type="text" placeholder="hunvreus" required>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("input-required", code_required, class="w-full max-w-xs") }}
|
||||
|
||||
<h3 id="example-badge"><a href="#example-badge">Badge</a></h3>
|
||||
|
||||
{% set code_badge %}<div role="group" class="field w-full max-w-xs">
|
||||
<label for="input-badge" class="flex items-center gap-2">
|
||||
Email
|
||||
<span class="badge-secondary">Recommended</span>
|
||||
</label>
|
||||
<input id="input-badge" type="email" placeholder="m@example.com">
|
||||
</div>{% endset %}
|
||||
{{ code_preview("input-badge", code_badge, class="w-full max-w-xs") }}
|
||||
|
||||
<h3 id="example-input-group"><a href="#example-input-group">Input Group</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Use the <a href="/components/input-group/">Input Group</a> patterns to add icons, text, or buttons inside an input.</p>
|
||||
</section>
|
||||
|
||||
{% set code_input_group %}<div class="relative w-full max-w-xs">
|
||||
<input class="input ps-9" type="search" placeholder="Search...">
|
||||
<div class="pointer-events-none absolute start-3 top-1/2 -translate-y-1/2 text-muted-foreground [&>svg]:size-4">
|
||||
{% lucide "search" %}
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("input-input-group", code_input_group, class="w-full max-w-xs") }}
|
||||
|
||||
<h3 id="example-button-group"><a href="#example-button-group">Button Group</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Use <a href="/components/button-group/">Button Group</a> when an input should be grouped with adjacent buttons.</p>
|
||||
</section>
|
||||
|
||||
{% set code_button_group %}<div role="group" aria-label="Search" class="button-group w-full max-w-xs">
|
||||
<input class="input" type="search" placeholder="Search...">
|
||||
<button type="submit" class="btn-icon-outline" aria-label="Search">
|
||||
{% lucide "search" %}
|
||||
</button>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("input-button-group", code_button_group, class="w-full max-w-xs") }}
|
||||
|
||||
<h3 id="example-form"><a href="#example-form">Form</a></h3>
|
||||
|
||||
{% set code %}<form class="form space-y-6 w-full">
|
||||
<div class="grid gap-3">
|
||||
<label for="input-form" class="label">Username</label>
|
||||
<input class="input" id="input-form" type="text" placeholder="hunvreus">
|
||||
<p class="text-muted-foreground text-sm">This is your public display name.</p>
|
||||
{% set code_form %}<form class="w-full max-w-sm space-y-6">
|
||||
<div role="group" class="field">
|
||||
<label for="input-form-username">Username</label>
|
||||
<input id="input-form-username" type="text" placeholder="hunvreus" required>
|
||||
<p>This is your public display name.</p>
|
||||
</div>
|
||||
<div role="group" class="field">
|
||||
<label for="input-form-email">Email</label>
|
||||
<input id="input-form-email" type="email" placeholder="m@example.com" required>
|
||||
</div>
|
||||
<button type="submit" class="btn">Submit</button>
|
||||
</form>{% endset %}
|
||||
{{ code_preview("input-form", code, "w-full max-w-sm") }}
|
||||
{{ code_preview("input-form", code_form, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Inputs support right-to-left layouts through native browser direction handling. Add <code>dir="rtl"</code> to the input, field, or an ancestor.</p>
|
||||
</section>
|
||||
|
||||
{% set code_rtl %}<div role="group" class="field w-full max-w-xs" dir="rtl">
|
||||
<label for="input-rtl-api-key">مفتاح API</label>
|
||||
<input id="input-rtl-api-key" type="password" placeholder="sk-...">
|
||||
<p>مفتاح API الخاص بك مشفر ومخزن بأمان.</p>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("input-rtl", code_rtl, class="w-full max-w-xs") }}
|
||||
|
||||
+166
-222
@@ -5,6 +5,11 @@ description: A versatile component that you can use to display any content.
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
children:
|
||||
- label: HTML
|
||||
id: usage-html
|
||||
- label: HTML structure
|
||||
id: usage-html-structure
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
@@ -24,47 +29,65 @@ toc:
|
||||
id: example-header
|
||||
- label: Link
|
||||
id: example-link
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
|
||||
<div class="alert mb-6">
|
||||
{% lucide "circle-alert" %}
|
||||
<h2>There is no dedicated Item component in Basecoat.</h2>
|
||||
</div>
|
||||
|
||||
{% set code_basic %}
|
||||
<div class="flex flex-col gap-6">
|
||||
<article class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-border p-4 gap-4">
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">Basic Item</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">A simple item with title and description.</p>
|
||||
</div>
|
||||
<article class="item" data-variant="outline">
|
||||
<section>
|
||||
<h3>Basic Item</h3>
|
||||
<p>A simple item with title and description.</p>
|
||||
</section>
|
||||
<aside>
|
||||
<button class="btn-sm-outline">Action</button>
|
||||
</article>
|
||||
|
||||
<a href="#" class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-border py-3 px-4 gap-2.5">
|
||||
<div class="flex shrink-0 items-center justify-center gap-2 [&_svg]:pointer-events-none bg-transparent [&_svg]:size-5">
|
||||
{% lucide "badge-check" %}
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">Your profile has been verified.</h3>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 [&_svg]:size-4">
|
||||
{% lucide "chevron-right" %}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</aside>
|
||||
</article>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("item-basic", code_basic | prettyHtml, class="w-full max-w-md") }}
|
||||
{{ code_preview("item-basic", code_basic, class="w-full max-w-md") }}
|
||||
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Items are pure HTML composition using Tailwind utility classes. Use semantic elements like <code><article></code> or <code><a></code>, apply flex layout, and compose with standard HTML elements like <code><h3></code>, <code><p></code>, <code><img></code>, or <code><button></code>.</p>
|
||||
<p>Browse the examples below and copy the pattern that fits your needs.</p>
|
||||
<p>Use <code>.item</code> on a semantic element such as <code><article></code> or <code><a></code>. Use <code>.item-group</code> when listing multiple items.</p>
|
||||
</section>
|
||||
|
||||
<h3 id="usage-html"><a href="#usage-html">HTML</a></h3>
|
||||
|
||||
{{ code_block(code_basic) }}
|
||||
|
||||
<h3 id="usage-html-structure"><a href="#usage-html-structure">HTML structure</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>The component has the following HTML structure:</p>
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><article class="item"></code> or <code class="highlight language-html"><a class="item"></code></dt>
|
||||
<dd>The item root. Use <code>data-variant="outline|muted"</code> and <code>data-size="sm|xs"</code> when needed.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><figure></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Media: icon, avatar, or image.</dd>
|
||||
<dt><code class="highlight language-html"><section></code></dt>
|
||||
<dd>Primary content. Headings and paragraphs are styled automatically.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><h2></code>, <code class="highlight language-html"><h3></code>, or <code class="highlight language-html"><h4></code></dt>
|
||||
<dd>The item title.</dd>
|
||||
<dt><code class="highlight language-html"><p></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>The item description.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code class="highlight language-html"><aside></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Actions or trailing metadata.</dd>
|
||||
<dt><code class="highlight language-html"><header></code>, <code class="highlight language-html"><footer></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Full-width item regions.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code class="highlight language-html"><div class="item-group" role="list"></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Groups related items. Use <code>role="listitem"</code> on child items when they are not native list items.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
@@ -72,249 +95,170 @@ toc:
|
||||
<h3 id="example-variants"><a href="#example-variants">Variants</a></h3>
|
||||
|
||||
{% set code_variants %}
|
||||
<div class="flex flex-col gap-6">
|
||||
<article class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-transparent p-4 gap-4">
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">Default Variant</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">Standard styling with subtle background and borders.</p>
|
||||
</div>
|
||||
<button class="btn-sm-outline">Open</button>
|
||||
<div class="item-group" role="list">
|
||||
<article class="item" role="listitem">
|
||||
<section>
|
||||
<h3>Default Variant</h3>
|
||||
<p>Transparent border for quiet item rows.</p>
|
||||
</section>
|
||||
<aside><button class="btn-sm-outline">Open</button></aside>
|
||||
</article>
|
||||
|
||||
<article class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-border p-4 gap-4">
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">Outline Variant</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">Outlined style with clear borders and transparent background.</p>
|
||||
</div>
|
||||
<button class="btn-sm-outline">Open</button>
|
||||
<article class="item" data-variant="outline" role="listitem">
|
||||
<section>
|
||||
<h3>Outline Variant</h3>
|
||||
<p>Outlined style with a visible border.</p>
|
||||
</section>
|
||||
<aside><button class="btn-sm-outline">Open</button></aside>
|
||||
</article>
|
||||
|
||||
<article class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-transparent bg-muted/50 p-4 gap-4">
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">Muted Variant</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">Subdued appearance with muted colors for secondary content.</p>
|
||||
</div>
|
||||
<button class="btn-sm-outline">Open</button>
|
||||
<article class="item" data-variant="muted" role="listitem">
|
||||
<section>
|
||||
<h3>Muted Variant</h3>
|
||||
<p>Subdued appearance for secondary content.</p>
|
||||
</section>
|
||||
<aside><button class="btn-sm-outline">Open</button></aside>
|
||||
</article>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("item-variants", code_variants) }}
|
||||
{{ code_preview("item-variants", code_variants, class="w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-size"><a href="#example-size">Size</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Use different padding and gap sizes for compact or spacious layouts.</p>
|
||||
</section>
|
||||
{% set code_size %}
|
||||
<div class="item-group" role="list">
|
||||
<article class="item" data-variant="outline" role="listitem">
|
||||
<section><h3>Default</h3><p>Default item size.</p></section>
|
||||
</article>
|
||||
<article class="item" data-variant="outline" data-size="sm" role="listitem">
|
||||
<section><h3>Small</h3><p>Small item size.</p></section>
|
||||
</article>
|
||||
<article class="item" data-variant="outline" data-size="xs" role="listitem">
|
||||
<section><h3>Extra Small</h3><p>Extra small item size.</p></section>
|
||||
</article>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("item-size", code_basic) }}
|
||||
{{ code_preview("item-size", code_size, class="w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-icon"><a href="#example-icon">Icon</a></h3>
|
||||
|
||||
{% set code_icon %}
|
||||
<article class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-border p-4 gap-4">
|
||||
<div class="flex shrink-0 items-center justify-center gap-2 self-start [&_svg]:pointer-events-none size-8 border rounded-sm bg-muted [&_svg:not([class*='size-'])]:size-4">
|
||||
<article class="item" data-variant="outline">
|
||||
<figure>
|
||||
{% lucide "shield-alert" %}
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">Security Alert</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">New login detected from unknown device.</p>
|
||||
</div>
|
||||
<button class="btn-sm-outline">Review</button>
|
||||
</figure>
|
||||
<section>
|
||||
<h3>Security Alert</h3>
|
||||
<p>New login detected from an unknown device.</p>
|
||||
</section>
|
||||
<aside><button class="btn-sm-outline">Review</button></aside>
|
||||
</article>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("item-icon", code_icon, class="max-w-lg") }}
|
||||
|
||||
{{ code_preview("item-icon", code_icon, class="w-full max-w-lg") }}
|
||||
|
||||
<h3 id="example-avatar"><a href="#example-avatar">Avatar</a></h3>
|
||||
|
||||
{% set code_avatar %}
|
||||
<div class="flex flex-col gap-6">
|
||||
<article class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-border p-4 gap-4">
|
||||
<img src="https://github.com/hunvreus.png" alt="hunvreus" class="size-10 rounded-full object-cover">
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">hunvreus</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">Last seen 5 months ago</p>
|
||||
</div>
|
||||
<button class="btn-icon-outline rounded-full">
|
||||
{% lucide "plus" %}
|
||||
</button>
|
||||
</article>
|
||||
|
||||
<article class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-border p-4 gap-4">
|
||||
<div class="flex -space-x-2 [&_img]:ring-background [&_img]:ring-2 [&_img]:grayscale [&_img]:size-8 [&_img]:shrink-0 [&_img]:object-cover [&_img]:rounded-full self-start">
|
||||
<img alt="@hunvreus" src="https://github.com/hunvreus.png">
|
||||
<img alt="@shadcn" src="https://github.com/shadcn.png">
|
||||
<img alt="@adamwathan" src="https://github.com/adamwathan.png">
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">No Team Members</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">Invite your team to collaborate on this project.</p>
|
||||
</div>
|
||||
<button class="btn-outline">
|
||||
Invite
|
||||
</button>
|
||||
</article>
|
||||
</div>
|
||||
<article class="item" data-variant="outline">
|
||||
<figure>
|
||||
<img src="https://github.com/hunvreus.png" alt="hunvreus">
|
||||
</figure>
|
||||
<section>
|
||||
<h3>hunvreus</h3>
|
||||
<p>Last seen 5 months ago.</p>
|
||||
</section>
|
||||
<aside>
|
||||
<button class="btn-icon-outline rounded-full">{% lucide "plus" %}</button>
|
||||
</aside>
|
||||
</article>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("item-avatar", code_avatar, class="w-full max-w-lg") }}
|
||||
{{ code_preview("item-avatar", code_avatar, class="w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-image"><a href="#example-image">Image</a></h3>
|
||||
|
||||
{% set code_image %}
|
||||
<div class="flex flex-col gap-6 w-full max-w-md">
|
||||
<a href="#" class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-border p-4 gap-4">
|
||||
<img src="https://avatar.vercel.sh/example_1" alt="Midnight City Lights" class="grayscale size-10 rounded-sm object-cover">
|
||||
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="w-fit items-center gap-2 text-sm leading-snug font-medium line-clamp-1">Midnight City Lights - <span class="text-muted-foreground">Electric Nights</span></h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">Neon Dreams</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1 flex-none text-center">
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">3:45</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="#" class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-border p-4 gap-4">
|
||||
<img src="https://avatar.vercel.sh/example_2" alt="Midnight City Lights" class="grayscale size-10 rounded-sm object-cover">
|
||||
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="w-fit items-center gap-2 text-sm leading-snug font-medium line-clamp-1">Coffee Shop Conversations - <span class="text-muted-foreground">Urban Stories</span></h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">The Morning Brew</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1 flex-none text-center">
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">4:05</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="#" class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-border p-4 gap-4">
|
||||
<img src="https://avatar.vercel.sh/example_3" alt="Midnight City Lights" class="grayscale size-10 rounded-sm object-cover">
|
||||
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="w-fit items-center gap-2 text-sm leading-snug font-medium line-clamp-1">Digital Rain - <span class="text-muted-foreground">Binary Beats</span></h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">Cyber Symphony</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1 flex-none text-center">
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">3:30</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<a href="#" class="item" data-variant="outline">
|
||||
<figure>
|
||||
<img src="https://avatar.vercel.sh/example_1" alt="Midnight City Lights">
|
||||
</figure>
|
||||
<section>
|
||||
<h3>Midnight City Lights</h3>
|
||||
<p>Neon Dreams</p>
|
||||
</section>
|
||||
<section>
|
||||
<p>3:45</p>
|
||||
</section>
|
||||
</a>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("item-image", code_image) }}
|
||||
|
||||
{{ code_preview("item-image", code_image, class="w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-group"><a href="#example-group">Group</a></h3>
|
||||
|
||||
{% set code_group %}
|
||||
<div class="flex flex-col">
|
||||
<article class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-transparent p-4 gap-4">
|
||||
<img src="https://github.com/hunvreus.png" alt="Ronan Berder" class="grayscale size-10 rounded-full object-cover">
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">hunvreus</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">hunvreus@gmail.com</p>
|
||||
</div>
|
||||
<button class="btn-icon-outline rounded-full">
|
||||
{% lucide "plus" %}
|
||||
</button>
|
||||
<div class="item-group" role="list">
|
||||
<article class="item" role="listitem">
|
||||
<figure><img src="https://github.com/hunvreus.png" alt="hunvreus"></figure>
|
||||
<section><h3>hunvreus</h3><p>hunvreus@gmail.com</p></section>
|
||||
<aside><button class="btn-icon-outline rounded-full">{% lucide "plus" %}</button></aside>
|
||||
</article>
|
||||
|
||||
<hr role="separator" class="border-border">
|
||||
|
||||
<article class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-transparent p-4 gap-4">
|
||||
<img src="https://github.com/shadcn.png" alt="shadcn" class="grayscale size-10 rounded-full object-cover">
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">shadcn</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">shadcn@vercel.com</p>
|
||||
</div>
|
||||
<button class="btn-icon-outline rounded-full">
|
||||
{% lucide "plus" %}
|
||||
</button>
|
||||
</article>
|
||||
|
||||
<hr role="separator" class="border-border">
|
||||
|
||||
<article class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-transparent p-4 gap-4">
|
||||
<img src="https://github.com/adamwathan.png" alt="Adam Wathan" class="grayscale size-10 rounded-full object-cover">
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">adamwathan</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">adam.wathan@gmail.com</p>
|
||||
</div>
|
||||
<button class="btn-icon-outline rounded-full">
|
||||
{% lucide "plus" %}
|
||||
</button>
|
||||
<hr role="separator">
|
||||
<article class="item" role="listitem">
|
||||
<figure><img src="https://github.com/shadcn.png" alt="shadcn"></figure>
|
||||
<section><h3>shadcn</h3><p>shadcn@vercel.com</p></section>
|
||||
<aside><button class="btn-icon-outline rounded-full">{% lucide "plus" %}</button></aside>
|
||||
</article>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("item-group", code_group, class="w-full max-w-md") }}
|
||||
|
||||
|
||||
<h3 id="example-header"><a href="#example-header">Header</a></h3>
|
||||
|
||||
{% set code_header %}
|
||||
<div class="flex-col grid grid-cols-3 gap-4">
|
||||
<article class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-border p-4 gap-4">
|
||||
<img alt="v0-1.5-sm" loading="lazy" class="grayscale aspect-square w-full rounded-sm object-cover flex-basis" src="https://avatar.vercel.sh/v0-1.5-sm">
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">v0-1.5-sm</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">Everyday tasks and UI generation.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-border p-4 gap-4">
|
||||
<img alt="v0-1.5-lg" loading="lazy" class="grayscale aspect-square w-full rounded-sm object-cover flex-basis" src="https://avatar.vercel.sh/v0-1.5-lg">
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">v0-1.5-lg</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">Advanced thinking or reasoning.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-border p-4 gap-4">
|
||||
<img alt="v0-2.0-mini" loading="lazy" class="grayscale aspect-square w-full rounded-sm object-cover flex-basis" src="https://avatar.vercel.sh/v0-2.0-mini">
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">v0-2.0-mini</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">Open Source model for everyone.</p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<article class="item" data-variant="outline">
|
||||
<header>
|
||||
<h3>v0-1.5-sm</h3>
|
||||
<span class="badge-outline">Model</span>
|
||||
</header>
|
||||
<figure>
|
||||
<img src="https://avatar.vercel.sh/v0-1.5-sm" alt="v0-1.5-sm">
|
||||
</figure>
|
||||
<section>
|
||||
<p>Everyday tasks and UI generation.</p>
|
||||
</section>
|
||||
</article>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("item-header", code_header) }}
|
||||
{{ code_preview("item-header", code_header, class="w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-link"><a href="#example-link">Link</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Items can be rendered as links by using an <code><a></code> element. Hover and focus states are applied automatically.</p>
|
||||
</section>
|
||||
|
||||
{% set code_link %}
|
||||
<div class="flex flex-col gap-6">
|
||||
<a href="#" class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-transparent p-4 gap-4">
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">Visit our documentation</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">Learn how to get started with our components.</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 [&_svg]:size-4">
|
||||
{% lucide "chevron-right" %}
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="#" class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-border p-4 gap-4">
|
||||
<div class="flex flex-1 flex-col gap-1">
|
||||
<h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">External resource</h3>
|
||||
<p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">Opens in a new tab with security attributes.</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 [&_svg]:size-4">
|
||||
{% lucide "external-link" %}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<a href="#" class="item">
|
||||
<section>
|
||||
<h3>Visit our documentation</h3>
|
||||
<p>Learn how to get started with our components.</p>
|
||||
</section>
|
||||
<aside>{% lucide "chevron-right" %}</aside>
|
||||
</a>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("item-link", code_link, class="w-full max-w-md") }}
|
||||
{{ code_preview("item-link", code_link, class="w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
{% set code_rtl %}
|
||||
<article class="item" data-variant="outline" dir="rtl">
|
||||
<figure>{% lucide "shield-alert" %}</figure>
|
||||
<section>
|
||||
<h3>تنبيه أمني</h3>
|
||||
<p>تم اكتشاف تسجيل دخول من جهاز غير معروف.</p>
|
||||
</section>
|
||||
<aside><button class="btn-sm-outline">مراجعة</button></aside>
|
||||
</article>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("item-rtl", code_rtl, class="w-full max-w-lg") }}
|
||||
|
||||
@@ -12,6 +12,10 @@ toc:
|
||||
id: example-group
|
||||
- label: Button
|
||||
id: example-button
|
||||
- label: Input Group
|
||||
id: example-input-group
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
@@ -19,13 +23,13 @@ toc:
|
||||
|
||||
{% set code_html %}
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<span class="inline-flex items-center gap-1">
|
||||
<span class="kbd-group">
|
||||
<kbd class="kbd">⌘</kbd>
|
||||
<kbd class="kbd">⇧</kbd>
|
||||
<kbd class="kbd">⌥</kbd>
|
||||
<kbd class="kbd">⌃</kbd>
|
||||
</span>
|
||||
<span class="inline-flex items-center gap-1">
|
||||
<span class="kbd-group">
|
||||
<kbd class="kbd">Ctrl</kbd>
|
||||
<span>+</span>
|
||||
<kbd class="kbd">B</kbd>
|
||||
@@ -38,7 +42,7 @@ toc:
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Use the <code><kbd></code> element with the <code>kbd</code> class:</p>
|
||||
<p>Use the <code><kbd></code> element with the <code>kbd</code> class. Use <code>kbd-group</code> to group multiple keys.</p>
|
||||
</section>
|
||||
|
||||
{% set code_simple %}
|
||||
@@ -54,7 +58,7 @@ toc:
|
||||
{% set group_html %}
|
||||
<p class="text-muted-foreground text-sm">
|
||||
Use
|
||||
<span class="inline-flex items-center gap-1">
|
||||
<span class="kbd-group">
|
||||
<kbd class="kbd">Ctrl + B</kbd>
|
||||
<kbd class="kbd">Ctrl + K</kbd>
|
||||
</span>
|
||||
@@ -68,9 +72,40 @@ toc:
|
||||
|
||||
{% set button_html %}
|
||||
<div class="flex flex-wrap items-center gap-x-4">
|
||||
<button class="btn-sm-outline pr-2">Accept <kbd class="kbd">⏎</kbd></button>
|
||||
<button class="btn-sm-outline pr-2">Cancel <kbd class="kbd">Esc</kbd></button>
|
||||
<button class="btn-sm-outline pe-2">Accept <kbd class="kbd">⏎</kbd></button>
|
||||
<button class="btn-sm-outline pe-2">Cancel <kbd class="kbd">Esc</kbd></button>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("kbd-button", button_html | prettyHtml) }}
|
||||
{{ code_preview("kbd-button", button_html | prettyHtml) }}
|
||||
|
||||
<h3 id="example-input-group"><a href="#example-input-group">Input Group</a></h3>
|
||||
|
||||
{% set input_group_html %}
|
||||
<div class="relative w-full max-w-sm">
|
||||
<input type="search" class="input ps-9 pe-12" placeholder="Search...">
|
||||
{% lucide "search", { "class": "absolute start-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" } %}
|
||||
<kbd class="kbd absolute end-3 top-1/2 -translate-y-1/2">/</kbd>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("kbd-input-group", input_group_html | prettyHtml) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Keyboard labels are direction-neutral. Surrounding spacing should use logical utilities when position matters.</p>
|
||||
</section>
|
||||
|
||||
{% set rtl_html %}
|
||||
<p dir="rtl" class="text-muted-foreground text-sm">
|
||||
استخدم
|
||||
<span class="kbd-group">
|
||||
<kbd class="kbd">Ctrl</kbd>
|
||||
<kbd class="kbd">K</kbd>
|
||||
</span>
|
||||
لفتح البحث
|
||||
</p>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("kbd-rtl", rtl_html | prettyHtml) }}
|
||||
|
||||
@@ -10,6 +10,8 @@ toc:
|
||||
children:
|
||||
- label: Disabled
|
||||
id: example-disabled
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
@@ -21,10 +23,10 @@ toc:
|
||||
</label>{% endset %}
|
||||
{{ code_preview("label", code) }}
|
||||
|
||||
<h2 id="usage">Usage</h2>
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Simply add the <code>label</code> class to your <code><label></code> element or have a parent with the <code>form</code> class (<a href="/components/form">read more about form</a>). Labels' style may be affected by the presence of a disabled peer or child element.</p>
|
||||
<p>Add the <code>label</code> class to your <code><label></code> element. Labels' style may be affected by the presence of a disabled peer or child element.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<label class="label" for="email">Your email address</label>{% endset %}
|
||||
@@ -38,4 +40,16 @@ toc:
|
||||
<label class="label" for="email">Email</label>
|
||||
<input class="input" id="email" type="email" placeholder="Email" disabled>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("label-disabled", code) }}
|
||||
{{ code_preview("label-disabled", code) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Labels support right-to-left layouts through native text flow. Add <code>dir="rtl"</code> to the label or an ancestor.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<div class="grid gap-3" dir="rtl">
|
||||
<label class="label" for="email-rtl">البريد الإلكتروني</label>
|
||||
<input class="input" id="email-rtl" type="email" placeholder="name@example.com">
|
||||
</div>{% endset %}
|
||||
{{ code_preview("label-rtl", code) }}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
---
|
||||
layout: layouts/page.njk
|
||||
title: Native Select
|
||||
description: A styled native HTML select element with consistent design system integration.
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Groups
|
||||
id: example-groups
|
||||
- label: Disabled
|
||||
id: example-disabled
|
||||
- label: Invalid
|
||||
id: example-invalid
|
||||
- label: Size
|
||||
id: example-size
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
- label: Native Select vs Select
|
||||
id: native-select-vs-select
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
|
||||
{% set code %}<select class="select w-[180px]" aria-label="Fruit">
|
||||
<option value="">Select a fruit</option>
|
||||
<option value="apple">Apple</option>
|
||||
<option value="banana">Banana</option>
|
||||
<option value="blueberry">Blueberry</option>
|
||||
<option value="pineapple">Pineapple</option>
|
||||
</select>{% endset %}
|
||||
{{ code_preview("native-select", code) }}
|
||||
|
||||
<section class="prose mt-6">
|
||||
<p>For a custom listbox with richer styling and JavaScript behavior, see the <a href="/components/select">Select</a> component.</p>
|
||||
</section>
|
||||
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Add the <code>select</code> class to a native <code><select></code>. Use standard <code><option></code> and <code><optgroup></code> elements.</p>
|
||||
</section>
|
||||
|
||||
{{ code_block(code) }}
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-groups"><a href="#example-groups">Groups</a></h3>
|
||||
|
||||
{% set code %}<select class="select w-[220px]" aria-label="Food">
|
||||
<option value="">Select food</option>
|
||||
<optgroup label="Fruits">
|
||||
<option value="apple">Apple</option>
|
||||
<option value="banana">Banana</option>
|
||||
<option value="blueberry">Blueberry</option>
|
||||
</optgroup>
|
||||
<optgroup label="Vegetables">
|
||||
<option value="carrot">Carrot</option>
|
||||
<option value="potato">Potato</option>
|
||||
<option value="tomato">Tomato</option>
|
||||
</optgroup>
|
||||
</select>{% endset %}
|
||||
{{ code_preview("native-select-groups", code) }}
|
||||
|
||||
<h3 id="example-disabled"><a href="#example-disabled">Disabled</a></h3>
|
||||
|
||||
{% set code %}<select class="select w-[220px]" aria-label="Fruit" disabled>
|
||||
<option value="">Select a fruit</option>
|
||||
<option value="apple">Apple</option>
|
||||
<option value="banana">Banana</option>
|
||||
</select>{% endset %}
|
||||
{{ code_preview("native-select-disabled", code) }}
|
||||
|
||||
<h3 id="example-invalid"><a href="#example-invalid">Invalid</a></h3>
|
||||
|
||||
{% set code %}<div role="group" class="field" data-invalid="true">
|
||||
<label for="native-select-invalid">Fruit</label>
|
||||
<select id="native-select-invalid" class="select w-full" aria-invalid="true" aria-describedby="native-select-invalid-error">
|
||||
<option value="">Select a fruit</option>
|
||||
<option value="apple">Apple</option>
|
||||
<option value="banana">Banana</option>
|
||||
</select>
|
||||
<p id="native-select-invalid-error" class="field-error">Select a fruit to continue.</p>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("native-select-invalid", code, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-size"><a href="#example-size">Size</a></h3>
|
||||
|
||||
{% set code %}<div class="flex flex-wrap items-center gap-3">
|
||||
<select class="select w-[180px]" aria-label="Default size">
|
||||
<option>Default</option>
|
||||
<option>Apple</option>
|
||||
<option>Banana</option>
|
||||
</select>
|
||||
<select class="select w-[180px]" data-size="sm" aria-label="Small size">
|
||||
<option>Small</option>
|
||||
<option>Apple</option>
|
||||
<option>Banana</option>
|
||||
</select>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("native-select-size", code) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Native select supports document direction. Set <code>dir="rtl"</code> on the select or a parent element.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<select dir="rtl" class="select w-[220px]" aria-label="الفاكهة">
|
||||
<option value="">اختر فاكهة</option>
|
||||
<option value="apple">تفاح</option>
|
||||
<option value="banana">موز</option>
|
||||
<option value="blueberry">توت</option>
|
||||
</select>{% endset %}
|
||||
{{ code_preview("native-select-rtl", code) }}
|
||||
|
||||
<h2 id="native-select-vs-select"><a href="#native-select-vs-select">Native Select vs Select</a></h2>
|
||||
|
||||
<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>
|
||||
</ul>
|
||||
</section>
|
||||
+175
-66
@@ -1,7 +1,7 @@
|
||||
---
|
||||
layout: layouts/page.njk
|
||||
title: Popover
|
||||
description: Displays rich content in a portal, triggered by a button.
|
||||
description: Displays rich content triggered by a button.
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
@@ -9,7 +9,7 @@ toc:
|
||||
- label: HTML + JavaScript
|
||||
id: usage-html-js
|
||||
children:
|
||||
- label: "Step 1 (Optional): Include the JavaScript file"
|
||||
- label: "Step 1: Include the JavaScript file"
|
||||
id: usage-html-js-1
|
||||
- label: "Step 2: Add your popover HTML"
|
||||
id: usage-html-js-2
|
||||
@@ -19,6 +19,19 @@ toc:
|
||||
id: usage-html-js-4
|
||||
- label: Jinja and Nunjucks
|
||||
id: usage-macro
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Basic
|
||||
id: example-basic
|
||||
- label: Align
|
||||
id: example-align
|
||||
- label: With Form
|
||||
id: example-form
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
- label: API Reference
|
||||
id: api-reference
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
@@ -30,39 +43,74 @@ toc:
|
||||
id="demo-popover",
|
||||
trigger="Open popover",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
popover_attrs={"class": "w-80"}
|
||||
popover_attrs={"class": "w-72"}
|
||||
) %}
|
||||
<div class="grid gap-4">
|
||||
<header class="grid gap-1.5">
|
||||
<h4 class="leading-none font-medium">Dimensions</h4>
|
||||
<p class="text-muted-foreground text-sm">Set the dimensions for the layer.</p>
|
||||
</header>
|
||||
<form class="form grid gap-2">
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-width">Width</label>
|
||||
<input type="text" id="demo-popover-width" value="100%" class="col-span-2 h-8" autofocus/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-max-width">Max. width</label>
|
||||
<input type="text" id="demo-popover-max-width" value="300px" class="col-span-2 h-8"/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-height">Height</label>
|
||||
<input type="text" id="demo-popover-height" value="25px" class="col-span-2 h-8"/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-max-height">Max. height</label>
|
||||
<input type="text" id="demo-popover-max-height" value="none" class="col-span-2 h-8"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<header>
|
||||
<h4>Dimensions</h4>
|
||||
<p>Set the dimensions for the layer.</p>
|
||||
</header>
|
||||
<form class="grid gap-2">
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-width">Width</label>
|
||||
<input type="text" id="demo-popover-width" value="100%" class="input col-span-2 h-8" autofocus>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-max-width">Max. width</label>
|
||||
<input type="text" id="demo-popover-max-width" value="300px" class="input col-span-2 h-8">
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-height">Height</label>
|
||||
<input type="text" id="demo-popover-height" value="25px" class="input col-span-2 h-8">
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-max-height">Max. height</label>
|
||||
<input type="text" id="demo-popover-max-height" value="none" class="input col-span-2 h-8">
|
||||
</div>
|
||||
</form>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("popover", code_html | prettyHtml) }}
|
||||
|
||||
{% set code_form %}
|
||||
{% call popover(
|
||||
id="popover-form",
|
||||
trigger="Open popover",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
popover_attrs={"class": "w-72"}
|
||||
) %}
|
||||
<header>
|
||||
<h4>Dimensions</h4>
|
||||
<p>Set the dimensions for the layer.</p>
|
||||
</header>
|
||||
<form class="grid gap-2">
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="popover-form-width">Width</label>
|
||||
<input type="text" id="popover-form-width" value="100%" class="input col-span-2 h-8" autofocus>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="popover-form-max-width">Max. width</label>
|
||||
<input type="text" id="popover-form-max-width" value="300px" class="input col-span-2 h-8">
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="popover-form-height">Height</label>
|
||||
<input type="text" id="popover-form-height" value="25px" class="input col-span-2 h-8">
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="popover-form-max-height">Max. height</label>
|
||||
<input type="text" id="popover-form-max-height" value="none" class="input col-span-2 h-8">
|
||||
</div>
|
||||
</form>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
|
||||
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Basecoat popovers are inline-positioned relative to the <code>.popover</code> wrapper. This differs from shadcn/ui's portalled Base UI implementation, but keeps the markup dependency-free and works with Basecoat's existing dropdown/select positioning model.</p>
|
||||
</section>
|
||||
|
||||
<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 file</a></h4>
|
||||
@@ -80,10 +128,14 @@ toc:
|
||||
Components with JavaScript
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="/installation/#install-cli">
|
||||
<a class="badge-outline" href="/cli">
|
||||
Use the CLI
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/js/popover.js" target="_blank">
|
||||
popover.js
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h4 id="usage-html-js-2"><a href="#usage-html-js-2">Step 2: Add your popover HTML</a></h4>
|
||||
@@ -94,17 +146,20 @@ toc:
|
||||
|
||||
<section class="prose">
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><button type="button" popovertarget="{ POPOVER_ID }"></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-expanded="false"</code>: tracks the popover's state.</li>
|
||||
<li><code>onclick</code>: a simple function to update <code>aria-expanded</code>.</li>
|
||||
</ul>
|
||||
<dt><code class="highlight language-html"><div class="popover"></code></dt>
|
||||
<dd>Relative wrapper for the trigger and inline popover content.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><button type="button" aria-expanded="false" aria-controls="{ POPOVER_ID }"></code></dt>
|
||||
<dd>Trigger button. The script toggles <code>aria-expanded</code> and opens/closes the content.</dd>
|
||||
<dt><code class="highlight language-html"><div data-popover id="{ POPOVER_ID }" aria-hidden="true"></code></dt>
|
||||
<dd>Popover content. Set <code>data-side="top|right|bottom|left"</code> and <code>data-align="start|center|end"</code> to control placement.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><header></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Header area. A heading inside the header is styled as the popover title, and a paragraph is styled as the description.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code class="highlight language-html"><div data-popover class="popover" id="{ POPOVER_ID }"></code></dt>
|
||||
<dd>You can set up the side and alignment of the popover using the <code>data-side</code> and <code>data-align</code> attributes.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
@@ -113,9 +168,9 @@ toc:
|
||||
<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>
|
||||
<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 popover opens, the component dispatches a custom (non-bubbling) <code>basecoat:popover</code> event on <code>document</code>. Other popover components (Combobox, Dropdown Menu, Popover and Select) listen for this to close any open popovers.</dd>
|
||||
<dd>When the popover opens, the component dispatches a custom event on <code>document</code>. Other popover-based components listen for this to close any open popovers.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
@@ -126,7 +181,7 @@ toc:
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
<a class="badge-outline" href="/installation/#install-macros" target="_blank">
|
||||
<a class="badge-outline" href="/cli#macros" target="_blank">
|
||||
Use Nunjucks or Jinja macros
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
@@ -144,31 +199,85 @@ toc:
|
||||
id="demo-popover",
|
||||
trigger="Open popover",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
popover_attrs={"class": "w-80 p-4"}
|
||||
popover_attrs={"class": "w-72", "data-align": "center"}
|
||||
) %}
|
||||
<div class="grid gap-4">
|
||||
<header class="grid gap-1.5">
|
||||
<h4 class="leading-none font-medium">Dimensions</h4>
|
||||
<p class="text-muted-foreground text-sm">Set the dimensions for the layer.</p>
|
||||
</header>
|
||||
<form class="form grid gap-2">
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-width">Width</label>
|
||||
<input type="text" id="demo-popover-width" value="100%" class="col-span-2 h-8"/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-max-width">Max. width</label>
|
||||
<input type="text" id="demo-popover-max-width" value="300px" class="col-span-2 h-8"/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-height">Height</label>
|
||||
<input type="text" id="demo-popover-height" value="25px" class="col-span-2 h-8"/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center gap-4">
|
||||
<label for="demo-popover-max-height">Max. height</label>
|
||||
<input type="text" id="demo-popover-max-height" value="none" class="col-span-2 h-8"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<header>
|
||||
<h4>Title</h4>
|
||||
<p>Description text here.</p>
|
||||
</header>
|
||||
{% endcall %}{% endraw %}{% endset %}
|
||||
{{ code_block(raw_code, "jinja") }}
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-basic"><a href="#example-basic">Basic</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{% call popover(
|
||||
id="popover-basic",
|
||||
trigger="Open",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
popover_attrs={"class": "w-72"}
|
||||
) %}
|
||||
<header>
|
||||
<h4>Title</h4>
|
||||
<p>Description text here.</p>
|
||||
</header>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("popover-basic", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-align"><a href="#example-align">Align</a></h3>
|
||||
|
||||
{% set code %}<div class="flex flex-wrap gap-3">
|
||||
{% call popover(id="popover-align-start", trigger="Start", trigger_attrs={"class": "btn-outline"}, popover_attrs={"class": "w-56", "data-align": "start"}) %}
|
||||
<p>Aligned to start.</p>
|
||||
{% endcall %}
|
||||
{% call popover(id="popover-align-center", trigger="Center", trigger_attrs={"class": "btn-outline"}, popover_attrs={"class": "w-56", "data-align": "center"}) %}
|
||||
<p>Aligned to center.</p>
|
||||
{% endcall %}
|
||||
{% call popover(id="popover-align-end", trigger="End", trigger_attrs={"class": "btn-outline"}, popover_attrs={"class": "w-56", "data-align": "end"}) %}
|
||||
<p>Aligned to end.</p>
|
||||
{% endcall %}
|
||||
</div>{% endset %}
|
||||
{{ code_preview("popover-align", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-form"><a href="#example-form">With Form</a></h3>
|
||||
|
||||
{{ code_preview("popover-form", code_form | prettyHtml) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Popover alignment uses logical start and end offsets. Set <code>dir="rtl"</code> on a parent to flip start/end alignment.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<div dir="rtl">
|
||||
{% call popover(
|
||||
id="popover-rtl",
|
||||
trigger="فتح",
|
||||
trigger_attrs={"class": "btn-outline"},
|
||||
popover_attrs={"class": "w-72", "data-align": "start"}
|
||||
) %}
|
||||
<header>
|
||||
<h4>العنوان</h4>
|
||||
<p>نص الوصف هنا.</p>
|
||||
</header>
|
||||
{% endcall %}
|
||||
</div>{% endset %}
|
||||
{{ code_preview("popover-rtl", code | prettyHtml) }}
|
||||
|
||||
<h2 id="api-reference"><a href="#api-reference">API Reference</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<dl>
|
||||
<dt><code class="highlight language-html">.popover</code></dt>
|
||||
<dd>Root wrapper. Must contain the trigger button and direct <code>[data-popover]</code> content.</dd>
|
||||
<dt><code class="highlight language-html">[data-popover]</code></dt>
|
||||
<dd>Popover content. Supports <code>data-side</code>, <code>data-align</code>, and <code>aria-hidden</code>.</dd>
|
||||
<dt><code class="highlight language-html">data-side</code></dt>
|
||||
<dd>Placement side: <code>top</code>, <code>right</code>, <code>bottom</code>, or <code>left</code>. Defaults to <code>bottom</code>.</dd>
|
||||
<dt><code class="highlight language-html">data-align</code></dt>
|
||||
<dd>Alignment: <code>start</code>, <code>center</code>, or <code>end</code>. Defaults to <code>start</code>.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
@@ -1,48 +1,219 @@
|
||||
---
|
||||
layout: layouts/page.njk
|
||||
title: Radio Group
|
||||
description: A set of checkable buttons—known as radio buttons—where no more than one of the buttons can be checked at a time.
|
||||
description: A set of checkable buttons where only one option can be selected at a time.
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
- label: Checked state
|
||||
id: checked-state
|
||||
- label: Invalid state
|
||||
id: invalid-state
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Form
|
||||
id: example-form
|
||||
- label: Basic
|
||||
id: example-basic
|
||||
- label: Description
|
||||
id: example-description
|
||||
- label: Choice Card
|
||||
id: example-choice-card
|
||||
- label: Fieldset
|
||||
id: example-fieldset
|
||||
- label: Disabled
|
||||
id: example-disabled
|
||||
- label: Invalid
|
||||
id: example-invalid
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
|
||||
{% set code %}<fieldset class="grid gap-3">
|
||||
<label class="label"><input type="radio" name="radio-group" value="default" class="input">Default</label>
|
||||
<label class="label"><input type="radio" name="radio-group" value="comfortable" class="input" checked>Comfortable</label>
|
||||
<label class="label"><input type="radio" name="radio-group" value="compact" class="input">Compact</label>
|
||||
{% set code %}<fieldset class="fieldset">
|
||||
<legend>Plan</legend>
|
||||
<div role="radiogroup" aria-label="Plan" data-slot="radio-group">
|
||||
<div role="group" class="field flex-row items-center gap-3">
|
||||
<input type="radio" id="plan-free" name="plan" value="free" class="input" checked>
|
||||
<label for="plan-free">Free</label>
|
||||
</div>
|
||||
<div role="group" class="field flex-row items-center gap-3">
|
||||
<input type="radio" id="plan-pro" name="plan" value="pro" class="input">
|
||||
<label for="plan-pro">Pro</label>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>{% endset %}
|
||||
{{ code_preview("radio-group", code) }}
|
||||
{{ code_preview("radio-group", code, "w-full max-w-sm") }}
|
||||
|
||||
<h2 id="usage">Usage</h2>
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Simply add the <code>input</code> class to your <code><input type="radio"></code> elements or have a parent with the <code>form</code> class (<a href="/components/form">read more about form</a>). You can also set the <code>aria-invalid</code> attribute to <code>true</code> to make the input invalid.</p>
|
||||
<p>Add the <code>input</code> class to your <code><input type="radio"></code> elements. Use a shared <code>name</code> attribute for mutually exclusive options, and wrap related options in a semantic <code><fieldset></code> or an element with <code>role="radiogroup"</code>.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<input type="radio" name="plan" class="input">{% endset %}
|
||||
{{ code_block(code) }}
|
||||
|
||||
<h2 id="checked-state"><a href="#checked-state">Checked state</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Use native HTML state. Add <code>checked</code> for the initially selected option, or read and update the element's <code>checked</code> property in JavaScript.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<input type="radio" name="plan" class="input" checked>{% endset %}
|
||||
{{ code_block(code) }}
|
||||
|
||||
<h2 id="invalid-state"><a href="#invalid-state">Invalid state</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Set <code>aria-invalid="true"</code> on the invalid radio and, when using <code>field</code>, set <code>data-invalid="true"</code> on the field wrapper.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<div role="group" class="field flex-row items-start gap-3" data-invalid="true">
|
||||
<input type="radio" id="radio-invalid" name="radio-invalid" class="input" aria-invalid="true">
|
||||
<div class="grid gap-1.5">
|
||||
<label for="radio-invalid">I agree to the terms</label>
|
||||
<p class="field-error">You must select an option to continue.</p>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("radio-invalid-state", code, "w-full max-w-sm") }}
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-form"><a href="#example-form">Form</a></h3>
|
||||
<h3 id="example-basic"><a href="#example-basic">Basic</a></h3>
|
||||
|
||||
{% set code %}<form class="form space-y-6 w-full">
|
||||
<div class="flex flex-col gap-3">
|
||||
<label for="demo-form-radio">Notify me about...</label>
|
||||
<fieldset id="demo-form-radio" class="grid gap-3">
|
||||
<label class="font-normal"><input type="radio" name="demo-form-radio" value="1">All new messages</label>
|
||||
<label class="font-normal"><input type="radio" name="demo-form-radio" value="2">Direct messages and mentions</label>
|
||||
<label class="font-normal"><input type="radio" name="demo-form-radio" value="3" >Nothing</label>
|
||||
</fieldset>
|
||||
{% set code %}<div role="radiogroup" aria-label="View density" data-slot="radio-group">
|
||||
<div role="group" class="field flex-row items-center gap-3">
|
||||
<input type="radio" id="density-default" name="density" value="default" class="input">
|
||||
<label for="density-default">Default</label>
|
||||
</div>
|
||||
<button type="submit" class="btn">Submit</button>
|
||||
</form>{% endset %}
|
||||
{{ code_preview("radio-group-form", code, "w-full max-w-sm") }}
|
||||
<div role="group" class="field flex-row items-center gap-3">
|
||||
<input type="radio" id="density-comfortable" name="density" value="comfortable" class="input" checked>
|
||||
<label for="density-comfortable">Comfortable</label>
|
||||
</div>
|
||||
<div role="group" class="field flex-row items-center gap-3">
|
||||
<input type="radio" id="density-compact" name="density" value="compact" class="input">
|
||||
<label for="density-compact">Compact</label>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("radio-basic", code, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-description"><a href="#example-description">Description</a></h3>
|
||||
|
||||
{% set code %}<div role="radiogroup" aria-label="Notifications" data-slot="radio-group">
|
||||
<div role="group" class="field flex-row items-start gap-3">
|
||||
<input type="radio" id="notify-all" name="notifications" value="all" class="input" checked aria-describedby="notify-all-description">
|
||||
<div class="grid gap-1.5">
|
||||
<label for="notify-all">All new messages</label>
|
||||
<p id="notify-all-description" class="field-description">Receive a notification for every new message.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div role="group" class="field flex-row items-start gap-3">
|
||||
<input type="radio" id="notify-mentions" name="notifications" value="mentions" class="input" aria-describedby="notify-mentions-description">
|
||||
<div class="grid gap-1.5">
|
||||
<label for="notify-mentions">Direct messages and mentions</label>
|
||||
<p id="notify-mentions-description" class="field-description">Only notify me for direct messages and mentions.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("radio-description", code, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-choice-card"><a href="#example-choice-card">Choice Card</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Wrap a <code>.field</code> in a native <code><label></code> when the whole card should select the option.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<div role="radiogroup" aria-label="Plans" data-slot="radio-group" class="w-full max-w-md">
|
||||
<label>
|
||||
<div role="group" class="field flex-row items-center justify-between">
|
||||
<div class="grid gap-1.5">
|
||||
<span>Starter</span>
|
||||
<span class="field-description">For small teams getting started.</span>
|
||||
</div>
|
||||
<input type="radio" name="choice-plan" value="starter" class="input" checked>
|
||||
</div>
|
||||
</label>
|
||||
<label>
|
||||
<div role="group" class="field flex-row items-center justify-between">
|
||||
<div class="grid gap-1.5">
|
||||
<span>Team</span>
|
||||
<span class="field-description">Collaboration tools for growing teams.</span>
|
||||
</div>
|
||||
<input type="radio" name="choice-plan" value="team" class="input">
|
||||
</div>
|
||||
</label>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("radio-choice-card", code, "w-full") }}
|
||||
|
||||
<h3 id="example-fieldset"><a href="#example-fieldset">Fieldset</a></h3>
|
||||
|
||||
{% set code %}<fieldset class="fieldset">
|
||||
<legend>Notify me about...</legend>
|
||||
<div role="radiogroup" aria-label="Notification preference" data-slot="radio-group">
|
||||
<div role="group" class="field flex-row items-center gap-3">
|
||||
<input class="input" type="radio" id="notify-all-fieldset" name="notify-fieldset" value="all" checked>
|
||||
<label for="notify-all-fieldset">All new messages</label>
|
||||
</div>
|
||||
<div role="group" class="field flex-row items-center gap-3">
|
||||
<input class="input" type="radio" id="notify-mentions-fieldset" name="notify-fieldset" value="mentions">
|
||||
<label for="notify-mentions-fieldset">Direct messages and mentions</label>
|
||||
</div>
|
||||
<div role="group" class="field flex-row items-center gap-3">
|
||||
<input class="input" type="radio" id="notify-none-fieldset" name="notify-fieldset" value="none">
|
||||
<label for="notify-none-fieldset">Nothing</label>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>{% endset %}
|
||||
{{ code_preview("radio-fieldset", code, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-disabled"><a href="#example-disabled">Disabled</a></h3>
|
||||
|
||||
{% set code %}<div role="radiogroup" aria-label="Disabled notifications" data-slot="radio-group">
|
||||
<div role="group" class="field flex-row items-center gap-3" data-disabled="true">
|
||||
<input type="radio" id="radio-disabled" name="radio-disabled" class="input" disabled>
|
||||
<label for="radio-disabled">Disabled option</label>
|
||||
</div>
|
||||
<div role="group" class="field flex-row items-center gap-3" data-disabled="true">
|
||||
<input type="radio" id="radio-disabled-checked" name="radio-disabled" class="input" checked disabled>
|
||||
<label for="radio-disabled-checked">Disabled checked option</label>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("radio-disabled", code, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-invalid"><a href="#example-invalid">Invalid</a></h3>
|
||||
|
||||
{% set code %}<fieldset class="fieldset" data-invalid="true">
|
||||
<legend>Plan</legend>
|
||||
<div role="radiogroup" aria-label="Invalid plan" data-slot="radio-group" aria-describedby="radio-group-error">
|
||||
<div role="group" class="field flex-row items-center gap-3" data-invalid="true">
|
||||
<input type="radio" id="radio-invalid-free" name="radio-invalid-plan" class="input" aria-invalid="true">
|
||||
<label for="radio-invalid-free">Free</label>
|
||||
</div>
|
||||
<div role="group" class="field flex-row items-center gap-3" data-invalid="true">
|
||||
<input type="radio" id="radio-invalid-pro" name="radio-invalid-plan" class="input" aria-invalid="true">
|
||||
<label for="radio-invalid-pro">Pro</label>
|
||||
</div>
|
||||
</div>
|
||||
<p id="radio-group-error" class="field-error">Select a plan to continue.</p>
|
||||
</fieldset>{% endset %}
|
||||
{{ code_preview("radio-invalid", code, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Radio group layout uses document direction and logical spacing. Set <code>dir="rtl"</code> on a parent to render right-to-left.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<div dir="rtl" role="radiogroup" aria-label="طريقة العرض" data-slot="radio-group">
|
||||
<div role="group" class="field flex-row items-center gap-3">
|
||||
<input type="radio" id="radio-rtl-default" name="radio-rtl" value="default" class="input" checked>
|
||||
<label for="radio-rtl-default">افتراضي</label>
|
||||
</div>
|
||||
<div role="group" class="field flex-row items-center gap-3">
|
||||
<input type="radio" id="radio-rtl-comfortable" name="radio-rtl" value="comfortable" class="input">
|
||||
<label for="radio-rtl-comfortable">مريح</label>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("radio-rtl", code, "w-full max-w-sm") }}
|
||||
|
||||
+113
-44
@@ -6,8 +6,6 @@ toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
children:
|
||||
- label: HTML
|
||||
id: usage-html
|
||||
- label: HTML + JavaScript
|
||||
id: usage-html-js
|
||||
children:
|
||||
@@ -26,45 +24,40 @@ toc:
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Align
|
||||
id: example-align
|
||||
- label: Groups
|
||||
id: example-groups
|
||||
- label: Scrollable
|
||||
id: example-scrollable
|
||||
- label: Disabled
|
||||
id: example-disabled
|
||||
- label: Invalid
|
||||
id: example-invalid
|
||||
- label: With icon
|
||||
id: example-with-icon
|
||||
- label: Multiple
|
||||
id: example-multiple
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
{% from "select.njk" import select %}
|
||||
|
||||
{% set code_native %}<select class="select w-[180px]">
|
||||
<optgroup label="Fruits">
|
||||
<option>Apple</option>
|
||||
<option>Banana</option>
|
||||
<option>Blueberry</option>
|
||||
<option>Grapes</option>
|
||||
<option>Pineapple</option>
|
||||
</optgroup>
|
||||
</select>{% endset %}
|
||||
{{ code_preview("select-native", code_native) }}
|
||||
|
||||
{% set code_html %}
|
||||
{{ select(
|
||||
trigger_attrs={"class": "w-[180px]"},
|
||||
is_combobox=true,
|
||||
items=[
|
||||
{
|
||||
type: "group",
|
||||
label: "Fruits",
|
||||
label: "Theme",
|
||||
items: [
|
||||
{ type: "item", value: "apple", label: "Apple" },
|
||||
{ type: "item", value: "banana", label: "Banana" },
|
||||
{ type: "item", value: "blueberry", label: "Blueberry" },
|
||||
{ type: "item", value: "grapes", label: "Grapes" },
|
||||
{ type: "item", value: "pineapple", label: "Pineapple" }
|
||||
{ type: "item", value: "light", label: "Light" },
|
||||
{ type: "item", value: "dark", label: "Dark" },
|
||||
{ type: "item", value: "system", label: "System" }
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -78,13 +71,6 @@ toc:
|
||||
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<h3 id="usage-html"><a href="#usage-html">HTML</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>If you use a <code><select></code> element, just add the <code>select</code> class to it or have a parent with the <code>form</code> class (<a href="/components/form">read more about form</a>).</p>
|
||||
</section>
|
||||
|
||||
{{ code_block(code_native) }}
|
||||
|
||||
<h3 id="usage-html-js"><a href="#usage-html-js">HTML + JavaScript</a></h3>
|
||||
|
||||
@@ -103,7 +89,7 @@ toc:
|
||||
Components with JavaScript
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="/installation/#install-cli">
|
||||
<a class="badge-outline" href="/cli">
|
||||
Use the CLI
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
@@ -306,7 +292,7 @@ toc:
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
<a class="badge-outline" href="/installation/#install-macros" target="_blank">
|
||||
<a class="badge-outline" href="/cli#macros" target="_blank">
|
||||
Use Nunjucks or Jinja macros
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
@@ -339,6 +325,62 @@ toc:
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-align"><a href="#example-align">Align</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Use <code>data-align</code> on the popover to align the list to the trigger edge. Basecoat keeps the popup below the control; it does not implement shadcn/ui's selected-item-over-trigger positioning.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}
|
||||
<div class="flex flex-wrap gap-3">
|
||||
{{ select(
|
||||
id="select-align-start",
|
||||
trigger_attrs={"class": "w-[180px]"},
|
||||
popover_attrs={"data-align": "start"},
|
||||
items=[
|
||||
{ value: "light", label: "Light" },
|
||||
{ value: "dark", label: "Dark" },
|
||||
{ value: "system", label: "System" }
|
||||
]
|
||||
) }}
|
||||
{{ select(
|
||||
id="select-align-end",
|
||||
trigger_attrs={"class": "w-[180px]"},
|
||||
popover_attrs={"data-align": "end"},
|
||||
items=[
|
||||
{ value: "light", label: "Light" },
|
||||
{ value: "dark", label: "Dark" },
|
||||
{ value: "system", label: "System" }
|
||||
]
|
||||
) }}
|
||||
</div>
|
||||
{% endset %}
|
||||
{{ code_preview("select-align", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-groups"><a href="#example-groups">Groups</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{% call select(
|
||||
id="select-groups",
|
||||
trigger_attrs={"class": "w-[220px]"}
|
||||
) %}
|
||||
<div role="group" aria-labelledby="select-groups-fruits">
|
||||
<div role="heading" id="select-groups-fruits">Fruits</div>
|
||||
<div role="option" data-value="apple">Apple</div>
|
||||
<div role="option" data-value="banana">Banana</div>
|
||||
<div role="option" data-value="blueberry">Blueberry</div>
|
||||
</div>
|
||||
<hr role="separator">
|
||||
<div role="group" aria-labelledby="select-groups-vegetables">
|
||||
<div role="heading" id="select-groups-vegetables">Vegetables</div>
|
||||
<div role="option" data-value="carrot">Carrot</div>
|
||||
<div role="option" data-value="potato">Potato</div>
|
||||
<div role="option" data-value="tomato">Tomato</div>
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% endset %}
|
||||
{{ code_preview("select-groups", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-scrollable"><a href="#example-scrollable">Scrollable</a></h3>
|
||||
|
||||
{% set code %}
|
||||
@@ -365,22 +407,27 @@ toc:
|
||||
|
||||
{{ code_preview("select-disabled", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-invalid-state"><a href="#example-invalid-state">Invalid State</a></h3>
|
||||
<h3 id="example-invalid"><a href="#example-invalid">Invalid</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{{ select(
|
||||
trigger_attrs={"aria-invalid": "true"},
|
||||
items=[
|
||||
{ value: "", label: "Select a role" },
|
||||
{ value: "admin", label: "Admin" },
|
||||
{ value: "editor", label: "Editor" },
|
||||
{ value: "viewer", label: "Viewer" },
|
||||
{ value: "guest", label: "Guest" }
|
||||
]
|
||||
) }}
|
||||
<div role="group" class="field" data-invalid="true">
|
||||
<label for="select-invalid-trigger">Role</label>
|
||||
{{ select(
|
||||
id="select-invalid",
|
||||
trigger_attrs={"aria-invalid": "true", "class": "w-[220px]"},
|
||||
items=[
|
||||
{ value: "", label: "Select a role" },
|
||||
{ value: "admin", label: "Admin" },
|
||||
{ value: "editor", label: "Editor" },
|
||||
{ value: "viewer", label: "Viewer" },
|
||||
{ value: "guest", label: "Guest" }
|
||||
]
|
||||
) }}
|
||||
<p class="field-error" role="alert">Select a role to continue.</p>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("select-disabled", code | prettyHtml) }}
|
||||
{{ code_preview("select-invalid", code | prettyHtml, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-with-icon"><a href="#example-with-icon">With icon</a></h3>
|
||||
|
||||
@@ -389,19 +436,19 @@ toc:
|
||||
id="select-with-icons",
|
||||
trigger_attrs={'class': 'w-[180px]'}
|
||||
) %}
|
||||
<div type="button" role="option" data-value="bar">
|
||||
<div role="option" data-value="bar">
|
||||
<span class="flex items-center gap-2">
|
||||
{% lucide "chart-bar", {"class": "text-muted-foreground"} %}
|
||||
Bar
|
||||
</span>
|
||||
</div>
|
||||
<div type="button" role="option" data-value="line">
|
||||
<div role="option" data-value="line">
|
||||
<span class="flex items-center gap-2">
|
||||
{% lucide "chart-line", {"class": "text-muted-foreground"} %}
|
||||
Line
|
||||
</span>
|
||||
</div>
|
||||
<div type="button" role="option" data-value="pie">
|
||||
<div role="option" data-value="pie">
|
||||
<span class="flex items-center gap-2">
|
||||
{% lucide "chart-pie", {"class": "text-muted-foreground"} %}
|
||||
Pie
|
||||
@@ -415,7 +462,7 @@ toc:
|
||||
<h3 id="example-multiple"><a href="#example-multiple">Multiple</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Enable multiple selection by setting adding <code>aria-multiselectable="true"</code> to the <code>listbox</code>. The selected options are displayed as a comma-separated list in the trigger button. Use <code>data-label</code> to specify clean text labels for options that contain HTML (like icons), otherwise the text content will be extracted automatically.</p>
|
||||
<p>Enable multiple selection by adding <code>aria-multiselectable="true"</code> to the <code>listbox</code>. The selected options are displayed as a comma-separated list in the trigger button. Use <code>data-label</code> to specify clean text labels for options that contain HTML (like icons), otherwise the text content will be extracted automatically.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}
|
||||
@@ -455,3 +502,25 @@ toc:
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("select-multiple", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Set <code>dir="rtl"</code> on the select root or a parent element.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}
|
||||
<div dir="rtl">
|
||||
{{ select(
|
||||
id="select-rtl",
|
||||
trigger_attrs={"class": "w-[180px]"},
|
||||
popover_attrs={"data-align": "start"},
|
||||
items=[
|
||||
{ value: "light", label: "فاتح" },
|
||||
{ value: "dark", label: "داكن" },
|
||||
{ value: "system", label: "النظام" }
|
||||
]
|
||||
) }}
|
||||
</div>
|
||||
{% endset %}
|
||||
{{ code_preview("select-rtl", code | prettyHtml) }}
|
||||
|
||||
@@ -15,8 +15,10 @@ toc:
|
||||
id: usage-html-js-2
|
||||
- label: HTML structure
|
||||
id: usage-html-js-3
|
||||
- label: JavaScript events
|
||||
- label: Basecoat differences
|
||||
id: usage-html-js-4
|
||||
- label: JavaScript events
|
||||
id: usage-html-js-5
|
||||
- label: Jinja and Nunjucks
|
||||
id: usage-macro
|
||||
---
|
||||
@@ -43,7 +45,7 @@ toc:
|
||||
Components with JavaScript
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="/installation/#install-cli">
|
||||
<a class="badge-outline" href="/cli">
|
||||
Use the CLI
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
@@ -90,80 +92,60 @@ toc:
|
||||
<section class="prose">
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><aside class="sidebar" aria-hidden="false"></code></dt>
|
||||
<dd>Wraps around the entire component. It can have the following attributes:
|
||||
<dd>Wraps the sidebar component. Supported attributes:
|
||||
<ul>
|
||||
<li><code>aria-hidden="true"</code>: controls the default state of the sidebar (hidden or visible).</li>
|
||||
<li><code>data-side="left"</code>: specifies the side of the sidebar (<code>left</code> or <code>right</code>, defaults to <code>left</code>).</li>
|
||||
<li><code>id</code>: target a specific sidebar from the <code>basecoat:sidebar</code> event.</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>
|
||||
<li><code>data-initial-mobile-open</code>: set to <code>true</code> to start open below the breakpoint.</li>
|
||||
<li><code>data-breakpoint</code>: pixel width used to switch between desktop and mobile behavior. Defaults to <code>768</code>.</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><nav></code></dt>
|
||||
<dd>
|
||||
<p>The navigation element that contains the sidebar's content. It can 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="menu"</code>: indicates that the button opens a menu.</li>
|
||||
<li><code>aria-controls="{ MENU_ID }"</code>: points to the menu's id.</li>
|
||||
<li><code>aria-expanded="false"</code>: tracks the popover's state.</li>
|
||||
</ul>
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><header></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>The header of the sidebar.</dd>
|
||||
<dt><code class="highlight language-html"><section></code></dt>
|
||||
<dd>The main navigation list.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><div role="group"></code></dt>
|
||||
<dd>Group of options, can have a <code>aria-labelledby</code> attribute to link to a heading.</dd>
|
||||
<dt><code class="highlight language-html"><span role="heading"></code></dt>
|
||||
<dd>Group heading, must have an <code>id</code> attribute if you use the <code>aria-labelledby</code> attribute on the group.</dd>
|
||||
<dt><code class="highlight language-html"><ul></code></dt>
|
||||
<dd>List of links or buttons.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><li></code></dt>
|
||||
<dd>Individual item.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><a></code></dt>
|
||||
<dd>A link. By default, clicking on a link will close the sidebar on mobile unless the <code>data-keep-mobile-sidebar-open</code> attribute is present. Set <code>aria-current="page"</code> on the active link.</dd>
|
||||
<dt><code class="highlight language-html"><button></code></dt>
|
||||
<dd>A button. By default, clicking on a button will close the sidebar on mobile unless the <code>data-keep-mobile-sidebar-open</code> attribute is present. If your app uses buttons for navigation, set <code>aria-current="page"</code> on the active item.</dd>
|
||||
<dt><code class="highlight language-html"><details></code></dt>
|
||||
<dd>Collapsible section.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><summary></code></dt>
|
||||
<dd>Summary of the collapsible section.</dd>
|
||||
<dt><code class="highlight language-html"><ul></code></dt>
|
||||
<dd>List of links or buttons.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code class="highlight language-html"><footer></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>The footer of the sidebar.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code class="highlight language-html"><nav aria-label="..."></code></dt>
|
||||
<dd>The semantic navigation landmark inside the sidebar.</dd>
|
||||
<dt><code class="highlight language-html"><header></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Top area for branding, workspace switchers, or primary controls.</dd>
|
||||
<dt><code class="highlight language-html"><section></code></dt>
|
||||
<dd>Scrollable sidebar content.</dd>
|
||||
<dt><code class="highlight language-html"><div role="group" aria-labelledby="..."></code></dt>
|
||||
<dd>A grouped set of navigation items. Use an <code>h3</code> with a matching <code>id</code> when the group has a visible label.</dd>
|
||||
<dt><code class="highlight language-html"><ul></code> / <code class="highlight language-html"><li></code></dt>
|
||||
<dd>Navigation list structure. Items may contain an <code>a</code>, a <code>button</code>, or a <code>details</code> submenu.</dd>
|
||||
<dt><code class="highlight language-html"><a aria-current="page"></code></dt>
|
||||
<dd>Use <code>aria-current="page"</code> for the active link. You can also use <code>data-active="true"</code> for non-link active items.</dd>
|
||||
<dt><code class="highlight language-html"><button type="button"></code></dt>
|
||||
<dd>Use buttons for actions. On mobile, links and buttons close the sidebar unless they include <code>data-keep-mobile-sidebar-open</code>.</dd>
|
||||
<dt><code class="highlight language-html"><details></code> / <code class="highlight language-html"><summary></code></dt>
|
||||
<dd>Use native disclosure for submenus. The nested <code>ul</code> contains the submenu items.</dd>
|
||||
<dt><code class="highlight language-html"><footer></code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd>Bottom area for account controls, settings, or secondary actions.</dd>
|
||||
<dt><code class="highlight language-html"><main></code></dt>
|
||||
<dd>A wrapper for the content of the page.</dd>
|
||||
<dd>The sibling content wrapper. Basecoat applies the desktop margin to the sibling that follows the sidebar.</dd>
|
||||
<dt><code class="highlight language-html"><button type="button" onclick="document.dispatchEvent(new CustomEvent('basecoat:sidebar'))"></code></dt>
|
||||
<dd>A button to toggle the sidebar. If you want to use multiple sidebars you will need to add unique ids to the sidebars (i.e. <code><aside class="sidebar" id="{SIDEBAR_ID}"></code>) and refer to them in the event <code>detail</code> (i.e. <code>document.dispatchEvent(new CustomEvent('basecoat:sidebar', { detail: { id: '{SIDEBAR_ID}' } }));</code>).</dd>
|
||||
<dd>Dispatches the event used to toggle, open, or close the sidebar.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
{% set code_current %}<!-- Server-side or router-driven current link -->
|
||||
<nav>
|
||||
{% set code_current %}<!-- Server-side or router-driven current item -->
|
||||
<nav aria-label="Sidebar navigation">
|
||||
<ul>
|
||||
<li><a href="/my-path?page=overview">Overview</a></li>
|
||||
<li><a href="/my-path?page=settings" aria-current="page">Settings</a></li>
|
||||
<li><button type="button" data-active="true">Active action</button></li>
|
||||
</ul>
|
||||
</nav>{% endset %}
|
||||
{{ code_block(code_current | prettyHtml, "html") }}
|
||||
|
||||
<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">Basecoat differences</a></h4>
|
||||
|
||||
<section class="prose">
|
||||
<p>Basecoat keeps sidebar intentionally smaller than shadcn/ui. It supports fixed left/right sidebars, mobile overlay behavior, grouped navigation, active states, and native <code>details</code> submenus.</p>
|
||||
<p>It does not currently expose shadcn/ui's React provider, rail, inset/floating variants, icon-only collapse mode, menu actions, menu badges, or menu skeleton API. Those can be composed manually if needed, but they are not part of the Basecoat sidebar contract.</p>
|
||||
<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>
|
||||
|
||||
<section class="prose">
|
||||
<dl>
|
||||
@@ -190,7 +172,7 @@ toc:
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
<a class="badge-outline" href="/installation/#install-macros" target="_blank">
|
||||
<a class="badge-outline" href="/cli#macros" target="_blank">
|
||||
Use Nunjucks or Jinja macros
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
|
||||
@@ -8,44 +8,103 @@ toc:
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Avatar
|
||||
id: example-avatar
|
||||
- label: Card
|
||||
id: example-card
|
||||
- label: Text
|
||||
id: example-text
|
||||
- label: Form
|
||||
id: example-form
|
||||
- label: Table
|
||||
id: example-table
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
|
||||
<div class="alert mb-6">
|
||||
{% lucide "circle-alert" %}
|
||||
<h2>There is no dedicated skeleton component in Basecoat.</h2>
|
||||
</div>
|
||||
|
||||
{% set code %}<div class="flex items-center gap-4">
|
||||
<div class="bg-accent animate-pulse size-10 shrink-0 rounded-full"></div>
|
||||
<div class="skeleton size-10 shrink-0 rounded-full"></div>
|
||||
<div class="grid gap-2">
|
||||
<div class="bg-accent animate-pulse rounded-md h-4 w-[150px]"></div>
|
||||
<div class="bg-accent animate-pulse rounded-md h-4 w-[100px]"></div>
|
||||
<div class="skeleton h-4 w-[150px]"></div>
|
||||
<div class="skeleton h-4 w-[100px]"></div>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("skeleton", code | prettyHtml) }}
|
||||
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<p class="prose">Simply use the <code>animate-pulse</code> class to create a skeleton.</p>
|
||||
<p class="prose">Use the <code>skeleton</code> class with sizing utilities to create loading placeholders.</p>
|
||||
|
||||
{{ code_block(code) }}
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-avatar"><a href="#example-avatar">Avatar</a></h3>
|
||||
|
||||
{% set code %}<div class="flex items-center gap-4">
|
||||
<div class="skeleton size-12 rounded-full"></div>
|
||||
<div class="grid gap-2">
|
||||
<div class="skeleton h-4 w-32"></div>
|
||||
<div class="skeleton h-4 w-48"></div>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("skeleton-avatar", code | prettyHtml) }}
|
||||
|
||||
<h3 id="example-card"><a href="#example-card">Card</a></h3>
|
||||
|
||||
{% set code %}<div class="card w-full @md:w-auto @md:min-w-sm">
|
||||
<header>
|
||||
<div class="bg-accent animate-pulse rounded-md h-4 w-2/3"></div>
|
||||
<div class="bg-accent animate-pulse rounded-md h-4 w-1/2"></div>
|
||||
<div class="skeleton h-4 w-2/3"></div>
|
||||
<div class="skeleton h-4 w-1/2"></div>
|
||||
</header>
|
||||
<section>
|
||||
<div class="bg-accent animate-pulse rounded-md aspect-square w-full"></div>
|
||||
<div class="skeleton aspect-square w-full"></div>
|
||||
</section>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("skeleton-card", code | prettyHtml, "w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-text"><a href="#example-text">Text</a></h3>
|
||||
|
||||
{% set code %}<div class="grid gap-2">
|
||||
<div class="skeleton h-4 w-full"></div>
|
||||
<div class="skeleton h-4 w-5/6"></div>
|
||||
<div class="skeleton h-4 w-2/3"></div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("skeleton-text", code | prettyHtml, "w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-form"><a href="#example-form">Form</a></h3>
|
||||
|
||||
{% set code %}<div class="grid gap-4 w-full max-w-sm">
|
||||
<div class="skeleton h-4 w-24"></div>
|
||||
<div class="skeleton h-9 w-full"></div>
|
||||
<div class="skeleton h-4 w-32"></div>
|
||||
<div class="skeleton h-9 w-full"></div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("skeleton-form", code | prettyHtml, "w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-table"><a href="#example-table">Table</a></h3>
|
||||
|
||||
{% set code %}<div class="grid gap-3 w-full">
|
||||
<div class="skeleton h-8 w-full"></div>
|
||||
<div class="skeleton h-8 w-full"></div>
|
||||
<div class="skeleton h-8 w-full"></div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("skeleton-table", code | prettyHtml, "w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Skeleton blocks are direction-neutral. Use logical layout utilities around them when spacing depends on direction.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<div dir="rtl" class="flex items-center gap-4">
|
||||
<div class="skeleton size-10 shrink-0 rounded-full"></div>
|
||||
<div class="grid gap-2">
|
||||
<div class="skeleton h-4 w-[150px]"></div>
|
||||
<div class="skeleton h-4 w-[100px]"></div>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("skeleton-rtl", code | prettyHtml) }}
|
||||
|
||||
@@ -5,6 +5,22 @@ description: An input where the user selects a value from within a given range.
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
children:
|
||||
- label: HTML + JavaScript
|
||||
id: usage-html-js
|
||||
- label: HTML structure
|
||||
id: usage-structure
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Default
|
||||
id: example-default
|
||||
- label: Controlled Label
|
||||
id: example-controlled-label
|
||||
- label: Disabled
|
||||
id: example-disabled
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
@@ -16,32 +32,78 @@ toc:
|
||||
min="0"
|
||||
max="100"
|
||||
value="50"
|
||||
>
|
||||
<script>
|
||||
(() => {
|
||||
const sliders = document.querySelectorAll('input[type="range"].input');
|
||||
if (!sliders) return;
|
||||
|
||||
const updateSlider = (el) => {
|
||||
const min = parseFloat(el.min || 0);
|
||||
const max = parseFloat(el.max || 100);
|
||||
const value = parseFloat(el.value);
|
||||
const percent = (max === min) ? 0 : ((value - min) / (max - min)) * 100;
|
||||
el.style.setProperty('--slider-value', `${percent}%`);
|
||||
};
|
||||
|
||||
sliders.forEach(slider => {
|
||||
updateSlider(slider);
|
||||
slider.addEventListener('input', (event) => updateSlider(event.target));
|
||||
});
|
||||
})();
|
||||
</script>{% endset %}
|
||||
>{% endset %}
|
||||
{{ code_preview("slider", code, "w-full max-w-sm") }}
|
||||
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<h3 id="usage-html-js"><a href="#usage-html-js">HTML + JavaScript</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>You can style an <code><input type="range"></code> element by either adding the <code>input</code> class or having a parent with the <code>form</code> class (<a href="/components/form">read more about form</a>). However, the range input also requries some Javacript code to be able to paint the left side of the slider.</p>
|
||||
<p>Use a native <code><input type="range"></code> with the <code>input</code> class. Include <code>range.js</code> so Basecoat can keep the filled track in sync with the input value.</p>
|
||||
</section>
|
||||
|
||||
{{ code_block(code) }}
|
||||
{% set code_script %}<script src="https://cdn.jsdelivr.net/npm/basecoat-css@{{ pkg.version }}/dist/js/basecoat.min.js" defer></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/basecoat-css@{{ pkg.version }}/dist/js/range.min.js" defer></script>{% 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
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/js/range.js" target="_blank">
|
||||
range.js
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{ code_block(code | prettyHtml, "html") }}
|
||||
|
||||
<h3 id="usage-structure"><a href="#usage-structure">HTML structure</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><input type="range" class="input"></code></dt>
|
||||
<dd>Native range input styled as a slider. Width utilities such as <code>w-full</code> can be added directly.</dd>
|
||||
</dl>
|
||||
<p>Basecoat intentionally uses the native range input. Unlike shadcn/ui's Base UI slider, it does not support multiple thumbs or vertical orientation.</p>
|
||||
</section>
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-default"><a href="#example-default">Default</a></h3>
|
||||
|
||||
{% set code %}<input type="range" class="input w-full" min="0" max="100" value="50">{% endset %}
|
||||
{{ code_preview("slider-default", code, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-controlled-label"><a href="#example-controlled-label">Controlled Label</a></h3>
|
||||
|
||||
{% set code %}<div class="grid w-full gap-3 max-w-sm">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<label class="label" for="temperature">Temperature</label>
|
||||
<output class="text-sm text-muted-foreground" for="temperature">50</output>
|
||||
</div>
|
||||
<input
|
||||
id="temperature"
|
||||
type="range"
|
||||
class="input w-full"
|
||||
min="0"
|
||||
max="100"
|
||||
value="50"
|
||||
oninput="this.previousElementSibling.querySelector('output').value = this.value"
|
||||
>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("slider-controlled-label", code, "w-full") }}
|
||||
|
||||
<h3 id="example-disabled"><a href="#example-disabled">Disabled</a></h3>
|
||||
|
||||
{% set code %}<input type="range" class="input w-full" min="0" max="100" value="50" disabled>{% endset %}
|
||||
{{ code_preview("slider-disabled", code, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
{% set code %}<div dir="rtl" class="w-full max-w-sm">
|
||||
<input type="range" class="input w-full" min="0" max="100" value="50">
|
||||
</div>{% endset %}
|
||||
{{ code_preview("slider-rtl", code, "w-full") }}
|
||||
|
||||
+124
-24
@@ -5,50 +5,150 @@ description: A control that allows the user to toggle between checked and not ch
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
- label: Checked state
|
||||
id: checked-state
|
||||
- label: Invalid state
|
||||
id: invalid-state
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Form
|
||||
id: example-form
|
||||
- label: Basic
|
||||
id: example-basic
|
||||
- label: Description
|
||||
id: example-description
|
||||
- label: Choice Card
|
||||
id: example-choice-card
|
||||
- label: Disabled
|
||||
id: example-disabled
|
||||
- label: Invalid
|
||||
id: example-invalid
|
||||
- label: Size
|
||||
id: example-size
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
|
||||
{% set code %}<label class="label">
|
||||
<input type="checkbox" name="switch" role="switch" class="input">
|
||||
Airplane Mode
|
||||
</label>{% endset %}
|
||||
{{ code_preview("switch", code) }}
|
||||
{% set code %}<div role="group" class="field flex-row items-center justify-between">
|
||||
<label for="airplane-mode">Airplane Mode</label>
|
||||
<input type="checkbox" id="airplane-mode" role="switch" class="input">
|
||||
</div>{% endset %}
|
||||
{{ code_preview("switch", code, "w-full max-w-sm") }}
|
||||
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>You can style a <code><input type="checkbox"></code> element as a switch by adding a <code>role="switch"</code> attribute, and adding the <code>input</code> class or having a parent with the <code>form</code> class (<a href="/components/form">read more about form</a>).</p>
|
||||
<p>Add <code>role="switch"</code> and the <code>input</code> class to a native <code><input type="checkbox"></code>. Use a native <code><label></code> or <code>aria-label</code> for accessible naming.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<input type="checkbox" role="switch" class="input">{% endset %}
|
||||
{{ code_block(code) }}
|
||||
|
||||
<h2 id="checked-state"><a href="#checked-state">Checked state</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Use native HTML state. Add <code>checked</code> for an initially enabled switch, or read and update the element's <code>checked</code> property in JavaScript.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<input type="checkbox" role="switch" class="input" checked>{% endset %}
|
||||
{{ code_block(code) }}
|
||||
|
||||
<h2 id="invalid-state"><a href="#invalid-state">Invalid state</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Set <code>aria-invalid="true"</code> on the switch and, when using <code>field</code>, set <code>data-invalid="true"</code> on the field wrapper.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<div role="group" class="field flex-row items-start justify-between gap-3" data-invalid="true">
|
||||
<div class="grid gap-1.5">
|
||||
<label for="switch-invalid-state">Marketing emails</label>
|
||||
<p class="field-error">Choose whether marketing emails are enabled.</p>
|
||||
</div>
|
||||
<input type="checkbox" id="switch-invalid-state" role="switch" class="input" aria-invalid="true">
|
||||
</div>{% endset %}
|
||||
{{ code_preview("switch-invalid-state", code, "w-full max-w-sm") }}
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-form"><a href="#example-form">Form</a></h3>
|
||||
<h3 id="example-basic"><a href="#example-basic">Basic</a></h3>
|
||||
|
||||
{% set code %}<form class="form grid gap-4">
|
||||
<h3 class="text-lg font-medium">Email Notifications</h3>
|
||||
<div class="gap-2 flex flex-row items-start justify-between rounded-lg border p-4 shadow-xs">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<label for="demo-form-switch" class="leading-normal">Marketing emails</label>
|
||||
<p class="text-muted-foreground text-sm">Receive emails about new products, features, and more.</p>
|
||||
</div>
|
||||
<input type="checkbox" id="demo-form-switch" role="switch">
|
||||
{% set code %}<div role="group" class="field flex-row items-center justify-between">
|
||||
<label for="switch-basic">Airplane Mode</label>
|
||||
<input type="checkbox" id="switch-basic" role="switch" class="input">
|
||||
</div>{% endset %}
|
||||
{{ code_preview("switch-basic", code, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-description"><a href="#example-description">Description</a></h3>
|
||||
|
||||
{% set code %}<div role="group" class="field flex-row items-start justify-between gap-3">
|
||||
<div class="grid gap-1.5">
|
||||
<label for="switch-description">Marketing emails</label>
|
||||
<p id="switch-description-text" class="field-description">Receive emails about new products, features, and more.</p>
|
||||
</div>
|
||||
<div class="gap-2 flex flex-row items-start justify-between rounded-lg border p-4 shadow-xs">
|
||||
<div class="flex flex-col gap-0.5 opacity-60">
|
||||
<label for="demo-form-switch-disabled" class="leading-normal">Marketing emails</label>
|
||||
<p class="text-muted-foreground text-sm">Receive emails about new products, features, and more.</p>
|
||||
<input type="checkbox" id="switch-description" role="switch" class="input" aria-describedby="switch-description-text" checked>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("switch-description", code, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-choice-card"><a href="#example-choice-card">Choice Card</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Wrap a <code>.field</code> in a native <code><label></code> when the whole card should toggle the switch.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<label class="w-full max-w-md">
|
||||
<div role="group" class="field flex-row items-center justify-between">
|
||||
<div class="grid gap-1.5">
|
||||
<span>Marketing emails</span>
|
||||
<span class="field-description">Receive emails about new products, features, and more.</span>
|
||||
</div>
|
||||
<input type="checkbox" id="demo-form-switch-disabled" role="switch" disabled>
|
||||
<input type="checkbox" role="switch" class="input" checked>
|
||||
</div>
|
||||
</form>{% endset %}
|
||||
{{ code_preview("switch-form", code, "w-full") }}
|
||||
</label>{% endset %}
|
||||
{{ code_preview("switch-choice-card", code, "w-full") }}
|
||||
|
||||
<h3 id="example-disabled"><a href="#example-disabled">Disabled</a></h3>
|
||||
|
||||
{% set code %}<div role="group" class="field flex-row items-center justify-between" data-disabled="true">
|
||||
<label for="switch-disabled">Airplane Mode</label>
|
||||
<input type="checkbox" id="switch-disabled" role="switch" class="input" disabled>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("switch-disabled", code, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-invalid"><a href="#example-invalid">Invalid</a></h3>
|
||||
|
||||
{% set code %}<div role="group" class="field flex-row items-start justify-between gap-3" data-invalid="true">
|
||||
<div class="grid gap-1.5">
|
||||
<label for="switch-invalid">Marketing emails</label>
|
||||
<p class="field-error">Choose whether marketing emails are enabled.</p>
|
||||
</div>
|
||||
<input type="checkbox" id="switch-invalid" role="switch" class="input" aria-invalid="true">
|
||||
</div>{% endset %}
|
||||
{{ code_preview("switch-invalid", code, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-size"><a href="#example-size">Size</a></h3>
|
||||
|
||||
{% set code %}<div class="grid gap-4">
|
||||
<div role="group" class="field flex-row items-center justify-between">
|
||||
<label for="switch-size-default">Default</label>
|
||||
<input type="checkbox" id="switch-size-default" role="switch" class="input" checked>
|
||||
</div>
|
||||
<div role="group" class="field flex-row items-center justify-between">
|
||||
<label for="switch-size-sm">Small</label>
|
||||
<input type="checkbox" id="switch-size-sm" role="switch" class="input" data-size="sm" checked>
|
||||
</div>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("switch-size", code, "w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Switch field layout uses document direction. Set <code>dir="rtl"</code> on a parent to render right-to-left.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<div dir="rtl" class="field flex-row items-center justify-between">
|
||||
<label for="switch-rtl">وضع الطيران</label>
|
||||
<input type="checkbox" id="switch-rtl" role="switch" class="input" checked>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("switch-rtl", code, "w-full max-w-sm") }}
|
||||
|
||||
@@ -5,12 +5,21 @@ description: A responsive table component.
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Footer
|
||||
id: example-footer
|
||||
- label: Actions
|
||||
id: example-actions
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
|
||||
{% set code %}<div class="overflow-x-auto">
|
||||
{% set code %}<div class="table-container">
|
||||
<table class="table">
|
||||
<caption>A list of your recent invoices.</caption>
|
||||
<thead>
|
||||
@@ -26,49 +35,49 @@ toc:
|
||||
<td class="font-medium">INV001</td>
|
||||
<td>Paid</td>
|
||||
<td>Credit Card</td>
|
||||
<td class="text-right">$250.00</td>
|
||||
<td class="text-end">$250.00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font-medium">INV002</td>
|
||||
<td>Pending</td>
|
||||
<td>PayPal</td>
|
||||
<td class="text-right">$150.00</td>
|
||||
<td class="text-end">$150.00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font-medium">INV003</td>
|
||||
<td>Unpaid</td>
|
||||
<td>Bank Transfer</td>
|
||||
<td class="text-right">$350.00</td>
|
||||
<td class="text-end">$350.00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font-medium">INV004</td>
|
||||
<td>Paid</td>
|
||||
<td>Paypal</td>
|
||||
<td class="text-right">$450.00</td>
|
||||
<td class="text-end">$450.00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font-medium">INV005</td>
|
||||
<td>Paid</td>
|
||||
<td>Credit Card</td>
|
||||
<td class="text-right">$550.00</td>
|
||||
<td class="text-end">$550.00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font-medium">INV006</td>
|
||||
<td>Pending</td>
|
||||
<td>Bank Transfer</td>
|
||||
<td class="text-right">$200.00</td>
|
||||
<td class="text-end">$200.00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font-medium">INV007</td>
|
||||
<td>Unpaid</td>
|
||||
<td>Credit Card</td>
|
||||
<td class="text-right">$300.00</td>
|
||||
<td class="text-end">$300.00</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="3">Total</td>
|
||||
<td class="text-right">$2,500.00</td>
|
||||
<td class="text-end">$2,500.00</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
@@ -78,10 +87,85 @@ toc:
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Simply add the <code>table</code> class to your table element.</p>
|
||||
<p>Add the <code>table</code> class to your table element. Wrap it in <code>table-container</code> when horizontal scrolling is needed.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<table class="table">
|
||||
{% set code %}<div class="table-container">
|
||||
<table class="table">
|
||||
<!-- Content of your table -->
|
||||
</table>
|
||||
</div>{% endset %}
|
||||
{{ code_block(code) }}
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-footer"><a href="#example-footer">Footer</a></h3>
|
||||
|
||||
{% set code_footer %}<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Invoice</th>
|
||||
<th class="text-end">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>INV001</td>
|
||||
<td class="text-end">$250.00</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td class="text-end">$250.00</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>{% endset %}
|
||||
{{ code_block(code) }}
|
||||
{{ code_preview("table-footer", code_footer, "w-full") }}
|
||||
|
||||
<h3 id="example-actions"><a href="#example-actions">Actions</a></h3>
|
||||
|
||||
{% set code_actions %}<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="font-medium">Project Alpha</td>
|
||||
<td class="text-end">
|
||||
<button type="button" class="btn-xs-outline">Open</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>{% endset %}
|
||||
{{ code_preview("table-actions", code_actions, "w-full") }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Tables support right-to-left layouts through native table direction and logical alignment utilities such as <code>text-end</code>.</p>
|
||||
</section>
|
||||
|
||||
{% set code_rtl %}<div class="table-container" dir="rtl">
|
||||
<table class="table">
|
||||
<caption>قائمة الفواتير الأخيرة.</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>الفاتورة</th>
|
||||
<th>الحالة</th>
|
||||
<th class="text-end">المبلغ</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="font-medium">INV001</td>
|
||||
<td>مدفوعة</td>
|
||||
<td class="text-end">$250.00</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("table-rtl", code_rtl, "w-full") }}
|
||||
|
||||
+154
-87
@@ -1,17 +1,17 @@
|
||||
---
|
||||
layout: layouts/page.njk
|
||||
title: Tabs
|
||||
description: A set of layered sections of content—known as tab panels—that are displayed one at a time.
|
||||
description: A set of layered sections of content that are displayed one at a time.
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
children:
|
||||
- label: HTML + JavaScript
|
||||
id: usage-default
|
||||
id: usage-html-js
|
||||
children:
|
||||
- label: "Step 1: Include the JavaScript files"
|
||||
id: usage-html-js-1
|
||||
- label: "Step 2: Add your select HTML"
|
||||
- label: "Step 2: Add your tabs HTML"
|
||||
id: usage-html-js-2
|
||||
- label: HTML structure
|
||||
id: usage-html-js-3
|
||||
@@ -19,13 +19,27 @@ toc:
|
||||
id: usage-html-js-4
|
||||
- label: Jinja and Nunjucks
|
||||
id: usage-macro
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Line
|
||||
id: example-line
|
||||
- label: Vertical
|
||||
id: example-vertical
|
||||
- label: Disabled
|
||||
id: example-disabled
|
||||
- label: Icons
|
||||
id: example-icons
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
- label: API Reference
|
||||
id: api-reference
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
{% from "tabs.njk" import tabs %}
|
||||
|
||||
{% set code_html %}
|
||||
{% set account_panel %}
|
||||
<div class="card">
|
||||
<header>
|
||||
@@ -33,14 +47,14 @@ toc:
|
||||
<p>Make changes to your account here. Click save when you're done.</p>
|
||||
</header>
|
||||
<section>
|
||||
<form class="form grid gap-6">
|
||||
<div class="grid gap-3">
|
||||
<form class="grid gap-4">
|
||||
<div role="group" class="field">
|
||||
<label for="demo-tabs-account-name">Name</label>
|
||||
<input type="text" id="demo-tabs-account-name" value="Pedro Duarte" />
|
||||
<input type="text" id="demo-tabs-account-name" value="Pedro Duarte">
|
||||
</div>
|
||||
<div class="grid gap-3">
|
||||
<div role="group" class="field">
|
||||
<label for="demo-tabs-account-username">Username</label>
|
||||
<input type="text" id="demo-tabs-account-username" value="@peduarte" />
|
||||
<input type="text" id="demo-tabs-account-username" value="@peduarte">
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
@@ -49,6 +63,7 @@ toc:
|
||||
</footer>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{% set password_panel %}
|
||||
<div class="card">
|
||||
<header>
|
||||
@@ -56,38 +71,43 @@ toc:
|
||||
<p>Change your password here. After saving, you'll be logged out.</p>
|
||||
</header>
|
||||
<section>
|
||||
<form class="form grid gap-6">
|
||||
<div class="grid gap-3">
|
||||
<form class="grid gap-4">
|
||||
<div role="group" class="field">
|
||||
<label for="demo-tabs-password-current">Current password</label>
|
||||
<input type="password" id="demo-tabs-password-current" />
|
||||
<input type="password" id="demo-tabs-password-current">
|
||||
</div>
|
||||
<div class="grid gap-3">
|
||||
<div role="group" class="field">
|
||||
<label for="demo-tabs-password-new">New password</label>
|
||||
<input type="password" id="demo-tabs-password-new"/>
|
||||
<input type="password" id="demo-tabs-password-new">
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<footer>
|
||||
<button type="button" class="btn">Save Password</button>
|
||||
<button type="button" class="btn">Save password</button>
|
||||
</footer>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{% set tabsets_demo = [
|
||||
{ tab: "Account", panel: account_panel },
|
||||
{ tab: "Password", panel: password_panel }
|
||||
{ tab: "Account", panel: account_panel },
|
||||
{ tab: "Password", panel: password_panel }
|
||||
] %}
|
||||
|
||||
{% set code_html %}
|
||||
{{ tabs(
|
||||
id='demo-tabs-with-panels',
|
||||
id="demo-tabs-with-panels",
|
||||
tabsets=tabsets_demo,
|
||||
main_attrs={ "class": "w-full" },
|
||||
tablist_attrs={ "class": "w-full" }
|
||||
tablist_attrs={ "class": "w-full", "aria-orientation": "horizontal" }
|
||||
) }}
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("tabs", code_html | prettyHtml, "max-w-[300px] w-full") }}
|
||||
{{ code_preview("tabs", code_html | prettyHtml, "max-w-[400px] w-full") }}
|
||||
|
||||
<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 files</a></h4>
|
||||
|
||||
<section class="prose">
|
||||
@@ -103,7 +123,7 @@ toc:
|
||||
Components with JavaScript
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="/installation/#install-cli">
|
||||
<a class="badge-outline" href="/cli">
|
||||
Use the CLI
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
@@ -113,7 +133,7 @@ toc:
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h4 id="usage-html-js-2"><a href="#usage-html-js-2">Step 2: Add your select HTML</a></h4>
|
||||
<h4 id="usage-html-js-2"><a href="#usage-html-js-2">Step 2: Add your tabs HTML</a></h4>
|
||||
|
||||
{{ code_block(code_html | prettyHtml, "html") }}
|
||||
|
||||
@@ -122,21 +142,19 @@ toc:
|
||||
<section class="prose">
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><div class="tabs"></code></dt>
|
||||
<dd>Wraps around the entire component.
|
||||
<dd>Root tabs container. It becomes horizontal or vertical based on the child tablist's <code>aria-orientation</code>.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><nav role="tablist" aria-orientation="horizontal"></code></dt>
|
||||
<dd>The tablist containing the tab buttons
|
||||
<dd>Tablist containing tab buttons. Set <code>aria-orientation="vertical"</code> for vertical tabs and <code>data-variant="line"</code> for the line style.
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><button role="tab" id="{ TAB_ID }" aria-controls="{ PANEL_ID }" aria-selected="false" tabindex="0"></code></dt>
|
||||
<dd>The tab button. When active, the <code>aria-selected</code> attribute is set to <code>true</code>. <code>tabindex="0"</code> is required for keyboard navigation.</dd>
|
||||
<dt><code class="highlight language-html"><button role="tab" id="{ TAB_ID }" aria-controls="{ PANEL_ID }" aria-selected="true" tabindex="0"></code></dt>
|
||||
<dd>Tab trigger. The active tab has <code>aria-selected="true"</code> and <code>tabindex="0"</code>. Inactive tabs use <code>tabindex="-1"</code>. Disabled tabs use <code>disabled</code> or <code>aria-disabled="true"</code>.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code class="highlight language-html"><div role="tabpanel" id="{ PANEL_ID }" aria-labelledby="{ TAB_ID }"></code></dt>
|
||||
<dd>Panel controlled by a tab. Inactive panels are hidden.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dl>
|
||||
<dt><code class="highlight language-html"><div role="tabpanel" id="{ PANEL_ID }" aria-labelledby="{ TAB_ID }" tabindex="-1" aria-selected="false"></code></dt>
|
||||
<dd>The tab panel. When active, the <code>aria-selected</code> attribute is set to <code>true</code>.</dd>
|
||||
</dl>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
@@ -145,7 +163,7 @@ toc:
|
||||
<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>
|
||||
<dd>Once the component is initialized, it dispatches a custom non-bubbling <code>basecoat:initialized</code> event on itself.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
@@ -156,7 +174,7 @@ toc:
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
<a class="badge-outline" href="/installation/#install-macros" target="_blank">
|
||||
<a class="badge-outline" href="/cli#macros" target="_blank">
|
||||
Use Nunjucks or Jinja macros
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
@@ -170,63 +188,112 @@ toc:
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% set raw_code %}{% raw %}{% set account_panel %}
|
||||
<div class="card">
|
||||
<header>
|
||||
<h2>Account</h2>
|
||||
<p>Make changes to your account here. Click save when you're done.</p>
|
||||
</header>
|
||||
<section>
|
||||
<form class="form grid gap-6">
|
||||
<div class="grid gap-3">
|
||||
<label for="demo-tabs-account-name">Name</label>
|
||||
<input type="text" id="demo-tabs-account-name" value="Pedro Duarte" />
|
||||
</div>
|
||||
<div class="grid gap-3">
|
||||
<label for="demo-tabs-account-username">Username</label>
|
||||
<input type="text" id="demo-tabs-account-username" value="@peduarte" />
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<footer>
|
||||
<button type="button" class="btn">Save changes</button>
|
||||
</footer>
|
||||
</div>
|
||||
{% endset %}
|
||||
{% set raw_code %}{% raw %}{{ tabs(
|
||||
id="account-tabs",
|
||||
tabsets=[
|
||||
{ tab: "Account", panel: account_panel },
|
||||
{ tab: "Password", panel: password_panel }
|
||||
],
|
||||
tablist_attrs={ "data-variant": "line" }
|
||||
) }}{% endraw %}{% endset %}
|
||||
{{ code_block(raw_code, "jinja") }}
|
||||
|
||||
{% set password_panel %}
|
||||
<div class="card">
|
||||
<header>
|
||||
<h2>Password</h2>
|
||||
<p>Change your password here. After saving, you'll be logged out.</p>
|
||||
</header>
|
||||
<section>
|
||||
<form class="form grid gap-6">
|
||||
<div class="grid gap-3">
|
||||
<label for="demo-tabs-password-current">Current password</label>
|
||||
<input type="password" id="demo-tabs-password-current" />
|
||||
</div>
|
||||
<div class="grid gap-3">
|
||||
<label for="demo-tabs-password-new">New password</label>
|
||||
<input type="password" id="demo-tabs-password-new"/>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<footer>
|
||||
<button type="button" class="btn">Save Password</button>
|
||||
</footer>
|
||||
</div>
|
||||
{% endset %}
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
{% set tabsets_demo = [
|
||||
{ tab: "Account", panel: account_panel },
|
||||
{ tab: "Password", panel: password_panel }
|
||||
] %}
|
||||
<h3 id="example-line"><a href="#example-line">Line</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{{ tabs(
|
||||
id='demo-tabs-with-panels',
|
||||
tabsets=tabsets_demo,
|
||||
id="tabs-line",
|
||||
tabsets=[
|
||||
{ tab: "Account", panel: "Make changes to your account here." },
|
||||
{ tab: "Password", panel: "Change your password here." },
|
||||
{ tab: "Settings", panel: "Update your settings here." }
|
||||
],
|
||||
tablist_attrs={ "data-variant": "line" }
|
||||
) }}
|
||||
{% endset %}
|
||||
{{ code_preview("tabs-line", code | prettyHtml, "w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-vertical"><a href="#example-vertical">Vertical</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{{ tabs(
|
||||
id="tabs-vertical",
|
||||
tabsets=[
|
||||
{ tab: "Account", panel: "Manage account settings." },
|
||||
{ tab: "Password", panel: "Update your password." },
|
||||
{ tab: "Notifications", panel: "Configure notifications." }
|
||||
],
|
||||
main_attrs={ "class": "w-full" },
|
||||
tablist_attrs={ "class": "w-full" }
|
||||
) }}{% endraw %}{% endset%}
|
||||
{{ code_block(raw_code, "jinja") }}
|
||||
tablist_attrs={ "aria-orientation": "vertical" }
|
||||
) }}
|
||||
{% endset %}
|
||||
{{ code_preview("tabs-vertical", code | prettyHtml, "w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-disabled"><a href="#example-disabled">Disabled</a></h3>
|
||||
|
||||
{% set code %}
|
||||
{{ tabs(
|
||||
id="tabs-disabled",
|
||||
tabsets=[
|
||||
{ tab: "Account", panel: "Account content." },
|
||||
{ tab: "Password", panel: "Password content.", tab_attrs: { "disabled": "disabled" } },
|
||||
{ tab: "Settings", panel: "Settings content." }
|
||||
]
|
||||
) }}
|
||||
{% endset %}
|
||||
{{ code_preview("tabs-disabled", code | prettyHtml, "w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-icons"><a href="#example-icons">Icons</a></h3>
|
||||
|
||||
{% set icon_account %}{% lucide "user" %}Account{% endset %}
|
||||
{% set icon_password %}{% lucide "lock" %}Password{% endset %}
|
||||
{% set icon_settings %}{% lucide "settings" %}Settings{% endset %}
|
||||
{% set code %}
|
||||
{{ tabs(
|
||||
id="tabs-icons",
|
||||
tabsets=[
|
||||
{ tab: icon_account, panel: "Account content." },
|
||||
{ tab: icon_password, panel: "Password content." },
|
||||
{ tab: icon_settings, panel: "Settings content." }
|
||||
]
|
||||
) }}
|
||||
{% endset %}
|
||||
{{ code_preview("tabs-icons", code | prettyHtml, "w-full max-w-md") }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Tabs support document direction. Set <code>dir="rtl"</code> on the tabs root or a parent element.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}
|
||||
<div dir="rtl">
|
||||
{{ tabs(
|
||||
id="tabs-rtl",
|
||||
tabsets=[
|
||||
{ tab: "الحساب", panel: "إعدادات الحساب." },
|
||||
{ tab: "كلمة المرور", panel: "تحديث كلمة المرور." },
|
||||
{ tab: "الإشعارات", panel: "إعدادات الإشعارات." }
|
||||
],
|
||||
tablist_attrs={ "data-variant": "line" }
|
||||
) }}
|
||||
</div>
|
||||
{% endset %}
|
||||
{{ code_preview("tabs-rtl", code | prettyHtml, "w-full max-w-md") }}
|
||||
|
||||
<h2 id="api-reference"><a href="#api-reference">API Reference</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<dl>
|
||||
<dt><code class="highlight language-html">.tabs</code></dt>
|
||||
<dd>Root component class.</dd>
|
||||
<dt><code class="highlight language-html">[role="tablist"]</code></dt>
|
||||
<dd>Tablist. Supports <code>aria-orientation="horizontal"</code>, <code>aria-orientation="vertical"</code>, and <code>data-variant="line"</code>.</dd>
|
||||
<dt><code class="highlight language-html">[role="tab"]</code></dt>
|
||||
<dd>Tab trigger. Supports <code>aria-selected</code>, <code>disabled</code>, and <code>aria-disabled="true"</code>.</dd>
|
||||
<dt><code class="highlight language-html">[role="tabpanel"]</code></dt>
|
||||
<dd>Tab panel associated with a tab by <code>aria-controls</code> and <code>aria-labelledby</code>.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
@@ -8,83 +8,93 @@ toc:
|
||||
- label: Examples
|
||||
id: examples
|
||||
children:
|
||||
- label: Default
|
||||
id: example-default
|
||||
- label: Invalid
|
||||
id: example-invalid
|
||||
- label: Field
|
||||
id: example-field
|
||||
- label: Disabled
|
||||
id: example-disabled
|
||||
- label: With label
|
||||
id: example-with-label
|
||||
- label: With text
|
||||
id: example-with-text
|
||||
- label: With button
|
||||
id: example-with-button
|
||||
- label: Invalid
|
||||
id: example-invalid
|
||||
- label: Button
|
||||
id: example-button
|
||||
- label: Form
|
||||
id: example-form
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
|
||||
{% set code %}<textarea class="textarea" placeholder="Type your message here"></textarea>{% endset %}
|
||||
{{ code_preview("button", code, "w-full") }}
|
||||
{{ code_preview("textarea", code, class="w-full max-w-xs") }}
|
||||
|
||||
<h2 id="usage">Usage</h2>
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>Simply add the <code>textarea</code> class to your <code><textarea></code> element or have a parent with the <code>form</code> class (<a href="/components/form">read more about form</a>). You can also set the <code>aria-invalid</code> attribute to <code>true</code> to make the textarea invalid.</p>
|
||||
<p>Add the <code>textarea</code> class to a <code><textarea></code> element. Textareas inside a <code>form</code> or <code>field</code> container also receive the same styling automatically.</p>
|
||||
<p>Use native HTML attributes for behavior: <code>disabled</code> for disabled textareas and <code>aria-invalid="true"</code> for invalid state styling.</p>
|
||||
</section>
|
||||
|
||||
{{ code_block(code) }}
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
{{ code_preview("textarea-default", code, "w-full") }}
|
||||
<h3 id="example-field"><a href="#example-field">Field</a></h3>
|
||||
|
||||
<h3 id="example-invalid"><a href="#example-invalid">Invalid</a></h3>
|
||||
<section class="prose">
|
||||
<p>Use <code>field</code> with a label and description to create an accessible textarea.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<textarea class="textarea" placeholder="Type your message here" aria-invalid="true"></textarea>{% endset %}
|
||||
{{ code_preview("textarea-invalid", code, "w-full") }}
|
||||
{% set code_field %}<div role="group" class="field w-full max-w-xs">
|
||||
<label for="textarea-field">Message</label>
|
||||
<textarea id="textarea-field" placeholder="Type your message here"></textarea>
|
||||
<p>Write a short message.</p>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("textarea-field", code_field, class="w-full max-w-xs") }}
|
||||
|
||||
<h3 id="example-disabled"><a href="#example-disabled">Disabled</a></h3>
|
||||
|
||||
{% set code %}<textarea class="textarea" placeholder="Type your message here" disabled></textarea>{% endset %}
|
||||
{{ code_preview("textarea-disabled", code, "w-full") }}
|
||||
{% set code_disabled %}<textarea class="textarea" placeholder="Type your message here" disabled></textarea>{% endset %}
|
||||
{{ code_preview("textarea-disabled", code_disabled, class="w-full max-w-xs") }}
|
||||
|
||||
<h3 id="example-with-label"><a href="#example-with-label">With label</a></h3>
|
||||
<h3 id="example-invalid"><a href="#example-invalid">Invalid</a></h3>
|
||||
|
||||
{% set code %}<div class="grid gap-3">
|
||||
<label for="textarea-with-label" class="label">Label</label>
|
||||
<textarea id="textarea-with-label" class="textarea" placeholder="Type your message here"></textarea>
|
||||
{% set code_invalid %}<div role="group" class="field w-full max-w-xs">
|
||||
<label for="textarea-invalid">Message</label>
|
||||
<textarea class="textarea" id="textarea-invalid" placeholder="Type your message here" aria-invalid="true"></textarea>
|
||||
<p>Message is required.</p>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("textarea-with-label", code, "w-full") }}
|
||||
{{ code_preview("textarea-invalid", code_invalid, class="w-full max-w-xs") }}
|
||||
|
||||
<h3 id="example-with-text"><a href="#example-with-text">With text</a></h3>
|
||||
<h3 id="example-button"><a href="#example-button">Button</a></h3>
|
||||
|
||||
{% set code %}<div class="grid gap-3">
|
||||
<label for="textarea-with-label" class="label">Label</label>
|
||||
<textarea id="textarea-with-label" class="textarea" placeholder="Type your message here"></textarea>
|
||||
<p class="text-muted-foreground text-sm">Type your message and press enter to send.</p>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("textarea-with-text", code, "w-full") }}
|
||||
|
||||
<h3 id="example-with-button"><a href="#example-with-button">With button</a></h3>
|
||||
|
||||
{% set code %}<div class="grid gap-3">
|
||||
<textarea id="textarea-with-label" class="textarea" placeholder="Type your message here"></textarea>
|
||||
{% set code_button %}<div class="grid w-full max-w-xs gap-3">
|
||||
<textarea class="textarea" placeholder="Type your message here"></textarea>
|
||||
<button type="submit" class="btn">Submit</button>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("textarea-form", code, "w-full") }}
|
||||
{{ code_preview("textarea-button", code_button, class="w-full max-w-xs") }}
|
||||
|
||||
<h3 id="example-form"><a href="#example-form">Form</a></h3>
|
||||
|
||||
{% set code %}<form class="form space-y-6">
|
||||
<div class="grid gap-3">
|
||||
<label for="textarea-form" class="label">Bio</label>
|
||||
{% set code_form %}<form class="w-full max-w-sm space-y-6">
|
||||
<div role="group" class="field">
|
||||
<label for="textarea-form">Bio</label>
|
||||
<textarea id="textarea-form" placeholder="Tell us a bit about yourself"></textarea>
|
||||
<p class="text-muted-foreground text-sm">You can @mention other users and organizations.</p>
|
||||
<p>You can @mention other users and organizations.</p>
|
||||
</div>
|
||||
<button type="submit" class="btn">Submit</button>
|
||||
</form>{% endset %}
|
||||
{{ code_preview("textarea-form", code, "w-full max-w-sm") }}
|
||||
{{ code_preview("textarea-form", code_form, class="w-full max-w-sm") }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
<p>Textareas support right-to-left layouts through native browser direction handling. Add <code>dir="rtl"</code> to the textarea, field, or an ancestor.</p>
|
||||
</section>
|
||||
|
||||
{% set code_rtl %}<div role="group" class="field w-full max-w-xs" dir="rtl">
|
||||
<label for="textarea-rtl">التعليقات</label>
|
||||
<textarea id="textarea-rtl" placeholder="تعليقاتك تساعدنا على التحسين..." rows="4"></textarea>
|
||||
<p>شاركنا أفكارك حول الخدمة.</p>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("textarea-rtl", code_rtl, class="w-full max-w-xs") }}
|
||||
|
||||
@@ -78,7 +78,7 @@ toc:
|
||||
Components with JavaScript
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="/installation/#install-cli">
|
||||
<a class="badge-outline" href="/cli">
|
||||
Use the CLI
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
@@ -232,7 +232,7 @@ toc:
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
<a class="badge-outline" href="/installation/#install-macros" target="_blank">
|
||||
<a class="badge-outline" href="/cli#macros" target="_blank">
|
||||
Use Nunjucks or Jinja macros
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
layout: layouts/page.njk
|
||||
title: Tooltip
|
||||
description: A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.
|
||||
description: A popup that displays information related to an element when the mouse hovers over it.
|
||||
toc:
|
||||
- label: Usage
|
||||
id: usage
|
||||
@@ -10,44 +10,86 @@ toc:
|
||||
children:
|
||||
- label: Default
|
||||
id: example-default
|
||||
- label: Right
|
||||
id: example-right
|
||||
- label: Bottom + Start
|
||||
id: example-bottom-start
|
||||
- label: Sides
|
||||
id: example-sides
|
||||
- label: Icon Button
|
||||
id: example-icon-button
|
||||
- label: Disabled Button
|
||||
id: example-disabled-button
|
||||
- label: Link
|
||||
id: example-link
|
||||
- label: RTL
|
||||
id: example-rtl
|
||||
---
|
||||
|
||||
{% from "macros/code_preview.njk" import code_preview %}
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
|
||||
{% set code %}<button class="btn-outline" data-tooltip="Add to library">Hover</button>{% endset %}
|
||||
{{ code_preview("button", code) }}
|
||||
{{ code_preview("tooltip", code) }}
|
||||
|
||||
<h2 id="usage">Usage</h2>
|
||||
<h2 id="usage"><a href="#usage">Usage</a></h2>
|
||||
|
||||
<section class="prose">
|
||||
<p>On any element, set the value of the <code>data-tooltip</code> attribute to the text you want to display in the tooltip. Additionally, you can set:</p>
|
||||
<ul>
|
||||
<li><code>data-side</code> attribute to <code>top</code>, <code>bottom</code>, <code>left</code>, or <code>right</code> to change the side of the tooltip.</li>
|
||||
<li><code>data-align</code> attribute to <code>start</code>, <code>center</code>, or <code>end</code> to change the alignment of the tooltip.</li>
|
||||
</ul>
|
||||
<p>Add <code>data-tooltip</code> to the trigger element. Use <code>data-side</code> and <code>data-align</code> to change the position.</p>
|
||||
<p>Basecoat tooltips are CSS-only and text-only. This intentionally differs from shadcn/ui's composed tooltip content, which can render arbitrary markup and uses a portal.</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<button class="btn-outline" data-tooltip="Tooltip text" data-side="bottom" data-align="center">Bottom left</button>{% endset %}
|
||||
{{ code_block(code) }}
|
||||
{% set code %}<button class="btn-outline" data-tooltip="Tooltip text" data-side="bottom" data-align="center">Bottom</button>{% endset %}
|
||||
{{ code_block(code | prettyHtml, "html") }}
|
||||
|
||||
<section class="prose">
|
||||
<dl>
|
||||
<dt><code>data-tooltip</code></dt>
|
||||
<dd>Tooltip text.</dd>
|
||||
<dt><code>data-side</code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd><code>top</code>, <code>bottom</code>, <code>left</code>, <code>right</code>, <code>inline-start</code>, or <code>inline-end</code>. Defaults to <code>top</code>.</dd>
|
||||
<dt><code>data-align</code> <span class="badge-secondary">Optional</span></dt>
|
||||
<dd><code>start</code>, <code>center</code>, or <code>end</code>. Defaults to <code>center</code>.</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<h2 id="examples"><a href="#examples">Examples</a></h2>
|
||||
|
||||
<h3 id="example-default"><a href="#example-default">Default</a></h3>
|
||||
|
||||
{% set code %}<button class="btn-outline" data-tooltip="Default tooltip">Default</button>{% endset %}
|
||||
{{ code_preview("button", code) }}
|
||||
{{ code_preview("tooltip-default", code) }}
|
||||
|
||||
<h3 id="example-right"><a href="#example-right">Right</a></h3>
|
||||
<h3 id="example-sides"><a href="#example-sides">Sides</a></h3>
|
||||
|
||||
{% set code %}<button class="btn-outline" data-tooltip="Right side tooltip" data-side="right">Right</button>{% endset %}
|
||||
{{ code_preview("button", code) }}
|
||||
{% set code %}<div class="flex flex-wrap gap-2">
|
||||
<button class="btn-outline" data-tooltip="Tooltip" data-side="inline-start">Inline Start</button>
|
||||
<button class="btn-outline" data-tooltip="Tooltip" data-side="top">Top</button>
|
||||
<button class="btn-outline" data-tooltip="Tooltip" data-side="bottom">Bottom</button>
|
||||
<button class="btn-outline" data-tooltip="Tooltip" data-side="inline-end">Inline End</button>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("tooltip-sides", code) }}
|
||||
|
||||
<h3 id="example-bottom-start"><a href="#example-bottom-start">Bottom + Start</a></h3>
|
||||
<h3 id="example-icon-button"><a href="#example-icon-button">Icon Button</a></h3>
|
||||
|
||||
{% set code %}<button class="btn-outline" data-tooltip="Bottom side and start aligned tooltip" data-side="bottom" data-align="start">Bottom + Start</button>{% endset %}
|
||||
{{ code_preview("button", code) }}
|
||||
{% set code %}<button class="btn-icon-ghost" data-tooltip="Additional information">
|
||||
{% lucide "info" %}
|
||||
<span class="sr-only">Info</span>
|
||||
</button>{% endset %}
|
||||
{{ code_preview("tooltip-icon-button", code) }}
|
||||
|
||||
<h3 id="example-disabled-button"><a href="#example-disabled-button">Disabled Button</a></h3>
|
||||
|
||||
{% set code %}<span class="inline-block" data-tooltip="This feature is currently unavailable">
|
||||
<button class="btn-outline" disabled>Disabled</button>
|
||||
</span>{% endset %}
|
||||
{{ code_preview("tooltip-disabled-button", code) }}
|
||||
|
||||
<h3 id="example-link"><a href="#example-link">Link</a></h3>
|
||||
|
||||
{% set code %}<a href="#" class="text-primary text-sm underline-offset-4 hover:underline" data-tooltip="Click to read the documentation">Learn more</a>{% endset %}
|
||||
{{ code_preview("tooltip-link", code) }}
|
||||
|
||||
<h3 id="example-rtl"><a href="#example-rtl">RTL</a></h3>
|
||||
|
||||
{% set code %}<div dir="rtl" class="flex gap-2">
|
||||
<button class="btn-outline" data-tooltip="تلميح" data-side="inline-start">Inline Start</button>
|
||||
<button class="btn-outline" data-tooltip="تلميح" data-side="inline-end">Inline End</button>
|
||||
</div>{% endset %}
|
||||
{{ code_preview("tooltip-rtl", code) }}
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
layout: layouts/page.njk
|
||||
title: Customization
|
||||
description: Configure icons, themes, fonts, and style overrides.
|
||||
toc:
|
||||
- label: Icons
|
||||
id: icons
|
||||
- label: Theming
|
||||
id: theming
|
||||
- label: Fonts
|
||||
id: fonts
|
||||
- label: Style overrides
|
||||
id: style-overrides
|
||||
---
|
||||
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
|
||||
<h2 id="icons"><a href="#icons">Icons</a></h2>
|
||||
|
||||
<div class="prose">
|
||||
<p>Basecoat uses <a href="https://lucide.dev" target="_blank">Lucide icons</a>. You have three options:</p>
|
||||
|
||||
<ul>
|
||||
<li><b>Copy SVG code</b>: Visit <a href="https://lucide.dev/icons" target="_blank">lucide.dev/icons</a>, click any icon to copy the SVG, and paste it directly in your HTML. Best for simple projects or occasional icon usage.</li>
|
||||
<li><b>Install lucide</b>: Follow the <a href="https://lucide.dev/guide/installation" target="_blank">installation guide</a> to install the <code>lucide</code> package and dynamically render icons. Best for projects that need many icons or want tree-shaking.</li>
|
||||
<li><b>Use framework packages</b>: Browse <a href="https://lucide.dev/packages" target="_blank">framework-specific packages</a> (React, Vue, Angular, etc.) for component-based icon usage. Best if you're using a supported framework.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
<a class="badge-outline" href="https://lucide.dev" target="_blank">
|
||||
Browse icons
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="https://lucide.dev/guide/installation" target="_blank">
|
||||
Installation guide
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="https://lucide.dev/packages" target="_blank">
|
||||
Packages
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2 id="theming"><a href="#theming">Theming</a></h2>
|
||||
|
||||
<div class="prose">
|
||||
<p>You can import any <a href="https://shadcn-ui.com" target="_blank">shadcn/ui</a> compatible theme (e.g. <a href="https://tweakcn.com" target="_blank">tweakcn</a>). For example:</p>
|
||||
<ul>
|
||||
<li>Go to <a href="https://ui.shadcn.com/themes" target="_blank">ui.shadcn.com/themes</a> and select a theme.</li>
|
||||
<li>Click "Copy code" and save the theme variables in a file (e.g. <code>theme.css</code>).</li>
|
||||
<li>
|
||||
Import the theme in your CSS file:
|
||||
{% set code %}@import "tailwindcss";
|
||||
@import "basecoat-css/sera";
|
||||
@import "./theme.css";{% endset %}
|
||||
{{ code_block(code, "css") }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
<a class="badge-outline" href="https://ui.shadcn.com/docs/theming" target="_blank">
|
||||
About shadcn/ui theming
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2 id="fonts"><a href="#fonts">Fonts</a></h2>
|
||||
|
||||
<div class="prose">
|
||||
<p>Basecoat does not ship web fonts by default. The Basecoat docs site uses the same font choices as the current shadcn/ui site: <a href="https://fonts.google.com/specimen/Geist" target="_blank">Geist</a> for sans and heading text, and <a href="https://fonts.google.com/specimen/Geist+Mono" target="_blank">Geist Mono</a> for monospace text.</p>
|
||||
<p>To use the same setup, load the fonts and override the font tokens after importing Basecoat:</p>
|
||||
</div>
|
||||
|
||||
{% set code %}@import "tailwindcss";
|
||||
@import "basecoat-css/sera";
|
||||
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@400&display=swap");
|
||||
|
||||
:root {
|
||||
--font-sans: "Geist", ui-sans-serif, system-ui, sans-serif;
|
||||
--font-heading: "Geist", ui-sans-serif, system-ui, sans-serif;
|
||||
--font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
}{% endset %}
|
||||
{{ code_block(code, "css") }}
|
||||
|
||||
<div class="prose">
|
||||
<p>Keep font overrides in a small CSS file imported after Basecoat. This makes fonts a project-level customization instead of a Basecoat package default.</p>
|
||||
</div>
|
||||
|
||||
<h2 id="style-overrides"><a href="#style-overrides">Style overrides</a></h2>
|
||||
|
||||
<div class="prose">
|
||||
<p>You can override default styles using Tailwind:</p>
|
||||
</div>
|
||||
|
||||
{% set code %}<button class="btn font-normal">Click me</button>{% endset %}
|
||||
{{ code_block(code, "html") }}
|
||||
|
||||
<div class="prose">
|
||||
<p>You can also extend or override the existing styles in your own Tailwind files:</p>
|
||||
</div>
|
||||
|
||||
{% set code %}@import "tailwindcss";
|
||||
@import "basecoat-css/sera";
|
||||
@import "./custom.css";{% endset %}
|
||||
{{ code_block(code, "css") }}
|
||||
|
||||
<div class="prose">
|
||||
<p>More importantly, you can use the <a href="#theming">theme variables</a> to customize many aspects of the components (colors, fonts, sizes, etc).</p>
|
||||
|
||||
<p><b>If you want to make more drastic changes</b>, you can <a href="https://github.com/hunvreus/basecoat/blob/main/src/css/basecoat.css" target="_blank">copy the <code>basecoat.css</code> file</a> into your project and make your changes there.</p>
|
||||
</div>
|
||||
+30
-30
@@ -91,21 +91,21 @@ layout: layouts/layout.njk
|
||||
<p>Manage your cookie settings here.</p>
|
||||
</header>
|
||||
<section class="text-sm grid gap-6">
|
||||
<label class="flex items-center justify-between gap-2">
|
||||
<label class="label flex items-center justify-between gap-2 text-sm font-normal normal-case tracking-normal">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<div class="font-medium">Strictly Necessary</div>
|
||||
<div class="text-muted-foreground">These cookies are essential in order to use the website and use its features.</div>
|
||||
</div>
|
||||
<input type="checkbox" role="switch" class="input" checked />
|
||||
</label>
|
||||
<label class="flex items-center justify-between gap-2">
|
||||
<label class="label flex items-center justify-between gap-2 text-sm font-normal normal-case tracking-normal">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<div class="font-medium">Functional Cookies</div>
|
||||
<div class="text-muted-foreground">These cookies allow the website to provide personalized functionality.</div>
|
||||
</div>
|
||||
<input type="checkbox" role="switch" class="input" />
|
||||
</label>
|
||||
<label class="flex items-center justify-between gap-2">
|
||||
<label class="label flex items-center justify-between gap-2 text-sm font-normal normal-case tracking-normal">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<div class="font-medium">Performance Cookies</div>
|
||||
<div class="text-muted-foreground">These cookies help to improve the performance of the website.</div>
|
||||
@@ -124,7 +124,7 @@ layout: layouts/layout.njk
|
||||
<p>Add a new payment method to your account.</p>
|
||||
</header>
|
||||
<section>
|
||||
<form class="form grid gap-6">
|
||||
<form class="grid gap-6">
|
||||
<ul class="flex gap-4">
|
||||
<li class="flex-1">
|
||||
<input type="radio" name="payment-method-type" id="payment-method-type-apple" class="hidden peer" />
|
||||
@@ -149,21 +149,21 @@ layout: layouts/layout.njk
|
||||
</li>
|
||||
</ul>
|
||||
<div class="grid gap-2">
|
||||
<label for="payment-method-name">Name</label>
|
||||
<input type="text" id="payment-method-name" placeholder="John Doe" />
|
||||
<label class="label" for="payment-method-name">Name</label>
|
||||
<input class="input" type="text" id="payment-method-name" placeholder="John Doe" />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<label for="payment-method-city">City</label>
|
||||
<input type="text" id="payment-method-city" placeholder="New York" />
|
||||
<label class="label" for="payment-method-city">City</label>
|
||||
<input class="input" type="text" id="payment-method-city" placeholder="New York" />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<label for="payment-method-card-number">Card Number</label>
|
||||
<input type="text" id="payment-method-card-number" placeholder="1234 5678 9012 3456" />
|
||||
<label class="label" for="payment-method-card-number">Card Number</label>
|
||||
<input class="input" type="text" id="payment-method-card-number" placeholder="1234 5678 9012 3456" />
|
||||
</div>
|
||||
<div class="flex gap-4">
|
||||
<div class="grid gap-2 flex-1">
|
||||
<label for="payment-method-expiration-month">Expires</label>
|
||||
<select id="payment-method-expiration-month" class="w-full">
|
||||
<label class="label" for="payment-method-expiration-month">Expires</label>
|
||||
<select id="payment-method-expiration-month" class="select w-full">
|
||||
<option value="01">January</option>
|
||||
<option value="02">February</option>
|
||||
<option value="03">March</option>
|
||||
@@ -179,8 +179,8 @@ layout: layouts/layout.njk
|
||||
</select>
|
||||
</div>
|
||||
<div class="grid gap-2 flex-1">
|
||||
<label for="payment-method-expiration-year">Year</label>
|
||||
<select id="payment-method-expiration-year" class="w-full">
|
||||
<label class="label" for="payment-method-expiration-year">Year</label>
|
||||
<select id="payment-method-expiration-year" class="select w-full">
|
||||
<option value="2024">2024</option>
|
||||
<option value="2025">2025</option>
|
||||
<option value="2026">2026</option>
|
||||
@@ -195,8 +195,8 @@ layout: layouts/layout.njk
|
||||
</select>
|
||||
</div>
|
||||
<div class="grid gap-2 flex-1">
|
||||
<label for="payment-method-cvv">CVV</label>
|
||||
<input type="text" id="payment-method-cvv" placeholder="123" maxlength="4" class="w-full" />
|
||||
<label class="label" for="payment-method-cvv">CVV</label>
|
||||
<input type="text" id="payment-method-cvv" placeholder="123" maxlength="4" class="input w-full" />
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn w-full">Continue</button>
|
||||
@@ -271,17 +271,17 @@ layout: layouts/layout.njk
|
||||
<span class="bg-card px-2 text-muted-foreground">Or continue with</span>
|
||||
</div>
|
||||
</div>
|
||||
<form class="form grid gap-6">
|
||||
<form class="grid gap-6">
|
||||
<div class="grid gap-2">
|
||||
<label for="demo-card-form-email">Email</label>
|
||||
<input type="email" id="demo-card-form-email">
|
||||
<label class="label" for="demo-card-form-email">Email</label>
|
||||
<input class="input" type="email" id="demo-card-form-email">
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<label for="demo-card-form-password">Password</label>
|
||||
<label class="label" for="demo-card-form-password">Password</label>
|
||||
<a href="#" class="ml-auto inline-block text-sm underline-offset-4 hover:underline">Forgot your password?</a>
|
||||
</div>
|
||||
<input type="password" id="demo-card-form-password">
|
||||
<input class="input" type="password" id="demo-card-form-password">
|
||||
</div>
|
||||
<button type="button" class="btn w-full">Create an account</button>
|
||||
</form>
|
||||
@@ -294,11 +294,11 @@ layout: layouts/layout.njk
|
||||
<p>What area are you having problems with?</p>
|
||||
</header>
|
||||
<section>
|
||||
<form class="form grid gap-6">
|
||||
<form class="grid gap-6">
|
||||
<div class="flex gap-4">
|
||||
<div class="grid gap-2 flex-1">
|
||||
<label for="report-issue-area">Area</label>
|
||||
<select id="report-issue-area" class="w-full">
|
||||
<label class="label" for="report-issue-area">Area</label>
|
||||
<select id="report-issue-area" class="select w-full">
|
||||
<option value="team">Team</option>
|
||||
<option value="billing">Billing</option>
|
||||
<option value="account">Account</option>
|
||||
@@ -307,8 +307,8 @@ layout: layouts/layout.njk
|
||||
</select>
|
||||
</div>
|
||||
<div class="grid gap-2 flex-1">
|
||||
<label for="report-issue-security-level">Security Level</label>
|
||||
<select id="report-issue-security-level" class="w-full">
|
||||
<label class="label" for="report-issue-security-level">Security Level</label>
|
||||
<select id="report-issue-security-level" class="select w-full">
|
||||
<option value="1">Severity 1 (Highest)</option>
|
||||
<option value="2">Severity 2</option>
|
||||
<option value="3">Severity 3</option>
|
||||
@@ -317,12 +317,12 @@ layout: layouts/layout.njk
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<label for="report-issue-subject">Subject</label>
|
||||
<input type="text" id="report-issue-subject" placeholder="I need help with..." class="w-full" />
|
||||
<label class="label" for="report-issue-subject">Subject</label>
|
||||
<input type="text" id="report-issue-subject" placeholder="I need help with..." class="input w-full" />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<label for="report-issue-description">Description</label>
|
||||
<textarea id="report-issue-description" placeholder="Please include all information relevant to your issue." class="w-full"></textarea>
|
||||
<label class="label" for="report-issue-description">Description</label>
|
||||
<textarea id="report-issue-description" placeholder="Please include all information relevant to your issue." class="textarea w-full"></textarea>
|
||||
</div>
|
||||
<footer class="flex items-center gap-4 justify-between">
|
||||
<button type="button" class="btn-sm-ghost">Cancel</button>
|
||||
|
||||
+36
-153
@@ -13,28 +13,18 @@ toc:
|
||||
- label: Installing using NPM
|
||||
id: install-npm
|
||||
children:
|
||||
- label: Add Tailwind CSS
|
||||
- label: "Step 1: Add Tailwind CSS"
|
||||
id: install-npm-tailwind
|
||||
- label: Add Basecoat
|
||||
- label: "Step 2: Add Basecoat"
|
||||
id: install-npm-basecoat
|
||||
- label: Import basecoat in your CSS
|
||||
- label: "Step 3: Import Basecoat"
|
||||
id: install-npm-import
|
||||
- label: (Optional) Add JavaScript files
|
||||
- label: "Step 4: Add JavaScript files"
|
||||
id: install-npm-js
|
||||
- label: That's it
|
||||
- label: "Step 5: Use components"
|
||||
id: install-npm-done
|
||||
- label: Components with JavaScript
|
||||
id: install-js
|
||||
- label: Use the CLI
|
||||
id: install-cli
|
||||
- label: Use Nunjucks or Jinja macros
|
||||
id: install-macros
|
||||
- label: Icons
|
||||
id: install-icons
|
||||
- label: Theming
|
||||
id: install-theming
|
||||
- label: Customization
|
||||
id: install-customization
|
||||
---
|
||||
|
||||
{% from "macros/code_block.njk" import code_block %}
|
||||
@@ -52,6 +42,13 @@ toc:
|
||||
<script src="https://cdn.jsdelivr.net/npm/basecoat-css@{{ pkg.version }}/dist/js/all.min.js" defer></script>{% endset %}
|
||||
{{ code_block(code, "html") }}
|
||||
|
||||
<section class="prose">
|
||||
<p>The default CDN stylesheet is the Basecoat backward-compatible default. To use a specific style, load the matching standalone stylesheet instead:</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/basecoat-css@{{ pkg.version }}/dist/basecoat-sera.cdn.min.css">{% endset %}
|
||||
{{ code_block(code, "html") }}
|
||||
|
||||
<h3 id="install-cdn-specific"><a href="#install-cdn-specific">Install specific components</a></h3>
|
||||
|
||||
<section class="prose">
|
||||
@@ -65,16 +62,16 @@ toc:
|
||||
|
||||
<h2 id="install-npm"><a href="#install-npm">Install using NPM</a></h2>
|
||||
|
||||
<ol class="[&>h3]:step steps mb-12 md:ml-4 md:border-l md:pl-8">
|
||||
<li class="step">
|
||||
<h3 class="mb-4 scroll-m-20 font-semibold tracking-tight" id="install-npm-tailwind"><a href="#install-npm-tailwind">Add Tailwind CSS</a></h3>
|
||||
<div class="grid gap-8 mb-12">
|
||||
<section>
|
||||
<h3 class="mb-4 scroll-m-20 font-semibold tracking-tight" id="install-npm-tailwind"><a href="#install-npm-tailwind">Step 1: Add Tailwind CSS</a></h3>
|
||||
<div class="prose">
|
||||
<p>Basecoat uses Tailwind CSS. You need to install it in your project.</p>
|
||||
<p><a href="https://tailwindcss.com/docs/installation" target="_blank">Follow the Tailwind CSS installation instructions to get started.</a></p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="step mt-8">
|
||||
<h3 class="mb-4 scroll-m-20 font-semibold tracking-tight" id="install-npm-basecoat"><a href="#install-npm-basecoat">Add Basecoat</a></h3>
|
||||
</section>
|
||||
<section>
|
||||
<h3 class="mb-4 scroll-m-20 font-semibold tracking-tight" id="install-npm-basecoat"><a href="#install-npm-basecoat">Step 2: Add Basecoat</a></h3>
|
||||
<div class="prose">
|
||||
<p>Add Basecoat to your Tailwind CSS file.</p>
|
||||
</div>
|
||||
@@ -92,15 +89,21 @@ toc:
|
||||
<div class="prose">
|
||||
<p>Alternatively, you can directly <a href="https://github.com/hunvreus/basecoat/blob/main/src/css/basecoat.css" target="_blank">copy the <code>basecoat.css</code> file</a> into your project.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="step mt-8">
|
||||
<h3 class="mb-4 scroll-m-20 font-semibold tracking-tight" id="install-npm-import"><a href="#install-npm-import">Import basecoat in your CSS</a></h3>
|
||||
</section>
|
||||
<section>
|
||||
<h3 class="mb-4 scroll-m-20 font-semibold tracking-tight" id="install-npm-import"><a href="#install-npm-import">Step 3: Import Basecoat</a></h3>
|
||||
{% set code %}@import "tailwindcss";
|
||||
@import "basecoat-css";{% endset %}
|
||||
{{ code_block(code, "css") }}
|
||||
</li>
|
||||
<li class="step mt-8">
|
||||
<h3 class="mb-4 scroll-m-20 font-semibold tracking-tight" id="install-npm-js"><a href="#install-npm-js">(Optional) Add JavaScript files</a></h3>
|
||||
<div class="prose">
|
||||
<p>The default import is the Basecoat backward-compatible default. To use a specific style, import the standalone style bundle instead:</p>
|
||||
</div>
|
||||
{% set code %}@import "tailwindcss";
|
||||
@import "basecoat-css/sera";{% endset %}
|
||||
{{ code_block(code, "css") }}
|
||||
</section>
|
||||
<section>
|
||||
<h3 class="mb-4 scroll-m-20 font-semibold tracking-tight" id="install-npm-js"><a href="#install-npm-js">Step 4: Add JavaScript files</a></h3>
|
||||
<div class="prose">
|
||||
<p>Some components <a href="#install-js">need some JavaScript</a> (e.g. the <a href="/components/tabs">Tabs</a> component). There are two ways to get it into your project:</p>
|
||||
|
||||
@@ -131,15 +134,15 @@ import 'basecoat-css/tabs';{% endraw %}{% endset %}
|
||||
<div class="prose">
|
||||
<p>See each component page for the minimal script required.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="step mt-8">
|
||||
<h3 class="mb-4 scroll-m-20 font-semibold tracking-tight" id="install-npm-done"><a href="#install-npm-done">That's it</a></h3>
|
||||
</section>
|
||||
<section>
|
||||
<h3 class="mb-4 scroll-m-20 font-semibold tracking-tight" id="install-npm-done"><a href="#install-npm-done">Step 5: Use components</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>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<h2 id="install-js"><a href="#install-js">Components with JavaScript</a></h2>
|
||||
|
||||
@@ -150,126 +153,6 @@ import 'basecoat-css/tabs';{% endraw %}{% endset %}
|
||||
|
||||
<ul>
|
||||
<li><b>CDN</b>: you either <a href="#install-cdn-all">add the code for all of the components</a> to the <code><head></code> of your HTML file, or just the file for the component you want to use as instructed on the component's page.</li>
|
||||
<li><b>Local file</b>: you download the script as a separate file and include it in your project (<a href="#install-cli">or let the CLI do it for you</a>). Once again, you have the choice to include the file for all components at once (<a><code>all.min.js</code> or <code>all.js</code>).</p>
|
||||
<li><b>Local file</b>: you download the script as a separate file and include it in your project. Once again, you have the choice to include the file for all components at once (<code>all.min.js</code> or <code>all.js</code>).</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2 id="install-cli"><a href="#install-cli">Use the CLI</a></h2>
|
||||
|
||||
<div class="prose">
|
||||
<p>If you're using Nunjucks or Jinja, you can use the CLI to install the CSS and macros for any of the complex components(e.g. <a href="/components/dialog">Dialog</a>). It will copy the JS and <code>.html.jinja</code> or <code>.njk</code> macros into your code.</p>
|
||||
<p>For example, to add the Dialog component, run one of the following commands:</p>
|
||||
</div>
|
||||
|
||||
{% set code_npm_exec %}npx basecoat-cli add dialog{% endset %}
|
||||
{% set code_pnpm_exec %}pnpm dlx basecoat-cli add dialog{% endset %}
|
||||
{% set code_bun_exec %}bunx basecoat-cli add dialog{% endset %}
|
||||
{% set code_yarn_exec %}yarn dlx basecoat-cli add dialog{% endset %}
|
||||
{% set exec_tabsets = [
|
||||
{ tab: "npm", panel: code_block(code_npm_exec, class="") },
|
||||
{ tab: "pnpm", panel: code_block(code_pnpm_exec, class="") },
|
||||
{ tab: "bun", panel: code_block(code_bun_exec, class="") },
|
||||
{ tab: "yarn", panel: code_block(code_yarn_exec, class="") }
|
||||
] %}
|
||||
{{ 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>
|
||||
</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>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>
|
||||
</div>
|
||||
|
||||
{% set code %}{% raw %}{% call select() %}
|
||||
<div role="group" aria-labelledby="fruit-options-label">
|
||||
<span id="fruit-options-label" role="heading">Fruits</span>
|
||||
<div role="option" data-value="apple">Apple</div>
|
||||
<div role="option" data-value="banana">Banana</div>
|
||||
<div role="option" data-value="blueberry">Blueberry</div>
|
||||
<div role="option" data-value="pineapple">Grapes</div>
|
||||
<div role="option" data-value="pineapple">Pineapple</div>
|
||||
</div>
|
||||
{% endcall %}{% endraw %}{% endset %}
|
||||
{{ code_block(code, "jinja") }}
|
||||
|
||||
<h2 id="install-icons"><a href="#install-icons">Icons</a></h2>
|
||||
|
||||
<div class="prose">
|
||||
<p>Basecoat uses <a href="https://lucide.dev" target="_blank">Lucide icons</a>. You have three options:</p>
|
||||
|
||||
<ul>
|
||||
<li><b>Copy SVG code</b>: Visit <a href="https://lucide.dev/icons" target="_blank">lucide.dev/icons</a>, click any icon to copy the SVG, and paste it directly in your HTML. Best for simple projects or occasional icon usage.</li>
|
||||
<li><b>Install lucide</b>: Follow the <a href="https://lucide.dev/guide/installation" target="_blank">installation guide</a> to install the <code>lucide</code> package and dynamically render icons. Best for projects that need many icons or want tree-shaking.</li>
|
||||
<li><b>Use framework packages</b>: Browse <a href="https://lucide.dev/packages" target="_blank">framework-specific packages</a> (React, Vue, Angular, etc.) for component-based icon usage. Best if you're using a supported framework.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
<a class="badge-outline" href="https://lucide.dev" target="_blank">
|
||||
Browse icons
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="https://lucide.dev/guide/installation" target="_blank">
|
||||
Installation guide
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
<a class="badge-outline" href="https://lucide.dev/packages" target="_blank">
|
||||
Packages
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2 id="install-theming"><a href="#install-theming">Theming</a></h2>
|
||||
|
||||
<div class="prose">
|
||||
<p>You can import any <a href="https://shadcn-ui.com" target="_blank">shadcn/ui</a> compatible theme (e.g. <a href="https://tweakcn.com" target="_blank">tweakcn</a>). For example:</p>
|
||||
<ul>
|
||||
<li>Go to <a href="https://ui.shadcn.com/themes" target="_blank">ui.shadcn.com/themes</a> and select a theme.</li>
|
||||
<li>Click "Copy code" and save the theme variables in a file (e.g. <code>theme.css</code>).</li>
|
||||
<li>
|
||||
Import the theme in your CSS file:
|
||||
{% set code %}@import "tailwindcss";
|
||||
@import "basecoat-css";
|
||||
@import "./theme.css";{% endset %}
|
||||
{{ code_block(code, "css") }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-6">
|
||||
<a class="badge-outline" href="https://ui.shadcn.com/docs/theming" target="_blank">
|
||||
About shadcn/ui theming
|
||||
{% lucide "arrow-right" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2 id="install-customization"><a href="#install-manual">Customization</a></h2>
|
||||
|
||||
<div class="prose">
|
||||
<section class="prose">
|
||||
<p>You can override default styles using Tailwind:</p>
|
||||
</section>
|
||||
|
||||
{% set code %}<button class="btn font-normal ">Click me</button>{% endset %}
|
||||
{{ code_block(code, "html") }}
|
||||
|
||||
<section class="prose">
|
||||
<p>You can also extend or override the existing styles in your own Tailwind files:</p>
|
||||
</section>
|
||||
|
||||
{% set code %}@import "tailwindcss";
|
||||
@import "basecoat-css";
|
||||
@import "./custom.css";{% endset %}
|
||||
{{ code_block(code, "css") }}
|
||||
|
||||
<section class="prose">
|
||||
<p>More importantly, you can use the <a href="#install-theming">theme variables</a> to customize many aspects of the components (colors, fonts, sizes, etc).</p>
|
||||
|
||||
<p><b>If you want to make more drastic changes</b>, you can <a href="https://github.com/hunvreus/basecoat/blob/main/src/css/basecoat.css" target="_blank">copy the <code>basecoat.css</code> file</a> into your project and make your changes there.</p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@ toc:
|
||||
<h2 id="how-it-works"><a href="#how-it-works">How it works</a></h2>
|
||||
|
||||
<div class="prose">
|
||||
<p>Add a single CSS file to your Tailwind setup. Use components by dropping in simple classes like <code>btn</code>, <code>form</code>, or <code>card</code>.</p>
|
||||
<p>Add a single CSS file to your Tailwind setup. Use components by dropping in simple classes like <code>btn</code>, <code>field</code>, or <code>card</code>.</p>
|
||||
|
||||
<p>Some components (like modals or dropdowns) use a tiny bit of vanilla JavaScript. You can skip it if you don't need interactivity.</p>
|
||||
|
||||
|
||||
+3
-3
@@ -21,9 +21,10 @@ Usage
|
||||
- CSS‑only components: use classes like `btn`, `card`, `input`, etc.
|
||||
- JS components: include the JS and use required roles/attributes (see below).
|
||||
|
||||
Theming
|
||||
- Tailwind projects can override design tokens (e.g., `primary`, `muted-foreground`).
|
||||
Theming and customization
|
||||
- Tailwind projects can override design tokens (e.g., `primary`, `muted-foreground`) and font tokens (`--font-sans`, `--font-mono`).
|
||||
- Without Tailwind, use CDN CSS and add overrides after the link tag.
|
||||
- See `/customization` for icons, themes, fonts, and style overrides.
|
||||
|
||||
Common pitfalls
|
||||
- Do not invent class names; use documented classes only.
|
||||
@@ -38,7 +39,6 @@ Components
|
||||
- Textarea (CSS): `.textarea` styled multiline input.
|
||||
- Label (CSS): `.label` for associated form labels.
|
||||
- Field (CSS): `.field` wrapper for label, input, hint, error.
|
||||
- Form (CSS): `.form` patterns for layout and validation styling.
|
||||
- Checkbox (CSS): styled native checkbox; use `input[type='checkbox']`.
|
||||
- Radio Group (CSS): `.radio-group` pattern using native radios.
|
||||
- Switch (CSS): styled checkbox with `role="switch"` if needed.
|
||||
|
||||
Reference in New Issue
Block a user