Prepare 1.0.0-beta.5

This commit is contained in:
Ronan Berder
2026-06-22 18:19:38 +08:00
parent 9806d6af63
commit cb72d80558
76 changed files with 3361 additions and 1424 deletions
+4 -1
View File
@@ -1,5 +1,8 @@
@import "tailwindcss" source(none);
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
@source "../../../../src/pages/**/*.{astro,html,js}";
@reference "tailwindcss";
@reference "../../../../src/css/base/base.css";
@import "../../../../src/css/basecoat-luma.css";
@import "../../../../node_modules/reallysimpledocs/src/css/styles/luma.css";
@import "reallysimpledocs/css";
@import "reallysimpledocs/css/styles/luma";
+4 -1
View File
@@ -1,5 +1,8 @@
@import "tailwindcss" source(none);
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
@source "../../../../src/pages/**/*.{astro,html,js}";
@reference "tailwindcss";
@reference "../../../../src/css/base/base.css";
@import "../../../../src/css/basecoat-lyra.css";
@import "../../../../node_modules/reallysimpledocs/src/css/styles/lyra.css";
@import "reallysimpledocs/css";
@import "reallysimpledocs/css/styles/lyra";
+4 -1
View File
@@ -1,5 +1,8 @@
@import "tailwindcss" source(none);
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
@source "../../../../src/pages/**/*.{astro,html,js}";
@reference "tailwindcss";
@reference "../../../../src/css/base/base.css";
@import "../../../../src/css/basecoat-maia.css";
@import "../../../../node_modules/reallysimpledocs/src/css/styles/maia.css";
@import "reallysimpledocs/css";
@import "reallysimpledocs/css/styles/maia";
+4 -1
View File
@@ -1,5 +1,8 @@
@import "tailwindcss" source(none);
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
@source "../../../../src/pages/**/*.{astro,html,js}";
@reference "tailwindcss";
@reference "../../../../src/css/base/base.css";
@import "../../../../src/css/basecoat-mira.css";
@import "../../../../node_modules/reallysimpledocs/src/css/styles/mira.css";
@import "reallysimpledocs/css";
@import "reallysimpledocs/css/styles/mira";
+4 -1
View File
@@ -1,5 +1,8 @@
@import "tailwindcss" source(none);
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
@source "../../../../src/pages/**/*.{astro,html,js}";
@reference "tailwindcss";
@reference "../../../../src/css/base/base.css";
@import "../../../../src/css/basecoat-nova.css";
@import "../../../../node_modules/reallysimpledocs/src/css/styles/nova.css";
@import "reallysimpledocs/css";
@import "reallysimpledocs/css/styles/nova";
+4 -1
View File
@@ -1,5 +1,8 @@
@import "tailwindcss" source(none);
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
@source "../../../../src/pages/**/*.{astro,html,js}";
@reference "tailwindcss";
@reference "../../../../src/css/base/base.css";
@import "../../../../src/css/basecoat-rhea.css";
@import "../../../../node_modules/reallysimpledocs/src/css/styles/rhea.css";
@import "reallysimpledocs/css";
@import "reallysimpledocs/css/styles/rhea";
+4 -1
View File
@@ -1,5 +1,8 @@
@import "tailwindcss" source(none);
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
@source "../../../../src/pages/**/*.{astro,html,js}";
@reference "tailwindcss";
@reference "../../../../src/css/base/base.css";
@import "../../../../src/css/basecoat-sera.css";
@import "../../../../node_modules/reallysimpledocs/src/css/styles/sera.css";
@import "reallysimpledocs/css";
@import "reallysimpledocs/css/styles/sera";
+4 -1
View File
@@ -1,5 +1,8 @@
@import "tailwindcss" source(none);
@source "../../../../docs/src/**/*.{astro,html,js,md,mdx}";
@source "../../../../src/pages/**/*.{astro,html,js}";
@reference "tailwindcss";
@reference "../../../../src/css/base/base.css";
@import "../../../../src/css/basecoat-vega.css";
@import "../../../../node_modules/reallysimpledocs/src/css/styles/vega.css";
@import "reallysimpledocs/css";
@import "reallysimpledocs/css/styles/vega";
+305 -57
View File
@@ -1,74 +1,322 @@
# Accordion
<Callout title="No dedicated component" icon="circle-alert">
There is no dedicated Accordion component in Basecoat.
</Callout>
A vertically stacked set of interactive headings that each reveal a section of content.
<Preview class="w-full max-w-md">
<Preview class="w-full max-w-sm">
<section class="accordion">
<details class="group border-b last:border-b-0">
<summary class="w-full focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] transition-all outline-none rounded-md">
<h2 class="flex flex-1 items-start justify-between gap-4 py-4 text-left text-sm font-medium hover:underline">
Is it accessible?
<svg class="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200 group-open:rotate-180 lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</h2>
</summary>
<section class="pb-4">
<p class="text-sm">Yes. It adheres to the WAI-ARIA design pattern.</p>
</section>
</details>
<details class="group border-b last:border-b-0">
<summary class="w-full focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] transition-all outline-none rounded-md">
<h2 class="flex flex-1 items-start justify-between gap-4 py-4 text-left text-sm font-medium hover:underline">
Is it styled?
<svg class="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200 group-open:rotate-180 lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</h2>
</summary>
<section class="pb-4">
<p class="text-sm">Yes. It comes with default styles that matches the other components' aesthetic.</p>
</section>
</details>
<details class="group border-b last:border-b-0">
<summary class="w-full focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] transition-all outline-none rounded-md">
<h2 class="flex flex-1 items-start justify-between gap-4 py-4 text-left text-sm font-medium hover:underline">
Is it animated?
<svg class="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200 group-open:rotate-180 lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</h2>
<details open>
<summary>
What are your shipping options?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
<p class="text-sm whitespace-pre-wrap">Yes. It's animated by default, but you can disable it if you prefer.</p>
We offer standard (5-7 days), express (2-3 days), and overnight shipping. Free shipping on international orders.
</section>
</details>
<details>
<summary>
What is your return policy?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
You can return items within 30 days of delivery. Items must be unused and in their original packaging.
</section>
</details>
<details>
<summary>
How can I contact customer support?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
Email support@example.com or use live chat during business hours.
</section>
</details>
</section>
<script>
(() => {
const accordions = document.querySelectorAll(".accordion");
accordions.forEach((accordion) => {
accordion.addEventListener("click", (event) => {
const summary = event.target.closest("summary");
if (!summary) return;
const details = summary.closest("details");
if (!details) return;
accordion.querySelectorAll("details").forEach((detailsEl) => {
if (detailsEl !== details) {
detailsEl.removeAttribute("open");
}
});
});
});
})();
</script>
</Preview>
## Usage
Basecoat styles native `<details>` disclosure animations. Use semantic `<details>` and `<summary>` markup, then add your own utility classes for the visual treatment.
<Steps>
<Step title="Include the JavaScript files">
```html
<details>
<summary>Is it accessible?</summary>
<p>Yes. It uses native disclosure semantics.</p>
</details>
<script src="https://cdn.jsdelivr.net/npm/basecoat-css@1.0.0-beta.4/dist/js/basecoat.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/basecoat-css@1.0.0-beta.4/dist/js/accordion.min.js" defer></script>
```
The example above show you how to use a small script to create an accordion with a group of `<details>` elements.
</Step>
<Step title="Add your accordion HTML">
```html
<section class="accordion">
<details open>
<summary>
Is it accessible?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
Yes. It uses native disclosure semantics.
</section>
</details>
</section>
```
</Step>
</Steps>
Basecoat uses native `<details>` and `<summary>` elements for disclosure semantics. The accordion JavaScript only enforces single-item behavior by default and prevents disabled items from toggling. Add `data-multiple` to the root when more than one item can be open at a time.
### HTML structure
<dl>
<dt><code>&lt;section class="accordion"&gt;</code></dt>
<dd>
Root accordion container. Add <code>data-multiple</code> to allow multiple open items.
<dl>
<dt><code>&lt;details open&gt;</code></dt>
<dd>
Accordion item. Use the native <code>open</code> attribute for the default expanded item. Add <code>aria-disabled="true"</code> when an item should not toggle.
<dl>
<dt><code>&lt;summary&gt;</code></dt>
<dd>Accordion trigger. Keep the trigger text visible and add an optional trailing icon.</dd>
<dt><code>&lt;section&gt;</code></dt>
<dd>Accordion content. Use any semantic element that fits your content.</dd>
</dl>
</dd>
</dl>
</dd>
</dl>
### JavaScript API
Accordion does not expose custom methods or events. Programmatically open or close an item with the native `details.open` boolean.
```js
document.querySelector("#billing").open = true
```
## Examples
### Basic
A basic accordion that shows one item at a time. The first item is open by default.
<Preview class="w-full max-w-sm">
<section class="accordion">
<details open>
<summary>
How do I reset my password?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
Click on 'Forgot Password' on the login page, enter your email address, and we'll send you a link to reset your password. The link will expire in 24 hours.
</section>
</details>
<details>
<summary>
Can I change my subscription plan?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
Yes. You can upgrade or downgrade your plan from the billing settings page.
</section>
</details>
<details>
<summary>
What payment methods do you accept?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
We accept all major credit cards, PayPal, and bank transfers for annual plans.
</section>
</details>
</section>
</Preview>
### Multiple
Use `data-multiple` to allow multiple items to be open at the same time.
<Preview class="w-full max-w-md">
<section class="accordion" data-multiple>
<details open>
<summary>
Notification Settings
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
Manage how you receive notifications. You can enable email alerts for updates or push notifications for mobile devices.
</section>
</details>
<details>
<summary>
Privacy & Security
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
Control account security, session history, and privacy preferences.
</section>
</details>
<details>
<summary>
Billing & Subscription
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
Review invoices, change plans, and update payment methods.
</section>
</details>
</section>
</Preview>
### Disabled
Use `aria-disabled="true"` on an item to disable it.
<Preview class="w-full max-w-md">
<section class="accordion">
<details open>
<summary>
Can I access my account history?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
Yes. Your account history is available from the activity page.
</section>
</details>
<details aria-disabled="true">
<summary>
Premium feature information
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
This item is disabled.
</section>
</details>
<details>
<summary>
How do I update my email address?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
Go to account settings, update your email, then confirm the change from your inbox.
</section>
</details>
</section>
</Preview>
### Borders
Add horizontal padding to the rows when using a bordered root.
<Preview class="w-full max-w-md">
<section class="accordion rounded-md border [&>details>summary]:px-4 [&>details>:not(summary)]:px-4">
<details id="billing" open>
<summary>
How does billing work?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
We offer monthly and annual subscription plans. Billing is charged at the beginning of each cycle, and you can cancel anytime. All plans include automatic backups, 24/7 support, and unlimited team members.
</section>
</details>
<details>
<summary>
Is my data secure?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
Data is encrypted in transit and at rest.
</section>
</details>
<details>
<summary>
What integrations do you support?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
We support Slack, GitHub, Google Workspace, and webhooks.
</section>
</details>
</section>
</Preview>
### Card
Wrap the accordion in a card for grouped support or billing content.
<Preview class="w-full max-w-md">
<div class="card">
<header>
<h2>Subscription & Billing</h2>
<p>Common questions about your account, plans, payments and cancellations.</p>
</header>
<section>
<section class="accordion">
<details open>
<summary>
What subscription plans do you offer?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
We offer three subscription tiers: Starter ($9/month), Professional ($29/month), and Enterprise ($99/month). Each plan includes increasing storage limits, API access, priority support, and team collaboration features.
</section>
</details>
<details>
<summary>
How does billing work?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
Billing is charged at the beginning of each cycle.
</section>
</details>
<details>
<summary>
How do I cancel my subscription?
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
Cancel from billing settings. Your plan remains active until the end of the current cycle.
</section>
</details>
</section>
</section>
</div>
</Preview>
### RTL
Accordion spacing uses logical properties. Set `dir="rtl"` on the accordion or a parent element.
<Preview class="w-full max-w-md">
<section class="accordion" dir="rtl">
<details open>
<summary>
كيف يمكنني إعادة تعيين كلمة المرور؟
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
انقر على 'نسيت كلمة المرور' في صفحة تسجيل الدخول، أدخل عنوان بريدك الإلكتروني، وسنرسل لك رابطا لإعادة تعيين كلمة المرور. سينتهي صلاحية الرابط خلال 24 ساعة.
</section>
</details>
<details>
<summary>
هل يمكنني تغيير خطة الاشتراك الخاصة بي؟
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
نعم، يمكنك تغيير الخطة من إعدادات الفوترة.
</section>
</details>
<details>
<summary>
ما هي طرق الدفع التي تقبلونها؟
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</summary>
<section>
نقبل بطاقات الائتمان الرئيسية والتحويلات البنكية.
</section>
</details>
</section>
</Preview>
+118 -30
View File
@@ -1,16 +1,18 @@
# Alert Dialog
A modal dialog that interrupts the user with important content and expects a response.
<Preview>
<button type="button" onclick="document.getElementById('demo-alert-dialog').showModal()" class="btn" data-variant="outline">Open alert dialog</button>
<dialog id="demo-alert-dialog" class="dialog" aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description">
<button type="button" onclick="document.getElementById('demo-alert-dialog').showModal()" class="btn" data-variant="outline">Show Dialog</button>
<dialog id="demo-alert-dialog" class="alert-dialog" aria-labelledby="demo-alert-dialog-title" aria-describedby="demo-alert-dialog-description">
<div>
<header>
<h2 id="alert-dialog-title">Are you absolutely sure?</h2>
<p id="alert-dialog-description">This action cannot be undone. This will permanently delete your account and remove your data from our servers.</p>
<h2 id="demo-alert-dialog-title">Are you absolutely sure?</h2>
<p id="demo-alert-dialog-description">This action cannot be undone. This will permanently delete your account and remove your data from our servers.</p>
</header>
<footer>
<button class="btn" data-variant="outline" onclick="document.getElementById('demo-alert-dialog').close()">Cancel</button>
<button class="btn" data-variant="primary" onclick="document.getElementById('demo-alert-dialog').close()">Continue</button>
<button class="btn" data-variant="outline" onclick="this.closest('dialog').close()">Cancel</button>
<button class="btn" onclick="this.closest('dialog').close()">Continue</button>
</footer>
</div>
</dialog>
@@ -18,23 +20,19 @@
## Usage
<Callout title="Template macros available" icon="code" action={{ label: "Read more", href: "/cli#dialog" }}>
This component can use the `dialog()` macro for Jinja and Nunjucks.
</Callout>
Alert Dialog uses the same `.dialog` class as [Dialog](/components/dialog), but the example omits the close button and does not close on backdrop click.
Alert Dialog uses a native `<dialog class="alert-dialog">` element. Omit the close button and backdrop-click handler when the user must choose an explicit action.
```html
<button type="button" onclick="document.getElementById('demo-alert-dialog').showModal()" class="btn" data-variant="outline">Open alert dialog</button>
<dialog id="demo-alert-dialog" class="dialog" aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description">
<button type="button" onclick="document.getElementById('demo-alert-dialog').showModal()" class="btn" data-variant="outline">Show Dialog</button>
<dialog id="demo-alert-dialog" class="alert-dialog" aria-labelledby="demo-alert-dialog-title" aria-describedby="demo-alert-dialog-description">
<div>
<header>
<h2 id="alert-dialog-title">Are you absolutely sure?</h2>
<p id="alert-dialog-description">This action cannot be undone. This will permanently delete your account and remove your data from our servers.</p>
<h2 id="demo-alert-dialog-title">Are you absolutely sure?</h2>
<p id="demo-alert-dialog-description">This action cannot be undone. This will permanently delete your account and remove your data from our servers.</p>
</header>
<footer>
<button class="btn" data-variant="outline" onclick="document.getElementById('demo-alert-dialog').close()">Cancel</button>
<button class="btn" data-variant="primary" onclick="document.getElementById('demo-alert-dialog').close()">Continue</button>
<button class="btn" data-variant="outline" onclick="this.closest('dialog').close()">Cancel</button>
<button class="btn" onclick="this.closest('dialog').close()">Continue</button>
</footer>
</div>
</dialog>
@@ -43,32 +41,122 @@ Alert Dialog uses the same `.dialog` class as [Dialog](/components/dialog), but
### HTML structure
<dl>
<dt><code>&lt;button type="button"&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>The trigger button to open the alert 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>&lt;dialog class="dialog" id="&#123; DIALOG_ID &#125;"&gt;</code></dt>
<dt><code>&lt;button type="button" onclick="dialog.showModal()"&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Trigger button. Basecoat uses the native <code>HTMLDialogElement.showModal()</code> method.</dd>
<dt><code>&lt;dialog class="alert-dialog" id="&#123; DIALOG_ID &#125;"&gt;</code></dt>
<dd>
Native modal dialog. Add <code>aria-labelledby</code> and <code>aria-describedby</code> when title and description are present.
<dl>
<dt><code>&lt;div&gt;</code></dt>
<dd>
Wraps the content of the alert dialog.
Dialog content surface.
<dl>
<dt><code>&lt;header&gt;</code></dt>
<dd>
Contains the header for the alert dialog:
Alert dialog header.
<dl>
<dt><code>&lt;h2&gt;</code></dt>
<dd>The title of the alert dialog, must have an <code>id</code> if you use the <code>aria-labelledby</code> attribute on the <code>&lt;dialog&gt;</code>.</dd>
<dt><code>&lt;p&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>The description of the alert dialog, must have an <code>id</code> if you use the <code>aria-describedby</code> attribute on the <code>&lt;dialog&gt;</code>.</dd>
<dt><code>&lt;figure&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Media area for an icon or image.</dd>
<dt><code>&lt;h2 id="&#123; TITLE_ID &#125;"&gt;</code></dt>
<dd>Alert dialog title. Reference it from <code>aria-labelledby</code>.</dd>
<dt><code>&lt;p id="&#123; DESCRIPTION_ID &#125;"&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Alert dialog description. Reference it from <code>aria-describedby</code>.</dd>
</dl>
</dd>
<dt><code>&lt;section&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>For the content.</dd>
<dt><code>&lt;footer&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Usually contains actions.</dd>
<dt><code>&lt;footer&gt;</code></dt>
<dd>Action area. Include a cancel action and the primary action.</dd>
</dl>
</dd>
</dl>
</dd>
</dl>
## Examples
### Basic
A basic alert dialog with a title, description, and cancel and continue buttons.
<Preview>
<button type="button" onclick="document.getElementById('alert-dialog-basic').showModal()" class="btn" data-variant="outline">Show Dialog</button>
<dialog id="alert-dialog-basic" class="alert-dialog" aria-labelledby="alert-dialog-basic-title" aria-describedby="alert-dialog-basic-description">
<div>
<header>
<h2 id="alert-dialog-basic-title">Are you absolutely sure?</h2>
<p id="alert-dialog-basic-description">This action cannot be undone. This will permanently delete your account and remove your data from our servers.</p>
</header>
<footer>
<button class="btn" data-variant="outline" onclick="this.closest('dialog').close()">Cancel</button>
<button class="btn" onclick="this.closest('dialog').close()">Continue</button>
</footer>
</div>
</dialog>
</Preview>
### Media
Use a `<figure>` in the header to add a media element such as an icon or image to the alert dialog.
<Preview>
<button type="button" onclick="document.getElementById('alert-dialog-media').showModal()" class="btn" data-variant="outline">Share Project</button>
<dialog id="alert-dialog-media" class="alert-dialog" aria-labelledby="alert-dialog-media-title" aria-describedby="alert-dialog-media-description">
<div>
<header>
<figure>
<svg class="lucide lucide-circle-fading-plus" 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"><path d="M12 2a10 10 0 0 1 7.38 16.75" /><path d="M12 8v8" /><path d="M16 12H8" /><path d="M2.5 8.88a10 10 0 0 0 0 6.24" /><path d="M4.64 4.64a10 10 0 0 0 0 14.72" /></svg>
</figure>
<h2 id="alert-dialog-media-title">Share this project?</h2>
<p id="alert-dialog-media-description">Anyone with the link will be able to view and edit this project.</p>
</header>
<footer>
<button class="btn" data-variant="outline" onclick="this.closest('dialog').close()">Cancel</button>
<button class="btn" onclick="this.closest('dialog').close()">Share</button>
</footer>
</div>
</dialog>
</Preview>
### Destructive
Use a destructive action button when the alert dialog confirms a destructive operation.
<Preview>
<button type="button" onclick="document.getElementById('alert-dialog-destructive').showModal()" class="btn" data-variant="destructive">Delete Chat</button>
<dialog id="alert-dialog-destructive" class="alert-dialog" aria-labelledby="alert-dialog-destructive-title" aria-describedby="alert-dialog-destructive-description">
<div>
<header>
<figure>
<svg class="lucide lucide-trash-2" 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"><path d="M10 11v6" /><path d="M14 11v6" /><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /><path d="M3 6h18" /><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></svg>
</figure>
<h2 id="alert-dialog-destructive-title">Delete this chat?</h2>
<p id="alert-dialog-destructive-description">This action cannot be undone. This will permanently delete the chat and remove it from your history.</p>
</header>
<footer>
<button class="btn" data-variant="outline" onclick="this.closest('dialog').close()">Cancel</button>
<button class="btn" data-variant="destructive" onclick="this.closest('dialog').close()">Delete</button>
</footer>
</div>
</dialog>
</Preview>
### RTL
Alert Dialog supports document direction. Set `dir="rtl"` on the dialog or a parent element.
<Preview>
<div dir="rtl">
<button type="button" onclick="document.getElementById('alert-dialog-rtl').showModal()" class="btn" data-variant="outline">إظهار الحوار</button>
<dialog id="alert-dialog-rtl" class="alert-dialog" aria-labelledby="alert-dialog-rtl-title" aria-describedby="alert-dialog-rtl-description">
<div>
<header>
<h2 id="alert-dialog-rtl-title">هل أنت متأكد تماماً؟</h2>
<p id="alert-dialog-rtl-description">لا يمكن التراجع عن هذا الإجراء. سيتم حذف حسابك نهائياً وإزالة بياناتك من خوادمنا.</p>
</header>
<footer>
<button class="btn" data-variant="outline" onclick="this.closest('dialog').close()">إلغاء</button>
<button class="btn" onclick="this.closest('dialog').close()">متابعة</button>
</footer>
</div>
</dialog>
</div>
</Preview>
+59 -74
View File
@@ -1,28 +1,16 @@
# Alert
<Preview>
<div class="grid w-full max-w-xl items-start gap-4">
<Preview class="w-full max-w-md">
<div class="grid items-start gap-4">
<div class="alert">
<svg class="lucide lucide-circle-check" 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"><circle cx="12" cy="12" r="10" /><path d="m9 12 2 2 4-4" /></svg>
<h2>Success! Your changes have been saved</h2>
<section>This is an alert with icon, title and description.</section>
<h2>Payment successful</h2>
<section>Your payment of $29.99 has been processed. A receipt has been sent to your email address.</section>
</div>
<div class="alert">
<svg class="lucide lucide-popcorn" 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"><path d="M18 8a2 2 0 0 0 0-4 2 2 0 0 0-4 0 2 2 0 0 0-4 0 2 2 0 0 0-4 0 2 2 0 0 0 0 4" /><path d="M10 22 9 8" /><path d="m14 22 1-14" /><path d="M20 8c.5 0 .9.4.8 1l-2.6 12c-.1.5-.7 1-1.2 1H7c-.6 0-1.1-.4-1.2-1L3.2 9c-.1-.6.3-1 .8-1Z" /></svg>
<h2>This Alert has a title and an icon. No description.</h2>
<section>This is an alert with icon, title and description.</section>
</div>
<div class="alert" data-variant="destructive">
<svg class="lucide lucide-circle-check" 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"><circle cx="12" cy="12" r="10" /><path d="m9 12 2 2 4-4" /></svg>
<h2>Unable to process your payment.</h2>
<section>
<p>Please verify your billing information and try again.</p>
<ul class="list-inside list-disc text-sm">
<li>Check your card details</li>
<li>Ensure sufficient funds</li>
<li>Verify billing address</li>
</ul>
</section>
<svg class="lucide lucide-info" 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"><circle cx="12" cy="12" r="10" /><path d="M12 16v-4" /><path d="M12 8h.01" /></svg>
<h2>New feature available</h2>
<section>We've added dark mode support. You can enable it in your account settings.</section>
</div>
</div>
</Preview>
@@ -33,9 +21,12 @@ Use `alert` for default styling. Add `data-variant="destructive"` for error stat
```html
<div class="alert">
<svg class="lucide lucide-circle-check" 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"><circle cx="12" cy="12" r="10" /><path d="m9 12 2 2 4-4" /></svg>
<h2>Success! Your changes have been saved</h2>
<section>This is an alert with icon, title and description.</section>
<svg class="lucide lucide-info" 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"><circle cx="12" cy="12" r="10" /><path d="M12 16v-4" /><path d="M12 8h.01" /></svg>
<h2>Heads up!</h2>
<section>You can add components and dependencies to your app using the cli.</section>
<footer>
<button type="button" class="btn" data-variant="outline">Enable</button>
</footer>
</div>
```
@@ -60,63 +51,53 @@ Use `alert` for default styling. Add `data-variant="destructive"` for error stat
## Examples
### Basic
A basic alert with an icon, title and description.
<Preview class="w-full max-w-md">
<div class="alert">
<svg class="lucide lucide-circle-check" 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"><circle cx="12" cy="12" r="10" /><path d="m9 12 2 2 4-4" /></svg>
<h2>Account updated successfully</h2>
<section>Your profile information has been saved. Changes will be reflected immediately.</section>
</div>
</Preview>
### Destructive
<Preview>
Use `data-variant="destructive"` to create a destructive alert.
<Preview class="w-full max-w-md">
<div class="alert" data-variant="destructive">
<svg class="lucide lucide-circle-alert" 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"><circle cx="12" cy="12" r="10" /><line x1="12" x2="12" y1="8" y2="12" /><line x1="12" x2="12.01" y1="16" y2="16" /></svg>
<h2>Something went wrong!</h2>
<section>Your session has expired. Please log in again.</section>
</div>
</Preview>
### No description
<Preview>
<div class="alert">
<svg class="lucide lucide-shield-alert" 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" ><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /><path d="M12 8v4" /><path d="M12 16h.01" /></svg>
<h2>This is a very long alert title that demonstrates how the component handles extended text content and potentially wraps across multiple lines</h2>
</div>
</Preview>
### No icon
<Preview>
<div class="alert">
<h2>Success! Your changes have been saved</h2>
<section>This is an alert with icon, title and description.</section>
<h2>Payment failed</h2>
<section>Your payment could not be processed. Please check your payment method and try again.</section>
</div>
</Preview>
### Action
<Preview>
<div class="grid w-full max-w-xl gap-4">
<div class="alert">
<svg class="lucide lucide-circle-alert" 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" ><circle cx="12" cy="12" r="10" /><line x1="12" x2="12" y1="8" y2="12" /><line x1="12" x2="12.01" y1="16" y2="16" /></svg>
<h2>The selected emails have been marked as spam.</h2>
<footer>
<button type="button" class="btn" data-variant="outline" data-size="xs">Undo</button>
</footer>
</div>
<div class="alert">
<svg class="lucide lucide-circle-alert" 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" ><circle cx="12" cy="12" r="10" /><line x1="12" x2="12" y1="8" y2="12" /><line x1="12" x2="12.01" y1="16" y2="16" /></svg>
<h2>The selected emails have been marked as spam.</h2>
<section>This is a longer alert description that demonstrates how the action stays aligned to the inline end.</section>
<footer>
<span class="badge">Badge</span>
</footer>
</div>
Use `<footer>` to add a button or other action element to the alert.
<Preview class="w-full max-w-md">
<div class="alert">
<h2>Dark mode is now available</h2>
<section>Enable it under your profile settings to get started.</section>
<footer>
<button type="button" class="btn" data-size="xs">Enable</button>
</footer>
</div>
</Preview>
### Custom Colors
### Custom colors
<Preview>
<div class="alert bg-amber-50 text-amber-900 dark:bg-amber-950 dark:text-amber-200">
<svg class="lucide lucide-triangle-alert" 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" ><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" /><path d="M12 9v4" /><path d="M12 17h.01" /></svg>
<h2>Storage almost full</h2>
<section class="text-amber-800 dark:text-amber-300">Upgrade your plan or delete unused files.</section>
You can customize the alert colors by adding custom classes such as `bg-amber-50 dark:bg-amber-950` to the alert.
<Preview class="w-full max-w-md">
<div class="alert border-amber-200 bg-amber-50 text-amber-900 dark:border-amber-900 dark:bg-amber-950 dark:text-amber-50">
<svg class="lucide lucide-triangle-alert" 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"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" /><path d="M12 9v4" /><path d="M12 17h.01" /></svg>
<h2>Your subscription will expire in 3 days.</h2>
<section>Renew now to avoid service interruption or upgrade to a paid plan to continue using the service.</section>
</div>
</Preview>
@@ -124,13 +105,17 @@ Use `alert` for default styling. Add `data-variant="destructive"` for error stat
Add `dir="rtl"` to the alert or an ancestor.
<Preview>
<div class="alert" dir="rtl">
<svg class="lucide lucide-info" 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" ><circle cx="12" cy="12" r="10" /><path d="M12 16v-4" /><path d="M12 8h.01" /></svg>
<h2>تم حفظ التغييرات</h2>
<section>تم تحديث إعداداتك بنجاح.</section>
<footer>
<button type="button" class="btn" data-variant="outline" data-size="xs">عرض</button>
</footer>
<Preview class="w-full max-w-md">
<div class="grid items-start gap-4" dir="rtl">
<div class="alert">
<svg class="lucide lucide-circle-check" 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"><circle cx="12" cy="12" r="10" /><path d="m9 12 2 2 4-4" /></svg>
<h2>تم الدفع بنجاح</h2>
<section>تمت معالجة دفعتك البالغة 29.99 دولارًا. تم إرسال إيصال إلى عنوان بريدك الإلكتروني.</section>
</div>
<div class="alert">
<svg class="lucide lucide-info" 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"><circle cx="12" cy="12" r="10" /><path d="M12 16v-4" /><path d="M12 8h.01" /></svg>
<h2>ميزة جديدة متاحة</h2>
<section>لقد أضفنا دعم الوضع الداكن. يمكنك تفعيله في إعدادات حسابك.</section>
</div>
</div>
</Preview>
+44 -35
View File
@@ -9,7 +9,6 @@
<span class="avatar rounded-lg">
<img src="https://github.com/evilrabbit.png" alt="@evilrabbit" />
<span>ER</span>
<span data-badge class="bg-green-600 dark:bg-green-800"></span>
</span>
<div class="avatar-group [&_.avatar]:grayscale">
<span class="avatar">
@@ -24,14 +23,13 @@
<img src="https://github.com/evilrabbit.png" alt="@evilrabbit" />
<span>ER</span>
</span>
<span data-count>+3</span>
</div>
</div>
</Preview>
## Usage
Add `class="avatar"` to a small inline container. Add `data-size="sm"` or `data-size="lg"` when needed. Put the image first and a fallback `<span>` after it.
Add `class="avatar"` to a small inline container. Add `data-size="sm"` or `data-size="lg"` when needed. Put the image first and a fallback `<span>` after it. Add `.avatar-badge` for a status badge or badge icon.
```html
<span class="avatar">
@@ -55,8 +53,8 @@ Add `class="avatar"` to a small inline container. Add `data-size="sm"` or `data-
<dd>The avatar image. Provide meaningful <code>alt</code> text.</dd>
<dt><code>&lt;span&gt;</code></dt>
<dd>Fallback initials or icon.</dd>
<dt><code>&lt;span data-badge&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Status badge positioned at the block-end inline-end corner.</dd>
<dt><code>&lt;span class="avatar-badge"&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Status badge positioned at the block-end inline-end corner. It can be empty or contain an icon.</dd>
</dl>
</dd>
<dt><code>&lt;span data-count&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
@@ -71,7 +69,7 @@ Add `class="avatar"` to a small inline container. Add `data-size="sm"` or `data-
<Preview>
<span class="avatar">
<img class="grayscale" src="https://github.com/shadcn.png" alt="@shadcn" />
<img src="https://github.com/shadcn.png" alt="@shadcn" />
<span>CN</span>
</span>
</Preview>
@@ -82,36 +80,36 @@ Add `class="avatar"` to a small inline container. Add `data-size="sm"` or `data-
<span class="avatar">
<img src="https://github.com/shadcn.png" alt="@shadcn" />
<span>CN</span>
<span data-badge class="bg-green-600 dark:bg-green-800"></span>
<span class="avatar-badge bg-green-600 dark:bg-green-800"></span>
</span>
</Preview>
### Badge With Icon
### Badge with icon
<Preview>
<span class="avatar grayscale">
<span class="avatar">
<img src="https://github.com/pranathip.png" alt="@pranathip" />
<span>PP</span>
<span data-badge>
<span class="avatar-badge">
<svg class="lucide lucide-plus" 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"><path d="M5 12h14" /><path d="M12 5v14" /></svg>
</span>
</span>
</Preview>
### Avatar Group
### Avatar group
<Preview>
<div class="avatar-group grayscale">
<div class="avatar-group">
<span class="avatar"><img src="https://github.com/shadcn.png" alt="@shadcn" /><span>CN</span></span>
<span class="avatar"><img src="https://github.com/maxleiter.png" alt="@maxleiter" /><span>LR</span></span>
<span class="avatar"><img src="https://github.com/evilrabbit.png" alt="@evilrabbit" /><span>ER</span></span>
</div>
</Preview>
### Avatar Group Count
### Avatar group count
<Preview>
<div class="avatar-group grayscale">
<div class="avatar-group">
<span class="avatar"><img src="https://github.com/shadcn.png" alt="@shadcn" /><span>CN</span></span>
<span class="avatar"><img src="https://github.com/maxleiter.png" alt="@maxleiter" /><span>LR</span></span>
<span class="avatar"><img src="https://github.com/evilrabbit.png" alt="@evilrabbit" /><span>ER</span></span>
@@ -119,10 +117,10 @@ Add `class="avatar"` to a small inline container. Add `data-size="sm"` or `data-
</div>
</Preview>
### Avatar Group With Icon
### Avatar group with icon
<Preview>
<div class="avatar-group grayscale">
<div class="avatar-group">
<span class="avatar"><img src="https://github.com/shadcn.png" alt="@shadcn" /><span>CN</span></span>
<span class="avatar"><img src="https://github.com/maxleiter.png" alt="@maxleiter" /><span>LR</span></span>
<span class="avatar"><img src="https://github.com/evilrabbit.png" alt="@evilrabbit" /><span>ER</span></span>
@@ -135,19 +133,32 @@ Add `class="avatar"` to a small inline container. Add `data-size="sm"` or `data-
### Sizes
<Preview>
<div class="flex flex-wrap items-center gap-2 grayscale">
<span class="avatar" data-size="sm">
<img src="https://github.com/shadcn.png" alt="@shadcn" />
<span>CN</span>
</span>
<span class="avatar">
<img src="https://github.com/shadcn.png" alt="@shadcn" />
<span>CN</span>
</span>
<span class="avatar" data-size="lg">
<img src="https://github.com/shadcn.png" alt="@shadcn" />
<span>CN</span>
</span>
<div class="grid gap-2">
<div class="flex flex-wrap items-center gap-2">
<span class="avatar" data-size="sm">
<img src="https://github.com/shadcn.png" alt="@shadcn" />
<span>CN</span>
</span>
<span class="avatar">
<img src="https://github.com/shadcn.png" alt="@shadcn" />
<span>CN</span>
</span>
<span class="avatar" data-size="lg">
<img src="https://github.com/shadcn.png" alt="@shadcn" />
<span>CN</span>
</span>
</div>
<div class="flex flex-wrap items-center gap-2">
<span class="avatar" data-size="sm">
<span>CN</span>
</span>
<span class="avatar">
<span>CN</span>
</span>
<span class="avatar" data-size="lg">
<span>CN</span>
</span>
</div>
</div>
</Preview>
@@ -176,21 +187,19 @@ Add `class="avatar"` to a small inline container. Add `data-size="sm"` or `data-
### RTL
<Preview>
<div class="flex flex-row flex-wrap items-center gap-6 md:gap-12" dir="rtl">
<div class="flex flex-row flex-wrap items-center gap-12" dir="rtl">
<span class="avatar">
<img class="grayscale" src="https://github.com/shadcn.png" alt="@shadcn" />
<img src="https://github.com/shadcn.png" alt="@shadcn" />
<span>CN</span>
</span>
<span class="avatar">
<span class="avatar rounded-lg">
<img src="https://github.com/evilrabbit.png" alt="@evilrabbit" />
<span>ER</span>
<span data-badge class="bg-green-600 dark:bg-green-800"></span>
</span>
<div class="avatar-group grayscale">
<div class="avatar-group [&_.avatar]:grayscale">
<span class="avatar"><img src="https://github.com/shadcn.png" alt="@shadcn" /><span>CN</span></span>
<span class="avatar"><img src="https://github.com/maxleiter.png" alt="@maxleiter" /><span>LR</span></span>
<span class="avatar"><img src="https://github.com/evilrabbit.png" alt="@evilrabbit" /><span>ER</span></span>
<span data-count>+٣</span>
</div>
</div>
</Preview>
+69 -55
View File
@@ -1,22 +1,11 @@
# Badge
<Preview>
<div class="flex flex-col items-center gap-2">
<div class="flex w-full flex-wrap gap-2">
<span class="badge">Badge</span>
<span class="badge" data-variant="secondary">Secondary</span>
<span class="badge" data-variant="destructive">Destructive</span>
<span class="badge" data-variant="outline">Outline</span>
</div>
<div class="flex w-full flex-wrap gap-2">
<span class="badge bg-blue-500 text-white dark:bg-blue-600">
<svg class="lucide lucide-badge-check" 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" ><path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" /><path d="m9 12 2 2 4-4" /></svg>
Verified
</span>
<span class="badge rounded-full h-5 min-w-5 px-1 font-mono tabular-nums">8</span>
<span class="badge rounded-full h-5 min-w-5 px-1 font-mono tabular-nums" data-variant="destructive">99</span>
<span class="badge rounded-full h-5 min-w-5 px-1 font-mono tabular-nums" data-variant="outline">20+</span>
</div>
<div class="flex flex-wrap gap-2">
<span class="badge">Badge</span>
<span class="badge" data-variant="secondary">Secondary</span>
<span class="badge" data-variant="destructive">Destructive</span>
<span class="badge" data-variant="outline">Outline</span>
</div>
</Preview>
@@ -27,76 +16,101 @@ Add `class="badge"` to an inline element. Add `data-variant` when the badge is n
Inline SVG icons are spaced automatically by the badge gap.
```html
<span class="badge">Badge</span>
<span class="badge">Default</span>
<span class="badge" data-variant="secondary">Secondary</span>
<span class="badge" data-variant="destructive">Destructive</span>
<span class="badge" data-variant="outline">Outline</span>
<span class="badge" data-variant="ghost">Ghost</span>
```
Badges support right-to-left layouts through native text flow. Flip directional icons explicitly.
## Examples
### Primary
### Variants
Use `data-variant` to change the badge variant.
<Preview>
<span class="badge">Primary</span>
</Preview>
### Secondary
<Preview>
<span class="badge" data-variant="secondary">Secondary</span>
</Preview>
### Destructive
<Preview>
<span class="badge" data-variant="destructive">Destructive</span>
</Preview>
### Outline
<Preview>
<span class="badge" data-variant="outline">Outline</span>
<div class="flex flex-wrap gap-2">
<span class="badge">Default</span>
<span class="badge" data-variant="secondary">Secondary</span>
<span class="badge" data-variant="destructive">Destructive</span>
<span class="badge" data-variant="outline">Outline</span>
<span class="badge" data-variant="ghost">Ghost</span>
</div>
</Preview>
### With icon
Use `data-icon="inline-start"` or `data-icon="inline-end"` on the icon for icon-aware spacing.
<Preview>
<span class="badge" data-variant="destructive">
<svg class="lucide lucide-circle-alert" 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" ><circle cx="12" cy="12" r="10" /><line x1="12" x2="12" y1="8" y2="12" /><line x1="12" x2="12.01" y1="16" y2="16" /></svg>
With icon
</span>
<div class="flex flex-wrap gap-2">
<span class="badge">
<svg data-icon="inline-start" class="lucide lucide-badge-check" 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"><path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" /><path d="m9 12 2 2 4-4" /></svg>
Verified
</span>
<span class="badge" data-variant="secondary">
Bookmark
<svg data-icon="inline-end" class="lucide lucide-bookmark" 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"><path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z" /></svg>
</span>
</div>
</Preview>
### With spinner
Use an inline loading icon with `data-icon="inline-start"` or `data-icon="inline-end"`.
<Preview>
<div class="flex flex-wrap gap-2">
<span class="badge" data-variant="destructive">
<svg aria-label="Loading" role="status" data-icon="inline-start" class="animate-spin lucide lucide-loader-circle" 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"><path d="M21 12a9 9 0 1 1-6.219-8.56" /></svg>
Deleting
</span>
<span class="badge" data-variant="secondary">
Generating
<svg aria-label="Loading" role="status" data-icon="inline-end" class="animate-spin lucide lucide-loader-circle" 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"><path d="M21 12a9 9 0 1 1-6.219-8.56" /></svg>
</span>
</div>
</Preview>
### Link
<Preview>
<a href="#" class="badge" data-variant="outline">
Link
<svg class="rtl:rotate-180 lucide lucide-arrow-right" 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" ><path d="M5 12h14" /><path d="m12 5 7 7-7 7" /></svg>
<a href="#" class="badge">
Open Link
<svg data-icon="inline-end" class="lucide lucide-arrow-up-right" 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"><path d="M7 7h10v10" /><path d="M7 17 17 7" /></svg>
</a>
</Preview>
### Custom Colors
### Custom colors
<Preview>
<span class="badge bg-green-50 text-green-700 dark:bg-green-950 dark:text-green-300">
Custom
</span>
<div class="flex flex-wrap gap-2">
<span class="badge bg-blue-50 text-blue-700 dark:bg-blue-950 dark:text-blue-300">Blue</span>
<span class="badge bg-green-50 text-green-700 dark:bg-green-950 dark:text-green-300">Green</span>
<span class="badge bg-sky-50 text-sky-700 dark:bg-sky-950 dark:text-sky-300">Sky</span>
<span class="badge bg-purple-50 text-purple-700 dark:bg-purple-950 dark:text-purple-300">Purple</span>
<span class="badge bg-red-50 text-red-700 dark:bg-red-950 dark:text-red-300">Red</span>
</div>
</Preview>
### RTL
Flip directional icons explicitly.
<Preview>
<div dir="rtl" class="flex flex-wrap gap-2">
<span class="badge">نشط</span>
<a href="#" class="badge" data-variant="outline">
التفاصيل
<svg class="rtl:rotate-180 lucide lucide-arrow-right" 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" ><path d="M5 12h14" /><path d="m12 5 7 7-7 7" /></svg>
</a>
<span class="badge">شارة</span>
<span class="badge" data-variant="secondary">ثانوي</span>
<span class="badge" data-variant="destructive">مدمر</span>
<span class="badge" data-variant="outline">مخطط</span>
<span class="badge">
<svg data-icon="inline-start" class="lucide lucide-badge-check" 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"><path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" /><path d="m9 12 2 2 4-4" /></svg>
متحقق
</span>
<span class="badge" data-variant="secondary">
إشارة مرجعية
<svg data-icon="inline-end" class="lucide lucide-bookmark" 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"><path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z" /></svg>
</span>
</div>
</Preview>
+152 -52
View File
@@ -1,62 +1,162 @@
# Breadcrumb
<Callout title="No dedicated component" icon="circle-alert">
There is no dedicated Breadcrumb component in Basecoat.
</Callout>
<Preview>
<ol class="text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5">
<li class="inline-flex items-center gap-1.5">
<a href="#" class="hover:text-foreground transition-colors">Home</a>
</li>
<li>
<svg class="size-3.5 lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li class="inline-flex items-center gap-1.5">
<div id="demo-breadcrumb-menu" class="dropdown-menu">
<button type="button" id="demo-breadcrumb-menu-trigger" aria-haspopup="menu" aria-controls="demo-breadcrumb-menu-menu" aria-expanded="false" class="flex size-9 items-center justify-center h-4 w-4 hover:text-foreground cursor-pointer">
<svg class="lucide lucide-ellipsis" 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"><circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" /><circle cx="5" cy="12" r="1" /></svg>
</button>
<div id="demo-breadcrumb-menu-popover" data-popover aria-hidden="true">
<div role="menu" id="demo-breadcrumb-menu-menu" aria-labelledby="demo-breadcrumb-menu-trigger">
<nav role="menu">
<button type="button" role="menuitem">Documentation</button>
<button type="button" role="menuitem">Themes</button>
<button type="button" role="menuitem">GitHub</button>
</nav>
</div>
</div>
</div>
</li>
<li>
<svg class="size-3.5 lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li class="inline-flex items-center gap-1.5">
<a href="#" class="hover:text-foreground transition-colors">Components</a>
</li>
<li>
<svg class="size-3.5 lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li class="inline-flex items-center gap-1.5">
<span class="text-foreground font-normal">Breadcrumb</span>
</li>
</ol>
<nav class="breadcrumb" aria-label="Breadcrumb">
<ol>
<li><a href="#">Home</a></li>
<li aria-hidden="true">
<svg data-rtl-flip class="lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li><a href="#">Components</a></li>
<li aria-hidden="true">
<svg data-rtl-flip class="lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li><span aria-current="page">Breadcrumb</span></li>
</ol>
</nav>
</Preview>
## Usage
You can style an ordered list of links combined with chevron icons as show below. The example above demonstrates a more complex use case using the [Dropdown Menu](/components/dropdown-menu) component.
Use a navigation landmark with an ordered list. Mark the current page with `aria-current="page"`.
```html
<ol class="text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5">
<li class="inline-flex items-center gap-1.5">
<a href="#" class="hover:text-foreground transition-colors">Home</a>
</li>
<li>
<svg class="size-3.5 lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li class="inline-flex items-center gap-1.5">
<a href="#" class="hover:text-foreground transition-colors">Components</a>
</li>
</ol>
<nav class="breadcrumb" aria-label="Breadcrumb">
<ol>
<li><a href="#">Home</a></li>
<li aria-hidden="true">
<svg data-rtl-flip class="lucide lucide-chevron-right" ...></svg>
</li>
<li><span aria-current="page">Breadcrumb</span></li>
</ol>
</nav>
```
## HTML structure
- `nav.breadcrumb` is the root landmark and should include `aria-label="Breadcrumb"`.
- `ol` contains the breadcrumb items in hierarchy order.
- `li > a` represents navigable ancestors.
- `li[aria-hidden="true"]` represents a visual separator.
- `[aria-current="page"]` represents the current page.
- `li > span[aria-hidden="true"]` represents a collapsed ellipsis.
## Examples
### Basic
<Preview>
<nav class="breadcrumb" aria-label="Breadcrumb">
<ol>
<li><a href="#">Home</a></li>
<li aria-hidden="true">
<svg data-rtl-flip class="lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li><a href="#">Components</a></li>
<li aria-hidden="true">
<svg data-rtl-flip class="lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li><span aria-current="page">Breadcrumb</span></li>
</ol>
</nav>
</Preview>
### Custom separator
<Preview>
<nav class="breadcrumb" aria-label="Breadcrumb">
<ol>
<li><a href="#">Home</a></li>
<li aria-hidden="true">
<svg class="lucide lucide-dot" 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"><circle cx="12.1" cy="12.1" r="1" /></svg>
</li>
<li><a href="#">Components</a></li>
<li aria-hidden="true">
<svg class="lucide lucide-dot" 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"><circle cx="12.1" cy="12.1" r="1" /></svg>
</li>
<li><span aria-current="page">Breadcrumb</span></li>
</ol>
</nav>
</Preview>
### Dropdown
<Preview>
<nav class="breadcrumb" aria-label="Breadcrumb">
<ol>
<li><a href="#">Home</a></li>
<li aria-hidden="true">
<svg data-rtl-flip class="lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li>
<div id="breadcrumb-dropdown" class="dropdown-menu">
<button type="button" id="breadcrumb-dropdown-trigger" class="btn" data-variant="ghost" data-size="icon-sm" aria-haspopup="menu" aria-controls="breadcrumb-dropdown-menu" aria-expanded="false">
<svg class="lucide lucide-ellipsis" 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"><circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" /><circle cx="5" cy="12" r="1" /></svg>
<span class="sr-only">Toggle menu</span>
</button>
<div id="breadcrumb-dropdown-popover" data-popover aria-hidden="true">
<div role="menu" id="breadcrumb-dropdown-menu" aria-labelledby="breadcrumb-dropdown-trigger">
<nav role="menu">
<button type="button" role="menuitem">Documentation</button>
<button type="button" role="menuitem">Themes</button>
<button type="button" role="menuitem">GitHub</button>
</nav>
</div>
</div>
</div>
</li>
<li aria-hidden="true">
<svg data-rtl-flip class="lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li><a href="#">Components</a></li>
<li aria-hidden="true">
<svg data-rtl-flip class="lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li><span aria-current="page">Breadcrumb</span></li>
</ol>
</nav>
</Preview>
### Collapsed
<Preview>
<nav class="breadcrumb" aria-label="Breadcrumb">
<ol>
<li><a href="#">Home</a></li>
<li aria-hidden="true">
<svg data-rtl-flip class="lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li>
<span aria-hidden="true">
<svg class="lucide lucide-ellipsis" 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"><circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" /><circle cx="5" cy="12" r="1" /></svg>
</span>
</li>
<li aria-hidden="true">
<svg data-rtl-flip class="lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li><a href="#">Components</a></li>
<li aria-hidden="true">
<svg data-rtl-flip class="lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li><span aria-current="page">Breadcrumb</span></li>
</ol>
</nav>
</Preview>
### RTL
<Preview>
<nav class="breadcrumb" aria-label="Breadcrumb" dir="rtl">
<ol>
<li><a href="#">الرئيسية</a></li>
<li aria-hidden="true">
<svg data-rtl-flip class="lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li><a href="#">المكونات</a></li>
<li aria-hidden="true">
<svg data-rtl-flip class="lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
</li>
<li><span aria-current="page">مسار التنقل</span></li>
</ol>
</nav>
</Preview>
+56 -43
View File
@@ -70,7 +70,7 @@ Use `data-orientation="vertical"` to stack items. Use `<hr role="separator" />`
Use `data-orientation="vertical"` to stack buttons vertically.
<Preview>
<div role="group" class="button-group" data-orientation="vertical">
<div role="group" aria-label="Media controls" class="button-group h-fit" data-orientation="vertical">
<button type="button" class="btn" data-variant="outline" data-size="icon" aria-label="Zoom in">
<svg class="lucide lucide-plus" 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"><path d="M5 12h14" /><path d="M12 5v14" /></svg>
</button>
@@ -113,6 +113,8 @@ Use `data-orientation="vertical"` to stack buttons vertically.
### Nested
Nest `button-group` components to keep controls joined within each group while adding spacing between groups.
<Preview>
<div role="group" aria-label="Pagination controls" class="button-group">
<div role="group" class="button-group">
@@ -124,10 +126,10 @@ Use `data-orientation="vertical"` to stack buttons vertically.
</div>
<div role="group" class="button-group">
<button type="button" class="btn" data-variant="outline" data-size="icon-sm" aria-label="Previous page">
<svg class="lucide lucide-chevron-left" 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"><path d="m15 18-6-6 6-6" /></svg>
<svg class="lucide lucide-arrow-left" 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"><path d="m12 19-7-7 7-7" /><path d="M19 12H5" /></svg>
</button>
<button type="button" class="btn" data-variant="outline" data-size="icon-sm" aria-label="Next page">
<svg class="lucide lucide-chevron-right" 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"><path d="m9 18 6-6-6-6" /></svg>
<svg class="lucide lucide-arrow-right" 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"><path d="M5 12h14" /><path d="m12 5 7 7-7 7" /></svg>
</button>
</div>
</div>
@@ -139,58 +141,60 @@ Use `<hr role="separator" />` to visually divide buttons within a group.
<Preview>
<div role="group" class="button-group">
<button type="button" class="btn" data-variant="secondary">Copy</button>
<button type="button" class="btn" data-variant="secondary" data-size="sm">Copy</button>
<hr role="separator" />
<button type="button" class="btn" data-variant="secondary">Paste</button>
<button type="button" class="btn" data-variant="secondary" data-size="sm">Paste</button>
</div>
</Preview>
### Split
Use a separator to create a split action with a primary action and a secondary menu trigger.
Use a separator to create a split action.
<Preview>
<div role="group" aria-label="Split save actions" class="button-group">
<button type="button" class="btn" data-variant="secondary">Save</button>
<div role="group" class="button-group">
<button type="button" class="btn" data-variant="secondary">Button</button>
<hr role="separator" />
<div id="dropdown-menu-465061" class="dropdown-menu">
<button type="button" id="dropdown-menu-465061-trigger" aria-haspopup="menu" aria-controls="dropdown-menu-465061-menu" aria-expanded="false" class="btn" data-variant="secondary" data-size="icon" aria-label="More save options">
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</button>
<div id="dropdown-menu-465061-popover" data-popover aria-hidden="true" data-align="end">
<div role="menu" id="dropdown-menu-465061-menu" aria-labelledby="dropdown-menu-465061-trigger">
<div role="menuitem">Save as draft</div>
<div role="menuitem">Save and publish</div>
<div role="menuitem">Save as template</div>
</div>
</div>
</div>
<button type="button" class="btn" data-variant="secondary" data-size="icon" aria-label="Add">
<svg class="lucide lucide-plus" 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"><path d="M5 12h14" /><path d="M12 5v14" /></svg>
</button>
</div>
</Preview>
### Input
<Preview>
<div role="group" aria-label="Search" class="button-group w-full max-w-sm">
<div role="group" aria-label="Search" class="button-group">
<input type="text" class="input" placeholder="Search..." />
<button type="button" class="btn" data-variant="outline" data-size="icon" aria-label="Search">
<button type="button" class="btn" data-variant="outline" aria-label="Search">
<svg class="lucide lucide-search" 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"><path d="m21 21-4.34-4.34" /><circle cx="11" cy="11" r="8" /></svg>
</button>
</div>
</Preview>
### Input Group
### Input group
<Preview>
<div role="group" aria-label="Amount" class="button-group w-full max-w-sm">
<span>$</span>
<input type="text" class="input" placeholder="0.00" />
<span>USD</span>
<div role="group" class="button-group">
<div role="group" class="button-group">
<button type="button" class="btn" data-variant="outline" data-size="icon" aria-label="Add attachment">
<svg class="lucide lucide-plus" 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"><path d="M5 12h14" /><path d="M12 5v14" /></svg>
</button>
</div>
<div role="group" class="button-group">
<div class="input-group">
<input type="text" placeholder="Send a message..." />
<div role="group" data-align="inline-end">
<button type="button" class="btn" data-variant="ghost" data-size="icon-xs" aria-label="Voice mode" aria-pressed="false">
<svg class="lucide lucide-audio-lines" 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"><path d="M2 10v3" /><path d="M6 6v11" /><path d="M10 3v18" /><path d="M14 8v7" /><path d="M18 5v13" /><path d="M22 10v3" /></svg>
</button>
</div>
</div>
</div>
</div>
</Preview>
### Dropdown Menu
### Dropdown menu
<Preview>
<div role="group" class="button-group">
@@ -240,7 +244,7 @@ Use a separator to create a split action with a primary action and a secondary m
### Select
<Preview>
<div class="flex w-fit items-stretch gap-2">
<div class="flex items-stretch gap-2">
<div role="group" aria-label="Currency amount" class="button-group">
<div id="select-797405" class="select">
<button type="button" class="[&amp;_[data-name]]:hidden" id="select-797405-trigger" aria-haspopup="listbox" aria-expanded="false" aria-controls="select-797405-listbox">
@@ -282,16 +286,16 @@ Use a separator to create a split action with a primary action and a secondary m
Copilot
</button>
<div id="popover-788465" class="popover">
<button id="popover-788465-trigger" type="button" aria-label="Open copilot tasks" aria-expanded="false" aria-controls="popover-788465-popover" class="btn" data-variant="outline" data-size="icon">
<button id="popover-788465-trigger" type="button" aria-label="Open Popover" aria-expanded="false" aria-controls="popover-788465-popover" class="btn" data-variant="outline" data-size="icon">
<svg class="lucide lucide-chevron-down" 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"><path d="m6 9 6 6 6-6" /></svg>
</button>
<div id="popover-788465-popover" data-popover aria-hidden="true" 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 task in natural language"></textarea>
<div id="popover-788465-popover" data-popover aria-hidden="true" class="w-72 text-sm" data-align="end">
<div>
<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 background and open a pull request for your review.</p>
</section>
<p class="text-muted-foreground">Describe your task in natural language.</p>
</div>
<textarea class="textarea min-h-16 w-full resize-none" placeholder="I need to..."></textarea>
<p class="text-muted-foreground text-xs">Copilot will open a pull request for review.</p>
</div>
</div>
</div>
@@ -304,12 +308,21 @@ Add `dir="rtl"` to the group or an ancestor. Flip directional icons explicitly.
<Preview>
<div dir="rtl">
<div role="group" aria-label="إجراءات الرسالة" class="button-group">
<button type="button" class="btn" data-variant="outline">أرشفة</button>
<button type="button" class="btn" data-variant="outline">تقرير</button>
<button type="button" class="btn" data-variant="outline">
إرسال
<svg class="rtl:rotate-180 lucide lucide-arrow-right" 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"><path d="M5 12h14" /><path d="m12 5 7 7-7 7" /></svg>
</button>
<div role="group" class="button-group">
<button type="button" class="btn" data-variant="outline" data-size="icon" aria-label="رجوع">
<svg class="rtl:rotate-180 lucide lucide-arrow-left" 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"><path d="m12 19-7-7 7-7" /><path d="M19 12H5" /></svg>
</button>
</div>
<div role="group" class="button-group">
<button type="button" class="btn" data-variant="outline">أرشفة</button>
<button type="button" class="btn" data-variant="outline">تقرير</button>
</div>
<div role="group" class="button-group">
<button type="button" class="btn" data-variant="outline">تأجيل</button>
<button type="button" class="btn" data-variant="outline" data-size="icon" aria-label="المزيد من الخيارات">
<svg class="lucide lucide-ellipsis" 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"><circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" /><circle cx="5" cy="12" r="1" /></svg>
</button>
</div>
</div>
</div>
</Preview>
+73 -19
View File
@@ -1,7 +1,12 @@
# Button
<Preview>
<button type="button" class="btn">Button</button>
<div class="flex flex-wrap items-center gap-2">
<button type="button" class="btn" data-variant="outline">Button</button>
<button type="button" class="btn" data-variant="outline" data-size="icon" aria-label="Submit">
<svg class="lucide lucide-arrow-up" 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" ><path d="m5 12 7-7 7 7" /><path d="M12 19V5" /></svg>
</button>
</div>
</Preview>
## Usage
@@ -66,10 +71,22 @@ Use `data-size` to change the size of the button.
<Preview>
<div class="flex flex-wrap items-center gap-2">
<button type="button" class="btn" data-size="xs">Extra Small</button>
<button type="button" class="btn" data-size="sm">Small</button>
<button type="button" class="btn">Default</button>
<button type="button" class="btn" data-size="lg">Large</button>
<button type="button" class="btn" data-variant="outline" data-size="xs">Extra Small</button>
<button type="button" class="btn" data-variant="outline" data-size="icon-xs" aria-label="Submit">
<svg class="lucide lucide-arrow-up-right" 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" ><path d="M7 7h10v10" /><path d="M7 17 17 7" /></svg>
</button>
<button type="button" class="btn" data-variant="outline" data-size="sm">Small</button>
<button type="button" class="btn" data-variant="outline" data-size="icon-sm" aria-label="Submit">
<svg class="lucide lucide-arrow-up-right" 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" ><path d="M7 7h10v10" /><path d="M7 17 17 7" /></svg>
</button>
<button type="button" class="btn" data-variant="outline">Default</button>
<button type="button" class="btn" data-variant="outline" data-size="icon" aria-label="Submit">
<svg class="lucide lucide-arrow-up-right" 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" ><path d="M7 7h10v10" /><path d="M7 17 17 7" /></svg>
</button>
<button type="button" class="btn" data-variant="outline" data-size="lg">Large</button>
<button type="button" class="btn" data-variant="outline" data-size="icon-lg" aria-label="Submit">
<svg class="lucide lucide-arrow-up-right" 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" ><path d="M7 7h10v10" /><path d="M7 17 17 7" /></svg>
</button>
</div>
</Preview>
@@ -113,16 +130,16 @@ Use `data-size` to change the size of the button.
<Preview>
<button type="button" class="btn" data-variant="outline" data-size="icon" aria-label="Open">
<svg class="lucide lucide-chevron-right" 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" ><path d="m9 18 6-6-6-6" /></svg>
<svg class="lucide lucide-circle-fading-arrow-up" 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" ><path d="M12 2a10 10 0 0 1 7.38 16.75" /><path d="m16 12-4-4-4 4" /><path d="M12 16V8" /><path d="M2.5 8.875a10 10 0 0 0-.5 3" /><path d="M2.83 16a10 10 0 0 0 2.43 3.4" /><path d="M4.636 5.235a10 10 0 0 1 .891-.857" /><path d="M8.644 21.42a10 10 0 0 0 7.631-.38" /></svg>
</button>
</Preview>
### With Icon
### With icon
Use `data-icon="inline-start"` or `data-icon="inline-end"` on inline icons to match upstream icon-aware spacing.
<Preview>
<button type="button" class="btn">
<button type="button" class="btn" data-variant="outline" data-size="sm">
<svg data-icon="inline-start" class="lucide lucide-git-branch" 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" ><path d="M15 6a9 9 0 0 0-9 9V3" /><circle cx="18" cy="6" r="3" /><circle cx="6" cy="18" r="3" /></svg>
New Branch
</button>
@@ -130,12 +147,11 @@ Use `data-icon="inline-start"` or `data-icon="inline-end"` on inline icons to ma
### Rounded
Use standard Tailwind classes to override the button radius.
Use the `rounded-full` class to make the button rounded.
<Preview>
<button type="button" class="btn rounded-full">
<button type="button" class="btn rounded-full" data-variant="outline" data-size="icon" aria-label="Scroll up">
<svg class="lucide lucide-arrow-up" 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" ><path d="m5 12 7-7 7 7" /><path d="M12 19V5" /></svg>
Rounded
</button>
</Preview>
@@ -144,31 +160,66 @@ Use standard Tailwind classes to override the button radius.
<Preview>
<div class="flex flex-wrap items-center gap-2">
<button type="button" class="btn" disabled>
<svg data-icon="inline-start" class="animate-spin lucide lucide-loader" 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" ><path d="M12 2v4" /><path d="m16.2 7.8 2.9-2.9" /><path d="M18 12h4" /><path d="m16.2 16.2 2.9 2.9" /><path d="M12 18v4" /><path d="m4.9 19.1 2.9-2.9" /><path d="M2 12h4" /><path d="m4.9 4.9 2.9 2.9" /></svg>
<svg data-icon="inline-start" class="animate-spin lucide lucide-loader-circle" 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" ><path d="M21 12a9 9 0 1 1-6.219-8.56" /></svg>
Generating
</button>
<button type="button" class="btn" data-variant="outline">
<button type="button" class="btn" data-variant="secondary" disabled>
Downloading
<svg data-icon="inline-end" class="animate-spin lucide lucide-loader" 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" ><path d="M12 2v4" /><path d="m16.2 7.8 2.9-2.9" /><path d="M18 12h4" /><path d="m16.2 16.2 2.9 2.9" /><path d="M12 18v4" /><path d="m4.9 19.1 2.9-2.9" /><path d="M2 12h4" /><path d="m4.9 4.9 2.9 2.9" /></svg>
<svg data-icon="inline-end" class="animate-spin lucide lucide-loader-circle" 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" ><path d="M21 12a9 9 0 1 1-6.219-8.56" /></svg>
</button>
</div>
</Preview>
### Button Group
### Button group
To group related buttons, use the [Button Group](/components/button-group/) component.
<Preview>
<div class="flex w-fit items-stretch gap-2">
<button type="button" class="btn" data-variant="outline" data-size="icon" aria-label="Go back">
<svg class="lucide lucide-arrow-left" 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" ><path d="m12 19-7-7 7-7" /><path d="M19 12H5" /></svg>
</button>
<div role="group" class="button-group">
<button type="button" class="btn" data-variant="outline">Archive</button>
<button type="button" class="btn" data-variant="outline">Report</button>
</div>
<div role="group" class="button-group">
<button type="button" class="btn" data-variant="outline">Snooze</button>
<button type="button" class="btn" data-variant="outline" data-size="icon" aria-label="More options">
<svg class="lucide lucide-ellipsis" 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" ><circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" /><circle cx="5" cy="12" r="1" /></svg>
</button>
<div id="button-docs-actions" class="dropdown-menu">
<button type="button" id="button-docs-actions-trigger" aria-label="More options" aria-haspopup="menu" aria-controls="button-docs-actions-menu" aria-expanded="false" class="btn" data-variant="outline" data-size="icon">
<svg class="lucide lucide-ellipsis" 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" ><circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" /><circle cx="5" cy="12" r="1" /></svg>
</button>
<div id="button-docs-actions-popover" data-popover aria-hidden="true" data-align="end">
<div role="menu" id="button-docs-actions-menu" aria-labelledby="button-docs-actions-trigger">
<div role="menuitem">
<svg class="lucide lucide-mail-check" 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" ><path d="M22 13V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v12c0 1.1.9 2 2 2h8" /><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" /><path d="m16 19 2 2 4-4" /></svg>
Mark as Read
</div>
<div role="menuitem">
<svg class="lucide lucide-archive" 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" ><rect width="20" height="5" x="2" y="3" rx="1" /><path d="M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8" /><path d="M10 12h4" /></svg>
Archive
</div>
<hr role="separator" />
<div role="menuitem">
<svg class="lucide lucide-clock" 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" ><circle cx="12" cy="12" r="10" /><path d="M12 6v6l4 2" /></svg>
Snooze
</div>
<div role="menuitem">
<svg class="lucide lucide-calendar" 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" ><path d="M8 2v4" /><path d="M16 2v4" /><rect width="18" height="18" x="3" y="4" rx="2" /><path d="M3 10h18" /></svg>
Add to Calendar
</div>
<div role="menuitem">
<svg class="lucide lucide-list-filter-plus" 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" ><path d="M12 5H2" /><path d="M6 12h12" /><path d="M9 19h6" /><path d="M16 5h6" /><path d="M19 8V2" /></svg>
Add to List
</div>
<hr role="separator" />
<div role="menuitem" class="text-destructive hover:bg-destructive/10 dark:hover:bg-destructive/20 focus:bg-destructive/10 dark:focus:bg-destructive/20 focus:text-destructive [&_svg]:!text-destructive">
<svg class="lucide lucide-trash-2" 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" ><path d="M10 11v6" /><path d="M14 11v6" /><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /><path d="M3 6h18" /><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></svg>
Trash
</div>
</div>
</div>
</div>
</div>
</div>
</Preview>
@@ -185,8 +236,11 @@ Add `dir="rtl"` to the button or an ancestor. Flip directional icons explicitly.
إرسال
<svg data-icon="inline-end" class="rtl:rotate-180 lucide lucide-arrow-right" 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" ><path d="M5 12h14" /><path d="m12 5 7 7-7 7" /></svg>
</button>
<button type="button" class="btn" data-variant="outline" data-size="icon" aria-label="Add">
<svg class="lucide lucide-plus" 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" ><path d="M5 12h14" /><path d="M12 5v14" /></svg>
</button>
<button type="button" class="btn" data-variant="secondary" disabled>
<svg class="animate-spin lucide lucide-loader" 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" ><path d="M12 2v4" /><path d="m16.2 7.8 2.9-2.9" /><path d="M18 12h4" /><path d="m16.2 16.2 2.9 2.9" /><path d="M12 18v4" /><path d="m4.9 19.1 2.9-2.9" /><path d="M2 12h4" /><path d="m4.9 4.9 2.9 2.9" /></svg>
<svg data-icon="inline-start" class="animate-spin lucide lucide-loader-circle" 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" ><path d="M21 12a9 9 0 1 1-6.219-8.56" /></svg>
جاري التحميل
</button>
</div>
+67 -87
View File
@@ -1,30 +1,32 @@
# Card
<Preview class="w-full max-w-sm">
<div class="card w-full">
<div class="card">
<header>
<h2>Login to your account</h2>
<p>Enter your details below to login to your account</p>
<p>Enter your email below to login to your account</p>
<div class="card-action">
<button type="button" class="btn" data-variant="link">Sign Up</button>
</div>
</header>
<section>
<form class="grid gap-6">
<div class="grid gap-2">
<label class="label" for="demo-card-form-email">Email</label>
<input class="input" type="email" id="demo-card-form-email" />
<label class="label" for="demo-card-email">Email</label>
<input class="input" type="email" id="demo-card-email" placeholder="m@example.com" required />
</div>
<div class="grid gap-2">
<div class="flex items-center gap-2">
<label class="label" for="demo-card-form-password">Password</label>
<div class="flex items-center">
<label class="label" for="demo-card-password">Password</label>
<a href="#" class="ms-auto inline-block text-sm underline-offset-4 hover:underline">Forgot your password?</a>
</div>
<input class="input" type="password" id="demo-card-form-password" />
<input class="input" type="password" id="demo-card-password" required />
</div>
</form>
</section>
<footer class="flex flex-col items-center gap-2">
<footer class="flex-col gap-2">
<button type="button" class="btn w-full">Login</button>
<button type="button" class="btn w-full" data-variant="outline">Login with Google</button>
<p class="mt-4 text-center text-sm">Don't have an account? <a href="#" class="underline-offset-4 hover:underline">Sign up</a></p>
</footer>
</div>
</Preview>
@@ -36,6 +38,7 @@
<header>
<h2>Card Title</h2>
<p>Card Description</p>
<div class="card-action">Card Action</div>
</header>
<section>
<p>Card Content</p>
@@ -46,7 +49,7 @@
</div>
```
### HTML structure
## HTML structure
<dl>
<dt><code>&lt;div class="card"&gt;</code></dt>
@@ -55,18 +58,20 @@
<dl>
<dt><code>&lt;header&gt;</code></dt>
<dd>
The header of the card.
The card header.
<dl>
<dt><code>&lt;h2&gt;</code></dt>
<dd>The title of the card.</dd>
<dt><code>&lt;h2&gt;</code> or <code>&lt;h3&gt;</code></dt>
<dd>The card title.</dd>
<dt><code>&lt;p&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>The description of the card.</dd>
<dd>The card description.</dd>
<dt><code>&lt;div class="card-action"&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Action or metadata placed in the top-right of the header. Use <code>&lt;menu class="card-action"&gt;</code> when the content is a list of commands.</dd>
</dl>
</dd>
<dt><code>&lt;section&gt;</code></dt>
<dd>The content of the card.</dd>
<dd>The main card content.</dd>
<dt><code>&lt;footer&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>The footer of the card.</dd>
<dd>Footer actions or secondary content at the bottom of the card.</dd>
</dl>
</dd>
</dl>
@@ -75,103 +80,78 @@
### Size
<Preview>
Use `data-size="sm"` to set the size of the card to small. The small size variant uses smaller spacing.
<Preview class="w-full max-w-sm">
<div class="card" data-size="sm">
<header>
<h2>Small Card</h2>
<p>A compact card with reduced spacing.</p>
<p>This card uses the small size variant.</p>
<div class="card-action">
<button type="button" class="btn" data-size="sm" data-variant="outline">Action</button>
</div>
</header>
<section>
<p class="text-sm">Use <code>data-size="sm"</code> for denser layouts.</p>
<p>The card component supports a size attribute that can be set to <code>sm</code> for a more compact appearance.</p>
</section>
</div>
</Preview>
### With content and footer
### Image
<Preview>
Add an image before the card header to create a card with an image.
<Preview class="w-full max-w-sm">
<div class="card">
<img
alt="Event cover"
class="aspect-video w-full object-cover"
src="https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=1200&auto=format&fit=crop&q=80"
/>
<header>
<h2>Meeting Notes</h2>
<p>Transcript from the meeting with the client.</p>
</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 ps-6">
<li>New analytics widgets for daily/weekly metrics</li>
<li>Simplified navigation menu</li>
<li>Dark mode support</li>
<li>Timeline: 6 weeks</li>
<li>Follow-up meeting scheduled for next Tuesday</li>
</ol>
</section>
<footer class="flex items-center">
<div class="flex -space-x-2 [&_img]:ring-card [&_img]:ring-2 [&_img]:grayscale [&_img]:size-8 [&_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" />
<h2>Design systems meetup</h2>
<p>A practical talk on component APIs, accessibility, and shipping faster.</p>
<div class="card-action">
<span class="badge" data-variant="secondary">Featured</span>
</div>
</footer>
</div>
</Preview>
### With image
<Preview>
<div class="card">
<header>
<h2>Is this an image?</h2>
<p>This is a card with an image.</p>
</header>
<section class="px-0">
<img
alt="Photo by Drew Beamer"
loading="lazy"
width="500"
height="500"
class="aspect-video object-cover" style="color:transparent"
srcset="
https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80&w=640&q=75 1x,
https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80&w=1080&q=75 2x
"
src="https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80&w=1080&q=75"
/>
</section>
<footer class="flex items-center gap-2">
<span class="badge" data-variant="outline">
<svg class="lucide lucide-bed" 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" ><path d="M2 4v16" /><path d="M2 8h18a2 2 0 0 1 2 2v10" /><path d="M2 17h20" /><path d="M6 8v9" /></svg>
1
</span>
<span class="badge" data-variant="outline">
<svg class="lucide lucide-bath" 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" ><path d="M10 4 8 6" /><path d="M17 19v2" /><path d="M2 12h20" /><path d="M7 19v2" /><path d="M9 5 7.621 3.621A2.121 2.121 0 0 0 4 5v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-5" /></svg>
2
</span>
<span class="badge" data-variant="outline">
<svg class="lucide lucide-land-plot" 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" ><path d="m12 8 6-3-6-3v10" /><path d="m8 11.99-5.5 3.14a1 1 0 0 0 0 1.74l8.5 4.86a2 2 0 0 0 2 0l8.5-4.86a1 1 0 0 0 0-1.74L16 12" /><path d="m6.49 12.85 11.02 6.3" /><path d="M17.51 12.85 6.5 19.15" /></svg>
350m²
</span>
<span class="ms-auto font-medium tabular-nums">
$135,000
</span>
<footer>
<button type="button" class="btn w-full">View Event</button>
</footer>
</div>
</Preview>
### RTL
## RTL
Add `dir="rtl"` to the card or an ancestor.
<Preview>
<Preview class="w-full max-w-sm">
<div class="card" dir="rtl">
<header>
<h2>إعدادات الحساب</h2>
<p>راجع إعدادات الأمان والإشعارات.</p>
<h2>تسجيل الدخول إلى حسابك</h2>
<p>أدخل بريدك الإلكتروني أدناه لتسجيل الدخول إلى حسابك</p>
<div class="card-action">
<button type="button" class="btn" data-variant="link">إنشاء حساب</button>
</div>
</header>
<section>
<p class="text-sm">يمكنك تعديل هذه الإعدادات في أي وقت.</p>
<form class="grid gap-6">
<div class="grid gap-2">
<label class="label" for="demo-card-rtl-email">البريد الإلكتروني</label>
<input class="input" type="email" id="demo-card-rtl-email" placeholder="m@example.com" required />
</div>
<div class="grid gap-2">
<div class="flex items-center">
<label class="label" for="demo-card-rtl-password">كلمة المرور</label>
<a href="#" class="ms-auto inline-block text-sm underline-offset-4 hover:underline">نسيت كلمة المرور؟</a>
</div>
<input class="input" type="password" id="demo-card-rtl-password" required />
</div>
</form>
</section>
<footer>
<button type="button" class="btn">حفظ</button>
<footer class="flex-col gap-2">
<button type="button" class="btn w-full">تسجيل الدخول</button>
<button type="button" class="btn w-full" data-variant="outline">تسجيل الدخول باستخدام Google</button>
</footer>
</div>
</Preview>
+6 -6
View File
@@ -149,37 +149,37 @@ window.basecoat.chart("#custom-chart", {
## Examples
### Bar Chart - Multiple
### Bar chart - multiple
<Preview class="w-full">
<canvas id="chart-example-visitors" aria-label="Monthly visitors by device"></canvas>
</Preview>
### Line Chart - Linear
### Line chart - linear
<Preview class="w-full">
<canvas id="chart-example-line" aria-label="Revenue line chart"></canvas>
</Preview>
### Line Chart - Step
### Line chart - step
<Preview class="w-full">
<canvas id="chart-example-step" aria-label="Support queue step chart"></canvas>
</Preview>
### Bar Chart - Stacked
### Bar chart - stacked
<Preview class="w-full">
<canvas id="chart-example-stacked" aria-label="Stacked pipeline bar chart"></canvas>
</Preview>
### Pie Chart - Donut
### Pie chart - donut
<Preview class="w-full">
<canvas id="chart-example-donut" aria-label="Traffic source donut chart"></canvas>
</Preview>
### Radar Chart
### Radar chart
<Preview class="w-full">
<canvas id="chart-example-radar" aria-label="Channel performance radar chart"></canvas>
+127 -53
View File
@@ -1,9 +1,31 @@
# Checkbox
<Preview>
<div role="group" class="field" data-orientation="horizontal">
<input type="checkbox" id="terms" class="input" />
<label for="terms">Accept terms and conditions</label>
<div role="group" class="fieldset">
<div role="group" class="field" data-orientation="horizontal">
<input type="checkbox" id="terms-checkbox" name="terms-checkbox" class="input" />
<label for="terms-checkbox">Accept terms and conditions</label>
</div>
<div role="group" class="field" data-orientation="horizontal">
<input type="checkbox" id="terms-checkbox-2" name="terms-checkbox-2" class="input" checked />
<section>
<label for="terms-checkbox-2">Accept terms and conditions</label>
<p>By clicking this checkbox, you agree to the terms.</p>
</section>
</div>
<div role="group" class="field" data-orientation="horizontal" data-disabled>
<input type="checkbox" id="toggle-checkbox" name="toggle-checkbox" class="input" disabled />
<label for="toggle-checkbox">Enable notifications</label>
</div>
<label>
<div role="group" class="field" data-orientation="horizontal">
<input type="checkbox" id="toggle-checkbox-2" name="toggle-checkbox-2" class="input" />
<section>
<h3>Enable notifications</h3>
<p>You can enable or disable notifications at any time.</p>
</section>
</div>
</label>
</div>
</Preview>
@@ -19,65 +41,83 @@ Add `class="input"` to checkbox inputs. Pair it with [Field](/components/field)
### Basic
Pair the checkbox with `field` and a native `<label>` for proper layout and labeling.
<Preview>
<div role="group" class="field" data-orientation="horizontal">
<input type="checkbox" id="checkbox-basic" class="input" />
<label for="checkbox-basic">Accept terms and conditions</label>
<div role="group" class="fieldset">
<div role="group" class="field" data-orientation="horizontal">
<input type="checkbox" id="terms-checkbox-basic" name="terms-checkbox-basic" class="input" />
<label for="terms-checkbox-basic">Accept terms and conditions</label>
</div>
</div>
</Preview>
### Description
<Preview>
<div role="group" class="field" data-orientation="horizontal">
<input type="checkbox" id="checkbox-description" class="input" aria-describedby="checkbox-description-text" />
<section>
<label for="checkbox-description">Accept terms and conditions</label>
<p id="checkbox-description-text">By clicking this checkbox, you agree to the terms and conditions.</p>
</section>
</div>
</Preview>
### Invalid
Use a `section` and paragraph for helper text.
<Preview>
<div role="group" class="field" data-orientation="horizontal" data-invalid="true">
<input type="checkbox" id="checkbox-invalid" class="input" aria-invalid="true" />
<label for="checkbox-invalid">Accept terms and conditions</label>
<div role="group" class="fieldset">
<div role="group" class="field" data-orientation="horizontal">
<input type="checkbox" id="terms-checkbox-desc" name="terms-checkbox-desc" class="input" checked aria-describedby="terms-checkbox-desc-description" />
<section>
<label for="terms-checkbox-desc">Accept terms and conditions</label>
<p id="terms-checkbox-desc-description">By clicking this checkbox, you agree to the terms and conditions.</p>
</section>
</div>
</div>
</Preview>
### Disabled
Use the `disabled` attribute to prevent interaction and add `data-disabled` to the `.field` component for disabled styles.
<Preview>
<div role="group" class="field" data-orientation="horizontal" data-disabled="true">
<input type="checkbox" id="checkbox-disabled" class="input" disabled />
<label for="checkbox-disabled">Accept terms and conditions</label>
<div role="group" class="fieldset">
<div role="group" class="field" data-orientation="horizontal" data-disabled>
<input type="checkbox" id="toggle-checkbox-disabled" name="toggle-checkbox-disabled" class="input" disabled />
<label for="toggle-checkbox-disabled">Enable notifications</label>
</div>
</div>
</Preview>
### Invalid
Set `aria-invalid="true"` on the checkbox and `data-invalid` on the field wrapper to show invalid styles.
<Preview>
<div role="group" class="fieldset">
<div role="group" class="field" data-orientation="horizontal" data-invalid>
<input type="checkbox" id="terms-checkbox-invalid" name="terms-checkbox-invalid" class="input" aria-invalid="true" />
<label for="terms-checkbox-invalid">Accept terms and conditions</label>
</div>
</div>
</Preview>
### Group
Use multiple fields to create a checkbox list.
<Preview>
<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">
<legend data-variant="label">Show these items on the desktop:</legend>
<p>Select the items you want to show on the desktop.</p>
<div role="group" aria-label="Desktop items" data-slot="checkbox-group">
<div role="group" class="field" data-orientation="horizontal">
<input class="input" type="checkbox" id="checkbox-recents" checked />
<label for="checkbox-recents">Recents</label>
<input class="input" type="checkbox" id="finder-pref-9k2-hard-disks-ljj-checkbox" name="finder-pref-9k2-hard-disks-ljj-checkbox" checked />
<label for="finder-pref-9k2-hard-disks-ljj-checkbox" class="font-normal">Hard disks</label>
</div>
<div role="group" class="field" data-orientation="horizontal">
<input class="input" type="checkbox" id="checkbox-home" checked />
<label for="checkbox-home">Home</label>
<input class="input" type="checkbox" id="finder-pref-9k2-external-disks-1yg-checkbox" name="finder-pref-9k2-external-disks-1yg-checkbox" checked />
<label for="finder-pref-9k2-external-disks-1yg-checkbox" class="font-normal">External disks</label>
</div>
<div role="group" class="field" data-orientation="horizontal">
<input class="input" type="checkbox" id="checkbox-applications" />
<label for="checkbox-applications">Applications</label>
<input class="input" type="checkbox" id="finder-pref-9k2-cds-dvds-fzt-checkbox" name="finder-pref-9k2-cds-dvds-fzt-checkbox" />
<label for="finder-pref-9k2-cds-dvds-fzt-checkbox" class="font-normal">CDs, DVDs, and iPods</label>
</div>
<div role="group" class="field" data-orientation="horizontal">
<input class="input" type="checkbox" id="checkbox-desktop" />
<label for="checkbox-desktop">Desktop</label>
<input class="input" type="checkbox" id="finder-pref-9k2-connected-servers-6l2-checkbox" name="finder-pref-9k2-connected-servers-6l2-checkbox" />
<label for="finder-pref-9k2-connected-servers-6l2-checkbox" class="font-normal">Connected servers</label>
</div>
</div>
</fieldset>
@@ -85,42 +125,76 @@ Add `class="input"` to checkbox inputs. Pair it with [Field](/components/field)
### Table
<Preview>
<Preview class="w-full">
<div class="table-container">
<table class="table">
<table class="table" data-checkbox-table>
<thead>
<tr>
<th><input type="checkbox" class="input" aria-label="Select all" /></th>
<th>Status</th>
<th class="w-8"><input type="checkbox" id="select-all-checkbox" name="select-all-checkbox" class="input" aria-label="Select all" /></th>
<th>Name</th>
<th>Email</th>
<th>Role</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 data-state="selected">
<td><input type="checkbox" id="row-1-checkbox" name="row-1-checkbox" class="input" aria-label="Select Sarah Chen" checked /></td>
<td class="font-medium">Sarah Chen</td>
<td>sarah.chen@example.com</td>
<td>Admin</td>
</tr>
<tr>
<td><input type="checkbox" class="input" aria-label="Select row 2" /></td>
<td>Inactive</td>
<td>noah@example.com</td>
<td><input type="checkbox" id="row-2-checkbox" name="row-2-checkbox" class="input" aria-label="Select Marcus Rodriguez" /></td>
<td class="font-medium">Marcus Rodriguez</td>
<td>marcus.rodriguez@example.com</td>
<td>User</td>
</tr>
<tr>
<td><input type="checkbox" id="row-3-checkbox" name="row-3-checkbox" class="input" aria-label="Select Priya Patel" /></td>
<td class="font-medium">Priya Patel</td>
<td>priya.patel@example.com</td>
<td>User</td>
</tr>
<tr>
<td><input type="checkbox" id="row-4-checkbox" name="row-4-checkbox" class="input" aria-label="Select David Kim" /></td>
<td class="font-medium">David Kim</td>
<td>david.kim@example.com</td>
<td>Editor</td>
</tr>
</tbody>
</table>
</div>
</Preview>
### RTL
## RTL
Checkbox layout uses document direction and logical spacing. Set `dir="rtl"` on a parent to render right-to-left.
Add `dir="rtl"` to the field group or an ancestor.
<Preview>
<div dir="rtl" class="field" data-orientation="horizontal">
<input type="checkbox" id="checkbox-rtl" class="input" aria-describedby="checkbox-rtl-description" checked />
<section>
<label for="checkbox-rtl">قبول الشروط والأحكام</label>
<p id="checkbox-rtl-description">يمكنك تغيير هذا الخيار في أي وقت.</p>
</section>
<div role="group" class="fieldset" dir="rtl">
<div role="group" class="field" data-orientation="horizontal">
<input type="checkbox" id="terms-checkbox-rtl" name="terms-checkbox" class="input" />
<label for="terms-checkbox-rtl">قبول الشروط والأحكام</label>
</div>
<div role="group" class="field" data-orientation="horizontal">
<input type="checkbox" id="terms-checkbox-2-rtl" name="terms-checkbox-2" class="input" checked aria-describedby="terms-checkbox-2-rtl-description" />
<section>
<label for="terms-checkbox-2-rtl">قبول الشروط والأحكام</label>
<p id="terms-checkbox-2-rtl-description">بالنقر على هذا المربع، فإنك توافق على الشروط.</p>
</section>
</div>
<div role="group" class="field" data-orientation="horizontal" data-disabled>
<input type="checkbox" id="toggle-checkbox-rtl" name="toggle-checkbox" class="input" disabled />
<label for="toggle-checkbox-rtl">تفعيل الإشعارات</label>
</div>
<label>
<div role="group" class="field" data-orientation="horizontal">
<input type="checkbox" id="toggle-checkbox-2-rtl" name="toggle-checkbox-2" class="input" />
<section>
<h3>تفعيل الإشعارات</h3>
<p>يمكنك تفعيل أو إلغاء تفعيل الإشعارات في أي وقت.</p>
</section>
</div>
</label>
</div>
</Preview>
+148 -103
View File
@@ -1,11 +1,11 @@
# Combobox
<Preview>
<div id="framework-combobox" class="combobox w-[240px]">
<div id="framework-combobox" class="combobox">
<input type="text" role="combobox" placeholder="Select a framework" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="framework-combobox-listbox" />
<svg aria-hidden="true" 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="combobox-trigger-icon"><path d="m6 9 6 6 6-6" /></svg>
<div id="framework-combobox-popover" data-popover aria-hidden="true">
<div role="listbox" id="framework-combobox-listbox" aria-orientation="vertical" data-empty="No framework found.">
<div role="listbox" id="framework-combobox-listbox" aria-orientation="vertical" data-empty="No items found.">
<div role="option" data-value="Next.js">Next.js</div>
<div role="option" data-value="SvelteKit">SvelteKit</div>
<div role="option" data-value="Nuxt.js">Nuxt.js</div>
@@ -41,7 +41,7 @@ Combobox is input-first. The visible input filters the list; the hidden input st
<input type="text" role="combobox" placeholder="Select a framework" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="framework-combobox-listbox" />
<svg aria-hidden="true" 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="combobox-trigger-icon"><path d="m6 9 6 6 6-6" /></svg>
<div id="framework-combobox-popover" data-popover aria-hidden="true">
<div role="listbox" id="framework-combobox-listbox" aria-orientation="vertical" data-empty="No framework found.">
<div role="listbox" id="framework-combobox-listbox" aria-orientation="vertical" data-empty="No items found.">
<div role="option" data-value="Next.js">Next.js</div>
<div role="option" data-value="SvelteKit">SvelteKit</div>
<div role="option" data-value="Nuxt.js">Nuxt.js</div>
@@ -61,40 +61,24 @@ Combobox is input-first. The visible input filters the list; the hidden input st
<dl>
<dt><code>&lt;div class="combobox"&gt;</code></dt>
<dd>
Root element. Add <code>data-auto-highlight="true"</code> to highlight the first match while filtering. Add <code>data-format="object"</code> to serialize selected values as <code>&#123; value, label &#125;</code> objects in the hidden input. Add <code>data-filter="manual"</code> when your app owns filtering, such as remote autocomplete or a local Lunr index.
Root element. Add <code>data-auto-highlight="true"</code> to make the first visible option active when the list opens or filters. Add <code>data-format="object"</code> to serialize selected values as objects. Add <code>data-filter="manual"</code> when your app owns filtering.
<dl>
<dt><code>&lt;input type="text" role="combobox"&gt;</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>&lt;div class="combobox-chips"&gt;</code> <span class="badge" data-variant="secondary">Multiple only</span></dt>
<dd>
Wraps selected chips and the editable input in multiple mode.
<dl>
<dt><code>&lt;input type="text" role="combobox"&gt;</code></dt>
<dd>The editable control in multiple mode.</dd>
</dl>
</dd>
<dd>The editable control. It can be a direct child, inside <code>.input-group</code>, or inside <code>data-popover</code> for popup mode.</dd>
<dt><code>&lt;button aria-haspopup="listbox"&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Opens the list from an input group or popup trigger. In popup mode, include a child with <code>data-value</code> and optional <code>data-placeholder</code> to display the selected value.</dd>
<dt><code>&lt;button data-clear&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Clears the current selection. Put the clear icon inside the button.</dd>
<dt><code>&lt;svg class="combobox-trigger-icon"&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Decorative trailing icon for input-style comboboxes. Replace the SVG to customize the icon.</dd>
<dt><code>&lt;div data-popover&gt;</code></dt>
<dd>
The suggestions popup. Use <code>data-side</code> and <code>data-align</code> like Popover.
<dl>
<dt><code>&lt;div role="listbox"&gt;</code></dt>
<dd>
The suggestions list. Add <code>aria-multiselectable="true"</code> in multiple mode and <code>data-empty</code> for the empty message.
<dl>
<dt><code>&lt;div role="option" data-value="..."&gt;</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>&lt;div role="group"&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Option group. Use <code>aria-labelledby</code> to connect it to its heading.</dd>
<dt><code>&lt;div role="heading"&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Group heading.</dd>
<dt><code>&lt;hr role="separator"&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Separator between groups or options.</dd>
</dl>
</dd>
</dl>
</dd>
<dd>The suggestions popup. Use <code>data-side</code> and <code>data-align</code> like Popover.</dd>
<dt><code>&lt;div role="listbox"&gt;</code></dt>
<dd>The suggestions list. Add <code>aria-multiselectable="true"</code> in multiple mode. The component uses that attribute to render selected chips around the input.</dd>
<dt><code>&lt;div role="option" data-value="..."&gt;</code></dt>
<dd>Selectable option. Options can contain custom HTML; use <code>data-label</code> when the input should display different text than the rendered content.</dd>
<dt><code>&lt;input type="hidden"&gt;</code></dt>
<dd>Submitted value. Single mode stores a string; multiple mode stores a JSON array. With <code>data-format="object"</code>, single mode stores a JSON object and multiple mode stores a JSON array of objects.</dd>
<dd>Submitted value. Single mode stores a string; multiple mode stores a JSON array. With <code>data-format="object"</code>, selected values are stored as objects.</dd>
</dl>
</dd>
</dl>
@@ -104,20 +88,19 @@ Combobox is input-first. The visible input filters the list; the hidden input st
| API | Type | Description |
| --- | --- | --- |
| `change` | Event | Dispatched on selection changes with `event.detail.value` and `event.detail.selected`. |
| `combobox.setValue(value)` | Method | Sets the selected value from a plain value or `{ value, label }` object, useful for remote autocomplete hydration. |
| `combobox.setValue(value)` | Method | Sets the selected value from a plain value or `{ value, label }` object. |
| `combobox.clear()` | Method | Clears the selected value. |
| `combobox.refresh()` | Method | Rescans options after children change inside the existing `role="listbox"` element. |
## Examples
### Multiple
<Preview>
<div id="frameworks-combobox" class="combobox" data-auto-highlight="true">
<div class="combobox-chips w-[320px]">
<input type="text" role="combobox" placeholder="Add framework" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="frameworks-combobox-listbox" />
</div>
<Preview class="w-full max-w-xs">
<div id="frameworks-combobox" class="combobox w-full" data-auto-highlight="true">
<input type="text" role="combobox" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="frameworks-combobox-listbox" />
<div id="frameworks-combobox-popover" data-popover aria-hidden="true">
<div role="listbox" id="frameworks-combobox-listbox" aria-orientation="vertical" aria-multiselectable="true" data-empty="No framework found.">
<div role="listbox" id="frameworks-combobox-listbox" aria-orientation="vertical" aria-multiselectable="true" data-empty="No items found.">
<div role="option" data-value="Next.js">Next.js</div>
<div role="option" data-value="SvelteKit">SvelteKit</div>
<div role="option" data-value="Nuxt.js">Nuxt.js</div>
@@ -129,26 +112,66 @@ Combobox is input-first. The visible input filters the list; the hidden input st
</div>
</Preview>
### Clear button
<Preview>
<div id="clear-combobox" class="combobox">
<div class="input-group">
<input type="text" role="combobox" placeholder="Select a framework" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="clear-combobox-listbox" />
<span role="group" data-align="inline-end">
<button type="button" data-clear aria-label="Clear selection"><svg class="size-4" aria-hidden="true" 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"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></button>
<button type="button" class="btn" data-variant="ghost" data-size="icon-xs" aria-label="Open suggestions" aria-haspopup="listbox" aria-expanded="false" aria-controls="clear-combobox-listbox"><svg aria-hidden="true" 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"><path d="m6 9 6 6 6-6" /></svg></button>
</span>
</div>
<div id="clear-combobox-popover" data-popover aria-hidden="true">
<div role="listbox" id="clear-combobox-listbox" aria-orientation="vertical" data-empty="No items found.">
<div role="option" data-value="Next.js">Next.js</div>
<div role="option" data-value="SvelteKit">SvelteKit</div>
<div role="option" data-value="Nuxt.js">Nuxt.js</div>
<div role="option" data-value="Remix">Remix</div>
<div role="option" data-value="Astro">Astro</div>
</div>
</div>
<input type="hidden" name="framework-clear" value="Next.js" />
</div>
</Preview>
### Groups
<Preview>
<div id="timezone-combobox" class="combobox w-[280px]">
<input type="text" role="combobox" placeholder="Search timezone" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="timezone-combobox-listbox" />
<div id="timezone-combobox" class="combobox">
<input type="text" role="combobox" placeholder="Select a timezone" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="timezone-combobox-listbox" />
<svg aria-hidden="true" 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="combobox-trigger-icon"><path d="m6 9 6 6 6-6" /></svg>
<div id="timezone-combobox-popover" data-popover aria-hidden="true">
<div role="listbox" id="timezone-combobox-listbox" aria-orientation="vertical" data-empty="No timezone found.">
<div role="listbox" id="timezone-combobox-listbox" class="scrollbar-sm" aria-orientation="vertical" data-empty="No timezones 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 role="option" data-value="(GMT-5) New York">(GMT-5) New York</div>
<div role="option" data-value="(GMT-8) Los Angeles">(GMT-8) Los Angeles</div>
<div role="option" data-value="(GMT-6) Chicago">(GMT-6) Chicago</div>
<div role="option" data-value="(GMT-5) Toronto">(GMT-5) Toronto</div>
<div role="option" data-value="(GMT-8) Vancouver">(GMT-8) Vancouver</div>
<div role="option" data-value="(GMT-3) São Paulo">(GMT-3) São Paulo</div>
</div>
<hr role="separator" />
<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 role="option" data-value="(GMT+0) London">(GMT+0) London</div>
<div role="option" data-value="(GMT+1) Paris">(GMT+1) Paris</div>
<div role="option" data-value="(GMT+1) Berlin">(GMT+1) Berlin</div>
<div role="option" data-value="(GMT+1) Rome">(GMT+1) Rome</div>
<div role="option" data-value="(GMT+1) Madrid">(GMT+1) Madrid</div>
<div role="option" data-value="(GMT+1) Amsterdam">(GMT+1) Amsterdam</div>
</div>
<hr role="separator" />
<div role="group" aria-labelledby="timezone-asia-pacific">
<div role="heading" id="timezone-asia-pacific">Asia/Pacific</div>
<div role="option" data-value="(GMT+9) Tokyo">(GMT+9) Tokyo</div>
<div role="option" data-value="(GMT+8) Shanghai">(GMT+8) Shanghai</div>
<div role="option" data-value="(GMT+8) Singapore">(GMT+8) Singapore</div>
<div role="option" data-value="(GMT+4) Dubai">(GMT+4) Dubai</div>
<div role="option" data-value="(GMT+11) Sydney">(GMT+11) Sydney</div>
<div role="option" data-value="(GMT+9) Seoul">(GMT+9) Seoul</div>
</div>
</div>
</div>
@@ -156,32 +179,17 @@ Combobox is input-first. The visible input filters the list; the hidden input st
</div>
</Preview>
### Custom Items
### Custom items
<Preview>
<div id="country-combobox" class="combobox w-[280px]">
<div id="country-combobox" class="combobox">
<input type="text" role="combobox" placeholder="Search countries" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="country-combobox-listbox" />
<svg aria-hidden="true" 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="combobox-trigger-icon"><path d="m6 9 6 6 6-6" /></svg>
<div id="country-combobox-popover" data-popover aria-hidden="true">
<div role="listbox" id="country-combobox-listbox" aria-orientation="vertical" 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>
<div role="listbox" id="country-combobox-listbox" aria-orientation="vertical" data-empty="No items 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</span></span></div>
<div role="option" data-value="australia" data-label="Australia" data-filter="Australia Oceania"><span class="flex flex-col"><span>Australia</span><span class="text-muted-foreground text-xs">Oceania</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</span></span></div>
</div>
</div>
<input type="hidden" value="" />
@@ -193,11 +201,11 @@ Combobox is input-first. The visible input filters the list; the hidden input st
<Preview>
<div role="group" class="field" data-invalid="true">
<label for="invalid-combobox-input">Framework</label>
<div id="invalid-combobox" class="combobox w-[240px]">
<div id="invalid-combobox" class="combobox">
<input type="text" role="combobox" placeholder="Select a framework" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="invalid-combobox-listbox" id="invalid-combobox-input" aria-invalid="true" />
<svg aria-hidden="true" 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="combobox-trigger-icon"><path d="m6 9 6 6 6-6" /></svg>
<div id="invalid-combobox-popover" data-popover aria-hidden="true">
<div role="listbox" id="invalid-combobox-listbox" aria-orientation="vertical" data-empty="No framework found.">
<div role="listbox" id="invalid-combobox-listbox" aria-orientation="vertical" data-empty="No items found.">
<div role="option" data-value="Next.js">Next.js</div>
<div role="option" data-value="SvelteKit">SvelteKit</div>
<div role="option" data-value="Nuxt.js">Nuxt.js</div>
@@ -214,26 +222,28 @@ Combobox is input-first. The visible input filters the list; the hidden input st
### Disabled
<Preview>
<div id="combobox-489412" class="combobox w-[240px]">
<input type="text" role="combobox" placeholder="Select a framework" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="combobox-489412-listbox" disabled="disabled" />
<div id="disabled-combobox" class="combobox">
<input type="text" role="combobox" placeholder="Select a framework" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="disabled-combobox-listbox" disabled />
<svg aria-hidden="true" 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="combobox-trigger-icon"><path d="m6 9 6 6 6-6" /></svg>
<div id="combobox-489412-popover" data-popover aria-hidden="true">
<div role="listbox" id="combobox-489412-listbox" aria-orientation="vertical">
<div role="option" data-value="nextjs">Next.js</div>
<div id="disabled-combobox-popover" data-popover aria-hidden="true">
<div role="listbox" id="disabled-combobox-listbox" aria-orientation="vertical">
<div role="option" data-value="Next.js">Next.js</div>
</div>
</div>
<input type="hidden" value="" />
</div>
</Preview>
### Auto Highlight
### Auto highlight
Auto highlight makes the first visible option active when the list opens or filters, so pressing Enter can select it without arrowing first.
<Preview>
<div id="combobox-906805" class="combobox w-[240px]" data-auto-highlight="true">
<input type="text" role="combobox" placeholder="Select a framework" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="combobox-906805-listbox" />
<div id="auto-highlight-combobox" class="combobox" data-auto-highlight="true">
<input type="text" role="combobox" placeholder="Select a framework" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="auto-highlight-combobox-listbox" />
<svg aria-hidden="true" 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="combobox-trigger-icon"><path d="m6 9 6 6 6-6" /></svg>
<div id="combobox-906805-popover" data-popover aria-hidden="true">
<div role="listbox" id="combobox-906805-listbox" aria-orientation="vertical">
<div id="auto-highlight-combobox-popover" data-popover aria-hidden="true">
<div role="listbox" id="auto-highlight-combobox-listbox" aria-orientation="vertical" data-empty="No items found.">
<div role="option" data-value="Next.js">Next.js</div>
<div role="option" data-value="SvelteKit">SvelteKit</div>
<div role="option" data-value="Nuxt.js">Nuxt.js</div>
@@ -245,19 +255,53 @@ Combobox is input-first. The visible input filters the list; the hidden input st
</div>
</Preview>
### Top Side
### Popup
<Preview>
<div id="combobox-775269" class="combobox w-[240px]">
<input type="text" role="combobox" placeholder="Select a framework" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="combobox-775269-listbox" />
<svg aria-hidden="true" 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="combobox-trigger-icon"><path d="m6 9 6 6 6-6" /></svg>
<div id="combobox-775269-popover" data-popover aria-hidden="true" data-side="top">
<div role="listbox" id="combobox-775269-listbox" aria-orientation="vertical">
<div role="option" data-value="Next.js">Next.js</div>
<div role="option" data-value="SvelteKit">SvelteKit</div>
<div role="option" data-value="Nuxt.js">Nuxt.js</div>
<div role="option" data-value="Remix">Remix</div>
<div role="option" data-value="Astro">Astro</div>
<div id="popup-combobox" class="combobox">
<button type="button" class="btn w-64 justify-between font-normal" data-variant="outline" aria-haspopup="listbox" aria-expanded="false" aria-controls="popup-combobox-listbox">
<span data-value data-placeholder="Select country">Select country</span>
<svg aria-hidden="true" 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"><path d="m6 9 6 6 6-6" /></svg>
</button>
<div id="popup-combobox-popover" data-popover aria-hidden="true">
<div class="input-group">
<input type="text" role="combobox" placeholder="Search" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="popup-combobox-listbox" />
</div>
<div role="listbox" id="popup-combobox-listbox" aria-orientation="vertical" data-empty="No items found.">
<div role="option" data-value="argentina" data-label="Argentina">Argentina</div>
<div role="option" data-value="australia" data-label="Australia">Australia</div>
<div role="option" data-value="brazil" data-label="Brazil">Brazil</div>
<div role="option" data-value="canada" data-label="Canada">Canada</div>
<div role="option" data-value="japan" data-label="Japan">Japan</div>
<div role="option" data-value="united-states" data-label="United States">United States</div>
</div>
</div>
<input type="hidden" value="" />
</div>
</Preview>
### Input group
<Preview>
<div id="input-group-combobox" class="combobox w-60">
<div class="input-group">
<span data-align="inline-start"><svg aria-hidden="true" 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"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg></span>
<input type="text" role="combobox" placeholder="Select a timezone" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="input-group-combobox-listbox" />
<button type="button" class="btn" data-variant="ghost" data-size="icon-xs" data-align="inline-end" aria-label="Open suggestions" aria-haspopup="listbox" aria-expanded="false" aria-controls="input-group-combobox-listbox"><svg aria-hidden="true" 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"><path d="m6 9 6 6 6-6" /></svg></button>
</div>
<div id="input-group-combobox-popover" data-popover aria-hidden="true">
<div role="listbox" id="input-group-combobox-listbox" aria-orientation="vertical" data-empty="No timezones found.">
<div role="group" aria-labelledby="input-group-americas">
<div role="heading" id="input-group-americas">Americas</div>
<div role="option" data-value="(GMT-5) New York">(GMT-5) New York</div>
<div role="option" data-value="(GMT-8) Los Angeles">(GMT-8) Los Angeles</div>
</div>
<hr role="separator" />
<div role="group" aria-labelledby="input-group-europe">
<div role="heading" id="input-group-europe">Europe</div>
<div role="option" data-value="(GMT+0) London">(GMT+0) London</div>
<div role="option" data-value="(GMT+1) Paris">(GMT+1) Paris</div>
</div>
</div>
</div>
<input type="hidden" value="" />
@@ -267,18 +311,19 @@ Combobox is input-first. The visible input filters the list; the hidden input st
### RTL
<Preview>
<div dir="rtl">
<div id="combobox-998046" class="combobox w-[240px]">
<input type="text" role="combobox" placeholder="اختر إطار عمل" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="combobox-998046-listbox" />
<svg aria-hidden="true" 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="combobox-trigger-icon"><path d="m6 9 6 6 6-6" /></svg>
<div id="combobox-998046-popover" data-popover aria-hidden="true">
<div role="listbox" id="combobox-998046-listbox" aria-orientation="vertical">
<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>
<div dir="rtl" class="field w-full max-w-xs">
<label for="rtl-combobox-input">الفئات</label>
<div id="rtl-combobox" class="combobox w-full" data-auto-highlight="true">
<input id="rtl-combobox-input" type="text" role="combobox" placeholder="أضف فئات" autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" aria-expanded="false" aria-controls="rtl-combobox-listbox" />
<div id="rtl-combobox-popover" data-popover aria-hidden="true">
<div role="listbox" id="rtl-combobox-listbox" aria-orientation="vertical" aria-multiselectable="true" data-empty="لم يتم العثور على فئات.">
<div role="option" data-value="technology" data-label="التكنولوجيا">التكنولوجيا</div>
<div role="option" data-value="design" data-label="التصميم">التصميم</div>
<div role="option" data-value="business" data-label="الأعمال">الأعمال</div>
<div role="option" data-value="marketing" data-label="التسويق">التسويق</div>
</div>
</div>
<input type="hidden" value="" />
<input type="hidden" value='["technology"]' />
</div>
</div>
</Preview>
+149 -202
View File
@@ -1,12 +1,12 @@
# Command
<Preview class="w-full max-w-sm">
<div id="demo-command-standalone" class="command border" aria-label="Command menu">
<div id="demo-command-standalone" class="command rounded-lg border" aria-label="Command menu">
<header>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
<input type="text" id="demo-command-standalone-input" placeholder="Type a command or search..." autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" role="combobox" aria-expanded="true" aria-controls="demo-command-standalone-menu" />
</header>
<div role="menu" id="demo-command-standalone-menu" aria-orientation="vertical" data-empty="No results found." class="scrollbar-sm">
<div role="menu" id="demo-command-standalone-menu" aria-orientation="vertical" data-empty="No results found.">
<div role="group" aria-labelledby="suggestions">
<span role="heading" id="suggestions">Suggestions</span>
<div role="menuitem" data-filter="Calendar" data-keywords="date event schedule">
@@ -28,24 +28,23 @@
<div role="menuitem" data-filter="Profile" data-keywords="user account">
<svg class="lucide lucide-user" 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"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>
<span>Profile</span>
<span data-shortcut>⌘P</span>
</div>
<div role="menuitem" data-filter="Billing" data-keywords="invoice payment">
<svg class="lucide lucide-credit-card" 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"><rect width="20" height="14" x="2" y="5" rx="2" /><line x1="2" x2="22" y1="10" y2="10" /></svg>
<span>Billing</span>
<span data-shortcut>⌘B</span>
</div>
<div role="menuitem" data-filter="Settings" data-keywords="config preferences">
<svg class="lucide lucide-settings" 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"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /><circle cx="12" cy="12" r="3" /></svg>
<span>Settings</span>
<span data-shortcut>⌘S</span>
</div>
</div>
</div>
</div>
</Preview>
## About
shadcn/ui's Command component is built on `cmdk`. Basecoat implements the same command-menu pattern with vanilla HTML, CSS, and a small JavaScript controller for filtering, active item navigation, and dialog closing.
## Usage
<Callout title="Template macros available" icon="code" action={{ label: "Read more", href: "/cli#command" }}>
@@ -69,7 +68,7 @@ shadcn/ui's Command component is built on `cmdk`. Basecoat implements the same c
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
<input type="text" id="demo-command-standalone-input" placeholder="Type a command or search..." autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" role="combobox" aria-expanded="true" aria-controls="demo-command-standalone-menu" />
</header>
<div role="menu" id="demo-command-standalone-menu" aria-orientation="vertical" data-empty="No results found." class="scrollbar-sm">
<div role="menu" id="demo-command-standalone-menu" aria-orientation="vertical" data-empty="No results found.">
<div role="group" aria-labelledby="suggestions">
<span role="heading" id="suggestions">Suggestions</span>
<div role="menuitem" data-filter="Calendar" data-keywords="date event schedule">
@@ -91,14 +90,17 @@ shadcn/ui's Command component is built on `cmdk`. Basecoat implements the same c
<div role="menuitem" data-filter="Profile" data-keywords="user account">
<svg class="lucide lucide-user" 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"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>
<span>Profile</span>
<span data-shortcut>⌘P</span>
</div>
<div role="menuitem" data-filter="Billing" data-keywords="invoice payment">
<svg class="lucide lucide-credit-card" 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"><rect width="20" height="14" x="2" y="5" rx="2" /><line x1="2" x2="22" y1="10" y2="10" /></svg>
<span>Billing</span>
<span data-shortcut>⌘B</span>
</div>
<div role="menuitem" data-filter="Settings" data-keywords="config preferences">
<svg class="lucide lucide-settings" 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"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /><circle cx="12" cy="12" r="3" /></svg>
<span>Settings</span>
<span data-shortcut>⌘S</span>
</div>
</div>
</div>
@@ -126,7 +128,7 @@ The command menu can be used standalone or inside a native dialog. The structure
<dl>
<dt><code>&lt;dialog class="command-dialog"&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>
Dialog variant. Wraps one <code>&lt;div class="command"&gt;</code>. Add <code>aria-label</code> or <code>aria-labelledby</code> for an accessible name.
Dialog variant. Wraps one direct child <code>&lt;div class="command"&gt;</code>. Add <code>aria-label</code> or <code>aria-labelledby</code> for an accessible name.
<dl>
<dt><code>&lt;div class="command"&gt;</code></dt>
<dd>
@@ -149,7 +151,7 @@ The command menu can be used standalone or inside a native dialog. The structure
<dt><code>&lt;span role="heading" id="&#123; HEADING_ID &#125;"&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Group heading. Use an <code>id</code> when the parent group uses <code>aria-labelledby</code>.</dd>
<dt><code>&lt;div role="menuitem"&gt;</code> or <code>&lt;a role="menuitem"&gt;</code></dt>
<dd>Selectable command item. Use <code>&lt;a&gt;</code> for navigation and <code>&lt;div&gt;</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>
<dd>Selectable command item. Use <code>&lt;a&gt;</code> for navigation and <code>&lt;div&gt;</code> for actions. Supports <code>data-filter</code>, <code>data-keywords</code>, <code>data-force</code>, <code>data-keep-command-open</code>, <code>aria-disabled="true"</code>, and <code>data-disabled="true"</code>.</dd>
<dt><code>&lt;span data-shortcut&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
<dd>Shortcut hint aligned to the inline end of the item.</dd>
<dt><code>&lt;span data-indicator&gt;</code> <span class="badge" data-variant="secondary">Optional</span></dt>
@@ -174,22 +176,75 @@ The command menu can be used standalone or inside a native dialog. The structure
### Basic
A simple command menu in a dialog. The button also shows the keyboard shortcut used in this example.
A simple command menu in a dialog.
<Preview>
<button type="button" class="btn" data-variant="outline" onclick="document.getElementById('command-basic').showModal()">
Open command menu
<kbd class="kbd">⌘J</kbd>
</button>
<button type="button" class="btn" data-variant="outline" onclick="document.getElementById('command-basic').showModal()">Open Menu</button>
<dialog id="command-basic" class="command-dialog" aria-label="Command menu" onclick="if (event.target === this) this.close()">
<div class="command">
<header>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
<input type="text" id="command-basic-input" placeholder="Type a command or search..." autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" role="combobox" aria-expanded="true" aria-controls="command-basic-menu" />
</header>
<div role="menu" id="command-basic-menu" aria-orientation="vertical" data-empty="No results found." class="scrollbar-sm">
<div role="menu" id="command-basic-menu" aria-orientation="vertical" data-empty="No results found.">
<div role="group" aria-labelledby="command-basic-suggestions">
<span role="heading" id="command-basic-suggestions">Suggestions</span>
<div role="menuitem" data-filter="Calendar">Calendar</div>
<div role="menuitem" data-filter="Search Emoji">Search Emoji</div>
<div role="menuitem" data-filter="Calculator">Calculator</div>
</div>
</div>
</div>
</dialog>
</Preview>
### Shortcuts
<Preview>
<button type="button" class="btn" data-variant="outline" onclick="document.getElementById('command-shortcuts-dialog').showModal()">Open Menu</button>
<dialog id="command-shortcuts-dialog" class="command-dialog" aria-label="Command menu" onclick="if (event.target === this) this.close()">
<div class="command">
<header>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
<input type="text" id="command-shortcuts-input" placeholder="Type a command or search..." autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" role="combobox" aria-expanded="true" aria-controls="command-shortcuts-menu" />
</header>
<div role="menu" id="command-shortcuts-menu" aria-orientation="vertical" data-empty="No results found.">
<div role="group" aria-labelledby="command-shortcuts-settings">
<span role="heading" id="command-shortcuts-settings">Settings</span>
<div role="menuitem" data-filter="Profile" data-keywords="user account">
<svg class="lucide lucide-user" 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"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>
<span>Profile</span>
<span data-shortcut>⌘P</span>
</div>
<div role="menuitem" data-filter="Billing" data-keywords="invoice payment">
<svg class="lucide lucide-credit-card" 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"><rect width="20" height="14" x="2" y="5" rx="2" /><line x1="2" x2="22" y1="10" y2="10" /></svg>
<span>Billing</span>
<span data-shortcut>⌘B</span>
</div>
<div role="menuitem" data-filter="Settings" data-keywords="config preferences">
<svg class="lucide lucide-settings" 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"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /><circle cx="12" cy="12" r="3" /></svg>
<span>Settings</span>
<span data-shortcut>⌘S</span>
</div>
</div>
</div>
</div>
</dialog>
</Preview>
### Groups
<Preview>
<button type="button" class="btn" data-variant="outline" onclick="document.getElementById('command-groups-dialog').showModal()">Open Menu</button>
<dialog id="command-groups-dialog" class="command-dialog" aria-label="Command menu" onclick="if (event.target === this) this.close()">
<div class="command">
<header>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
<input type="text" id="command-groups-input" placeholder="Type a command or search..." autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" role="combobox" aria-expanded="true" aria-controls="command-groups-menu" />
</header>
<div role="menu" id="command-groups-menu" aria-orientation="vertical" data-empty="No results found.">
<div role="group" aria-labelledby="command-groups-suggestions">
<span role="heading" id="command-groups-suggestions">Suggestions</span>
<div role="menuitem" data-filter="Calendar" data-keywords="date event schedule">
<svg class="lucide lucide-calendar" 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"><path d="M8 2v4" /><path d="M16 2v4" /><rect width="18" height="18" x="3" y="4" rx="2" /><path d="M3 10h18" /></svg>
<span>Calendar</span>
@@ -198,235 +253,105 @@ A simple command menu in a dialog. The button also shows the keyboard shortcut u
<svg class="lucide lucide-smile" 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"><circle cx="12" cy="12" r="10" /><path d="M8 14s1.5 2 4 2 4-2 4-2" /><line x1="9" x2="9.01" y1="9" y2="9" /><line x1="15" x2="15.01" y1="9" y2="9" /></svg>
<span>Search Emoji</span>
</div>
<div role="menuitem" aria-disabled="true">
<div role="menuitem" data-filter="Calculator">
<svg class="lucide lucide-calculator" 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"><rect width="16" height="20" x="4" y="2" rx="2" /><line x1="8" x2="16" y1="6" y2="6" /><line x1="16" x2="16" y1="14" y2="18" /><path d="M16 10h.01" /><path d="M12 10h.01" /><path d="M8 10h.01" /><path d="M12 14h.01" /><path d="M8 14h.01" /><path d="M12 18h.01" /><path d="M8 18h.01" /></svg>
<span>Calculator</span>
</div>
</div>
<hr role="separator" />
<div role="group" aria-labelledby="command-basic-settings">
<span role="heading" id="command-basic-settings">Settings</span>
<div role="group" aria-labelledby="command-groups-settings">
<span role="heading" id="command-groups-settings">Settings</span>
<div role="menuitem" data-filter="Profile" data-keywords="user account">
<svg class="lucide lucide-user" 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"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>
<span>Profile</span>
<span data-shortcut>⌘P</span>
</div>
<div role="menuitem" data-filter="Billing" data-keywords="invoice payment">
<svg class="lucide lucide-credit-card" 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"><rect width="20" height="14" x="2" y="5" rx="2" /><line x1="2" x2="22" y1="10" y2="10" /></svg>
<span>Billing</span>
<span data-shortcut>⌘B</span>
</div>
<div role="menuitem" data-filter="Settings" data-keywords="config preferences">
<svg class="lucide lucide-settings" 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"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /><circle cx="12" cy="12" r="3" /></svg>
<span>Settings</span>
<span data-shortcut>⌘S</span>
</div>
</div>
</div>
</div>
</dialog>
<script>
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>
</Preview>
### Shortcuts
<Preview class="w-full max-w-sm">
<div id="command-shortcuts" class="command border" aria-label="Command menu">
<header>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
<input type="text" id="command-shortcuts-input" placeholder="Type a command or search..." autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" role="combobox" aria-expanded="true" aria-controls="command-shortcuts-menu" />
</header>
<div role="menu" id="command-shortcuts-menu" aria-orientation="vertical" data-empty="No results found." class="scrollbar-sm">
<div role="group" aria-labelledby="command-shortcuts-suggestions">
<span role="heading" id="command-shortcuts-suggestions">Suggestions</span>
<div role="menuitem" data-filter="Calendar" data-keywords="date event schedule">
<svg class="lucide lucide-calendar" 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"><path d="M8 2v4" /><path d="M16 2v4" /><rect width="18" height="18" x="3" y="4" rx="2" /><path d="M3 10h18" /></svg>
<span>Calendar</span>
<span data-shortcut>⌘K</span>
</div>
<div role="menuitem" data-filter="Search Emoji" data-keywords="emoji smile reaction">
<svg class="lucide lucide-smile" 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"><circle cx="12" cy="12" r="10" /><path d="M8 14s1.5 2 4 2 4-2 4-2" /><line x1="9" x2="9.01" y1="9" y2="9" /><line x1="15" x2="15.01" y1="9" y2="9" /></svg>
<span>Search Emoji</span>
<span data-shortcut>⌘E</span>
</div>
<div role="menuitem" aria-disabled="true">
<svg class="lucide lucide-calculator" 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"><rect width="16" height="20" x="4" y="2" rx="2" /><line x1="8" x2="16" y1="6" y2="6" /><line x1="16" x2="16" y1="14" y2="18" /><path d="M16 10h.01" /><path d="M12 10h.01" /><path d="M8 10h.01" /><path d="M12 14h.01" /><path d="M8 14h.01" /><path d="M12 18h.01" /><path d="M8 18h.01" /></svg>
<span>Calculator</span>
</div>
</div>
<hr role="separator" />
<div role="group" aria-labelledby="command-shortcuts-settings">
<span role="heading" id="command-shortcuts-settings">Settings</span>
<div role="menuitem" data-filter="Profile" data-keywords="user account">
<svg class="lucide lucide-user" 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"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>
<span>Profile</span>
<span data-shortcut>⌘P</span>
</div>
<div role="menuitem" data-filter="Billing" data-keywords="invoice payment">
<svg class="lucide lucide-credit-card" 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"><rect width="20" height="14" x="2" y="5" rx="2" /><line x1="2" x2="22" y1="10" y2="10" /></svg>
<span>Billing</span>
<span data-shortcut>⌘B</span>
</div>
<div role="menuitem" data-filter="Settings" data-keywords="config preferences">
<svg class="lucide lucide-settings" 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"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /><circle cx="12" cy="12" r="3" /></svg>
<span>Settings</span>
<span data-shortcut>⌘S</span>
</div>
</div>
</div>
</div>
</Preview>
### Groups
<Preview class="w-full max-w-sm">
<div id="command-groups" class="command border" aria-label="Command menu">
<header>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
<input type="text" id="command-groups-input" placeholder="Type a command or search..." autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" role="combobox" aria-expanded="true" aria-controls="command-groups-menu" />
</header>
<div role="menu" id="command-groups-menu" aria-orientation="vertical" data-empty="No results found." class="scrollbar-sm">
<div role="group" aria-labelledby="command-groups-suggestions">
<span role="heading" id="command-groups-suggestions">Suggestions</span>
<div role="menuitem" data-filter="Calendar" data-keywords="date event schedule">
<svg class="lucide lucide-calendar" 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"><path d="M8 2v4" /><path d="M16 2v4" /><rect width="18" height="18" x="3" y="4" rx="2" /><path d="M3 10h18" /></svg>
<span>Calendar</span>
</div>
<div role="menuitem" data-filter="Search Emoji" data-keywords="emoji smile reaction">
<svg class="lucide lucide-smile" 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"><circle cx="12" cy="12" r="10" /><path d="M8 14s1.5 2 4 2 4-2 4-2" /><line x1="9" x2="9.01" y1="9" y2="9" /><line x1="15" x2="15.01" y1="9" y2="9" /></svg>
<span>Search Emoji</span>
</div>
<div role="menuitem" aria-disabled="true">
<svg class="lucide lucide-calculator" 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"><rect width="16" height="20" x="4" y="2" rx="2" /><line x1="8" x2="16" y1="6" y2="6" /><line x1="16" x2="16" y1="14" y2="18" /><path d="M16 10h.01" /><path d="M12 10h.01" /><path d="M8 10h.01" /><path d="M12 14h.01" /><path d="M8 14h.01" /><path d="M12 18h.01" /><path d="M8 18h.01" /></svg>
<span>Calculator</span>
</div>
</div>
<hr role="separator" />
<div role="group" aria-labelledby="command-groups-settings">
<span role="heading" id="command-groups-settings">Settings</span>
<div role="menuitem" data-filter="Profile" data-keywords="user account">
<svg class="lucide lucide-user" 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"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>
<span>Profile</span>
</div>
<div role="menuitem" data-filter="Billing" data-keywords="invoice payment">
<svg class="lucide lucide-credit-card" 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"><rect width="20" height="14" x="2" y="5" rx="2" /><line x1="2" x2="22" y1="10" y2="10" /></svg>
<span>Billing</span>
</div>
<div role="menuitem" data-filter="Settings" data-keywords="config preferences">
<svg class="lucide lucide-settings" 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"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /><circle cx="12" cy="12" r="3" /></svg>
<span>Settings</span>
</div>
</div>
</div>
</div>
</Preview>
### Scrollable
<Preview class="w-full max-w-sm">
<div id="command-scrollable" class="command border" aria-label="Command menu">
<header>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
<input type="text" id="command-scrollable-input" placeholder="Type a command or search..." autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" role="combobox" aria-expanded="true" aria-controls="command-scrollable-menu" />
</header>
<div role="menu" id="command-scrollable-menu" aria-orientation="vertical" data-empty="No results found." class="scrollbar-sm">
<div role="group" aria-labelledby="command-scrollable-heading">
<span role="heading" id="command-scrollable-heading">Pages</span>
<div role="menuitem" data-filter="Page 1">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 1</span>
<Preview>
<button type="button" class="btn" data-variant="outline" onclick="document.getElementById('command-scrollable-dialog').showModal()">Open Menu</button>
<dialog id="command-scrollable-dialog" class="command-dialog" aria-label="Command menu" onclick="if (event.target === this) this.close()">
<div class="command">
<header>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
<input type="text" id="command-scrollable-input" placeholder="Type a command or search..." autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" role="combobox" aria-expanded="true" aria-controls="command-scrollable-menu" />
</header>
<div role="menu" id="command-scrollable-menu" aria-orientation="vertical" data-empty="No results found.">
<div role="group" aria-labelledby="command-scrollable-navigation">
<span role="heading" id="command-scrollable-navigation">Navigation</span>
<div role="menuitem" data-filter="Home"><svg class="lucide lucide-home" 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"><path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" /><path d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /></svg><span>Home</span><span data-shortcut>⌘H</span></div>
<div role="menuitem" data-filter="Inbox"><svg class="lucide lucide-inbox" 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"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12" /><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" /></svg><span>Inbox</span><span data-shortcut>⌘I</span></div>
<div role="menuitem" data-filter="Documents"><svg class="lucide lucide-file-text" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /><path d="M10 9H8" /><path d="M16 13H8" /><path d="M16 17H8" /></svg><span>Documents</span><span data-shortcut>⌘D</span></div>
<div role="menuitem" data-filter="Folders"><svg class="lucide lucide-folder" 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"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" /></svg><span>Folders</span><span data-shortcut>⌘F</span></div>
</div>
<div role="menuitem" data-filter="Page 2">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 2</span>
<hr role="separator" />
<div role="group" aria-labelledby="command-scrollable-actions">
<span role="heading" id="command-scrollable-actions">Actions</span>
<div role="menuitem" data-filter="New File"><svg class="lucide lucide-plus" 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"><path d="M5 12h14" /><path d="M12 5v14" /></svg><span>New File</span><span data-shortcut>⌘N</span></div>
<div role="menuitem" data-filter="New Folder"><svg class="lucide lucide-folder-plus" 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"><path d="M12 10v6" /><path d="M9 13h6" /><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" /></svg><span>New Folder</span><span data-shortcut>⇧⌘N</span></div>
<div role="menuitem" data-filter="Copy"><svg class="lucide lucide-copy" 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"><rect width="14" height="14" x="8" y="8" rx="2" ry="2" /><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" /></svg><span>Copy</span><span data-shortcut>⌘C</span></div>
<div role="menuitem" data-filter="Cut"><svg class="lucide lucide-scissors" 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"><circle cx="6" cy="6" r="3" /><path d="M8.12 8.12 12 12" /><path d="M20 4 8.12 15.88" /><circle cx="6" cy="18" r="3" /><path d="M14.8 14.8 20 20" /></svg><span>Cut</span><span data-shortcut>⌘X</span></div>
<div role="menuitem" data-filter="Paste"><svg class="lucide lucide-clipboard-paste" 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"><path d="M11 14h10" /><path d="M16 4h2a2 2 0 0 1 2 2v1.344" /><path d="m17 18 4-4-4-4" /><path d="M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 1.793-1.113" /><rect x="8" y="2" width="8" height="4" rx="1" /></svg><span>Paste</span><span data-shortcut>⌘V</span></div>
<div role="menuitem" data-filter="Delete"><svg class="lucide lucide-trash" 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"><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /><path d="M3 6h18" /><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></svg><span>Delete</span><span data-shortcut>⌫</span></div>
</div>
<div role="menuitem" data-filter="Page 3">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 3</span>
<hr role="separator" />
<div role="group" aria-labelledby="command-scrollable-view">
<span role="heading" id="command-scrollable-view">View</span>
<div role="menuitem" data-filter="Grid View"><svg class="lucide lucide-layout-grid" 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"><rect width="7" height="7" x="3" y="3" rx="1" /><rect width="7" height="7" x="14" y="3" rx="1" /><rect width="7" height="7" x="14" y="14" rx="1" /><rect width="7" height="7" x="3" y="14" rx="1" /></svg><span>Grid View</span></div>
<div role="menuitem" data-filter="List View"><svg class="lucide lucide-list" 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"><path d="M3 5h.01" /><path d="M3 12h.01" /><path d="M3 19h.01" /><path d="M8 5h13" /><path d="M8 12h13" /><path d="M8 19h13" /></svg><span>List View</span></div>
<div role="menuitem" data-filter="Zoom In"><svg class="lucide lucide-zoom-in" 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"><circle cx="11" cy="11" r="8" /><line x1="21" x2="16.65" y1="21" y2="16.65" /><line x1="11" x2="11" y1="8" y2="14" /><line x1="8" x2="14" y1="11" y2="11" /></svg><span>Zoom In</span><span data-shortcut>⌘+</span></div>
<div role="menuitem" data-filter="Zoom Out"><svg class="lucide lucide-zoom-out" 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"><circle cx="11" cy="11" r="8" /><line x1="21" x2="16.65" y1="21" y2="16.65" /><line x1="8" x2="14" y1="11" y2="11" /></svg><span>Zoom Out</span><span data-shortcut>⌘-</span></div>
</div>
<div role="menuitem" data-filter="Page 4">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 4</span>
<hr role="separator" />
<div role="group" aria-labelledby="command-scrollable-account">
<span role="heading" id="command-scrollable-account">Account</span>
<div role="menuitem" data-filter="Profile"><svg class="lucide lucide-user" 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"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg><span>Profile</span><span data-shortcut>⌘P</span></div>
<div role="menuitem" data-filter="Billing"><svg class="lucide lucide-credit-card" 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"><rect width="20" height="14" x="2" y="5" rx="2" /><line x1="2" x2="22" y1="10" y2="10" /></svg><span>Billing</span><span data-shortcut>⌘B</span></div>
<div role="menuitem" data-filter="Settings"><svg class="lucide lucide-settings" 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"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /><circle cx="12" cy="12" r="3" /></svg><span>Settings</span><span data-shortcut>⌘S</span></div>
<div role="menuitem" data-filter="Notifications"><svg class="lucide lucide-bell" 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"><path d="M10.268 21a2 2 0 0 0 3.464 0" /><path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></svg><span>Notifications</span></div>
<div role="menuitem" data-filter="Help & Support"><svg class="lucide lucide-circle-help" 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"><circle cx="12" cy="12" r="10" /><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" /><path d="M12 17h.01" /></svg><span>Help &amp; Support</span></div>
</div>
<div role="menuitem" data-filter="Page 5">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 5</span>
</div>
<div role="menuitem" data-filter="Page 6">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 6</span>
</div>
<div role="menuitem" data-filter="Page 7">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 7</span>
</div>
<div role="menuitem" data-filter="Page 8">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 8</span>
</div>
<div role="menuitem" data-filter="Page 9">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 9</span>
</div>
<div role="menuitem" data-filter="Page 10">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 10</span>
</div>
<div role="menuitem" data-filter="Page 11">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 11</span>
</div>
<div role="menuitem" data-filter="Page 12">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 12</span>
</div>
<div role="menuitem" data-filter="Page 13">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 13</span>
</div>
<div role="menuitem" data-filter="Page 14">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 14</span>
</div>
<div role="menuitem" data-filter="Page 15">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 15</span>
</div>
<div role="menuitem" data-filter="Page 16">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 16</span>
</div>
<div role="menuitem" data-filter="Page 17">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 17</span>
</div>
<div role="menuitem" data-filter="Page 18">
<svg class="lucide lucide-file" 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"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /><path d="M14 2v5a1 1 0 0 0 1 1h5" /></svg>
<span>Page 18</span>
<hr role="separator" />
<div role="group" aria-labelledby="command-scrollable-tools">
<span role="heading" id="command-scrollable-tools">Tools</span>
<div role="menuitem" data-filter="Calculator"><svg class="lucide lucide-calculator" 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"><rect width="16" height="20" x="4" y="2" rx="2" /><line x1="8" x2="16" y1="6" y2="6" /><line x1="16" x2="16" y1="14" y2="18" /><path d="M16 10h.01" /><path d="M12 10h.01" /><path d="M8 10h.01" /><path d="M12 14h.01" /><path d="M8 14h.01" /><path d="M12 18h.01" /><path d="M8 18h.01" /></svg><span>Calculator</span></div>
<div role="menuitem" data-filter="Calendar"><svg class="lucide lucide-calendar" 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"><path d="M8 2v4" /><path d="M16 2v4" /><rect width="18" height="18" x="3" y="4" rx="2" /><path d="M3 10h18" /></svg><span>Calendar</span></div>
<div role="menuitem" data-filter="Image Editor"><svg class="lucide lucide-image" 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"><rect width="18" height="18" x="3" y="3" rx="2" ry="2" /><circle cx="9" cy="9" r="2" /><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" /></svg><span>Image Editor</span></div>
<div role="menuitem" data-filter="Code Editor"><svg class="lucide lucide-code" 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"><path d="m16 18 6-6-6-6" /><path d="m8 6-6 6 6 6" /></svg><span>Code Editor</span></div>
</div>
</div>
</div>
</div>
</dialog>
</Preview>
### RTL
## RTL
Command supports document direction. Set `dir="rtl"` on the command root or a parent element.
Add `dir="rtl"` to the command root or an ancestor.
<Preview class="w-full max-w-sm">
<div dir="rtl">
<div id="command-rtl" class="command border" aria-label="Command menu">
<header>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
<input type="text" id="command-rtl-input" placeholder="اكتب أمراً أو ابحث..." autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" role="combobox" aria-expanded="true" aria-controls="command-rtl-menu" />
<input type="text" id="command-rtl-input" placeholder="اكتب أمرًا أو ابحث..." autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list" role="combobox" aria-expanded="true" aria-controls="command-rtl-menu" />
</header>
<div role="menu" id="command-rtl-menu" aria-orientation="vertical" data-empty="No results found." class="scrollbar-sm">
<div role="menu" id="command-rtl-menu" aria-orientation="vertical" data-empty="لم يتم العثور على نتائج.">
<div role="group" aria-labelledby="command-rtl-suggestions">
<span role="heading" id="command-rtl-suggestions">اقتراحات</span>
<div role="menuitem" data-filter="Calendar">
@@ -434,6 +359,28 @@ Command supports document direction. Set `dir="rtl"` on the command root or a pa
<span>التقويم</span>
<span data-shortcut>⌘K</span>
</div>
<div role="menuitem" data-filter="Search Emoji">
<svg class="lucide lucide-smile" 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"><circle cx="12" cy="12" r="10" /><path d="M8 14s1.5 2 4 2 4-2 4-2" /><line x1="9" x2="9.01" y1="9" y2="9" /><line x1="15" x2="15.01" y1="9" y2="9" /></svg>
<span>البحث عن الرموز التعبيرية</span>
</div>
<div role="menuitem" aria-disabled="true" data-filter="Calculator">
<svg class="lucide lucide-calculator" 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"><rect width="16" height="20" x="4" y="2" rx="2" /><line x1="8" x2="16" y1="6" y2="6" /><line x1="16" x2="16" y1="14" y2="18" /><path d="M16 10h.01" /><path d="M12 10h.01" /><path d="M8 10h.01" /><path d="M12 14h.01" /><path d="M8 14h.01" /><path d="M12 18h.01" /><path d="M8 18h.01" /></svg>
<span>الآلة الحاسبة</span>
</div>
</div>
<hr role="separator" />
<div role="group" aria-labelledby="command-rtl-settings">
<span role="heading" id="command-rtl-settings">الإعدادات</span>
<div role="menuitem" data-filter="Profile">
<svg class="lucide lucide-user" 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"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>
<span>الملف الشخصي</span>
<span data-shortcut>⌘P</span>
</div>
<div role="menuitem" data-filter="Billing">
<svg class="lucide lucide-credit-card" 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"><rect width="20" height="14" x="2" y="5" rx="2" /><line x1="2" x2="22" y1="10" y2="10" /></svg>
<span>الفوترة</span>
<span data-shortcut>⌘B</span>
</div>
<div role="menuitem" data-filter="Settings">
<svg class="lucide lucide-settings" 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"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /><circle cx="12" cy="12" r="3" /></svg>
<span>الإعدادات</span>
+15 -11
View File
@@ -77,7 +77,7 @@ Basecoat uses the native `<dialog>` element and `showModal()`. This differs from
<dd>Trigger button. Basecoat intentionally uses the native <code>HTMLDialogElement.showModal()</code> method.</dd>
<dt><code>&lt;dialog class="dialog" id="&#123; DIALOG_ID &#125;"&gt;</code></dt>
<dd>
Native modal dialog. Add <code>aria-labelledby="&#123; TITLE_ID &#125;"</code> and <code>aria-describedby="&#123; DESCRIPTION_ID &#125;"</code> when title and description are present. The macro also adds backdrop-click close handling.
Native modal dialog. Add <code>aria-labelledby="&#123; TITLE_ID &#125;"</code> and <code>aria-describedby="&#123; DESCRIPTION_ID &#125;"</code> when title and description are present. Add width utilities to the inner <code>&lt;div&gt;</code> when a dialog needs a custom size. The macro also adds backdrop-click close handling.
<dl>
<dt><code>&lt;div&gt;</code></dt>
<dd>
@@ -107,7 +107,7 @@ Basecoat uses the native `<dialog>` element and `showModal()`. This differs from
## Examples
### Custom Close Button
### Custom close button
<Preview>
<button type="button" onclick="document.getElementById('dialog-custom-close').showModal()" class="btn" data-variant="outline">Share</button>
@@ -120,7 +120,7 @@ Basecoat uses the native `<dialog>` element and `showModal()`. This differs from
<section class="flex items-center gap-2">
<div class="grid flex-1 gap-2">
<label class="label sr-only" for="dialog-custom-close-link">Link</label>
<input class="input" id="dialog-custom-close-link" value="https://ui.shadcn.com/docs/installation" readonly />
<input class="input" id="dialog-custom-close-link" value="https://basecoatui.com/installation/" readonly />
</div>
</section>
<footer class="sm:justify-start">
@@ -130,7 +130,7 @@ Basecoat uses the native `<dialog>` element and `showModal()`. This differs from
</dialog>
</Preview>
### No Close Button
### No close button
<Preview>
<button type="button" onclick="document.getElementById('dialog-no-close').showModal()" class="btn" data-variant="outline">No Close Button</button>
@@ -144,7 +144,7 @@ Basecoat uses the native `<dialog>` element and `showModal()`. This differs from
</dialog>
</Preview>
### Sticky Footer
### Sticky footer
<Preview>
<button type="button" onclick="document.getElementById('dialog-sticky-footer').showModal()" class="btn" data-variant="outline">Sticky Footer</button>
@@ -174,7 +174,7 @@ Basecoat uses the native `<dialog>` element and `showModal()`. This differs from
</dialog>
</Preview>
### Scrollable Content
### Scrollable content
<Preview>
<button type="button" onclick="document.getElementById('dialog-scrollable').showModal()" class="btn" data-variant="outline">Scrollable Content</button>
@@ -209,24 +209,28 @@ Dialog positioning and close button placement use logical properties. Set `dir="
<Preview>
<div dir="rtl">
<button type="button" onclick="document.getElementById('dialog-rtl').showModal()" class="btn" data-variant="outline">فتح</button>
<button type="button" onclick="document.getElementById('dialog-rtl').showModal()" class="btn" data-variant="outline">فتح الحوار</button>
<dialog id="dialog-rtl" class="dialog" aria-labelledby="dialog-rtl-title" aria-describedby="dialog-rtl-description" onclick="if (event.target === this) this.close()">
<div>
<div class="sm:max-w-sm">
<header>
<h2 id="dialog-rtl-title">تعديل الملف الشخصي</h2>
<p id="dialog-rtl-description">قم بتحديث بياناتك ثم احفظ التغييرات.</p>
<p id="dialog-rtl-description">قم بإجراء تغييرات على ملفك الشخصي هنا. انقر فوق حفظ عند الانتهاء.</p>
</header>
<section>
<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="ليلى" />
<input class="input" id="dialog-rtl-name" name="name" value="Pedro Duarte" />
</div>
<div class="grid gap-3">
<label class="label" for="dialog-rtl-username">اسم المستخدم</label>
<input class="input" id="dialog-rtl-username" name="username" value="@peduarte" />
</div>
</form>
</section>
<footer>
<button class="btn" data-variant="outline" onclick="this.closest('dialog').close()">إلغاء</button>
<button class="btn" onclick="this.closest('dialog').close()">حفظ</button>
<button class="btn" onclick="this.closest('dialog').close()">حفظ التغييرات</button>
</footer>
<button type="button" class="btn" data-variant="ghost" data-size="icon-sm" aria-label="Close dialog" onclick="this.closest('dialog').close()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18" /><path d="m6 6 12 12" /></svg>
+262 -82
View File
@@ -3,7 +3,7 @@
<Preview>
<div id="demo-dropdown-menu" class="dropdown-menu">
<button type="button" id="demo-dropdown-menu-trigger" aria-haspopup="menu" aria-controls="demo-dropdown-menu-menu" aria-expanded="false" class="btn" data-variant="outline">Open</button>
<div id="demo-dropdown-menu-popover" data-popover aria-hidden="true" class="min-w-56">
<div id="demo-dropdown-menu-popover" data-popover aria-hidden="true" class="w-40">
<div role="menu" id="demo-dropdown-menu-menu" aria-labelledby="demo-dropdown-menu-trigger">
<div role="group" aria-labelledby="demo-dropdown-account">
<div role="heading" id="demo-dropdown-account">My Account</div>
@@ -15,15 +15,32 @@
<span>Billing</span>
<kbd>⌘B</kbd>
</div>
<div role="menuitem">Settings</div>
<div role="menuitem">Keyboard shortcuts</div>
<div role="menuitem">
<span>Settings</span>
<kbd>⌘S</kbd>
</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="group">
<div role="menuitem">Team</div>
<div role="menuitem">
<span>New Team</span>
<kbd>⌘+T</kbd>
</div>
</div>
<hr role="separator" />
<div role="menuitem">Log out</div>
<div role="group">
<div role="menuitem">GitHub</div>
<div role="menuitem">Support</div>
<div role="menuitem" aria-disabled="true">API</div>
</div>
<hr role="separator" />
<div role="group">
<div role="menuitem">
<span>Log out</span>
<kbd>⇧⌘Q</kbd>
</div>
</div>
</div>
</div>
</div>
@@ -48,7 +65,7 @@ Basecoat dropdown menus are inline-positioned relative to the `.dropdown-menu` w
```html
<div id="demo-dropdown-menu" class="dropdown-menu">
<button type="button" id="demo-dropdown-menu-trigger" aria-haspopup="menu" aria-controls="demo-dropdown-menu-menu" aria-expanded="false" class="btn" data-variant="outline">Open</button>
<div id="demo-dropdown-menu-popover" data-popover aria-hidden="true" class="min-w-56">
<div id="demo-dropdown-menu-popover" data-popover aria-hidden="true" class="w-40">
<div role="menu" id="demo-dropdown-menu-menu" aria-labelledby="demo-dropdown-menu-trigger">
<div role="group" aria-labelledby="demo-dropdown-account">
<div role="heading" id="demo-dropdown-account">My Account</div>
@@ -60,15 +77,32 @@ Basecoat dropdown menus are inline-positioned relative to the `.dropdown-menu` w
<span>Billing</span>
<kbd>⌘B</kbd>
</div>
<div role="menuitem">Settings</div>
<div role="menuitem">Keyboard shortcuts</div>
<div role="menuitem">
<span>Settings</span>
<kbd>⌘S</kbd>
</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="group">
<div role="menuitem">Team</div>
<div role="menuitem">
<span>New Team</span>
<kbd>⌘+T</kbd>
</div>
</div>
<hr role="separator" />
<div role="menuitem">Log out</div>
<div role="group">
<div role="menuitem">GitHub</div>
<div role="menuitem">Support</div>
<div role="menuitem" aria-disabled="true">API</div>
</div>
<hr role="separator" />
<div role="group">
<div role="menuitem">
<span>Log out</span>
<kbd>⇧⌘Q</kbd>
</div>
</div>
</div>
</div>
</div>
@@ -141,7 +175,8 @@ Basecoat dropdown menus are inline-positioned relative to the `.dropdown-menu` w
<div role="menuitem">Settings</div>
</div>
<hr role="separator" />
<div role="menuitem">Team</div>
<div role="menuitem">GitHub</div>
<div role="menuitem">Support</div>
<div role="menuitem" aria-disabled="true">API</div>
</div>
</div>
@@ -155,22 +190,25 @@ Basecoat dropdown menus are inline-positioned relative to the `.dropdown-menu` w
<button type="button" id="dropdown-shortcuts-trigger" aria-haspopup="menu" aria-controls="dropdown-shortcuts-menu" aria-expanded="false" class="btn" data-variant="outline">Open</button>
<div id="dropdown-shortcuts-popover" data-popover aria-hidden="true" class="min-w-46">
<div role="menu" id="dropdown-shortcuts-menu" aria-labelledby="dropdown-shortcuts-trigger">
<div role="menuitem">
<span>New Tab</span>
<kbd>⌘T</kbd>
</div>
<div role="menuitem">
<span>New Window</span>
<kbd>⌘N</kbd>
</div>
<div role="menuitem">
<span>New Private Window</span>
<kbd>⇧⌘N</kbd>
<div role="group" aria-labelledby="dropdown-shortcuts-account">
<div role="heading" id="dropdown-shortcuts-account">My Account</div>
<div role="menuitem">
<span>Profile</span>
<kbd>⇧⌘P</kbd>
</div>
<div role="menuitem">
<span>Billing</span>
<kbd>⌘B</kbd>
</div>
<div role="menuitem">
<span>Settings</span>
<kbd>⌘S</kbd>
</div>
</div>
<hr role="separator" />
<div role="menuitem">
<span>Print</span>
<kbd>⌘P</kbd>
<span>Log out</span>
<kbd>⇧⌘Q</kbd>
</div>
</div>
</div>
@@ -211,7 +249,7 @@ Basecoat dropdown menus are inline-positioned relative to the `.dropdown-menu` w
<Preview>
<div id="dropdown-checkboxes" class="dropdown-menu">
<button type="button" id="dropdown-checkboxes-trigger" aria-haspopup="menu" aria-controls="dropdown-checkboxes-menu" aria-expanded="false" class="btn" data-variant="outline">Open</button>
<div id="dropdown-checkboxes-popover" data-popover aria-hidden="true" class="min-w-32">
<div id="dropdown-checkboxes-popover" data-popover aria-hidden="true" class="w-40">
<div role="menu" id="dropdown-checkboxes-menu" aria-labelledby="dropdown-checkboxes-trigger">
<div role="group" aria-labelledby="dropdown-checkboxes-label">
<div role="heading" id="dropdown-checkboxes-label">Appearance</div>
@@ -219,42 +257,63 @@ Basecoat dropdown menus are inline-positioned relative to the `.dropdown-menu` w
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
<span>Status Bar</span>
<kbd>⌘S</kbd>
</div>
<div role="menuitemcheckbox" aria-checked="false">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
<span>Activity Bar</span>
<kbd>⌘A</kbd>
Status Bar
</div>
<div role="menuitemcheckbox" aria-checked="false" aria-disabled="true">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
<span>Panel</span>
<kbd>⌘P</kbd>
Activity Bar
</div>
<div role="menuitemcheckbox" aria-checked="false">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
Panel
</div>
</div>
</div>
</div>
</div>
<script>
(() => {
const dropdownMenu = document.querySelector("#dropdown-checkboxes");
const checkboxes = dropdownMenu.querySelectorAll('[role="menuitemcheckbox"]');
checkboxes.forEach((checkbox) => {
checkbox.addEventListener("click", () => {
if (checkbox.getAttribute("aria-disabled") === "true") return;
checkbox.setAttribute("aria-checked", checkbox.getAttribute("aria-checked") !== "true");
});
});
})();
</script>
</Preview>
### Radio Group
### Checkboxes Icons
<Preview>
<div id="dropdown-checkboxes-icons" class="dropdown-menu">
<button type="button" id="dropdown-checkboxes-icons-trigger" aria-haspopup="menu" aria-controls="dropdown-checkboxes-icons-menu" aria-expanded="false" class="btn" data-variant="outline">Notifications</button>
<div id="dropdown-checkboxes-icons-popover" data-popover aria-hidden="true" class="w-48">
<div role="menu" id="dropdown-checkboxes-icons-menu" aria-labelledby="dropdown-checkboxes-icons-trigger">
<div role="group" aria-labelledby="dropdown-checkboxes-icons-label">
<div role="heading" id="dropdown-checkboxes-icons-label">Notification Preferences</div>
<div role="menuitemcheckbox" aria-checked="true">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
<svg class="lucide lucide-mail" 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"><path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" /><rect x="2" y="4" width="20" height="16" rx="2" /></svg>
Email notifications
</div>
<div role="menuitemcheckbox" aria-checked="false">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
<svg class="lucide lucide-message-square" 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"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" /></svg>
SMS notifications
</div>
<div role="menuitemcheckbox" aria-checked="true">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
<svg class="lucide lucide-bell" 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"><path d="M10.268 21a2 2 0 0 0 3.464 0" /><path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></svg>
Push notifications
</div>
</div>
</div>
</div>
</div>
</Preview>
### Radio group
<Preview>
<div id="dropdown-radio-group" class="dropdown-menu">
@@ -285,33 +344,103 @@ Basecoat dropdown menus are inline-positioned relative to the `.dropdown-menu` w
</div>
</div>
</div>
<script>
(() => {
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>
</Preview>
### Radio Icons
<Preview>
<div id="dropdown-radio-icons" class="dropdown-menu">
<button type="button" id="dropdown-radio-icons-trigger" aria-haspopup="menu" aria-controls="dropdown-radio-icons-menu" aria-expanded="false" class="btn" data-variant="outline">Payment Method</button>
<div id="dropdown-radio-icons-popover" data-popover aria-hidden="true" class="min-w-56">
<div role="menu" id="dropdown-radio-icons-menu" aria-labelledby="dropdown-radio-icons-trigger">
<div role="group" aria-labelledby="dropdown-radio-icons-label">
<div role="heading" id="dropdown-radio-icons-label">Select Payment Method</div>
<div role="menuitemradio" aria-checked="true">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
<svg class="lucide lucide-credit-card" 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"><rect width="20" height="14" x="2" y="5" rx="2" /><line x1="2" x2="22" y1="10" y2="10" /></svg>
Credit Card
</div>
<div role="menuitemradio" aria-checked="false">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
<svg class="lucide lucide-wallet" 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"><path d="M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1" /><path d="M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4" /></svg>
PayPal
</div>
<div role="menuitemradio" aria-checked="false">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
<svg class="lucide lucide-building-2" 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"><path d="M6 22V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18Z" /><path d="M6 12H4a2 2 0 0 0-2 2v8h20v-8a2 2 0 0 0-2-2h-2" /><path d="M10 6h4" /><path d="M10 10h4" /><path d="M10 14h4" /><path d="M10 18h4" /></svg>
Bank Transfer
</div>
</div>
</div>
</div>
</div>
</Preview>
### Destructive
<Preview>
<div id="dropdown-destructive" class="dropdown-menu">
<button type="button" id="dropdown-destructive-trigger" aria-haspopup="menu" aria-controls="dropdown-destructive-menu" aria-expanded="false" class="btn" data-variant="outline">Open</button>
<button type="button" id="dropdown-destructive-trigger" aria-haspopup="menu" aria-controls="dropdown-destructive-menu" aria-expanded="false" class="btn" data-variant="outline">Actions</button>
<div id="dropdown-destructive-popover" data-popover aria-hidden="true" class="min-w-32">
<div role="menu" id="dropdown-destructive-menu" aria-labelledby="dropdown-destructive-trigger">
<div role="menuitem">Edit</div>
<div role="menuitem">Duplicate</div>
<div role="group">
<div role="menuitem">
<svg class="lucide lucide-pencil" 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"><path d="M12 20h9" /><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4Z" /></svg>
Edit
</div>
<div role="menuitem">
<svg class="lucide lucide-share" 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"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" /><polyline points="16 6 12 2 8 6" /><line x1="12" x2="12" y1="2" y2="15" /></svg>
Share
</div>
</div>
<hr role="separator" />
<div role="menuitem" data-variant="destructive">
<svg class="lucide lucide-trash-2" 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"><path d="M10 11v6" /><path d="M14 11v6" /><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /><path d="M3 6h18" /><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></svg>
Delete
<div role="group">
<div role="menuitem" data-variant="destructive">
<svg class="lucide lucide-trash" 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"><path d="M3 6h18" /><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /></svg>
Delete
</div>
</div>
</div>
</div>
</div>
</Preview>
### Avatar
<Preview>
<div id="dropdown-avatar" class="dropdown-menu">
<button type="button" id="dropdown-avatar-trigger" aria-haspopup="menu" aria-controls="dropdown-avatar-menu" aria-expanded="false" class="btn rounded-full" data-variant="ghost" data-size="icon">
<span class="avatar">
<img src="https://github.com/shadcn.png" alt="shadcn" />
<span>LR</span>
</span>
</button>
<div id="dropdown-avatar-popover" data-popover aria-hidden="true" data-align="end">
<div role="menu" id="dropdown-avatar-menu" aria-labelledby="dropdown-avatar-trigger">
<div role="group">
<div role="menuitem">
<svg class="lucide lucide-badge-check" 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"><path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" /><path d="m9 12 2 2 4-4" /></svg>
Account
</div>
<div role="menuitem">
<svg class="lucide lucide-credit-card" 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"><rect width="20" height="14" x="2" y="5" rx="2" /><line x1="2" x2="22" y1="10" y2="10" /></svg>
Billing
</div>
<div role="menuitem">
<svg class="lucide lucide-bell" 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"><path d="M10.268 21a2 2 0 0 0 3.464 0" /><path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></svg>
Notifications
</div>
</div>
<hr role="separator" />
<div role="menuitem">
<svg class="lucide lucide-log-out" 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"><path d="m16 17 5-5-5-5" /><path d="M21 12H9" /><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" /></svg>
Sign Out
</div>
</div>
</div>
@@ -325,20 +454,71 @@ Dropdown menus support document direction. Set `dir="rtl"` on the dropdown root
<Preview>
<div dir="rtl">
<div id="dropdown-rtl" class="dropdown-menu">
<button type="button" id="dropdown-rtl-trigger" aria-haspopup="menu" aria-controls="dropdown-rtl-menu" aria-expanded="false" class="btn" data-variant="outline">فتح</button>
<div id="dropdown-rtl-popover" data-popover aria-hidden="true" class="min-w-32" data-align="end">
<button type="button" id="dropdown-rtl-trigger" aria-haspopup="menu" aria-controls="dropdown-rtl-menu" aria-expanded="false" class="btn" data-variant="outline">افتح القائمة</button>
<div id="dropdown-rtl-popover" data-popover aria-hidden="true" class="w-36" data-align="end">
<div role="menu" id="dropdown-rtl-menu" aria-labelledby="dropdown-rtl-trigger">
<div role="heading" id="dropdown-rtl-label">الحساب</div>
<div role="menuitem">
<span>الملف الشخصي</span>
<kbd>⌘P</kbd>
</div>
<div role="menuitem">
<span>الإعدادات</span>
<kbd>⌘S</kbd>
<div role="group" aria-labelledby="dropdown-rtl-team">
<div role="heading" id="dropdown-rtl-team">الفريق</div>
<div role="menuitem">الفريق</div>
<div role="menuitem">
<span>فريق جديد</span>
<kbd>⌘+T</kbd>
</div>
</div>
<hr role="separator" />
<div role="menuitem" data-variant="destructive">حذف</div>
<div role="group" aria-labelledby="dropdown-rtl-view">
<div role="heading" id="dropdown-rtl-view">عرض</div>
<div role="menuitemcheckbox" aria-checked="true">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
شريط الحالة
</div>
<div role="menuitemcheckbox" aria-checked="false">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
شريط النشاط
</div>
<div role="menuitemcheckbox" aria-checked="false">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
اللوحة
</div>
</div>
<hr role="separator" />
<div role="group" aria-labelledby="dropdown-rtl-position">
<div role="heading" id="dropdown-rtl-position">الموضع</div>
<div role="menuitemradio" aria-checked="false">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
أعلى
</div>
<div role="menuitemradio" aria-checked="true">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
أسفل
</div>
<div role="menuitemradio" aria-checked="false">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
يمين
</div>
<div role="menuitemradio" aria-checked="false">
<span data-indicator>
<svg class="lucide lucide-check" 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"><path d="M20 6 9 17l-5-5" /></svg>
</span>
يسار
</div>
</div>
<hr role="separator" />
<div role="group">
<div role="menuitem" data-variant="destructive">تسجيل الخروج</div>
</div>
</div>
</div>
</div>
+43 -14
View File
@@ -14,7 +14,7 @@
<button class="btn">Create Project</button>
<button class="btn" data-variant="outline">Import Project</button>
</div>
<a href="#" class="btn text-muted-foreground" data-variant="link">
<a href="#" class="btn text-muted-foreground" data-variant="link" data-size="sm">
Learn More
<svg class="lucide lucide-arrow-up-right" 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" ><path d="M7 7h10v10" /><path d="M7 17 17 7" /></svg>
</a>
@@ -40,7 +40,7 @@ Add `class="empty"` to a semantic container. The component styles common childre
<button class="btn">Create Project</button>
<button class="btn" data-variant="outline">Import Project</button>
</div>
<a href="#" class="btn text-muted-foreground" data-variant="link">
<a href="#" class="btn text-muted-foreground" data-variant="link" data-size="sm">
Learn More
<svg class="lucide lucide-arrow-up-right" 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" ><path d="M7 7h10v10" /><path d="M7 17 17 7" /></svg>
</a>
@@ -77,8 +77,8 @@ Add `class="empty"` to a semantic container. The component styles common childre
### Outline
<Preview>
<section class="empty border">
<Preview class="w-full">
<section class="empty border border-dashed">
<header>
<figure>
<svg class="lucide lucide-cloud" 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" ><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z" /></svg>
@@ -92,16 +92,36 @@ Add `class="empty"` to a semantic container. The component styles common childre
</section>
</Preview>
### Background
<Preview class="w-full">
<section class="empty h-full bg-muted/30">
<header>
<figure>
<svg class="lucide lucide-bell" 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"><path d="M10.268 21a2 2 0 0 0 3.464 0" /><path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></svg>
</figure>
<h3>No Notifications</h3>
<p class="max-w-xs text-pretty">You're all caught up. New notifications will appear here.</p>
</header>
<footer>
<button class="btn" data-variant="outline">
<svg class="lucide lucide-refresh-ccw" 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"><path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" /><path d="M3 3v5h5" /><path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16" /><path d="M16 16h5v5" /></svg>
Refresh
</button>
</footer>
</section>
</Preview>
### Avatar
<Preview>
<section class="empty">
<header>
<figure class="size-12 rounded-full">
<img alt="@shadcn" src="https://github.com/shadcn.png" />
<img class="grayscale" 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>
<p>This user is currently offline. You can leave a message to notify them or try again later.</p>
</header>
<footer>
<button class="btn" data-size="sm">Leave Message</button>
@@ -109,15 +129,15 @@ Add `class="empty"` to a semantic container. The component styles common childre
</section>
</Preview>
### Avatar Group
### Avatar group
<Preview>
<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" />
<img alt="@maxleiter" src="https://github.com/maxleiter.png" />
<img alt="@evilrabbit" src="https://github.com/evilrabbit.png" />
</figure>
<h3>No Team Members</h3>
<p>Invite your team to collaborate on this project.</p>
@@ -131,7 +151,7 @@ Add `class="empty"` to a semantic container. The component styles common childre
</section>
</Preview>
### Input Group
### Input group
<Preview>
<section class="empty">
@@ -141,9 +161,11 @@ Add `class="empty"` to a semantic container. The component styles common childre
</header>
<footer>
<div class="input-group sm:w-3/4">
<svg class="lucide lucide-search" 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" ><path d="m21 21-4.34-4.34" /><circle cx="11" cy="11" r="8" /></svg>
<input type="search" placeholder="Try searching for pages..." />
<kbd class="kbd">/</kbd>
<span data-align="start" aria-hidden="true">
<svg class="lucide lucide-search" 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" ><path d="m21 21-4.34-4.34" /><circle cx="11" cy="11" r="8" /></svg>
</span>
<span data-align="end"><kbd class="kbd">/</kbd></span>
</div>
<p>Need help? <a href="#">Contact support</a></p>
</footer>
@@ -159,10 +181,17 @@ Add `class="empty"` to a semantic container. The component styles common childre
<svg class="lucide lucide-folder-code" 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" ><path d="M10 10.5 8 13l2 2.5" /><path d="m14 10.5 2 2.5-2 2.5" /><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2z" /></svg>
</figure>
<h3>لا توجد مشاريع بعد</h3>
<p>ابدأ بإنشاء مشروعك الأول.</p>
<p>لم تقم بإنشاء أي مشاريع بعد. ابدأ بإنشاء مشروعك الأول.</p>
</header>
<footer>
<button class="btn" data-size="sm">إنشاء مشروع</button>
<div class="flex flex-row justify-center gap-2">
<button class="btn">إنشاء مشروع</button>
<button class="btn" data-variant="outline">استيراد مشروع</button>
</div>
<a href="#" class="btn text-muted-foreground" data-variant="link" data-size="sm">
تعرف على المزيد
<svg class="lucide lucide-arrow-up-right rtl:rotate-270" data-icon="inline-end" 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" ><path d="M7 7h10v10" /><path d="M7 17 17 7" /></svg>
</a>
</footer>
</section>
</Preview>
+5 -22
View File
@@ -1,7 +1,7 @@
# Field
<Preview>
<form class="w-full max-w-md">
<Preview class="w-full max-w-md">
<form>
<div role="group" aria-label="Checkout fields" class="fieldset">
<fieldset class="fieldset">
<legend>Payment Method</legend>
@@ -186,23 +186,6 @@ Use `class="field"` for one control and `class="fieldset"` for related controls.
<p id="price-range-description">Set your maximum budget <span id="price-range-output">(up to $800)</span>.</p>
<input id="price-range" type="range" min="0" max="1000" step="10" value="800" aria-describedby="price-range-description" class="input" />
</div>
<script>
(() => {
const input = document.getElementById("price-range");
const output = document.getElementById("price-range-output");
if (!input || !output) return;
const updateRange = () => {
const min = Number(input.min || 0);
const max = Number(input.max || 100);
const value = Number(input.value || 0);
const percent = max === min ? 0 : ((value - min) / (max - min)) * 100;
input.style.setProperty("--slider-value", `${percent}%`);
output.textContent = `(up to $${value})`;
};
input.addEventListener("input", updateRange);
updateRange();
})();
</script>
</Preview>
### Fieldset
@@ -304,7 +287,7 @@ Use `class="field"` for one control and `class="fieldset"` for related controls.
</div>
</Preview>
### Choice Card
### Choice card
Wrap a `.field` inside a `<label>` to create selectable field cards. This works with radio, checkbox, and switch controls.
@@ -335,7 +318,7 @@ Wrap a `.field` inside a `<label>` to create selectable field cards. This works
</fieldset>
</Preview>
### Field Group
### Field group
Stack fields inside a group and use `.field-separator` to divide sections.
@@ -371,7 +354,7 @@ Stack fields inside a group and use `.field-separator` to divide sections.
</div>
</Preview>
### Responsive Layout
### Responsive layout
Use `data-orientation="responsive"` to stack by default and switch to horizontal in wider containers.
+5 -5
View File
@@ -49,7 +49,7 @@ Add `class="input-group"` to the root. Native controls stay semantic and become
Use `data-align="start|end"` on addon wrappers to position them inline. Use `data-orientation="vertical"` with `header` or `footer` for block placement.
### inline-start
### Inline start
<Preview class="w-full max-w-sm">
<div class="field max-w-sm">
@@ -64,7 +64,7 @@ Use `data-align="start|end"` on addon wrappers to position them inline. Use `dat
</div>
</Preview>
### inline-end
### Inline end
<Preview class="w-full max-w-sm">
<div class="field max-w-sm">
@@ -79,7 +79,7 @@ Use `data-align="start|end"` on addon wrappers to position them inline. Use `dat
</div>
</Preview>
### block-start
### Block start
<Preview class="w-full max-w-sm">
<div role="group" aria-label="Block start input group examples" class="fieldset max-w-sm">
@@ -110,7 +110,7 @@ Use `data-align="start|end"` on addon wrappers to position them inline. Use `dat
</div>
</Preview>
### block-end
### Block end
<Preview class="w-full max-w-sm">
<div role="group" aria-label="Block end input group examples" class="fieldset max-w-sm">
@@ -338,7 +338,7 @@ Use `data-align="start|end"` on addon wrappers to position them inline. Use `dat
</div>
</Preview>
### Custom Input
### Custom input
<Preview class="w-full max-w-sm">
<div class="input-group" data-orientation="vertical">
+15 -15
View File
@@ -27,17 +27,17 @@ Use native HTML attributes for behavior: `disabled` for disabled inputs, `requir
Use `field` with a label and description to create an accessible field.
<Preview class="w-full max-w-xs">
<div role="group" class="field w-full max-w-xs">
<div role="group" class="field">
<label for="input-field">Email</label>
<input id="input-field" type="email" placeholder="m@example.com" />
<p>Enter your email address.</p>
</div>
</Preview>
### Field Group
### Field group
<Preview class="w-full max-w-xs">
<div class="grid w-full max-w-xs gap-4">
<div class="grid gap-4">
<div role="group" class="field">
<label for="input-first-name">First name</label>
<input id="input-first-name" type="text" placeholder="Jane" />
@@ -58,7 +58,7 @@ Use `field` with a label and description to create an accessible field.
### Invalid
<Preview class="w-full max-w-xs">
<div role="group" class="field w-full max-w-xs">
<div role="group" class="field">
<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>
@@ -74,7 +74,7 @@ Use `field` with a label and description to create an accessible field.
### Inline
<Preview class="w-full max-w-xs">
<div class="flex w-full max-w-xs items-center gap-2">
<div class="flex items-center gap-2">
<input class="input" type="search" placeholder="Search..." />
<button type="submit" class="btn">Search</button>
</div>
@@ -82,8 +82,8 @@ Use `field` with a label and description to create an accessible field.
### Grid
<Preview class="w-full max-w-xl">
<div class="grid w-full gap-4 md:grid-cols-2">
<Preview class="w-full max-w-sm">
<div class="grid 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" />
@@ -93,7 +93,7 @@ Use `field` with a label and description to create an accessible field.
### Required
<Preview class="w-full max-w-xs">
<div role="group" class="field w-full max-w-xs">
<div role="group" class="field">
<label for="input-required">Username <span class="text-destructive">*</span></label>
<input id="input-required" type="text" placeholder="hunvreus" required />
</div>
@@ -102,7 +102,7 @@ Use `field` with a label and description to create an accessible field.
### Badge
<Preview class="w-full max-w-xs">
<div role="group" class="field w-full max-w-xs">
<div role="group" class="field">
<label for="input-badge" class="flex w-full items-center gap-2">
Email
<span class="badge ml-auto" data-variant="secondary">Recommended</span>
@@ -111,12 +111,12 @@ Use `field` with a label and description to create an accessible field.
</div>
</Preview>
### Input Group
### Input group
Use the [Input Group](/components/input-group/) patterns to add icons, text, or buttons inside an input.
<Preview class="w-full max-w-xs">
<div class="relative w-full max-w-xs">
<div class="relative">
<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">
<svg class="lucide lucide-search" 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" ><path d="m21 21-4.34-4.34" /><circle cx="11" cy="11" r="8" /></svg>
@@ -124,12 +124,12 @@ Use the [Input Group](/components/input-group/) patterns to add icons, text, or
</div>
</Preview>
### Button Group
### Button group
Use [Button Group](/components/button-group/) when an input should be grouped with adjacent buttons.
<Preview class="w-full max-w-xs">
<div role="group" aria-label="Search" class="button-group w-full max-w-xs">
<div role="group" aria-label="Search" class="button-group">
<input class="input" type="search" placeholder="Search..." />
<button type="submit" class="btn" data-variant="outline" data-size="icon" aria-label="Search">
<svg class="lucide lucide-search" 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" ><path d="m21 21-4.34-4.34" /><circle cx="11" cy="11" r="8" /></svg>
@@ -140,7 +140,7 @@ Use [Button Group](/components/button-group/) when an input should be grouped wi
### Form
<Preview class="w-full max-w-sm">
<form class="w-full max-w-sm space-y-6">
<form class="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 />
@@ -159,7 +159,7 @@ Use [Button Group](/components/button-group/) when an input should be grouped wi
Inputs support right-to-left layouts through native browser direction handling. Add `dir="rtl"` to the input, field, or an ancestor.
<Preview class="w-full max-w-xs">
<div role="group" class="field w-full max-w-xs" dir="rtl">
<div role="group" class="field" dir="rtl">
<label for="input-rtl-api-key">مفتاح API</label>
<input id="input-rtl-api-key" type="password" placeholder="sk-..." />
<p>مفتاح API الخاص بك مشفر ومخزن بأمان.</p>
+1 -1
View File
@@ -56,7 +56,7 @@ Keyboard labels are direction-neutral. Surrounding spacing should use logical ut
</div>
</Preview>
### Input Group
### Input group
<Preview>
<div class="relative w-full max-w-sm">
+1 -1
View File
@@ -174,7 +174,7 @@ Basecoat popovers are inline-positioned relative to the `.popover` wrapper. This
</div>
</Preview>
### With Form
### With form
<Preview>
<div id="popover-form" class="popover">
+8 -8
View File
@@ -1,7 +1,7 @@
# Progress
<Preview class="w-full max-w-sm">
<div class="progress" role="progressbar" aria-label="Loading" aria-valuenow="66" aria-valuemin="0" aria-valuemax="100">
<Preview>
<div class="progress max-w-sm" role="progressbar" aria-label="Loading" aria-valuenow="66" aria-valuemin="0" aria-valuemax="100">
<span style="width: 66%"></span>
</div>
</Preview>
@@ -29,11 +29,11 @@ Add `class="progress"` and `role="progressbar"` to the root. Use one child `<spa
### Label
<Preview class="w-full max-w-sm">
<div class="field">
<div class="flex items-center gap-3">
<Preview>
<div class="field max-w-sm">
<div class="flex items-center gap-3 text-sm">
<label id="progress-label">Upload progress</label>
<output class="text-muted-foreground ms-auto text-sm tabular-nums">66%</output>
<output class="ms-auto tabular-nums">66%</output>
</div>
<div class="progress" role="progressbar" aria-labelledby="progress-label" aria-valuenow="66" aria-valuemin="0" aria-valuemax="100">
<span style="width: 66%"></span>
@@ -45,8 +45,8 @@ Add `class="progress"` and `role="progressbar"` to the root. Use one child `<spa
Progress uses logical direction. Set `dir="rtl"` on the progress root or a parent to fill from the right.
<Preview class="w-full max-w-sm">
<div dir="rtl" class="progress" role="progressbar" aria-label="تحميل" aria-valuenow="66" aria-valuemin="0" aria-valuemax="100">
<Preview>
<div dir="rtl" class="progress max-w-sm" role="progressbar" aria-label="تحميل" aria-valuenow="66" aria-valuemin="0" aria-valuemax="100">
<span style="width: 66%"></span>
</div>
</Preview>
+1 -1
View File
@@ -66,7 +66,7 @@ Add `class="input"` to radio inputs. Use a shared `name` attribute for mutually
</div>
</Preview>
### Choice Card
### Choice card
Wrap a `.field` in a native `<label>` when the whole card should select the option.
+1 -1
View File
@@ -79,7 +79,7 @@ Add a spinner to a badge to indicate a loading state. Place the spinner before o
</div>
</Preview>
### Input Group
### Input group
<Preview class="w-full max-w-md">
<div class="flex w-full max-w-md flex-col gap-4">
+1 -1
View File
@@ -38,7 +38,7 @@ Add `class="input"` and `role="switch"` to a native checkbox. Use a native `<lab
</div>
</Preview>
### Choice Card
### Choice card
Use a wrapping `<label>` when the whole card should toggle the switch.
+2 -2
View File
@@ -37,7 +37,7 @@ Basecoat tooltips are CSS-only and text-only. This intentionally differs from sh
</div>
</Preview>
### Icon Button
### Icon button
<Preview>
<button class="btn" data-variant="ghost" data-size="icon" data-tooltip="Additional information" aria-label="Info">
@@ -45,7 +45,7 @@ Basecoat tooltips are CSS-only and text-only. This intentionally differs from sh
</button>
</Preview>
### Disabled Button
### Disabled button
<Preview>
<span class="inline-block" data-tooltip="This feature is currently unavailable">
+4
View File
@@ -52,6 +52,10 @@ Or load the Basecoat runtime once, then only the component scripts you use.
</Step>
</Steps>
<Callout title="Icons are not bundled" icon="info">
Basecoat examples use inline Lucide SVGs, but Basecoat does not ship an icon package. Copy SVGs into your markup or install your preferred icon library. See [Customization > Icons](/customization#icons).
</Callout>
### CDN files
| File | Description |
+3
View File
@@ -7,6 +7,7 @@ import miraUrl from "../assets/styles/style-mira.css?url";
import lumaUrl from "../assets/styles/style-luma.css?url";
import seraUrl from "../assets/styles/style-sera.css?url";
import rheaUrl from "../assets/styles/style-rhea.css?url";
import demoInteractionsUrl from "./demo-interactions.js?url";
const styleUrls = {
vega: vegaUrl,
@@ -78,3 +79,5 @@ const styleUrls = {
if (!existing) document.head.appendChild(link);
})();
</script>
<script type="module" src={demoInteractionsUrl}></script>
+106
View File
@@ -0,0 +1,106 @@
import "../../../src/js/basecoat.js";
import "../../../src/js/accordion.js";
const initPriceRange = () => {
const input = document.getElementById("price-range");
const output = document.getElementById("price-range-output");
if (!input || !output || input.dataset.demoRangeInitialized) return;
input.dataset.demoRangeInitialized = "true";
const updateRange = () => {
const min = Number(input.min || 0);
const max = Number(input.max || 100);
const value = Number(input.value || 0);
const percent = max === min ? 0 : ((value - min) / (max - min)) * 100;
input.style.setProperty("--slider-value", `${percent}%`);
output.textContent = `(up to $${value})`;
};
input.addEventListener("input", updateRange);
updateRange();
};
const initCheckboxTables = () => {
document.querySelectorAll("[data-checkbox-table]").forEach((table) => {
if (table.dataset.demoCheckboxTableInitialized) return;
table.dataset.demoCheckboxTableInitialized = "true";
const selectAll = table.querySelector("thead input[type='checkbox']");
const rows = Array.from(table.querySelectorAll("tbody tr"));
const rowCheckboxes = rows
.map((row) => row.querySelector("input[type='checkbox']"))
.filter(Boolean);
const syncRows = () => {
rows.forEach((row) => {
const checkbox = row.querySelector("input[type='checkbox']");
if (checkbox?.checked) {
row.dataset.state = "selected";
} else {
row.removeAttribute("data-state");
}
});
};
const syncSelectAll = () => {
if (!selectAll || rowCheckboxes.length === 0) return;
const checkedCount = rowCheckboxes.filter((checkbox) => checkbox.checked).length;
selectAll.checked = checkedCount === rowCheckboxes.length;
selectAll.indeterminate = checkedCount > 0 && checkedCount < rowCheckboxes.length;
};
const sync = () => {
syncRows();
syncSelectAll();
};
selectAll?.addEventListener("change", () => {
rowCheckboxes.forEach((checkbox) => {
checkbox.checked = selectAll.checked;
});
sync();
});
rowCheckboxes.forEach((checkbox) => {
checkbox.addEventListener("change", sync);
});
sync();
});
};
const initDemos = () => {
initPriceRange();
initCheckboxTables();
};
document.addEventListener("keydown", (event) => {
if (!(event.metaKey || event.ctrlKey) || event.key !== "j") return;
const dialog = document.getElementById("command-basic");
if (!dialog) return;
event.preventDefault();
dialog.open ? dialog.close() : dialog.showModal();
dialog.querySelector("header input")?.focus();
});
document.addEventListener("click", (event) => {
const checkbox = event.target.closest("#dropdown-checkboxes [role='menuitemcheckbox']");
if (checkbox) {
if (checkbox.getAttribute("aria-disabled") === "true") return;
checkbox.setAttribute("aria-checked", checkbox.getAttribute("aria-checked") !== "true");
return;
}
const radio = event.target.closest("#dropdown-radio-group [role='menuitemradio']");
if (radio) {
const group = radio.closest("#dropdown-radio-group");
group?.querySelectorAll("[role='menuitemradio']").forEach((item) => item.setAttribute("aria-checked", "false"));
radio.setAttribute("aria-checked", "true");
}
});
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initDemos, { once: true });
} else {
initDemos();
}