mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-11 04:38:18 +00:00
Prepare 1.0.0-beta.5
This commit is contained in:
@@ -76,7 +76,10 @@
|
||||
- Do not include React-only APIs such as `asChild`.
|
||||
- Do not add "Composition" sections unless they describe stable Basecoat markup.
|
||||
- Do not add API Reference sections for native/CSS-only components unless there is a real Basecoat API.
|
||||
- Structure component docs as: title and preview, `## Usage`, optional `### HTML structure` for stable markup requirements, optional `### JavaScript API` for components with Basecoat JS methods/events/options, then `## Examples`.
|
||||
- Keep `Usage` focused on the minimal working snippet and important Basecoat/upstream differences; put variants, states, RTL, and richer patterns under `Examples`.
|
||||
- Keep snippets minimal and aligned with current behavior.
|
||||
- In HTML snippets, keep text-only elements on one line, such as `<p>Text</p>`; use multiline formatting when the element contains child elements or when line length would hurt readability.
|
||||
- Document intentional differences from upstream near the relevant example.
|
||||
|
||||
## Validation
|
||||
|
||||
@@ -2,6 +2,20 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
## [1.0.0-beta.5] - 2026-06-22
|
||||
|
||||
### Added
|
||||
|
||||
- Added a dedicated Accordion component with native `<details>` markup, single-item JavaScript behavior, `data-multiple`, disabled item handling, style-pack spacing, and docs.
|
||||
- Added a dedicated Breadcrumb component with semantic `nav`/`ol` markup, style-pack visuals, collapsed and dropdown examples, and docs.
|
||||
- Added documented Card action support with `.card-action`.
|
||||
- Added Combobox clear button, popup trigger, and input-group support, with docs aligned to upstream examples.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Combobox single selection reopening immediately after selection and filtering the reopened list to the selected value.
|
||||
- Improved Combobox multiple chips and remove buttons to match style-pack button sizing more closely.
|
||||
|
||||
## [1.0.0-beta.4] - 2026-06-20
|
||||
|
||||
### Changed
|
||||
|
||||
+2
-37
@@ -1,41 +1,6 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import reallySimpleDocs from 'reallysimpledocs/astro';
|
||||
|
||||
const rsdCssPath = (relativePath) =>
|
||||
fileURLToPath(new URL(`./node_modules/reallysimpledocs/src/css/${relativePath}`, import.meta.url));
|
||||
|
||||
function basecoatDocsStyles() {
|
||||
return {
|
||||
name: 'basecoat-docs-styles',
|
||||
hooks: {
|
||||
'astro:config:setup': ({ config }) => {
|
||||
const root = fileURLToPath(config.root);
|
||||
const generatedStyles = path.join(root, '.astro', 'reallysimpledocs', 'styles.css');
|
||||
|
||||
fs.mkdirSync(path.dirname(generatedStyles), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
generatedStyles,
|
||||
[
|
||||
'@import "tailwindcss/theme";',
|
||||
'@import "tailwindcss/utilities";',
|
||||
'@reference "basecoat-css/base";',
|
||||
`@import ${JSON.stringify(rsdCssPath('components.css'))};`,
|
||||
`@import ${JSON.stringify(rsdCssPath('sources.css'))};`,
|
||||
`@import ${JSON.stringify(rsdCssPath('custom.css'))};`,
|
||||
`@import ${JSON.stringify(rsdCssPath('overrides.css'))};`,
|
||||
`@source ${JSON.stringify(fileURLToPath(new URL('./node_modules/reallysimpledocs/src/runtime/**/*.{astro,js,ts}', import.meta.url)))};`,
|
||||
`@source ${JSON.stringify(path.join(root, 'docs/src/**/*.{md,mdx}'))};`,
|
||||
'',
|
||||
].join('\n'),
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
site: process.env.SITE_URL || 'https://basecoatui.com',
|
||||
vite: {
|
||||
@@ -51,13 +16,14 @@ export default defineConfig({
|
||||
docsDir: './docs/src',
|
||||
routeBase: '/',
|
||||
style: 'vega',
|
||||
css: false,
|
||||
components: {
|
||||
Head: './docs/src/site/StyleHead.astro',
|
||||
ContentHeader: './docs/src/site/ContentHeader.astro',
|
||||
},
|
||||
site: {
|
||||
title: 'Basecoat',
|
||||
subtitle: 'v1.0.0-beta.2',
|
||||
subtitle: 'v1.0.0-beta.4',
|
||||
description: 'A components library built with Tailwind CSS that works with any web stack.',
|
||||
url: process.env.SITE_URL || 'https://basecoatui.com',
|
||||
keywords: ['components', 'component library', 'component system', 'UI', 'UI kit', 'shadcn', 'shadcn/ui', 'Tailwind CSS', 'Tailwind', 'CSS', 'HTML', 'Jinja', 'Nunjucks', 'JS', 'JavaScript', 'vanilla JS', 'vanilla JavaScript'],
|
||||
@@ -75,6 +41,5 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
}),
|
||||
basecoatDocsStyles(),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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><section class="accordion"></code></dt>
|
||||
<dd>
|
||||
Root accordion container. Add <code>data-multiple</code> to allow multiple open items.
|
||||
<dl>
|
||||
<dt><code><details open></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><summary></code></dt>
|
||||
<dd>Accordion trigger. Keep the trigger text visible and add an optional trailing icon.</dd>
|
||||
<dt><code><section></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>
|
||||
|
||||
@@ -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><button type="button"></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><dialog class="dialog" id="{ DIALOG_ID }"></code></dt>
|
||||
<dt><code><button type="button" onclick="dialog.showModal()"></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><dialog class="alert-dialog" id="{ DIALOG_ID }"></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><div></code></dt>
|
||||
<dd>
|
||||
Wraps the content of the alert dialog.
|
||||
Dialog content surface.
|
||||
<dl>
|
||||
<dt><code><header></code></dt>
|
||||
<dd>
|
||||
Contains the header for the alert dialog:
|
||||
Alert dialog header.
|
||||
<dl>
|
||||
<dt><code><h2></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><dialog></code>.</dd>
|
||||
<dt><code><p></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><dialog></code>.</dd>
|
||||
<dt><code><figure></code> <span class="badge" data-variant="secondary">Optional</span></dt>
|
||||
<dd>Media area for an icon or image.</dd>
|
||||
<dt><code><h2 id="{ TITLE_ID }"></code></dt>
|
||||
<dd>Alert dialog title. Reference it from <code>aria-labelledby</code>.</dd>
|
||||
<dt><code><p id="{ DESCRIPTION_ID }"></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><section></code> <span class="badge" data-variant="secondary">Optional</span></dt>
|
||||
<dd>For the content.</dd>
|
||||
<dt><code><footer></code> <span class="badge" data-variant="secondary">Optional</span></dt>
|
||||
<dd>Usually contains actions.</dd>
|
||||
<dt><code><footer></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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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><span></code></dt>
|
||||
<dd>Fallback initials or icon.</dd>
|
||||
<dt><code><span data-badge></code> <span class="badge" data-variant="secondary">Optional</span></dt>
|
||||
<dd>Status badge positioned at the block-end inline-end corner.</dd>
|
||||
<dt><code><span class="avatar-badge"></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><span data-count></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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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="[&_[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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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><div class="card"></code></dt>
|
||||
@@ -55,18 +58,20 @@
|
||||
<dl>
|
||||
<dt><code><header></code></dt>
|
||||
<dd>
|
||||
The header of the card.
|
||||
The card header.
|
||||
<dl>
|
||||
<dt><code><h2></code></dt>
|
||||
<dd>The title of the card.</dd>
|
||||
<dt><code><h2></code> or <code><h3></code></dt>
|
||||
<dd>The card title.</dd>
|
||||
<dt><code><p></code> <span class="badge" data-variant="secondary">Optional</span></dt>
|
||||
<dd>The description of the card.</dd>
|
||||
<dd>The card description.</dd>
|
||||
<dt><code><div class="card-action"></code> <span class="badge" data-variant="secondary">Optional</span></dt>
|
||||
<dd>Action or metadata placed in the top-right of the header. Use <code><menu class="card-action"></code> when the content is a list of commands.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code><section></code></dt>
|
||||
<dd>The content of the card.</dd>
|
||||
<dd>The main card content.</dd>
|
||||
<dt><code><footer></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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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><div class="combobox"></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>{ value, label }</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><input type="text" role="combobox"></code></dt>
|
||||
<dd>The editable control. It should include <code>aria-expanded</code>, <code>aria-controls</code>, and <code>aria-autocomplete="list"</code>.</dd>
|
||||
<dt><code><div class="combobox-chips"></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><input type="text" role="combobox"></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><button aria-haspopup="listbox"></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><button data-clear></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><svg class="combobox-trigger-icon"></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><div data-popover></code></dt>
|
||||
<dd>
|
||||
The suggestions popup. Use <code>data-side</code> and <code>data-align</code> like Popover.
|
||||
<dl>
|
||||
<dt><code><div role="listbox"></code></dt>
|
||||
<dd>
|
||||
The suggestions list. Add <code>aria-multiselectable="true"</code> in multiple mode and <code>data-empty</code> for the empty message.
|
||||
<dl>
|
||||
<dt><code><div role="option" data-value="..."></code></dt>
|
||||
<dd>Selectable option. Options can contain custom HTML; use <code>data-label</code> when the visible input should use different text than the rendered content.</dd>
|
||||
<dt><code><div role="group"></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><div role="heading"></code> <span class="badge" data-variant="secondary">Optional</span></dt>
|
||||
<dd>Group heading.</dd>
|
||||
<dt><code><hr role="separator"></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><div role="listbox"></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><div role="option" data-value="..."></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><input type="hidden"></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
@@ -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><dialog class="command-dialog"></code> <span class="badge" data-variant="secondary">Optional</span></dt>
|
||||
<dd>
|
||||
Dialog variant. Wraps one <code><div class="command"></code>. Add <code>aria-label</code> or <code>aria-labelledby</code> for an accessible name.
|
||||
Dialog variant. Wraps one direct child <code><div class="command"></code>. Add <code>aria-label</code> or <code>aria-labelledby</code> for an accessible name.
|
||||
<dl>
|
||||
<dt><code><div class="command"></code></dt>
|
||||
<dd>
|
||||
@@ -149,7 +151,7 @@ The command menu can be used standalone or inside a native dialog. The structure
|
||||
<dt><code><span role="heading" id="{ HEADING_ID }"></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><div role="menuitem"></code> or <code><a role="menuitem"></code></dt>
|
||||
<dd>Selectable command item. Use <code><a></code> for navigation and <code><div></code> for actions. Supports <code>data-filter</code>, <code>data-keywords</code>, <code>data-force</code>, <code>data-keep-command-open</code>, and <code>aria-disabled="true"</code>.</dd>
|
||||
<dd>Selectable command item. Use <code><a></code> for navigation and <code><div></code> for actions. Supports <code>data-filter</code>, <code>data-keywords</code>, <code>data-force</code>, <code>data-keep-command-open</code>, <code>aria-disabled="true"</code>, and <code>data-disabled="true"</code>.</dd>
|
||||
<dt><code><span data-shortcut></code> <span class="badge" data-variant="secondary">Optional</span></dt>
|
||||
<dd>Shortcut hint aligned to the inline end of the item.</dd>
|
||||
<dt><code><span data-indicator></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 & 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>
|
||||
|
||||
@@ -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><dialog class="dialog" id="{ DIALOG_ID }"></code></dt>
|
||||
<dd>
|
||||
Native modal dialog. Add <code>aria-labelledby="{ TITLE_ID }"</code> and <code>aria-describedby="{ DESCRIPTION_ID }"</code> when title and description are present. The macro also adds backdrop-click close handling.
|
||||
Native modal dialog. Add <code>aria-labelledby="{ TITLE_ID }"</code> and <code>aria-describedby="{ DESCRIPTION_ID }"</code> when title and description are present. Add width utilities to the inner <code><div></code> when a dialog needs a custom size. The macro also adds backdrop-click close handling.
|
||||
<dl>
|
||||
<dt><code><div></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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
Generated
+6
-6
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "basecoat",
|
||||
"version": "1.0.0-beta.4",
|
||||
"version": "1.0.0-beta.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "basecoat",
|
||||
"version": "1.0.0-beta.4",
|
||||
"version": "1.0.0-beta.5",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
@@ -41,12 +41,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"astro": "^6.4.6",
|
||||
"basecoat-css": "1.0.0-beta.3",
|
||||
"basecoat-css": "^1.0.0-beta.4",
|
||||
"tailwindcss": "4.1.17"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"astro": "^6.4.6",
|
||||
"basecoat-css": "^1.0.0-beta.3",
|
||||
"basecoat-css": "^1.0.0-beta.4",
|
||||
"tailwindcss": ">=4.1.17 <5"
|
||||
}
|
||||
},
|
||||
@@ -8951,7 +8951,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "basecoat-cli",
|
||||
"version": "1.0.0-beta.4",
|
||||
"version": "1.0.0-beta.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "^13.1.0",
|
||||
@@ -8964,7 +8964,7 @@
|
||||
},
|
||||
"packages/css": {
|
||||
"name": "basecoat-css",
|
||||
"version": "1.0.0-beta.4",
|
||||
"version": "1.0.0-beta.5",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "basecoat",
|
||||
"version": "1.0.0-beta.4",
|
||||
"version": "1.0.0-beta.5",
|
||||
"description": "shadcn/ui for the rest of us",
|
||||
"author": "hunvreus",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "basecoat-cli",
|
||||
"version": "1.0.0-beta.4",
|
||||
"version": "1.0.0-beta.5",
|
||||
"description": "Add Basecoat components to your project",
|
||||
"author": "hunvreus",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "basecoat-css",
|
||||
"version": "1.0.0-beta.4",
|
||||
"version": "1.0.0-beta.5",
|
||||
"description": "Tailwind CSS for Basecoat components",
|
||||
"author": {
|
||||
"name": "hunvreus",
|
||||
|
||||
+227
-111
@@ -1,161 +1,277 @@
|
||||
# RSD Astro Docs Authoring Handoff
|
||||
# Basecoat docs migration to latest ReallySimpleDocs
|
||||
|
||||
## Purpose
|
||||
|
||||
Capture the current thinking for showing rendered Basecoat examples plus source code when ReallySimpleDocs is used as an Astro integration.
|
||||
Migrate the Basecoat docs site to the latest local ReallySimpleDocs integration in `../reallysimpledocs`.
|
||||
|
||||
The next session should decide whether Basecoat keeps its current Nunjucks-generated RSD docs flow, or whether RSD should grow an Astro-native optional examples API for richer docs pages.
|
||||
This plan is for the next agent. It is based on the current Basecoat repo at `/Users/hunvreus/Workspace/basecoat` and the current RSD repo at `/Users/hunvreus/Workspace/reallysimpledocs`.
|
||||
|
||||
## Current state
|
||||
|
||||
- ReallySimpleDocs now ships as an Astro integration package.
|
||||
- RSD content is currently plain Markdown rendered through `marked`.
|
||||
- RSD docs pages are generated by:
|
||||
- `/Users/hunvreus/Workspace/reallysimpledocs/src/runtime/pages/doc.astro`
|
||||
- `/Users/hunvreus/Workspace/reallysimpledocs/src/runtime/components/DocsLayout.astro`
|
||||
- `/Users/hunvreus/Workspace/reallysimpledocs/src/runtime/lib/docs.js`
|
||||
- RSD does not currently support MDX, Astro components inside docs Markdown, or React islands inside docs content.
|
||||
- RSD does load Basecoat CSS/JS for generated docs pages, so raw Basecoat HTML works.
|
||||
- Basecoat already has a richer docs source flow:
|
||||
- source docs under `/Users/hunvreus/Workspace/basecoat/docs/src`
|
||||
- Nunjucks macros under `/Users/hunvreus/Workspace/basecoat/docs/src/_includes/macros`
|
||||
- generated RSD-compatible docs under `/Users/hunvreus/Workspace/basecoat/docs/generated`
|
||||
- generator: `/Users/hunvreus/Workspace/basecoat/scripts/generate-rsd-docs.js`
|
||||
- Basecoat depends on RSD through `package.json`: `reallysimpledocs: "file:../reallysimpledocs"`.
|
||||
- Basecoat docs source is already in `docs/src` and is now mostly `.mdx`.
|
||||
- RSD now supports Markdown and MDX pages from `docsDir`, with default MDX components available without imports.
|
||||
- Basecoat config already points RSD at source docs:
|
||||
- `docsDir: "./docs/src"`
|
||||
- `routeBase: "/"`
|
||||
- `style: "vega"`
|
||||
- `components.Head: "./docs/src/site/StyleHead.astro"`
|
||||
- `components.ContentHeader: "./docs/src/site/ContentHeader.astro"`
|
||||
- Latest RSD no longer has a `js` option. It always inserts managed JavaScript for theme initialization, Basecoat JS, copy-code behavior, copy-page behavior, search, and TOC state.
|
||||
- The old generated-docs flow is no longer the main path. `docs/generated/**` is ignored and should not be hand-edited.
|
||||
|
||||
## Existing Basecoat pattern
|
||||
## Current blocker
|
||||
|
||||
Basecoat currently authors examples with Nunjucks and renders them to portable Markdown/HTML before RSD sees them.
|
||||
`npm run docs:build` currently fails after `npm run build` succeeds.
|
||||
|
||||
Example source:
|
||||
Observed failure:
|
||||
|
||||
```njk
|
||||
{% set code_html %}
|
||||
{{ tabs(
|
||||
id="demo-tabs-with-panels",
|
||||
tabsets=tabsets_demo,
|
||||
main_attrs={ "class": "w-full" }
|
||||
) }}
|
||||
{% endset %}
|
||||
|
||||
{{ code_preview("tabs", code_html | prettyHtml, "max-w-[400px] w-full") }}
|
||||
```text
|
||||
[@tailwindcss/vite:generate:build] Can't resolve
|
||||
/Users/hunvreus/Workspace/basecoat/node_modules/reallysimpledocs/src/css/components.css
|
||||
in /Users/hunvreus/Workspace/basecoat/.astro/reallysimpledocs/styles.css
|
||||
```
|
||||
|
||||
Relevant macro:
|
||||
Cause:
|
||||
|
||||
- `/Users/hunvreus/Workspace/basecoat/docs/src/_includes/macros/code_preview.njk`
|
||||
- `astro.config.mjs` has a custom `basecoatDocsStyles()` integration that writes `.astro/reallysimpledocs/styles.css`.
|
||||
- That custom stylesheet imports private RSD internals that no longer exist in latest RSD:
|
||||
- `node_modules/reallysimpledocs/src/css/components.css`
|
||||
- `node_modules/reallysimpledocs/src/css/sources.css`
|
||||
- Basecoat style-switcher assets also import removed private RSD style files:
|
||||
- `docs/src/assets/styles/style-{vega,nova,maia,lyra,mira,luma,sera,rhea}.css`
|
||||
- each imports `node_modules/reallysimpledocs/src/css/styles/<style>.css`
|
||||
|
||||
That macro renders:
|
||||
Latest RSD no longer owns Basecoat style files. With managed CSS enabled, it imports public `basecoat-css` entrypoints and its own small CSS files:
|
||||
|
||||
- a Basecoat `tabs` component
|
||||
- a Preview tab containing rendered HTML
|
||||
- a Code tab containing escaped source
|
||||
- no React, no MDX, no client hydration beyond Basecoat JS
|
||||
- `basecoat-css/base`
|
||||
- `basecoat-css/styles/<style>`
|
||||
- `reallysimpledocs/css`
|
||||
- `reallysimpledocs/css/styles/<style>`
|
||||
|
||||
This is good for Basecoat because Basecoat is framework-agnostic and already ships Nunjucks/Jinja macros.
|
||||
Because Basecoat needs live style switching, Basecoat docs should set `css: false` and own the full docs stylesheet pipeline. Keep RSD-managed JavaScript on; there is no `js: false` option anymore.
|
||||
|
||||
## Decision so far
|
||||
## Migration target
|
||||
|
||||
Nunjucks is not Astro-native.
|
||||
Basecoat should stop depending on private RSD source CSS paths except, if strictly necessary, RSD's own stable small CSS files.
|
||||
|
||||
For a generic Astro site using RSD, the Astro-native options are:
|
||||
The docs site should use:
|
||||
|
||||
- `.astro` components
|
||||
- MDX for components inside docs
|
||||
- optional framework islands only when needed
|
||||
- Basecoat's own generated style bundles for Basecoat component visuals.
|
||||
- `css: false` in the RSD integration, so Basecoat controls docs CSS and style switching.
|
||||
- RSD-managed JavaScript, always on.
|
||||
- RSD's integration for routing, layout, MDX, search, Markdown exports, `llms.txt`, and `llms-full.txt`.
|
||||
- RSD component overrides for site-specific chrome.
|
||||
- No `site.repository`-style UI metadata. Header actions belong in `ContentHeader`.
|
||||
|
||||
For RSD itself, do not make React the default path. It would pull RSD away from the current lightweight, portable Markdown model.
|
||||
## Work plan
|
||||
|
||||
## Likely best direction
|
||||
### 1. Remove the custom RSD stylesheet writer
|
||||
|
||||
Keep RSD core simple:
|
||||
Edit `astro.config.mjs`.
|
||||
|
||||
- Markdown pages
|
||||
- generated HTML
|
||||
- Basecoat JS
|
||||
- search, Markdown exports, `llms.txt`, and `llms-full.txt`
|
||||
Remove:
|
||||
|
||||
Then add richer authoring as an optional layer.
|
||||
- `fs`, `path`, and `fileURLToPath` imports if they become unused.
|
||||
- `rsdCssPath()`.
|
||||
- `basecoatDocsStyles()`.
|
||||
- `basecoatDocsStyles()` from `integrations`.
|
||||
|
||||
Two viable layers:
|
||||
Do not write `.astro/reallysimpledocs/styles.css` manually. Latest RSD already generates that virtual stylesheet.
|
||||
|
||||
1. **Basecoat-specific generated docs flow**
|
||||
- Keep using Nunjucks macros and `scripts/generate-rsd-docs.js`.
|
||||
- Improve macros as needed.
|
||||
- Output remains plain Markdown/HTML consumed by RSD.
|
||||
- Best for Basecoat itself.
|
||||
Keep the RSD integration options:
|
||||
|
||||
2. **Astro-native optional RSD examples API**
|
||||
- Add optional MDX support later.
|
||||
- Provide components such as `Preview.astro`, `CodeBlock.astro`, `Callout.astro`, maybe `Tabs.astro`.
|
||||
- Prefer file-based examples so source and rendered output stay in sync.
|
||||
|
||||
Preferred Astro-native example shape:
|
||||
|
||||
```mdx
|
||||
import Preview from "reallysimpledocs/components/Preview.astro";
|
||||
import ButtonDemo from "../examples/ButtonDemo.astro";
|
||||
import buttonSource from "../examples/ButtonDemo.astro?raw";
|
||||
|
||||
<Preview code={buttonSource}>
|
||||
<ButtonDemo />
|
||||
</Preview>
|
||||
```js
|
||||
reallySimpleDocs({
|
||||
docsDir: "./docs/src",
|
||||
routeBase: "/",
|
||||
css: false,
|
||||
components: {
|
||||
Head: "./docs/src/site/StyleHead.astro",
|
||||
ContentHeader: "./docs/src/site/ContentHeader.astro",
|
||||
},
|
||||
site: { ... },
|
||||
})
|
||||
```
|
||||
|
||||
This is more Astro-native than Nunjucks, but it requires RSD to support MDX or an Astro content path. It also makes docs less portable than plain Markdown.
|
||||
### 2. Move Basecoat docs CSS to owned style assets
|
||||
|
||||
## Avoid for now
|
||||
Do not let RSD generate the docs stylesheet for Basecoat. Set `css: false`, then make `docs/src/assets/styles/style-*.css` the full docs stylesheets that `StyleHead.astro` swaps.
|
||||
|
||||
- Do not add React just for examples.
|
||||
- Do not make MDX the default content model.
|
||||
- Do not invent directive syntax such as:
|
||||
Do not import removed RSD CSS files from `docs/src/assets/styles/style-*.css`.
|
||||
|
||||
```md
|
||||
::preview{class="max-w-sm"}
|
||||
...
|
||||
::
|
||||
```
|
||||
Recommended target for each style asset:
|
||||
|
||||
RSD uses `marked` today, so directive syntax would require another parser or custom extension. It is not currently supported.
|
||||
```css
|
||||
@import "tailwindcss" source(none);
|
||||
@reference "tailwindcss";
|
||||
@reference "../../../../src/css/base/base.css";
|
||||
@import "../../../../src/css/basecoat-vega.css";
|
||||
@import "reallysimpledocs/css";
|
||||
@import "reallysimpledocs/css/styles/vega";
|
||||
```
|
||||
|
||||
## Open questions
|
||||
Use the matching `basecoat-<style>.css` file for each style.
|
||||
Use the matching `reallysimpledocs/css/styles/<style>` file for RSD's style-specific preview and code-surface rules.
|
||||
|
||||
- Should Basecoat keep source docs in Nunjucks permanently, or move docs authoring to Astro/MDX once RSD is Astro-first?
|
||||
- Should RSD expose `Preview.astro` and related components as public package API?
|
||||
- If RSD supports MDX later, should that be:
|
||||
- an opt-in mode,
|
||||
- a separate integration option,
|
||||
- or a separate package?
|
||||
- How should source-code extraction work?
|
||||
- explicit `code` prop,
|
||||
- file import with `?raw`,
|
||||
- or a build-time plugin that captures source from children?
|
||||
Remove this line from every style asset:
|
||||
|
||||
## Suggested next steps
|
||||
```css
|
||||
@import "../../../../node_modules/reallysimpledocs/src/css/styles/<style>.css";
|
||||
```
|
||||
|
||||
1. Keep Basecoat's existing Nunjucks generator working for the current RSD beta.
|
||||
2. Improve `code_preview.njk` only if the rendered RSD output needs better Basecoat markup or accessibility.
|
||||
3. In RSD, do not add React or MDX until there is a concrete consumer need.
|
||||
4. If richer Astro-native examples become necessary, prototype a small `Preview.astro` with file-based `?raw` source imports.
|
||||
5. Validate any prototype against:
|
||||
- RSD static build
|
||||
- search index output
|
||||
- Markdown exports
|
||||
- `llms.txt` / `llms-full.txt`
|
||||
- Basecoat JS behavior
|
||||
`reallysimpledocs/css` is the public shared RSD CSS export. `reallysimpledocs/css/styles/<style>` provides the matching RSD style-specific CSS without importing Basecoat again.
|
||||
|
||||
## Validation commands
|
||||
Do not reintroduce removed `components.css`, `sources.css`, or `styles/*`.
|
||||
|
||||
For Basecoat generated docs:
|
||||
### 3. Keep the style switcher, but make it compatible with latest RSD
|
||||
|
||||
Files:
|
||||
|
||||
- `docs/src/site/StyleHead.astro`
|
||||
- `docs/src/site/ContentHeader.astro`
|
||||
- `src/pages/index.astro`
|
||||
|
||||
Current behavior:
|
||||
|
||||
- Loads one docs style asset based on `localStorage.styleVariant`.
|
||||
- Adds `data-style-variant` to `<html>`.
|
||||
- Swaps `<link id="docs-style-variant">`.
|
||||
|
||||
Keep this behavior for Basecoat's style demo. With `css: false`, RSD's `style` option is unnecessary for Basecoat docs; Basecoat's style switcher owns the active stylesheet.
|
||||
|
||||
Audit duplication:
|
||||
|
||||
- Make sure the page does not load two competing full Basecoat style bundles in a way that makes non-Vega styles override Vega instead of standing alone.
|
||||
- If duplicates are unavoidable during migration, document the temporary state in this file before handing off.
|
||||
|
||||
### 4. Fix navigation shape only if RSD rejects it
|
||||
|
||||
`docs/src/docs.json` currently uses item objects with `type: "item"`.
|
||||
|
||||
Latest RSD accepts page objects by `slug` and link objects by `url`; it does not require `type: "item"` for page/link entries based on current docs. Do not mass-rewrite navigation unless the build or runtime proves it is needed.
|
||||
|
||||
If there is a failure, normalize entries to:
|
||||
|
||||
```json
|
||||
{ "slug": "installation", "label": "Installation", "icon": "download" }
|
||||
```
|
||||
|
||||
and external links to:
|
||||
|
||||
```json
|
||||
{ "label": "GitHub", "url": "https://github.com/hunvreus/basecoat", "icon": "github" }
|
||||
```
|
||||
|
||||
Note: existing inline SVG icons in `docs.json` may or may not be supported by current RSD's icon pipeline. Verify before changing.
|
||||
|
||||
### 5. Verify MDX docs against current RSD components
|
||||
|
||||
Basecoat docs use RSD components heavily:
|
||||
|
||||
- `Preview`
|
||||
- `Code`
|
||||
- `CodeGroup`
|
||||
- `Callout`
|
||||
- `Steps`
|
||||
- `Step`
|
||||
|
||||
Latest RSD makes common components available by default in MDX. Do not add imports unless a page fails.
|
||||
|
||||
Known special case:
|
||||
|
||||
- `docs/src/components/chart.mdx` imports `../site/chart-examples.js?url`.
|
||||
|
||||
Verify that MDX import still works after CSS fixes.
|
||||
|
||||
### 6. Validate generated outputs and routes
|
||||
|
||||
Run from `/Users/hunvreus/Workspace/basecoat`:
|
||||
|
||||
```bash
|
||||
cd /Users/hunvreus/Workspace/basecoat
|
||||
npm run build
|
||||
npm run docs:build
|
||||
```
|
||||
|
||||
For RSD package behavior:
|
||||
Expected docs outputs include:
|
||||
|
||||
- `/index.html`
|
||||
- component pages under `/components/.../index.html`
|
||||
- `/search-index.json`
|
||||
- per-page Markdown exports such as `/installation.md`
|
||||
- `/llms.txt`
|
||||
- `/llms-full.txt`
|
||||
|
||||
Then run:
|
||||
|
||||
```bash
|
||||
cd /Users/hunvreus/Workspace/reallysimpledocs
|
||||
npm run check
|
||||
npm run docs:dev
|
||||
```
|
||||
|
||||
Browser-check:
|
||||
|
||||
- desktop and mobile widths
|
||||
- search dialog
|
||||
- Markdown/AI links
|
||||
- style switcher for all eight styles
|
||||
- dark mode
|
||||
- component previews using JS behavior:
|
||||
- dropdown menu
|
||||
- select
|
||||
- combobox
|
||||
- command
|
||||
- dialog
|
||||
- tabs
|
||||
- toast
|
||||
- chart
|
||||
|
||||
### 7. Clean up stale docs once build passes
|
||||
|
||||
Update or remove outdated handoff text that says RSD does not support MDX. This file has already been updated for the latest RSD state.
|
||||
|
||||
Do not edit generated docs:
|
||||
|
||||
- `docs/generated/**`
|
||||
- `dist/**`
|
||||
- `packages/*/dist/**`
|
||||
|
||||
If build scripts regenerate committed source CSS entrypoints, use the scripts and review the generated source diffs before committing.
|
||||
|
||||
## Files likely touched
|
||||
|
||||
- `astro.config.mjs`
|
||||
- `docs/src/assets/styles/style-vega.css`
|
||||
- `docs/src/assets/styles/style-nova.css`
|
||||
- `docs/src/assets/styles/style-maia.css`
|
||||
- `docs/src/assets/styles/style-lyra.css`
|
||||
- `docs/src/assets/styles/style-mira.css`
|
||||
- `docs/src/assets/styles/style-luma.css`
|
||||
- `docs/src/assets/styles/style-sera.css`
|
||||
- `docs/src/assets/styles/style-rhea.css`
|
||||
- Maybe `docs/src/site/StyleHead.astro`
|
||||
- Maybe `docs/src/site/ContentHeader.astro`
|
||||
- Maybe `docs/src/docs.json`
|
||||
|
||||
## Do not do
|
||||
|
||||
- Do not restore `basecoatDocsStyles()` by copying newer RSD internals.
|
||||
- Do not import RSD files that are not public or intentionally stable.
|
||||
- Do not add or use `js: false`; latest RSD always manages JavaScript.
|
||||
- Do not reintroduce a generated `docs/generated` pipeline unless explicitly requested.
|
||||
- Do not put GitHub/header actions into `site` metadata.
|
||||
- Do not hand-edit build outputs.
|
||||
|
||||
## Open questions for the implementation agent
|
||||
|
||||
- Should inline SVG icons in `docs/src/docs.json` be kept, or should they move to Lucide icon names plus header/footer overrides?
|
||||
|
||||
## Recommended first patch
|
||||
|
||||
Start with the smallest build-fixing patch:
|
||||
|
||||
1. Delete `basecoatDocsStyles()` from `astro.config.mjs`.
|
||||
2. Add `css: false` to the RSD integration options.
|
||||
3. Remove all `node_modules/reallysimpledocs/src/css/styles/<style>.css` imports from `docs/src/assets/styles/style-*.css`.
|
||||
4. Import Basecoat's own full style bundle in each style asset, plus RSD's own CSS through a public export if available.
|
||||
5. Run `npm run docs:build`.
|
||||
6. If layout CSS is missing, fix that through a public RSD CSS export instead of private source imports.
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ async function build() {
|
||||
|
||||
// Create combined component files
|
||||
console.log('Creating combined component files...');
|
||||
const componentsToCombine = ['basecoat.js', 'command.js', 'combobox.js', 'dropdown-menu.js', 'popover.js', 'range.js', 'select.js', 'sidebar.js', 'tabs.js', 'toast.js'];
|
||||
const componentsToCombine = ['basecoat.js', 'accordion.js', 'command.js', 'combobox.js', 'dropdown-menu.js', 'popover.js', 'range.js', 'select.js', 'sidebar.js', 'tabs.js', 'toast.js'];
|
||||
const componentPaths = componentsToCombine.map(f => path.join(srcJsDir, f));
|
||||
|
||||
// Create non-minified bundle
|
||||
|
||||
@@ -6,9 +6,11 @@ const excludedComponents = new Set(['form']);
|
||||
|
||||
// Keep cascade stable for components whose selectors intentionally compose.
|
||||
const componentOrder = [
|
||||
'accordion',
|
||||
'alert',
|
||||
'avatar',
|
||||
'badge',
|
||||
'breadcrumb',
|
||||
'button',
|
||||
'button-group',
|
||||
'card',
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.269 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/* Components */
|
||||
@import "./components/accordion.css";
|
||||
@import "./components/alert.css";
|
||||
@import "./components/avatar.css";
|
||||
@import "./components/badge.css";
|
||||
@import "./components/breadcrumb.css";
|
||||
@import "./components/button.css";
|
||||
@import "./components/button-group.css";
|
||||
@import "./components/card.css";
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/* Accordion */
|
||||
@layer components {
|
||||
.accordion {
|
||||
@apply flex w-full flex-col;
|
||||
}
|
||||
|
||||
.accordion > details > summary {
|
||||
@apply relative flex w-full flex-1 cursor-pointer list-none items-start justify-between border border-transparent transition-all outline-none;
|
||||
}
|
||||
|
||||
.accordion > details > summary > svg:last-child {
|
||||
@apply pointer-events-none shrink-0 transition-transform;
|
||||
}
|
||||
|
||||
.accordion > details[open] > summary > svg:last-child {
|
||||
@apply rotate-180;
|
||||
}
|
||||
|
||||
.accordion > details[aria-disabled='true'] > summary,
|
||||
.accordion > details > summary[aria-disabled='true'] {
|
||||
@apply pointer-events-none opacity-50;
|
||||
}
|
||||
|
||||
.accordion > details > :not(summary) {
|
||||
@apply overflow-hidden;
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,21 @@
|
||||
.alert > section {
|
||||
@apply grid justify-items-start gap-1;
|
||||
|
||||
:where(p, ul, ol) {
|
||||
@apply m-0;
|
||||
}
|
||||
|
||||
:where(p, ul, ol):not(:last-child) {
|
||||
@apply mb-4;
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply list-inside list-disc;
|
||||
}
|
||||
|
||||
ol {
|
||||
@apply list-inside list-decimal;
|
||||
}
|
||||
}
|
||||
|
||||
.alert > footer {
|
||||
|
||||
@@ -8,27 +8,27 @@
|
||||
@apply absolute inset-0 aspect-square size-full rounded-[inherit] object-cover;
|
||||
}
|
||||
|
||||
.avatar > span:not([data-badge]) {
|
||||
.avatar > span:not(.avatar-badge) {
|
||||
@apply flex size-full items-center justify-center rounded-[inherit] text-sm;
|
||||
}
|
||||
|
||||
.avatar[data-size='sm'] > span:not([data-badge]) {
|
||||
.avatar[data-size='sm'] > span:not(.avatar-badge) {
|
||||
@apply text-xs;
|
||||
}
|
||||
|
||||
.avatar > [data-badge] {
|
||||
.avatar > .avatar-badge {
|
||||
@apply absolute end-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-blend-color ring-2 select-none;
|
||||
}
|
||||
|
||||
.avatar[data-size='sm'] > [data-badge] {
|
||||
.avatar[data-size='sm'] > .avatar-badge {
|
||||
@apply size-2 [&>svg]:hidden;
|
||||
}
|
||||
|
||||
.avatar > [data-badge] {
|
||||
.avatar > .avatar-badge {
|
||||
@apply size-2.5 [&>svg]:size-2;
|
||||
}
|
||||
|
||||
.avatar[data-size='lg'] > [data-badge] {
|
||||
.avatar[data-size='lg'] > .avatar-badge {
|
||||
@apply size-3 [&>svg]:size-2;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/* Breadcrumb */
|
||||
@layer components {
|
||||
.breadcrumb > ol {
|
||||
@apply m-0 flex list-none flex-wrap items-center p-0 break-words;
|
||||
}
|
||||
|
||||
.breadcrumb li {
|
||||
@apply inline-flex items-center;
|
||||
}
|
||||
|
||||
.breadcrumb a {
|
||||
@apply rounded-sm outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50;
|
||||
}
|
||||
|
||||
.breadcrumb [aria-current='page'] {
|
||||
@apply cursor-default;
|
||||
}
|
||||
|
||||
.breadcrumb li[aria-hidden='true'] {
|
||||
@apply select-none;
|
||||
}
|
||||
|
||||
.breadcrumb li > span[aria-hidden='true'] {
|
||||
@apply flex items-center justify-center;
|
||||
}
|
||||
|
||||
.breadcrumb [data-rtl-flip] {
|
||||
@apply rtl:rotate-180;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,10 @@
|
||||
@apply col-start-2 row-span-2 row-start-1 self-start justify-self-end;
|
||||
}
|
||||
|
||||
> header > menu.card-action {
|
||||
@apply m-0 list-none p-0;
|
||||
}
|
||||
|
||||
> footer {
|
||||
@apply flex items-center;
|
||||
}
|
||||
|
||||
@@ -8,18 +8,39 @@
|
||||
@apply w-full min-w-0 outline-hidden placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50;
|
||||
}
|
||||
|
||||
.combobox .input-group input[role='combobox'],
|
||||
.combobox [data-popover] input[role='combobox'] {
|
||||
@apply min-w-0 outline-hidden placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50;
|
||||
}
|
||||
|
||||
.combobox > svg,
|
||||
.combobox-trigger-icon {
|
||||
@apply pointer-events-none absolute shrink-0;
|
||||
}
|
||||
|
||||
.combobox:has(> .combobox-chips) > .combobox-trigger-icon {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.combobox [data-clear] {
|
||||
@apply inline-flex size-6 shrink-0 cursor-default appearance-none items-center justify-center border-0 bg-transparent p-0 text-current disabled:pointer-events-none disabled:opacity-50;
|
||||
}
|
||||
|
||||
.combobox [data-clear][hidden] {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.combobox .input-group:has([data-clear]:not([hidden])) button[aria-haspopup='listbox'] {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.combobox [data-popover] {
|
||||
@apply isolate overflow-hidden;
|
||||
min-width: max(100%, 9rem);
|
||||
}
|
||||
|
||||
.combobox [role='listbox'] {
|
||||
@apply outline-hidden overflow-y-auto;
|
||||
@apply outline-hidden overflow-x-hidden overflow-y-auto;
|
||||
}
|
||||
|
||||
.combobox [role='listbox'] [role='group']:not(:has([role='option']:not([aria-hidden='true']))) {
|
||||
@@ -73,6 +94,6 @@
|
||||
}
|
||||
|
||||
.combobox-chip-remove {
|
||||
@apply inline-flex items-center justify-center;
|
||||
@apply inline-flex shrink-0 items-center justify-center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
> * {
|
||||
> .command {
|
||||
@apply scale-100;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
@apply opacity-0;
|
||||
}
|
||||
|
||||
> * {
|
||||
> .command {
|
||||
@apply scale-95;
|
||||
}
|
||||
}
|
||||
@@ -33,19 +33,19 @@
|
||||
@apply opacity-0 transition-all transition-discrete;
|
||||
}
|
||||
|
||||
> * {
|
||||
@apply fixed top-1/3 left-1/2 z-50 flex max-h-[min(640px,calc(100%_-_2rem))] w-full max-w-[calc(100%_-_2rem)] -translate-x-1/2 translate-y-0 scale-95 flex-col overflow-hidden transition-all sm:max-w-lg;
|
||||
> .command {
|
||||
@apply fixed top-1/3 left-1/2 z-50 flex max-h-[min(640px,calc(100%_-_2rem))] w-full max-w-[calc(100%_-_2rem)] -translate-x-1/2 translate-y-0 scale-95 flex-col overflow-hidden transition-all sm:max-w-sm;
|
||||
}
|
||||
|
||||
> * > :is(button, form[method='dialog']) {
|
||||
> .command > :is(button, form[method='dialog']) {
|
||||
@apply absolute;
|
||||
}
|
||||
|
||||
> * > :is(button, form[method='dialog'] > button) {
|
||||
> .command > :is(button, form[method='dialog'] > button) {
|
||||
@apply inline-flex items-center justify-center opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0;
|
||||
}
|
||||
|
||||
> * > :is(button, form[method='dialog'] > button) > svg:not([class*='size-']) {
|
||||
> .command > :is(button, form[method='dialog'] > button) > svg:not([class*='size-']) {
|
||||
@apply size-4;
|
||||
}
|
||||
}
|
||||
@@ -68,14 +68,20 @@
|
||||
|
||||
.command [role='menu'] {
|
||||
@apply overflow-x-hidden overflow-y-auto outline-hidden;
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.command [role='menuitem'] {
|
||||
@apply aria-hidden:hidden relative flex w-full cursor-default items-center truncate outline-hidden select-none disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0;
|
||||
@apply aria-hidden:hidden relative flex w-full cursor-default items-center outline-hidden select-none disabled:pointer-events-none disabled:opacity-50 data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0;
|
||||
}
|
||||
|
||||
.command [role='menuitem'].active {
|
||||
@apply bg-accent text-accent-foreground;
|
||||
.command [role='menuitem'].active,
|
||||
.command [role='menuitem'][data-selected='true'] {
|
||||
@apply bg-muted text-foreground;
|
||||
}
|
||||
|
||||
.command [role='group'] {
|
||||
@@ -103,7 +109,8 @@
|
||||
}
|
||||
|
||||
.command [role='menuitem'][data-checked='true'] [data-indicator],
|
||||
.command [role='menuitem'][aria-selected='true'] [data-indicator] {
|
||||
.command [role='menuitem'][aria-selected='true'] [data-indicator],
|
||||
.command [role='menuitem'][data-selected='true'] [data-indicator] {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
@@ -124,7 +131,7 @@
|
||||
}
|
||||
|
||||
.command:not([data-command-initialized]) [role='menuitem']:not([aria-disabled='true']):not(:disabled):hover {
|
||||
@apply bg-accent text-accent-foreground;
|
||||
@apply bg-muted text-foreground;
|
||||
}
|
||||
|
||||
.command:has(> header input:not(:placeholder-shown)) [role='separator'] {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* Dialog */
|
||||
@layer components {
|
||||
.dialog {
|
||||
.dialog,
|
||||
.alert-dialog {
|
||||
@apply bg-transparent p-0 text-inherit opacity-0 transition-all transition-discrete backdrop:opacity-0 backdrop:transition-all backdrop:transition-discrete;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
@@ -24,7 +25,9 @@
|
||||
> * {
|
||||
@apply fixed top-1/2 left-1/2 z-50 flex max-h-[calc(100%_-_2rem)] w-full -translate-x-1/2 -translate-y-1/2 scale-95 flex-col transition-all outline-hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.dialog {
|
||||
> * > header {
|
||||
@apply flex flex-col;
|
||||
}
|
||||
@@ -49,4 +52,30 @@
|
||||
@apply size-4;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-dialog {
|
||||
> * > header {
|
||||
@apply grid grid-rows-[auto_1fr] place-items-center text-center sm:place-items-start sm:text-start;
|
||||
}
|
||||
|
||||
> * > header:has(> figure) {
|
||||
@apply grid-rows-[auto_auto_1fr] sm:grid-rows-[auto_1fr];
|
||||
}
|
||||
|
||||
> * > header > figure {
|
||||
@apply m-0 shrink-0 sm:row-span-2 [&_svg]:pointer-events-none [&_svg]:shrink-0;
|
||||
}
|
||||
|
||||
> * > header > figure:has(img) {
|
||||
@apply overflow-hidden [&_img]:size-full [&_img]:object-cover;
|
||||
}
|
||||
|
||||
> * > header:has(> figure) > :is(h2, p) {
|
||||
@apply sm:col-start-2;
|
||||
}
|
||||
|
||||
> * > footer {
|
||||
@apply flex flex-col-reverse sm:flex-row sm:justify-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
}
|
||||
|
||||
.dropdown-menu :is(kbd:last-child, .kbd:last-child):not([data-shortcut]) {
|
||||
@apply ms-8;
|
||||
@apply ps-8;
|
||||
}
|
||||
|
||||
.dropdown-menu [data-indicator] {
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
@layer theme, base, components, utilities;
|
||||
@import "tailwindcss" source("../pages");
|
||||
|
||||
.theme-neutral {
|
||||
--chart-1: oklch(0.87 0 0);
|
||||
--chart-2: oklch(0.556 0 0);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
}
|
||||
|
||||
:root {
|
||||
--landing-demo-surface-radius: 0.375rem;
|
||||
--chart-bar-radius: 6px;
|
||||
}
|
||||
|
||||
[data-style-variant="nova"] {
|
||||
--landing-demo-surface-radius: 0.5rem;
|
||||
}
|
||||
|
||||
[data-style-variant="maia"],
|
||||
[data-style-variant="luma"],
|
||||
[data-style-variant="rhea"] {
|
||||
--landing-demo-surface-radius: 1rem;
|
||||
}
|
||||
|
||||
[data-style-variant="lyra"],
|
||||
[data-style-variant="sera"] {
|
||||
--landing-demo-surface-radius: 0;
|
||||
--chart-bar-radius: 0;
|
||||
}
|
||||
|
||||
.landing-demo-surface {
|
||||
border-radius: var(--landing-demo-surface-radius);
|
||||
}
|
||||
|
||||
.theme-neutral .card > footer,
|
||||
.theme-neutral hr {
|
||||
border-color: var(--border);
|
||||
}
|
||||
|
||||
.landing-demo-chart-bar {
|
||||
border-start-start-radius: var(--chart-bar-radius);
|
||||
border-start-end-radius: var(--chart-bar-radius);
|
||||
}
|
||||
+98
-16
@@ -1,5 +1,26 @@
|
||||
/* Button */
|
||||
/* Accordion */
|
||||
@layer components {
|
||||
.accordion {
|
||||
@apply overflow-hidden rounded-2xl border;
|
||||
}
|
||||
|
||||
.accordion > details {
|
||||
@apply open:bg-muted/50 not-last:border-b;
|
||||
}
|
||||
|
||||
.accordion > details > summary {
|
||||
@apply gap-6 p-4 text-left text-sm font-medium hover:underline;
|
||||
}
|
||||
|
||||
.accordion > details > summary > svg:last-child {
|
||||
@apply text-muted-foreground ms-auto size-4;
|
||||
}
|
||||
|
||||
.accordion > details > :not(summary) {
|
||||
@apply px-4 pt-0 pb-4 text-sm;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.btn {
|
||||
@apply rounded-4xl border border-transparent bg-clip-padding text-sm font-medium focus-visible:border-ring focus-visible:ring-ring/30 focus-visible:ring-[3px] aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 active:not-aria-[haspopup]:translate-y-px [&_svg:not([class*='size-'])]:size-4;
|
||||
}
|
||||
@@ -255,7 +276,7 @@
|
||||
@apply size-10;
|
||||
}
|
||||
|
||||
.avatar > span:not([data-badge]) {
|
||||
.avatar > span:not(.avatar-badge) {
|
||||
@apply bg-muted text-muted-foreground rounded-[inherit];
|
||||
}
|
||||
|
||||
@@ -263,7 +284,7 @@
|
||||
@apply rounded-[inherit];
|
||||
}
|
||||
|
||||
.avatar > [data-badge] {
|
||||
.avatar > .avatar-badge {
|
||||
@apply bg-primary text-primary-foreground ring-background;
|
||||
}
|
||||
|
||||
@@ -309,17 +330,42 @@
|
||||
@apply text-primary underline-offset-4 hover:underline;
|
||||
}
|
||||
|
||||
/* Command */
|
||||
.command {
|
||||
@apply bg-popover text-popover-foreground rounded-4xl p-1;
|
||||
/* Breadcrumb */
|
||||
.breadcrumb > ol {
|
||||
@apply text-muted-foreground gap-1.5 text-sm sm:gap-2.5;
|
||||
}
|
||||
|
||||
.command-dialog > * {
|
||||
@apply rounded-4xl p-0;
|
||||
.breadcrumb li {
|
||||
@apply gap-1.5;
|
||||
}
|
||||
|
||||
.breadcrumb a {
|
||||
@apply transition-colors hover:text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb [aria-current='page'] {
|
||||
@apply font-normal text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb li[aria-hidden='true'] > svg {
|
||||
@apply size-3.5;
|
||||
}
|
||||
|
||||
.breadcrumb li > span[aria-hidden='true'] {
|
||||
@apply size-5 [&>svg]:size-4;
|
||||
}
|
||||
|
||||
/* Command */
|
||||
.command {
|
||||
@apply bg-popover text-popover-foreground rounded-4xl! p-1;
|
||||
}
|
||||
|
||||
.command-dialog > .command {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 rounded-4xl! shadow-xl ring-1 duration-100;
|
||||
}
|
||||
|
||||
.command > header {
|
||||
@apply m-1 mb-0 h-9 gap-2 rounded-3xl bg-input/50 px-3;
|
||||
@apply m-1 mb-0 h-9 gap-0 rounded-3xl bg-input/50 px-3;
|
||||
}
|
||||
|
||||
.command > header svg {
|
||||
@@ -327,7 +373,7 @@
|
||||
}
|
||||
|
||||
.command > header input {
|
||||
@apply h-full text-sm placeholder:text-muted-foreground;
|
||||
@apply ps-1.5 text-sm placeholder:text-muted-foreground;
|
||||
}
|
||||
|
||||
.command [role='menu'] {
|
||||
@@ -355,7 +401,7 @@
|
||||
}
|
||||
|
||||
.command [role='menuitem'].active {
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
}
|
||||
|
||||
.command [data-shortcut] {
|
||||
@@ -378,11 +424,15 @@
|
||||
}
|
||||
|
||||
.combobox [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 max-h-72 rounded-3xl p-1.5 shadow-lg ring-1 duration-100;
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 max-h-72 gap-0 rounded-3xl p-0 shadow-lg ring-1 duration-100;
|
||||
}
|
||||
|
||||
.combobox [role='listbox'] {
|
||||
@apply max-h-[min(18rem,var(--available-height,18rem))] scroll-py-1.5 overflow-y-auto;
|
||||
@apply max-h-[min(18rem,var(--available-height,18rem))] scroll-py-1.5 overflow-y-auto p-1.5;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true']))) {
|
||||
@apply p-0;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@@ -399,7 +449,7 @@
|
||||
|
||||
.combobox [role='option'].active,
|
||||
.combobox [role='option']:focus-visible {
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
}
|
||||
|
||||
.combobox [role='group'] {
|
||||
@@ -421,6 +471,14 @@
|
||||
.combobox-chip-remove {
|
||||
@apply -ms-1 opacity-50 hover:opacity-100 [&_svg]:size-3.5;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > .input-group {
|
||||
@apply m-1.5 mb-0 h-8 w-auto border-input/30 bg-input/50 shadow-none focus-within:border-inherit focus-within:ring-0;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > input[role='combobox'] {
|
||||
@apply mb-1 w-full;
|
||||
}
|
||||
/* Select */
|
||||
*:not(select).select > button {
|
||||
@apply bg-input/50 border-transparent focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-9 data-[size=sm]:h-8 gap-1.5 rounded-3xl border px-3 py-2 text-sm transition-[color,box-shadow,background-color] focus-visible:ring-3 aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4;
|
||||
@@ -473,10 +531,15 @@
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop,
|
||||
.alert-dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/30 backdrop-blur-sm duration-100;
|
||||
}
|
||||
|
||||
.alert-dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 max-w-xs gap-6 rounded-4xl p-6 shadow-xl ring-1 duration-100 sm:max-w-md;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 max-w-[calc(100%-2rem)] gap-6 rounded-4xl p-6 text-sm shadow-xl ring-1 duration-100 sm:max-w-md;
|
||||
}
|
||||
@@ -493,6 +556,15 @@
|
||||
@apply gap-1.5;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header {
|
||||
@apply gap-x-6 gap-y-1.5;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > figure {
|
||||
@apply bg-muted mb-2 inline-flex size-16 items-center justify-center rounded-full [&>svg:not([class*='size-'])]:size-8;
|
||||
}
|
||||
|
||||
.alert-dialog > * > footer,
|
||||
.dialog > * > footer {
|
||||
@apply gap-2;
|
||||
}
|
||||
@@ -502,11 +574,21 @@
|
||||
@apply text-base leading-none font-medium;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > h2,
|
||||
.alert-dialog > * > [data-title] {
|
||||
@apply text-lg font-medium;
|
||||
}
|
||||
|
||||
.dialog > * > header > p,
|
||||
.dialog > * > [data-description] {
|
||||
@apply text-muted-foreground text-sm leading-normal [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > p,
|
||||
.alert-dialog > * > [data-description] {
|
||||
@apply text-muted-foreground text-sm text-balance md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
/* Dropdown Menu */
|
||||
.dropdown-menu > [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 min-w-48 gap-0 overflow-x-hidden overflow-y-auto rounded-3xl p-1.5 shadow-lg ring-1 duration-100;
|
||||
@@ -553,11 +635,11 @@
|
||||
@apply bg-border/50 -mx-1.5 my-1.5 h-px;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-muted-foreground font-sans text-xs tracking-widest;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-accent-foreground;
|
||||
}
|
||||
|
||||
|
||||
+87
-13
@@ -1,5 +1,22 @@
|
||||
/* Button */
|
||||
/* Accordion */
|
||||
@layer components {
|
||||
.accordion > details {
|
||||
@apply not-last:border-b;
|
||||
}
|
||||
|
||||
.accordion > details > summary {
|
||||
@apply focus-visible:ring-ring/50 focus-visible:border-ring rounded-none py-2.5 text-left text-xs font-medium hover:underline focus-visible:ring-1;
|
||||
}
|
||||
|
||||
.accordion > details > summary > svg:last-child {
|
||||
@apply text-muted-foreground ms-auto size-4;
|
||||
}
|
||||
|
||||
.accordion > details > :not(summary) {
|
||||
@apply pt-0 pb-2.5 text-xs;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.btn {
|
||||
@apply rounded-none border border-transparent bg-clip-padding text-xs font-medium focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[1px] aria-invalid:border-destructive aria-invalid:ring-[1px] aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 active:not-aria-[haspopup]:translate-y-px [&_svg:not([class*='size-'])]:size-4;
|
||||
}
|
||||
@@ -247,7 +264,7 @@
|
||||
@apply size-10;
|
||||
}
|
||||
|
||||
.avatar > span:not([data-badge]) {
|
||||
.avatar > span:not(.avatar-badge) {
|
||||
@apply bg-muted text-muted-foreground rounded-[inherit];
|
||||
}
|
||||
|
||||
@@ -255,7 +272,7 @@
|
||||
@apply rounded-[inherit];
|
||||
}
|
||||
|
||||
.avatar > [data-badge] {
|
||||
.avatar > .avatar-badge {
|
||||
@apply bg-primary text-primary-foreground ring-background;
|
||||
}
|
||||
|
||||
@@ -301,17 +318,42 @@
|
||||
@apply text-primary underline-offset-4 hover:underline;
|
||||
}
|
||||
|
||||
/* Breadcrumb */
|
||||
.breadcrumb > ol {
|
||||
@apply text-muted-foreground gap-1.5 text-xs;
|
||||
}
|
||||
|
||||
.breadcrumb li {
|
||||
@apply gap-1;
|
||||
}
|
||||
|
||||
.breadcrumb a {
|
||||
@apply transition-colors hover:text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb [aria-current='page'] {
|
||||
@apply font-normal text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb li[aria-hidden='true'] > svg {
|
||||
@apply size-3.5;
|
||||
}
|
||||
|
||||
.breadcrumb li > span[aria-hidden='true'] {
|
||||
@apply size-5 [&>svg]:size-4;
|
||||
}
|
||||
|
||||
/* Command */
|
||||
.command {
|
||||
@apply bg-popover text-popover-foreground rounded-none;
|
||||
}
|
||||
|
||||
.command-dialog > * {
|
||||
@apply rounded-none p-0;
|
||||
.command-dialog > .command {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 rounded-none ring-1 duration-100;
|
||||
}
|
||||
|
||||
.command > header {
|
||||
@apply h-8 gap-2 border-b bg-input/30 px-2;
|
||||
@apply h-8 gap-0 border-b bg-input/30 px-2;
|
||||
}
|
||||
|
||||
.command > header svg {
|
||||
@@ -319,7 +361,7 @@
|
||||
}
|
||||
|
||||
.command > header input {
|
||||
@apply h-full text-xs placeholder:text-muted-foreground;
|
||||
@apply ps-1.5 text-xs placeholder:text-muted-foreground;
|
||||
}
|
||||
|
||||
.command [role='menu'] {
|
||||
@@ -347,7 +389,7 @@
|
||||
}
|
||||
|
||||
.command [role='menuitem'].active {
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
}
|
||||
|
||||
.command [data-shortcut] {
|
||||
@@ -361,7 +403,7 @@
|
||||
|
||||
/* Combobox */
|
||||
.combobox > input[role='combobox'] {
|
||||
@apply border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-8 rounded-none border bg-transparent py-2 ps-2.5 pe-8 text-xs transition-colors focus-visible:ring-1 aria-invalid:ring-1;
|
||||
@apply border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-none border bg-transparent py-2 ps-2.5 pe-8 text-xs transition-colors focus-visible:ring-1 aria-invalid:ring-1;
|
||||
}
|
||||
|
||||
.combobox > svg,
|
||||
@@ -370,7 +412,7 @@
|
||||
}
|
||||
|
||||
.combobox [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-h-72 rounded-none shadow-md ring-1 duration-100;
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-h-72 gap-0 rounded-none p-0 shadow-md ring-1 duration-100;
|
||||
}
|
||||
|
||||
.combobox [role='listbox'] {
|
||||
@@ -391,7 +433,7 @@
|
||||
|
||||
.combobox [role='option'].active,
|
||||
.combobox [role='option']:focus-visible {
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
}
|
||||
|
||||
.combobox [role='separator'] {
|
||||
@@ -409,6 +451,14 @@
|
||||
.combobox-chip-remove {
|
||||
@apply -ms-1 opacity-50 hover:opacity-100 [&_svg]:size-3.5;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > .input-group {
|
||||
@apply m-1 mb-0 h-8 w-auto border-input/30 bg-input/30 shadow-none focus-within:border-inherit focus-within:ring-0;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > input[role='combobox'] {
|
||||
@apply mb-1 w-full;
|
||||
}
|
||||
/* Select */
|
||||
*:not(select).select > button {
|
||||
@apply border-input dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-8 data-[size=sm]:h-7 gap-1.5 rounded-none data-[size=sm]:rounded-none border bg-transparent py-2 ps-2.5 pe-2 text-xs transition-colors select-none focus-visible:ring-1 aria-invalid:ring-1 [&_svg:not([class*='size-'])]:size-4;
|
||||
@@ -461,10 +511,15 @@
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop,
|
||||
.alert-dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/10 backdrop-blur-xs duration-100;
|
||||
}
|
||||
|
||||
.alert-dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-w-xs gap-4 rounded-none p-4 ring-1 duration-100 sm:max-w-sm;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-w-[calc(100%-2rem)] gap-4 rounded-none p-4 text-xs/relaxed ring-1 duration-100 sm:max-w-sm;
|
||||
}
|
||||
@@ -477,6 +532,15 @@
|
||||
@apply gap-1 text-start;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header {
|
||||
@apply gap-x-4 gap-y-1.5;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > figure {
|
||||
@apply bg-muted mb-2 inline-flex size-10 items-center justify-center rounded-none [&>svg:not([class*='size-'])]:size-6;
|
||||
}
|
||||
|
||||
.alert-dialog > * > footer,
|
||||
.dialog > * > footer {
|
||||
@apply gap-2;
|
||||
}
|
||||
@@ -486,11 +550,21 @@
|
||||
@apply text-sm font-medium;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > h2,
|
||||
.alert-dialog > * > [data-title] {
|
||||
@apply text-sm font-medium;
|
||||
}
|
||||
|
||||
.dialog > * > header > p,
|
||||
.dialog > * > [data-description] {
|
||||
@apply text-muted-foreground text-xs/relaxed [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > p,
|
||||
.alert-dialog > * > [data-description] {
|
||||
@apply text-muted-foreground text-xs/relaxed text-balance md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
/* Dropdown Menu */
|
||||
.dropdown-menu > [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 gap-0 overflow-x-hidden overflow-y-auto rounded-none p-0 shadow-md ring-1 duration-100;
|
||||
@@ -537,11 +611,11 @@
|
||||
@apply bg-border -mx-1 h-px;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-muted-foreground font-sans text-xs tracking-widest;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-accent-foreground;
|
||||
}
|
||||
|
||||
|
||||
+98
-16
@@ -1,5 +1,26 @@
|
||||
/* Button */
|
||||
/* Accordion */
|
||||
@layer components {
|
||||
.accordion {
|
||||
@apply overflow-hidden rounded-2xl border;
|
||||
}
|
||||
|
||||
.accordion > details {
|
||||
@apply open:bg-muted/50 not-last:border-b;
|
||||
}
|
||||
|
||||
.accordion > details > summary {
|
||||
@apply gap-6 p-4 text-left text-sm font-medium hover:underline;
|
||||
}
|
||||
|
||||
.accordion > details > summary > svg:last-child {
|
||||
@apply text-muted-foreground ms-auto size-4;
|
||||
}
|
||||
|
||||
.accordion > details > :not(summary) {
|
||||
@apply px-4 pt-0 pb-4 text-sm;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.btn {
|
||||
@apply rounded-4xl border border-transparent bg-clip-padding text-sm font-medium focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 active:not-aria-[haspopup]:translate-y-px [&_svg:not([class*='size-'])]:size-4;
|
||||
}
|
||||
@@ -247,7 +268,7 @@
|
||||
@apply size-10;
|
||||
}
|
||||
|
||||
.avatar > span:not([data-badge]) {
|
||||
.avatar > span:not(.avatar-badge) {
|
||||
@apply bg-muted text-muted-foreground rounded-[inherit];
|
||||
}
|
||||
|
||||
@@ -255,7 +276,7 @@
|
||||
@apply rounded-[inherit];
|
||||
}
|
||||
|
||||
.avatar > [data-badge] {
|
||||
.avatar > .avatar-badge {
|
||||
@apply bg-primary text-primary-foreground ring-background;
|
||||
}
|
||||
|
||||
@@ -301,17 +322,42 @@
|
||||
@apply text-primary underline-offset-4 hover:underline;
|
||||
}
|
||||
|
||||
/* Command */
|
||||
.command {
|
||||
@apply bg-popover text-popover-foreground rounded-4xl p-1;
|
||||
/* Breadcrumb */
|
||||
.breadcrumb > ol {
|
||||
@apply text-muted-foreground gap-1.5 text-sm sm:gap-2.5;
|
||||
}
|
||||
|
||||
.command-dialog > * {
|
||||
@apply rounded-4xl p-0;
|
||||
.breadcrumb li {
|
||||
@apply gap-1.5;
|
||||
}
|
||||
|
||||
.breadcrumb a {
|
||||
@apply transition-colors hover:text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb [aria-current='page'] {
|
||||
@apply font-normal text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb li[aria-hidden='true'] > svg {
|
||||
@apply size-3.5;
|
||||
}
|
||||
|
||||
.breadcrumb li > span[aria-hidden='true'] {
|
||||
@apply size-5 [&>svg]:size-4;
|
||||
}
|
||||
|
||||
/* Command */
|
||||
.command {
|
||||
@apply bg-popover text-popover-foreground rounded-4xl! p-1;
|
||||
}
|
||||
|
||||
.command-dialog > .command {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 rounded-4xl! ring-1 duration-100;
|
||||
}
|
||||
|
||||
.command > header {
|
||||
@apply m-1 mb-0 h-9 gap-2 rounded-3xl bg-input/30 px-3;
|
||||
@apply m-1 mb-0 h-9 gap-0 rounded-3xl bg-input/30 px-3;
|
||||
}
|
||||
|
||||
.command > header svg {
|
||||
@@ -319,7 +365,7 @@
|
||||
}
|
||||
|
||||
.command > header input {
|
||||
@apply h-full text-sm placeholder:text-muted-foreground;
|
||||
@apply ps-1.5 text-sm placeholder:text-muted-foreground;
|
||||
}
|
||||
|
||||
.command [role='menu'] {
|
||||
@@ -347,7 +393,7 @@
|
||||
}
|
||||
|
||||
.command [role='menuitem'].active {
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
}
|
||||
|
||||
.command [data-shortcut] {
|
||||
@@ -370,11 +416,15 @@
|
||||
}
|
||||
|
||||
.combobox [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 max-h-72 rounded-2xl p-1 shadow-2xl ring-1 duration-100;
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 max-h-72 gap-0 rounded-2xl p-0 shadow-2xl ring-1 duration-100;
|
||||
}
|
||||
|
||||
.combobox [role='listbox'] {
|
||||
@apply max-h-[min(18rem,var(--available-height,18rem))] scroll-py-1 overflow-y-auto;
|
||||
@apply max-h-[min(18rem,var(--available-height,18rem))] scroll-py-1 overflow-y-auto p-1;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true']))) {
|
||||
@apply p-0;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@@ -391,7 +441,7 @@
|
||||
|
||||
.combobox [role='option'].active,
|
||||
.combobox [role='option']:focus-visible {
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
}
|
||||
|
||||
.combobox [role='group'] {
|
||||
@@ -413,6 +463,14 @@
|
||||
.combobox-chip-remove {
|
||||
@apply -ms-1 opacity-50 hover:opacity-100 [&_svg]:size-3.5;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > .input-group {
|
||||
@apply m-1 mb-0 h-9 w-auto border-none bg-input/30 shadow-none focus-within:border-inherit focus-within:ring-0;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > input[role='combobox'] {
|
||||
@apply mb-1 w-full;
|
||||
}
|
||||
/* Select */
|
||||
*:not(select).select > button {
|
||||
@apply border-input bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-9 data-[size=sm]:h-8 gap-1.5 rounded-4xl border px-3 py-2 text-sm transition-colors focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4;
|
||||
@@ -465,10 +523,15 @@
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop,
|
||||
.alert-dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/80 backdrop-blur-xs duration-100;
|
||||
}
|
||||
|
||||
.alert-dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 max-w-xs gap-6 rounded-4xl p-6 ring-1 duration-100 sm:max-w-md;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 max-w-[calc(100%-2rem)] gap-6 rounded-4xl p-6 text-sm ring-1 duration-100 sm:max-w-md;
|
||||
}
|
||||
@@ -481,6 +544,15 @@
|
||||
@apply gap-2;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header {
|
||||
@apply gap-x-6 gap-y-1.5;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > figure {
|
||||
@apply bg-muted mb-2 inline-flex size-16 items-center justify-center rounded-full [&>svg:not([class*='size-'])]:size-8;
|
||||
}
|
||||
|
||||
.alert-dialog > * > footer,
|
||||
.dialog > * > footer {
|
||||
@apply gap-2;
|
||||
}
|
||||
@@ -490,11 +562,21 @@
|
||||
@apply text-base leading-none font-medium;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > h2,
|
||||
.alert-dialog > * > [data-title] {
|
||||
@apply text-lg font-medium;
|
||||
}
|
||||
|
||||
.dialog > * > header > p,
|
||||
.dialog > * > [data-description] {
|
||||
@apply text-muted-foreground text-sm leading-normal [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > p,
|
||||
.alert-dialog > * > [data-description] {
|
||||
@apply text-muted-foreground text-sm text-balance md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
/* Dropdown Menu */
|
||||
.dropdown-menu > [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 min-w-48 gap-0 overflow-x-hidden overflow-y-auto rounded-2xl p-1 shadow-2xl ring-1 duration-100;
|
||||
@@ -541,11 +623,11 @@
|
||||
@apply bg-border/50 -mx-1 my-1 h-px;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-muted-foreground font-sans text-xs tracking-widest;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-accent-foreground;
|
||||
}
|
||||
|
||||
|
||||
+98
-16
@@ -1,5 +1,26 @@
|
||||
/* Button */
|
||||
/* Accordion */
|
||||
@layer components {
|
||||
.accordion {
|
||||
@apply overflow-hidden rounded-md border;
|
||||
}
|
||||
|
||||
.accordion > details {
|
||||
@apply open:bg-muted/50 not-last:border-b;
|
||||
}
|
||||
|
||||
.accordion > details > summary {
|
||||
@apply gap-6 p-2 text-left text-xs/relaxed font-medium hover:underline;
|
||||
}
|
||||
|
||||
.accordion > details > summary > svg:last-child {
|
||||
@apply text-muted-foreground ms-auto size-4;
|
||||
}
|
||||
|
||||
.accordion > details > :not(summary) {
|
||||
@apply px-2 pt-0 pb-4 text-xs/relaxed;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.btn {
|
||||
@apply rounded-md border border-transparent bg-clip-padding text-xs/relaxed font-medium focus-visible:border-ring focus-visible:ring-ring/30 focus-visible:ring-[2px] aria-invalid:border-destructive aria-invalid:ring-[2px] aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 active:not-aria-[haspopup]:translate-y-px [&_svg:not([class*='size-'])]:size-4;
|
||||
}
|
||||
@@ -247,7 +268,7 @@
|
||||
@apply size-10;
|
||||
}
|
||||
|
||||
.avatar > span:not([data-badge]) {
|
||||
.avatar > span:not(.avatar-badge) {
|
||||
@apply bg-muted text-muted-foreground rounded-[inherit];
|
||||
}
|
||||
|
||||
@@ -255,7 +276,7 @@
|
||||
@apply rounded-[inherit];
|
||||
}
|
||||
|
||||
.avatar > [data-badge] {
|
||||
.avatar > .avatar-badge {
|
||||
@apply bg-primary text-primary-foreground ring-background;
|
||||
}
|
||||
|
||||
@@ -301,17 +322,42 @@
|
||||
@apply text-primary underline-offset-4 hover:underline;
|
||||
}
|
||||
|
||||
/* Command */
|
||||
.command {
|
||||
@apply bg-popover text-popover-foreground rounded-xl p-1;
|
||||
/* Breadcrumb */
|
||||
.breadcrumb > ol {
|
||||
@apply text-muted-foreground gap-1.5 text-xs/relaxed;
|
||||
}
|
||||
|
||||
.command-dialog > * {
|
||||
@apply rounded-xl p-0;
|
||||
.breadcrumb li {
|
||||
@apply gap-1;
|
||||
}
|
||||
|
||||
.breadcrumb a {
|
||||
@apply transition-colors hover:text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb [aria-current='page'] {
|
||||
@apply font-normal text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb li[aria-hidden='true'] > svg {
|
||||
@apply size-3.5;
|
||||
}
|
||||
|
||||
.breadcrumb li > span[aria-hidden='true'] {
|
||||
@apply size-4 [&>svg]:size-3.5;
|
||||
}
|
||||
|
||||
/* Command */
|
||||
.command {
|
||||
@apply bg-popover text-popover-foreground rounded-xl! p-1;
|
||||
}
|
||||
|
||||
.command-dialog > .command {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 rounded-xl! ring-1 duration-100;
|
||||
}
|
||||
|
||||
.command > header {
|
||||
@apply m-1 mb-0 h-8 gap-2 rounded-lg bg-input/20 px-2 dark:bg-input/30;
|
||||
@apply m-1 mb-0 h-8 gap-0 rounded-lg bg-input/20 px-2 dark:bg-input/30;
|
||||
}
|
||||
|
||||
.command > header svg {
|
||||
@@ -319,7 +365,7 @@
|
||||
}
|
||||
|
||||
.command > header input {
|
||||
@apply h-full text-xs/relaxed placeholder:text-muted-foreground;
|
||||
@apply ps-1.5 text-xs/relaxed placeholder:text-muted-foreground;
|
||||
}
|
||||
|
||||
.command [role='menu'] {
|
||||
@@ -347,7 +393,7 @@
|
||||
}
|
||||
|
||||
.command [role='menuitem'].active {
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
}
|
||||
|
||||
.command [data-shortcut] {
|
||||
@@ -370,11 +416,15 @@
|
||||
}
|
||||
|
||||
.combobox [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 dark:bg-popover max-h-72 rounded-lg p-1 shadow-md ring-1 duration-100;
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 dark:bg-popover max-h-72 gap-0 rounded-lg p-0 shadow-md ring-1 duration-100;
|
||||
}
|
||||
|
||||
.combobox [role='listbox'] {
|
||||
@apply max-h-[min(18rem,var(--available-height,18rem))] scroll-py-1 overflow-y-auto;
|
||||
@apply max-h-[min(18rem,var(--available-height,18rem))] scroll-py-1 overflow-y-auto p-1;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true']))) {
|
||||
@apply p-0;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@@ -391,7 +441,7 @@
|
||||
|
||||
.combobox [role='option'].active,
|
||||
.combobox [role='option']:focus-visible {
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
}
|
||||
|
||||
.combobox [role='group'] {
|
||||
@@ -413,6 +463,14 @@
|
||||
.combobox-chip-remove {
|
||||
@apply -ms-1 opacity-50 hover:opacity-100 [&_svg]:size-3;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > .input-group {
|
||||
@apply m-1 mb-0 h-7 w-auto border-none bg-input/20 shadow-none focus-within:border-inherit focus-within:ring-0;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > input[role='combobox'] {
|
||||
@apply mb-1 w-full;
|
||||
}
|
||||
/* Select */
|
||||
*:not(select).select > button {
|
||||
@apply border-input bg-input/20 dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-7 data-[size=sm]:h-6 data-[size=sm]:text-[0.625rem] gap-1.5 rounded-md border px-2 py-1.5 text-xs/relaxed transition-colors focus-visible:ring-2 aria-invalid:ring-2 [&_svg:not([class*='size-'])]:size-3.5;
|
||||
@@ -469,10 +527,15 @@
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop,
|
||||
.alert-dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/80 backdrop-blur-xs duration-100;
|
||||
}
|
||||
|
||||
.alert-dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-w-xs gap-3 rounded-xl p-4 ring-1 duration-100 sm:max-w-sm;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-w-[calc(100%-2rem)] gap-4 rounded-xl p-4 text-xs/relaxed ring-1 duration-100 sm:max-w-sm;
|
||||
}
|
||||
@@ -485,6 +548,15 @@
|
||||
@apply gap-1;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header {
|
||||
@apply gap-x-4 gap-y-1;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > figure {
|
||||
@apply bg-muted mb-2 inline-flex size-8 items-center justify-center rounded-md [&>svg:not([class*='size-'])]:size-4;
|
||||
}
|
||||
|
||||
.alert-dialog > * > footer,
|
||||
.dialog > * > footer {
|
||||
@apply gap-2;
|
||||
}
|
||||
@@ -494,11 +566,21 @@
|
||||
@apply text-sm font-medium;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > h2,
|
||||
.alert-dialog > * > [data-title] {
|
||||
@apply text-sm font-medium;
|
||||
}
|
||||
|
||||
.dialog > * > header > p,
|
||||
.dialog > * > [data-description] {
|
||||
@apply text-muted-foreground text-xs/relaxed [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > p,
|
||||
.alert-dialog > * > [data-description] {
|
||||
@apply text-muted-foreground text-xs/relaxed text-balance md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
/* Dropdown Menu */
|
||||
.dropdown-menu > [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 gap-0 overflow-x-hidden overflow-y-auto rounded-lg p-1 shadow-md ring-1 duration-100;
|
||||
@@ -545,11 +627,11 @@
|
||||
@apply bg-border/50 -mx-1 my-1 h-px;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-muted-foreground font-sans text-[0.625rem] tracking-widest;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-accent-foreground;
|
||||
}
|
||||
|
||||
|
||||
+96
-18
@@ -1,5 +1,22 @@
|
||||
/* Button */
|
||||
/* Accordion */
|
||||
@layer components {
|
||||
.accordion > details {
|
||||
@apply not-last:border-b;
|
||||
}
|
||||
|
||||
.accordion > details > summary {
|
||||
@apply focus-visible:ring-ring/50 focus-visible:border-ring rounded-lg py-2.5 text-left text-sm font-medium hover:underline focus-visible:ring-3;
|
||||
}
|
||||
|
||||
.accordion > details > summary > svg:last-child {
|
||||
@apply text-muted-foreground ms-auto size-4;
|
||||
}
|
||||
|
||||
.accordion > details > :not(summary) {
|
||||
@apply pt-0 pb-2.5 text-sm;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.btn {
|
||||
@apply rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 active:not-aria-[haspopup]:translate-y-px [&_svg:not([class*='size-'])]:size-4;
|
||||
}
|
||||
@@ -247,7 +264,7 @@
|
||||
@apply size-10;
|
||||
}
|
||||
|
||||
.avatar > span:not([data-badge]) {
|
||||
.avatar > span:not(.avatar-badge) {
|
||||
@apply bg-muted text-muted-foreground rounded-[inherit];
|
||||
}
|
||||
|
||||
@@ -255,7 +272,7 @@
|
||||
@apply rounded-[inherit];
|
||||
}
|
||||
|
||||
.avatar > [data-badge] {
|
||||
.avatar > .avatar-badge {
|
||||
@apply bg-primary text-primary-foreground ring-background;
|
||||
}
|
||||
|
||||
@@ -301,17 +318,42 @@
|
||||
@apply text-primary underline-offset-4 hover:underline;
|
||||
}
|
||||
|
||||
/* Command */
|
||||
.command {
|
||||
@apply bg-popover text-popover-foreground rounded-xl p-1;
|
||||
/* Breadcrumb */
|
||||
.breadcrumb > ol {
|
||||
@apply text-muted-foreground gap-1.5 text-sm;
|
||||
}
|
||||
|
||||
.command-dialog > * {
|
||||
@apply rounded-xl p-0;
|
||||
.breadcrumb li {
|
||||
@apply gap-1;
|
||||
}
|
||||
|
||||
.breadcrumb a {
|
||||
@apply transition-colors hover:text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb [aria-current='page'] {
|
||||
@apply font-normal text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb li[aria-hidden='true'] > svg {
|
||||
@apply size-3.5;
|
||||
}
|
||||
|
||||
.breadcrumb li > span[aria-hidden='true'] {
|
||||
@apply size-5 [&>svg]:size-4;
|
||||
}
|
||||
|
||||
/* Command */
|
||||
.command {
|
||||
@apply bg-popover text-popover-foreground rounded-xl! p-1;
|
||||
}
|
||||
|
||||
.command-dialog > .command {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 rounded-xl! ring-1 duration-100;
|
||||
}
|
||||
|
||||
.command > header {
|
||||
@apply m-1 mb-0 h-8 gap-2 rounded-lg border border-input/30 bg-input/30 px-2;
|
||||
@apply m-1 mb-0 h-8 gap-0 rounded-lg border border-input/30 bg-input/30 px-2;
|
||||
}
|
||||
|
||||
.command > header svg {
|
||||
@@ -319,7 +361,7 @@
|
||||
}
|
||||
|
||||
.command > header input {
|
||||
@apply h-full text-sm placeholder:text-muted-foreground;
|
||||
@apply ps-1.5 text-sm placeholder:text-muted-foreground;
|
||||
}
|
||||
|
||||
.command [role='menu'] {
|
||||
@@ -343,11 +385,11 @@
|
||||
}
|
||||
|
||||
.command [role='menuitem'] {
|
||||
@apply gap-2 rounded-sm px-2 py-1.5 text-sm [&_svg:not([class*='size-'])]:size-4;
|
||||
@apply gap-2 rounded-sm px-2 py-1.5 text-sm [&_svg:not([class*='size-'])]:size-4 in-[.command-dialog]:rounded-lg;
|
||||
}
|
||||
|
||||
.command [role='menuitem'].active {
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
}
|
||||
|
||||
.command [data-shortcut] {
|
||||
@@ -361,7 +403,7 @@
|
||||
|
||||
/* Combobox */
|
||||
.combobox > input[role='combobox'] {
|
||||
@apply border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-8 rounded-lg border bg-transparent py-1 ps-2.5 pe-8 text-sm transition-colors focus-visible:ring-3 aria-invalid:ring-3;
|
||||
@apply border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-lg border bg-transparent py-1 ps-2.5 pe-8 text-sm transition-colors focus-visible:ring-3 aria-invalid:ring-3;
|
||||
}
|
||||
|
||||
.combobox > svg,
|
||||
@@ -370,11 +412,15 @@
|
||||
}
|
||||
|
||||
.combobox [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-h-72 rounded-lg p-1 shadow-md ring-1 duration-100;
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-h-72 gap-0 rounded-lg p-0 shadow-md ring-1 duration-100;
|
||||
}
|
||||
|
||||
.combobox [role='listbox'] {
|
||||
@apply max-h-[min(18rem,var(--available-height,18rem))] scroll-py-1 overflow-y-auto;
|
||||
@apply max-h-[min(18rem,var(--available-height,18rem))] scroll-py-1 overflow-y-auto p-1;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true']))) {
|
||||
@apply p-0;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@@ -391,7 +437,7 @@
|
||||
|
||||
.combobox [role='option'].active,
|
||||
.combobox [role='option']:focus-visible {
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
}
|
||||
|
||||
.combobox [role='group'] {
|
||||
@@ -413,6 +459,14 @@
|
||||
.combobox-chip-remove {
|
||||
@apply -ms-1 opacity-50 hover:opacity-100 [&_svg]:size-3.5;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > .input-group {
|
||||
@apply m-1 mb-0 h-8 w-auto border-input/30 bg-input/30 shadow-none focus-within:border-inherit focus-within:ring-0;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > input[role='combobox'] {
|
||||
@apply mb-1 w-full;
|
||||
}
|
||||
/* Select */
|
||||
*:not(select).select > button {
|
||||
@apply border-input dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-8 data-[size=sm]:h-7 gap-1.5 rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] border bg-transparent py-2 ps-2.5 pe-2 text-sm transition-colors select-none focus-visible:ring-3 aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4;
|
||||
@@ -465,10 +519,15 @@
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop,
|
||||
.alert-dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/10 backdrop-blur-xs duration-100;
|
||||
}
|
||||
|
||||
.alert-dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-w-xs gap-4 rounded-xl p-4 ring-1 duration-100 sm:max-w-sm;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-w-[calc(100%-2rem)] gap-4 rounded-xl p-4 text-sm ring-1 duration-100 sm:max-w-sm;
|
||||
}
|
||||
@@ -481,6 +540,15 @@
|
||||
@apply gap-2;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header {
|
||||
@apply gap-x-4 gap-y-1.5;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > figure {
|
||||
@apply bg-muted mb-2 inline-flex size-10 items-center justify-center rounded-md [&>svg:not([class*='size-'])]:size-6;
|
||||
}
|
||||
|
||||
.alert-dialog > * > footer,
|
||||
.dialog > * > footer {
|
||||
@apply bg-muted/50 -mx-4 -mb-4 gap-2 rounded-b-xl border-t p-4;
|
||||
}
|
||||
@@ -490,11 +558,21 @@
|
||||
@apply text-base leading-none font-medium;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > h2,
|
||||
.alert-dialog > * > [data-title] {
|
||||
@apply text-base font-medium;
|
||||
}
|
||||
|
||||
.dialog > * > header > p,
|
||||
.dialog > * > [data-description] {
|
||||
@apply text-muted-foreground text-sm leading-normal [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > p,
|
||||
.alert-dialog > * > [data-description] {
|
||||
@apply text-muted-foreground text-sm text-balance md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
/* Dropdown Menu */
|
||||
.dropdown-menu > [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 gap-0 overflow-x-hidden overflow-y-auto rounded-lg p-1 shadow-md ring-1 duration-100;
|
||||
@@ -541,11 +619,11 @@
|
||||
@apply bg-border -mx-1 my-1 h-px;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-muted-foreground font-sans text-xs tracking-widest;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-accent-foreground;
|
||||
}
|
||||
|
||||
|
||||
+102
-20
@@ -1,5 +1,26 @@
|
||||
/* Button */
|
||||
/* Accordion */
|
||||
@layer components {
|
||||
.accordion {
|
||||
@apply overflow-hidden rounded-2xl border;
|
||||
}
|
||||
|
||||
.accordion > details {
|
||||
@apply open:bg-muted/50 not-last:border-b;
|
||||
}
|
||||
|
||||
.accordion > details > summary {
|
||||
@apply gap-6 p-4 text-left text-sm font-medium hover:underline;
|
||||
}
|
||||
|
||||
.accordion > details > summary > svg:last-child {
|
||||
@apply text-muted-foreground ms-auto size-4;
|
||||
}
|
||||
|
||||
.accordion > details > :not(summary) {
|
||||
@apply px-4 pt-0 pb-4 text-sm;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.btn {
|
||||
@apply rounded-2xl border border-transparent bg-clip-padding text-sm font-medium focus-visible:border-ring focus-visible:ring-ring/30 focus-visible:ring-[3px] aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 active:not-aria-[haspopup]:translate-y-px [&_svg:not([class*='size-'])]:size-4;
|
||||
}
|
||||
@@ -255,7 +276,7 @@
|
||||
@apply size-10;
|
||||
}
|
||||
|
||||
.avatar > span:not([data-badge]) {
|
||||
.avatar > span:not(.avatar-badge) {
|
||||
@apply bg-muted text-muted-foreground rounded-[inherit];
|
||||
}
|
||||
|
||||
@@ -263,7 +284,7 @@
|
||||
@apply rounded-[inherit];
|
||||
}
|
||||
|
||||
.avatar > [data-badge] {
|
||||
.avatar > .avatar-badge {
|
||||
@apply bg-primary text-primary-foreground ring-background;
|
||||
}
|
||||
|
||||
@@ -309,17 +330,42 @@
|
||||
@apply text-primary underline-offset-4 hover:underline;
|
||||
}
|
||||
|
||||
/* Command */
|
||||
.command {
|
||||
@apply bg-popover text-popover-foreground rounded-3xl p-1;
|
||||
/* Breadcrumb */
|
||||
.breadcrumb > ol {
|
||||
@apply text-muted-foreground gap-1.5 text-sm sm:gap-2.5;
|
||||
}
|
||||
|
||||
.command-dialog > * {
|
||||
@apply rounded-3xl p-0;
|
||||
.breadcrumb li {
|
||||
@apply gap-1.5;
|
||||
}
|
||||
|
||||
.breadcrumb a {
|
||||
@apply transition-colors hover:text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb [aria-current='page'] {
|
||||
@apply font-normal text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb li[aria-hidden='true'] > svg {
|
||||
@apply size-3.5;
|
||||
}
|
||||
|
||||
.breadcrumb li > span[aria-hidden='true'] {
|
||||
@apply size-5 [&>svg]:size-4;
|
||||
}
|
||||
|
||||
/* Command */
|
||||
.command {
|
||||
@apply bg-popover text-popover-foreground rounded-3xl! p-1;
|
||||
}
|
||||
|
||||
.command-dialog > .command {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 rounded-3xl! shadow-xl ring-1 duration-100;
|
||||
}
|
||||
|
||||
.command > header {
|
||||
@apply m-1 mb-0 h-8 gap-2 rounded-2xl bg-input/50 px-2;
|
||||
@apply m-1 mb-0 h-8 gap-0 rounded-2xl bg-input/50 px-2;
|
||||
}
|
||||
|
||||
.command > header svg {
|
||||
@@ -327,7 +373,7 @@
|
||||
}
|
||||
|
||||
.command > header input {
|
||||
@apply h-full text-sm placeholder:text-muted-foreground;
|
||||
@apply ps-1.5 text-sm placeholder:text-muted-foreground;
|
||||
}
|
||||
|
||||
.command [role='menu'] {
|
||||
@@ -355,7 +401,7 @@
|
||||
}
|
||||
|
||||
.command [role='menuitem'].active {
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
}
|
||||
|
||||
.command [data-shortcut] {
|
||||
@@ -378,11 +424,15 @@
|
||||
}
|
||||
|
||||
.combobox [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 max-h-72 rounded-2xl p-1 shadow-lg ring-1 duration-100;
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 max-h-72 gap-0 rounded-2xl p-0 shadow-lg ring-1 duration-100;
|
||||
}
|
||||
|
||||
.combobox [role='listbox'] {
|
||||
@apply max-h-[min(18rem,var(--available-height,18rem))] scroll-py-1 overflow-y-auto;
|
||||
@apply max-h-[min(18rem,var(--available-height,18rem))] scroll-py-1 overflow-y-auto p-1;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true']))) {
|
||||
@apply p-0;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@@ -399,7 +449,7 @@
|
||||
|
||||
.combobox [role='option'].active,
|
||||
.combobox [role='option']:focus-visible {
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
}
|
||||
|
||||
.combobox [role='group'] {
|
||||
@@ -421,6 +471,14 @@
|
||||
.combobox-chip-remove {
|
||||
@apply -ms-0.5 size-4.5 opacity-50 hover:opacity-100 [&_svg]:size-3.5;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > .input-group {
|
||||
@apply m-1 mb-0 h-8 w-auto border-input/30 bg-input/50 shadow-none focus-within:border-inherit focus-within:ring-0;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > input[role='combobox'] {
|
||||
@apply mb-1 w-full;
|
||||
}
|
||||
/* Select */
|
||||
*:not(select).select > button {
|
||||
@apply bg-input/50 border-transparent focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-8 data-[size=sm]:h-7 gap-1.5 rounded-2xl border px-3 py-2 text-sm transition-[color,box-shadow] duration-200 focus-visible:ring-3 aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4;
|
||||
@@ -473,12 +531,17 @@
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop,
|
||||
.alert-dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/30 backdrop-blur-sm duration-100;
|
||||
}
|
||||
|
||||
.alert-dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 max-w-xs gap-6 rounded-3xl p-6 shadow-xl ring-1 duration-100 sm:max-w-md;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 max-w-[calc(100%-2rem)] gap-6 rounded-[min(var(--radius-4xl),24px)] p-6 text-sm shadow-xl ring-1 duration-100 sm:max-w-md;
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 max-w-[calc(100%-2rem)] gap-6 rounded-3xl p-6 text-sm shadow-xl ring-1 duration-100 sm:max-w-md;
|
||||
}
|
||||
|
||||
.dialog > * > :is(button, form[method='dialog']) {
|
||||
@@ -493,6 +556,15 @@
|
||||
@apply gap-1.5;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header {
|
||||
@apply gap-x-6 gap-y-1.5;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > figure {
|
||||
@apply bg-muted mb-2 inline-flex size-16 items-center justify-center rounded-full [&>svg:not([class*='size-'])]:size-8;
|
||||
}
|
||||
|
||||
.alert-dialog > * > footer,
|
||||
.dialog > * > footer {
|
||||
@apply gap-2;
|
||||
}
|
||||
@@ -502,11 +574,21 @@
|
||||
@apply text-base leading-none font-medium;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > h2,
|
||||
.alert-dialog > * > [data-title] {
|
||||
@apply text-lg font-medium;
|
||||
}
|
||||
|
||||
.dialog > * > header > p,
|
||||
.dialog > * > [data-description] {
|
||||
@apply text-muted-foreground text-sm leading-normal [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > p,
|
||||
.alert-dialog > * > [data-description] {
|
||||
@apply text-muted-foreground text-sm text-balance md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
/* Dropdown Menu */
|
||||
.dropdown-menu > [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 gap-0 overflow-x-hidden overflow-y-auto rounded-2xl p-1 shadow-lg ring-1 duration-100;
|
||||
@@ -553,11 +635,11 @@
|
||||
@apply bg-border/50 -mx-1 my-1 h-px;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-muted-foreground font-sans text-xs tracking-widest;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-accent-foreground;
|
||||
}
|
||||
|
||||
@@ -582,7 +664,7 @@
|
||||
|
||||
/* Card */
|
||||
.card {
|
||||
@apply bg-card text-card-foreground ring-foreground/5 dark:ring-foreground/10 overflow-hidden rounded-[min(var(--radius-4xl),24px)] text-sm shadow-sm ring-1 has-[>img:first-child]:pt-0 [&>img:first-child]:rounded-t-[min(var(--radius-4xl),24px)] [&>img:last-child]:rounded-b-[min(var(--radius-4xl),24px)];
|
||||
@apply bg-card text-card-foreground ring-foreground/5 dark:ring-foreground/10 overflow-hidden rounded-3xl text-sm shadow-sm ring-1 has-[>img:first-child]:pt-0 [&>img:first-child]:rounded-t-3xl [&>img:last-child]:rounded-b-3xl;
|
||||
}
|
||||
|
||||
.card {
|
||||
@@ -594,7 +676,7 @@
|
||||
}
|
||||
|
||||
.card > header {
|
||||
@apply gap-1.5 rounded-t-[min(var(--radius-4xl),24px)] px-5 [.border-b]:pb-5;
|
||||
@apply gap-1.5 rounded-t-3xl px-5 [.border-b]:pb-5;
|
||||
}
|
||||
|
||||
.card[data-size='sm'] > header {
|
||||
@@ -618,7 +700,7 @@
|
||||
}
|
||||
|
||||
.card > footer {
|
||||
@apply rounded-b-[min(var(--radius-4xl),24px)] px-5 [.border-t]:pt-5;
|
||||
@apply rounded-b-3xl px-5 [.border-t]:pt-5;
|
||||
}
|
||||
|
||||
.card[data-size='sm'] > footer {
|
||||
|
||||
+91
-13
@@ -1,5 +1,22 @@
|
||||
/* Button */
|
||||
/* Accordion */
|
||||
@layer components {
|
||||
.accordion > details {
|
||||
@apply not-last:border-b;
|
||||
}
|
||||
|
||||
.accordion > details > summary {
|
||||
@apply focus-visible:ring-ring/30 focus-visible:border-ring rounded-none gap-6 py-4 text-left text-sm font-semibold hover:underline focus-visible:ring-2;
|
||||
}
|
||||
|
||||
.accordion > details > summary > svg:last-child {
|
||||
@apply text-muted-foreground ms-auto size-3.5;
|
||||
}
|
||||
|
||||
.accordion > details > :not(summary) {
|
||||
@apply pt-0 pb-4 text-sm;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.btn {
|
||||
@apply rounded-none border border-transparent bg-clip-padding text-xs font-semibold uppercase tracking-widest focus-visible:border-ring focus-visible:ring-ring/30 focus-visible:ring-[2px] aria-invalid:border-destructive aria-invalid:ring-[2px] aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 active:not-aria-[haspopup]:translate-y-px [&_svg:not([class*='size-'])]:size-3.5;
|
||||
}
|
||||
@@ -263,7 +280,7 @@
|
||||
@apply size-10;
|
||||
}
|
||||
|
||||
.avatar > span:not([data-badge]) {
|
||||
.avatar > span:not(.avatar-badge) {
|
||||
@apply bg-muted text-muted-foreground rounded-[inherit];
|
||||
}
|
||||
|
||||
@@ -271,7 +288,7 @@
|
||||
@apply rounded-[inherit];
|
||||
}
|
||||
|
||||
.avatar > [data-badge] {
|
||||
.avatar > .avatar-badge {
|
||||
@apply bg-primary text-primary-foreground ring-background;
|
||||
}
|
||||
|
||||
@@ -317,17 +334,42 @@
|
||||
@apply text-foreground underline-offset-4 hover:underline;
|
||||
}
|
||||
|
||||
/* Breadcrumb */
|
||||
.breadcrumb > ol {
|
||||
@apply text-muted-foreground gap-1.5 text-sm sm:gap-2.5;
|
||||
}
|
||||
|
||||
.breadcrumb li {
|
||||
@apply gap-1.5;
|
||||
}
|
||||
|
||||
.breadcrumb a {
|
||||
@apply transition-colors hover:text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb [aria-current='page'] {
|
||||
@apply font-normal text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb li[aria-hidden='true'] > svg {
|
||||
@apply size-3.5;
|
||||
}
|
||||
|
||||
.breadcrumb li > span[aria-hidden='true'] {
|
||||
@apply size-5 [&>svg]:size-4;
|
||||
}
|
||||
|
||||
/* Command */
|
||||
.command {
|
||||
@apply bg-popover text-popover-foreground rounded-none;
|
||||
}
|
||||
|
||||
.command-dialog > * {
|
||||
@apply rounded-none p-0;
|
||||
.command-dialog > .command {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 rounded-none shadow-md ring-1 duration-100;
|
||||
}
|
||||
|
||||
.command > header {
|
||||
@apply m-1 gap-2 border-b border-b-input bg-transparent px-3;
|
||||
@apply m-1 gap-0 border-b border-b-input bg-transparent px-3;
|
||||
}
|
||||
|
||||
.command > header svg {
|
||||
@@ -335,7 +377,7 @@
|
||||
}
|
||||
|
||||
.command > header input {
|
||||
@apply h-9 px-2 text-sm placeholder:text-muted-foreground;
|
||||
@apply h-9 ps-1.5 pe-2 text-sm placeholder:text-muted-foreground;
|
||||
}
|
||||
|
||||
.command [role='menu'] {
|
||||
@@ -363,7 +405,7 @@
|
||||
}
|
||||
|
||||
.command [role='menuitem'].active {
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
}
|
||||
|
||||
.command [data-shortcut] {
|
||||
@@ -386,11 +428,15 @@
|
||||
}
|
||||
|
||||
.combobox [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-h-72 rounded-none p-1.5 shadow-md ring-1 duration-100;
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-h-72 gap-0 rounded-none p-0 shadow-md ring-1 duration-100;
|
||||
}
|
||||
|
||||
.combobox [role='listbox'] {
|
||||
@apply max-h-[min(18rem,var(--available-height,18rem))] scroll-py-1.5 overflow-y-auto;
|
||||
@apply max-h-[min(18rem,var(--available-height,18rem))] scroll-py-1.5 overflow-y-auto p-1.5;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true']))) {
|
||||
@apply p-0;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@@ -407,7 +453,7 @@
|
||||
|
||||
.combobox [role='option'].active,
|
||||
.combobox [role='option']:focus-visible {
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
}
|
||||
|
||||
.combobox [role='group'] {
|
||||
@@ -429,6 +475,14 @@
|
||||
.combobox-chip-remove {
|
||||
@apply -ms-1 opacity-50 hover:opacity-100 [&_svg]:size-3.5;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > .input-group {
|
||||
@apply m-1.5 mb-0 h-8 w-auto border-transparent border-b-input bg-transparent px-2.5 shadow-none focus-within:border-transparent focus-within:ring-0;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > input[role='combobox'] {
|
||||
@apply mb-1 w-full;
|
||||
}
|
||||
/* Select */
|
||||
*:not(select).select > button {
|
||||
@apply border-transparent border-b-input bg-transparent focus-visible:border-b-ring aria-invalid:border-b-destructive dark:aria-invalid:border-b-destructive/50 h-10 data-[size=sm]:h-9 gap-1.5 rounded-none border px-0 py-2 text-sm transition-[color,border-color] [&_svg:not([class*='size-'])]:size-3.5;
|
||||
@@ -481,10 +535,15 @@
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop,
|
||||
.alert-dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/20 backdrop-blur-sm duration-100;
|
||||
}
|
||||
|
||||
.alert-dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-w-xs gap-6 rounded-none p-6 shadow-md ring-1 duration-100 sm:max-w-md;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-w-[calc(100%-2rem)] gap-6 rounded-none p-6 text-sm shadow-md ring-1 duration-100 sm:max-w-md;
|
||||
}
|
||||
@@ -501,6 +560,15 @@
|
||||
@apply gap-2;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header {
|
||||
@apply gap-x-6 gap-y-1.5;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > figure {
|
||||
@apply bg-muted mb-2 inline-flex size-16 items-center justify-center rounded-none [&>svg:not([class*='size-'])]:size-8;
|
||||
}
|
||||
|
||||
.alert-dialog > * > footer,
|
||||
.dialog > * > footer {
|
||||
@apply gap-2;
|
||||
}
|
||||
@@ -510,11 +578,21 @@
|
||||
@apply text-lg leading-none font-semibold tracking-wider uppercase;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > h2,
|
||||
.alert-dialog > * > [data-title] {
|
||||
@apply text-lg font-semibold tracking-wider uppercase;
|
||||
}
|
||||
|
||||
.dialog > * > header > p,
|
||||
.dialog > * > [data-description] {
|
||||
@apply text-muted-foreground mt-0.5 text-sm leading-relaxed [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > p,
|
||||
.alert-dialog > * > [data-description] {
|
||||
@apply text-muted-foreground mt-0.5 text-sm leading-relaxed text-balance md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
/* Dropdown Menu */
|
||||
.dropdown-menu > [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 min-w-48 gap-0 overflow-x-hidden overflow-y-auto rounded-none p-1.5 shadow-md ring-1 duration-100;
|
||||
@@ -561,11 +639,11 @@
|
||||
@apply bg-border/50 -mx-1.5 my-1.5 h-px;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-muted-foreground font-sans text-xs tracking-widest;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-accent-foreground;
|
||||
}
|
||||
|
||||
|
||||
+95
-17
@@ -1,5 +1,22 @@
|
||||
/* Button */
|
||||
/* Accordion */
|
||||
@layer components {
|
||||
.accordion > details {
|
||||
@apply not-last:border-b;
|
||||
}
|
||||
|
||||
.accordion > details > summary {
|
||||
@apply focus-visible:ring-ring/50 focus-visible:border-ring rounded-md py-4 text-left text-sm font-medium hover:underline focus-visible:ring-3;
|
||||
}
|
||||
|
||||
.accordion > details > summary > svg:last-child {
|
||||
@apply text-muted-foreground ms-auto size-4;
|
||||
}
|
||||
|
||||
.accordion > details > :not(summary) {
|
||||
@apply pt-0 pb-4 text-sm;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.btn {
|
||||
@apply rounded-md border border-transparent bg-clip-padding text-sm font-medium focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 active:not-aria-[haspopup]:translate-y-px [&_svg:not([class*='size-'])]:size-4;
|
||||
}
|
||||
@@ -247,7 +264,7 @@
|
||||
@apply size-10;
|
||||
}
|
||||
|
||||
.avatar > span:not([data-badge]) {
|
||||
.avatar > span:not(.avatar-badge) {
|
||||
@apply bg-muted text-muted-foreground rounded-[inherit];
|
||||
}
|
||||
|
||||
@@ -255,7 +272,7 @@
|
||||
@apply rounded-[inherit];
|
||||
}
|
||||
|
||||
.avatar > [data-badge] {
|
||||
.avatar > .avatar-badge {
|
||||
@apply bg-primary text-primary-foreground ring-background;
|
||||
}
|
||||
|
||||
@@ -301,17 +318,42 @@
|
||||
@apply text-primary underline-offset-4 hover:underline;
|
||||
}
|
||||
|
||||
/* Command */
|
||||
.command {
|
||||
@apply bg-popover text-popover-foreground rounded-xl p-1;
|
||||
/* Breadcrumb */
|
||||
.breadcrumb > ol {
|
||||
@apply text-muted-foreground gap-1.5 text-sm sm:gap-2.5;
|
||||
}
|
||||
|
||||
.command-dialog > * {
|
||||
@apply rounded-xl p-0;
|
||||
.breadcrumb li {
|
||||
@apply gap-1.5;
|
||||
}
|
||||
|
||||
.breadcrumb a {
|
||||
@apply transition-colors hover:text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb [aria-current='page'] {
|
||||
@apply font-normal text-foreground;
|
||||
}
|
||||
|
||||
.breadcrumb li[aria-hidden='true'] > svg {
|
||||
@apply size-3.5;
|
||||
}
|
||||
|
||||
.breadcrumb li > span[aria-hidden='true'] {
|
||||
@apply size-5 [&>svg]:size-4;
|
||||
}
|
||||
|
||||
/* Command */
|
||||
.command {
|
||||
@apply bg-popover text-popover-foreground rounded-xl! p-1;
|
||||
}
|
||||
|
||||
.command-dialog > .command {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 rounded-xl! ring-1 duration-100;
|
||||
}
|
||||
|
||||
.command > header {
|
||||
@apply m-1 mb-0 h-8 gap-2 rounded-lg border border-input/30 bg-input/30 px-2;
|
||||
@apply m-1 mb-0 h-8 gap-0 rounded-lg border border-input/30 bg-input/30 px-2;
|
||||
}
|
||||
|
||||
.command > header svg {
|
||||
@@ -319,7 +361,7 @@
|
||||
}
|
||||
|
||||
.command > header input {
|
||||
@apply h-full text-sm placeholder:text-muted-foreground;
|
||||
@apply ps-1.5 text-sm placeholder:text-muted-foreground;
|
||||
}
|
||||
|
||||
.command [role='menu'] {
|
||||
@@ -343,11 +385,11 @@
|
||||
}
|
||||
|
||||
.command [role='menuitem'] {
|
||||
@apply gap-2 rounded-sm px-2 py-1.5 text-sm [&_svg:not([class*='size-'])]:size-4;
|
||||
@apply gap-2 rounded-sm px-2 py-1.5 text-sm [&_svg:not([class*='size-'])]:size-4 in-[.command-dialog]:rounded-lg;
|
||||
}
|
||||
|
||||
.command [role='menuitem'].active {
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
}
|
||||
|
||||
.command [data-shortcut] {
|
||||
@@ -370,11 +412,15 @@
|
||||
}
|
||||
|
||||
.combobox [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-h-72 rounded-md p-1 shadow-md ring-1 duration-100;
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-h-72 gap-0 rounded-md p-0 shadow-md ring-1 duration-100;
|
||||
}
|
||||
|
||||
.combobox [role='listbox'] {
|
||||
@apply max-h-[min(18rem,var(--available-height,18rem))] scroll-py-1 overflow-y-auto;
|
||||
@apply max-h-[min(18rem,var(--available-height,18rem))] scroll-py-1 overflow-y-auto p-1;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true']))) {
|
||||
@apply p-0;
|
||||
}
|
||||
|
||||
.combobox [role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
|
||||
@@ -391,7 +437,7 @@
|
||||
|
||||
.combobox [role='option'].active,
|
||||
.combobox [role='option']:focus-visible {
|
||||
@apply bg-muted text-foreground [&_svg]:text-foreground;
|
||||
@apply bg-accent text-accent-foreground [&_svg]:text-accent-foreground;
|
||||
}
|
||||
|
||||
.combobox [role='group'] {
|
||||
@@ -413,6 +459,14 @@
|
||||
.combobox-chip-remove {
|
||||
@apply -ms-1 opacity-50 hover:opacity-100 [&_svg]:size-3.5;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > .input-group {
|
||||
@apply m-1 mb-0 h-8 w-auto border-input/30 bg-input/30 shadow-none focus-within:border-inherit focus-within:ring-0;
|
||||
}
|
||||
|
||||
.combobox [data-popover] > input[role='combobox'] {
|
||||
@apply mb-1 w-full;
|
||||
}
|
||||
/* Select */
|
||||
*:not(select).select > button {
|
||||
@apply border-input dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-9 data-[size=sm]:h-8 gap-1.5 rounded-md border bg-transparent py-2 ps-2.5 pe-2 text-sm shadow-xs transition-[color,box-shadow] focus-visible:ring-3 aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4;
|
||||
@@ -465,10 +519,15 @@
|
||||
|
||||
/* Dialog */
|
||||
.dialog::backdrop,
|
||||
.alert-dialog::backdrop,
|
||||
.command-dialog::backdrop {
|
||||
@apply bg-black/10 backdrop-blur-xs duration-100;
|
||||
}
|
||||
|
||||
.alert-dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-w-xs gap-6 rounded-xl p-6 ring-1 duration-100 sm:max-w-lg;
|
||||
}
|
||||
|
||||
.dialog > * {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 max-w-[calc(100%-2rem)] gap-6 rounded-xl p-6 text-sm ring-1 duration-100 sm:max-w-md;
|
||||
}
|
||||
@@ -481,6 +540,15 @@
|
||||
@apply gap-2;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header {
|
||||
@apply gap-x-6 gap-y-1.5;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > figure {
|
||||
@apply bg-muted mb-2 inline-flex size-16 items-center justify-center rounded-md [&>svg:not([class*='size-'])]:size-8;
|
||||
}
|
||||
|
||||
.alert-dialog > * > footer,
|
||||
.dialog > * > footer {
|
||||
@apply gap-2;
|
||||
}
|
||||
@@ -490,11 +558,21 @@
|
||||
@apply leading-none font-medium;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > h2,
|
||||
.alert-dialog > * > [data-title] {
|
||||
@apply text-lg font-medium;
|
||||
}
|
||||
|
||||
.dialog > * > header > p,
|
||||
.dialog > * > [data-description] {
|
||||
@apply text-muted-foreground text-sm leading-normal [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
.alert-dialog > * > header > p,
|
||||
.alert-dialog > * > [data-description] {
|
||||
@apply text-muted-foreground text-sm text-balance md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a:hover]:text-foreground;
|
||||
}
|
||||
|
||||
/* Dropdown Menu */
|
||||
.dropdown-menu > [data-popover] {
|
||||
@apply bg-popover text-popover-foreground ring-foreground/10 gap-0 overflow-x-hidden overflow-y-auto rounded-md p-1 shadow-md ring-1 duration-100;
|
||||
@@ -541,11 +619,11 @@
|
||||
@apply bg-border -mx-1 my-1 h-px;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-muted-foreground font-sans text-xs tracking-widest;
|
||||
}
|
||||
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child) {
|
||||
.dropdown-menu :is([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']):is(:focus-visible, .active) :is([data-shortcut], kbd:last-child, .kbd:last-child) {
|
||||
@apply text-accent-foreground;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
multiple=false,
|
||||
placeholder=None,
|
||||
close_on_select=false,
|
||||
clear=false,
|
||||
auto_highlight=false,
|
||||
format="value",
|
||||
main_attrs={},
|
||||
@@ -51,7 +52,23 @@
|
||||
{% endfor %}
|
||||
>
|
||||
{% if multiple %}
|
||||
<div class="combobox-chips {{ input_attrs.class }}">
|
||||
<input
|
||||
type="text"
|
||||
role="combobox"
|
||||
class="{{ input_attrs.class }}"
|
||||
placeholder="{{ placeholder or '' }}"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="false"
|
||||
aria-controls="{{ id }}-listbox"
|
||||
{% for key, value in input_attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% elif clear %}
|
||||
<div class="input-group {{ input_attrs.class }}">
|
||||
<input
|
||||
type="text"
|
||||
role="combobox"
|
||||
@@ -66,6 +83,14 @@
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
<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="{{ id }}-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>
|
||||
{% else %}
|
||||
<input
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
id="{{ id }}-menu"
|
||||
aria-orientation="vertical"
|
||||
data-empty="{{ empty_text }}"
|
||||
class="scrollbar-sm {{ menu_attrs.class }}"
|
||||
class="{{ menu_attrs.class }}"
|
||||
{% for key, value in menu_attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
@@ -121,7 +121,7 @@
|
||||
id="{{ id }}-menu"
|
||||
aria-orientation="vertical"
|
||||
data-empty="{{ empty_text }}"
|
||||
class="scrollbar-sm {{ menu_attrs.class }}"
|
||||
class="{{ menu_attrs.class }}"
|
||||
{% for key, value in menu_attrs.items() %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
(() => {
|
||||
const states = new WeakMap();
|
||||
|
||||
const isDisabled = (details) => {
|
||||
const summary = details.querySelector(':scope > summary');
|
||||
return details.getAttribute('aria-disabled') === 'true'
|
||||
|| details.dataset.disabled === 'true'
|
||||
|| summary?.getAttribute('aria-disabled') === 'true';
|
||||
};
|
||||
|
||||
const isMultiple = (root) => root.hasAttribute('data-multiple');
|
||||
|
||||
const closeSiblings = (root, activeDetails) => {
|
||||
if (isMultiple(root) || !activeDetails.open) return;
|
||||
root.querySelectorAll(':scope > details[open]').forEach((details) => {
|
||||
if (details !== activeDetails) details.open = false;
|
||||
});
|
||||
};
|
||||
|
||||
const initAccordion = (root) => {
|
||||
if (root.dataset.accordionInitialized) return;
|
||||
|
||||
const handleClick = (event) => {
|
||||
const summary = event.target.closest('summary');
|
||||
const details = summary?.closest('details');
|
||||
if (!details || details.parentElement !== root || !isDisabled(details)) return;
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const handleKeydown = (event) => {
|
||||
if (event.key !== 'Enter' && event.key !== ' ') return;
|
||||
const summary = event.target.closest('summary');
|
||||
const details = summary?.closest('details');
|
||||
if (!details || details.parentElement !== root || !isDisabled(details)) return;
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const handleToggle = (event) => {
|
||||
const details = event.target;
|
||||
if (details.parentElement !== root) return;
|
||||
if (isDisabled(details)) {
|
||||
details.open = false;
|
||||
return;
|
||||
}
|
||||
closeSiblings(root, details);
|
||||
};
|
||||
|
||||
root.addEventListener('click', handleClick);
|
||||
root.addEventListener('keydown', handleKeydown);
|
||||
root.addEventListener('toggle', handleToggle, true);
|
||||
root.querySelectorAll(':scope > details[open]').forEach((details) => closeSiblings(root, details));
|
||||
|
||||
states.set(root, { handleClick, handleToggle });
|
||||
root._destroy = () => {
|
||||
root.removeEventListener('click', handleClick);
|
||||
root.removeEventListener('keydown', handleKeydown);
|
||||
root.removeEventListener('toggle', handleToggle, true);
|
||||
states.delete(root);
|
||||
};
|
||||
|
||||
root.dataset.accordionInitialized = 'true';
|
||||
root.dispatchEvent(new CustomEvent('basecoat:initialized'));
|
||||
};
|
||||
|
||||
if (window.basecoat) {
|
||||
window.basecoat.register('accordion', '.accordion:not([data-accordion-initialized])', initAccordion);
|
||||
}
|
||||
})();
|
||||
+115
-26
@@ -2,12 +2,29 @@
|
||||
const states = new WeakMap();
|
||||
|
||||
const getElements = (root) => {
|
||||
const input = root.querySelector(':scope > input[role="combobox"], :scope > .combobox-chips input[role="combobox"]');
|
||||
const chips = root.querySelector(':scope > .combobox-chips');
|
||||
const popover = root.querySelector(':scope > [data-popover]');
|
||||
const input = root.querySelector(':scope > input[role="combobox"], :scope > .input-group input[role="combobox"], :scope > .combobox-chips input[role="combobox"]')
|
||||
|| popover?.querySelector('input[role="combobox"]');
|
||||
const chips = root.querySelector(':scope > .combobox-chips');
|
||||
const popupTrigger = root.querySelector(':scope > button[aria-haspopup="listbox"]');
|
||||
const trigger = popupTrigger || root.querySelector(':scope > .input-group button[aria-haspopup="listbox"]');
|
||||
const clearButton = root.querySelector('[data-clear]');
|
||||
const valueTarget = popupTrigger?.querySelector('[data-value]') || (popupTrigger?.matches('[data-value]') ? popupTrigger : null);
|
||||
const listbox = popover ? popover.querySelector('[role="listbox"]') : null;
|
||||
const hiddenInput = root.querySelector(':scope > input[type="hidden"]');
|
||||
return { input, chips, popover, listbox, hiddenInput };
|
||||
return { input, chips, trigger, clearButton, valueTarget, popover, listbox, hiddenInput };
|
||||
};
|
||||
|
||||
const ensureMultipleInputSurface = (root, elements) => {
|
||||
if (elements.listbox?.getAttribute('aria-multiselectable') !== 'true' || elements.chips || !elements.input) return elements;
|
||||
if (elements.input.parentElement !== root) return elements;
|
||||
|
||||
const chips = document.createElement('div');
|
||||
chips.className = 'combobox-chips';
|
||||
root.insertBefore(chips, elements.input);
|
||||
chips.appendChild(elements.input);
|
||||
|
||||
return getElements(root);
|
||||
};
|
||||
|
||||
const getValue = option => option.dataset.value ?? option.textContent.trim();
|
||||
@@ -111,7 +128,7 @@
|
||||
state.popover.dataset.empty = String(state.visibleOptions.length === 0);
|
||||
};
|
||||
|
||||
const filterOptions = (state, { preserveActive = false } = {}) => {
|
||||
const filterOptions = (state, { preserveActive = false, search: forcedSearch } = {}) => {
|
||||
const previousActive = state.activeIndex > -1 ? state.options[state.activeIndex] : null;
|
||||
state.visibleOptions = [];
|
||||
|
||||
@@ -127,7 +144,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const search = state.input.value.trim().toLowerCase();
|
||||
const search = (forcedSearch ?? state.input.value).trim().toLowerCase();
|
||||
|
||||
state.allOptions.forEach(option => {
|
||||
if (option.hasAttribute('data-force')) {
|
||||
@@ -161,20 +178,42 @@
|
||||
const chip = document.createElement('span');
|
||||
chip.className = 'combobox-chip';
|
||||
chip.dataset.value = entry.value;
|
||||
chip.textContent = entry.label;
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.textContent = entry.label;
|
||||
|
||||
const remove = document.createElement('button');
|
||||
remove.type = 'button';
|
||||
remove.className = 'combobox-chip-remove';
|
||||
remove.className = 'combobox-chip-remove btn';
|
||||
remove.dataset.variant = 'ghost';
|
||||
remove.dataset.size = 'icon-xs';
|
||||
remove.setAttribute('aria-label', `Remove ${entry.label}`);
|
||||
remove.innerHTML = '<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"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>';
|
||||
remove.addEventListener('click', () => root.deselect(entry.value));
|
||||
remove.addEventListener('click', (event) => {
|
||||
event.stopPropagation();
|
||||
root.deselect(entry.value);
|
||||
state.input.focus();
|
||||
});
|
||||
|
||||
chip.appendChild(label);
|
||||
chip.appendChild(remove);
|
||||
state.chips.insertBefore(chip, state.input);
|
||||
});
|
||||
};
|
||||
|
||||
const syncTriggerValue = (state) => {
|
||||
if (!state.valueTarget) return;
|
||||
const selected = getSelection(state);
|
||||
state.valueTarget.textContent = state.isMultiple
|
||||
? selected.map(entry => entry.label).join(', ')
|
||||
: (selected[0]?.label || state.valueTarget.dataset.placeholder || '');
|
||||
};
|
||||
|
||||
const syncClearButton = (state) => {
|
||||
if (!state.clearButton) return;
|
||||
state.clearButton.hidden = getSelection(state).length === 0 && state.input.value === '';
|
||||
};
|
||||
|
||||
const syncSelectedOptions = (state) => {
|
||||
state.options.forEach(option => {
|
||||
if (state.selected.has(getValue(option))) {
|
||||
@@ -195,7 +234,7 @@
|
||||
state.input.value = '';
|
||||
} else if (normalized[0]) {
|
||||
state.selected.set(normalized[0].value, normalized[0]);
|
||||
state.input.value = normalized[0].label;
|
||||
state.input.value = state.valueTarget ? '' : normalized[0].label;
|
||||
} else {
|
||||
state.input.value = '';
|
||||
}
|
||||
@@ -206,6 +245,8 @@
|
||||
renderChips(root);
|
||||
filterOptions(state, { preserveActive: true });
|
||||
}
|
||||
syncTriggerValue(state);
|
||||
syncClearButton(state);
|
||||
|
||||
if (triggerEvent) {
|
||||
root.dispatchEvent(new CustomEvent('change', {
|
||||
@@ -219,15 +260,35 @@
|
||||
if (state.popover.getAttribute('aria-hidden') === 'true') return;
|
||||
state.popover.setAttribute('aria-hidden', 'true');
|
||||
state.input.setAttribute('aria-expanded', 'false');
|
||||
state.trigger?.setAttribute('aria-expanded', 'false');
|
||||
setActiveOption(state, -1);
|
||||
if (focusInput) state.input.focus();
|
||||
if (focusInput) {
|
||||
state.skipOpenOnFocus = true;
|
||||
state.input.focus();
|
||||
requestAnimationFrame(() => { state.skipOpenOnFocus = false; });
|
||||
}
|
||||
};
|
||||
|
||||
const openPopover = (state) => {
|
||||
const { root } = state;
|
||||
if (state.popover.getAttribute('aria-hidden') === 'false') return;
|
||||
document.dispatchEvent(new CustomEvent('basecoat:popover', { detail: { source: root } }));
|
||||
root.refresh();
|
||||
const selected = getSelection(state)[0];
|
||||
if (state.valueTarget) state.input.value = '';
|
||||
const search = !state.isMultiple && selected && state.input.value === selected.label ? '' : undefined;
|
||||
filterOptions(state, { search });
|
||||
state.popover.setAttribute('aria-hidden', 'false');
|
||||
state.input.setAttribute('aria-expanded', 'true');
|
||||
state.trigger?.setAttribute('aria-expanded', 'true');
|
||||
if (state.trigger) state.input.focus();
|
||||
};
|
||||
|
||||
const refreshCombobox = (root) => {
|
||||
const state = states.get(root);
|
||||
if (!state) return;
|
||||
|
||||
const elements = getElements(root);
|
||||
let elements = getElements(root);
|
||||
if (!elements.input || !elements.popover || !elements.listbox || !elements.hiddenInput) {
|
||||
const missing = [];
|
||||
if (!elements.input) missing.push('input');
|
||||
@@ -238,6 +299,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
elements = ensureMultipleInputSurface(root, elements);
|
||||
const previousValue = elements.hiddenInput.value;
|
||||
const previousInputValue = elements.input.value;
|
||||
Object.assign(state, elements, getOptions(elements.listbox));
|
||||
@@ -264,6 +326,8 @@
|
||||
filterOptions(state, { preserveActive: true });
|
||||
}
|
||||
}
|
||||
syncTriggerValue(state);
|
||||
syncClearButton(state);
|
||||
};
|
||||
|
||||
const resolveEntry = (state, value) => {
|
||||
@@ -290,7 +354,8 @@
|
||||
if (state.closeOnSelect) root.close(true);
|
||||
} else {
|
||||
setSelected(root, entry);
|
||||
root.close(true);
|
||||
root.close(state.trigger ? false : true);
|
||||
state.trigger?.focus();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -302,6 +367,15 @@
|
||||
setSelected(root, getSelection(state).filter(item => item.value !== normalized));
|
||||
};
|
||||
|
||||
const clearValue = (root) => {
|
||||
const state = states.get(root);
|
||||
setSelected(root, [], true);
|
||||
state.input.value = '';
|
||||
filterOptions(state);
|
||||
syncClearButton(state);
|
||||
if (state.popover.getAttribute('aria-hidden') === 'false') state.input.focus();
|
||||
};
|
||||
|
||||
const handleKeydown = (event, root) => {
|
||||
const state = states.get(root);
|
||||
if (!['ArrowDown', 'ArrowUp', 'Enter', 'Home', 'End', 'Escape', 'Backspace'].includes(event.key)) return;
|
||||
@@ -322,7 +396,7 @@
|
||||
|
||||
if (!isOpen && ['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) {
|
||||
event.preventDefault();
|
||||
root.open();
|
||||
openPopover(state);
|
||||
}
|
||||
|
||||
if (state.popover.getAttribute('aria-hidden') === 'true') return;
|
||||
@@ -355,7 +429,7 @@
|
||||
const initCombobox = (root) => {
|
||||
if (root.dataset.comboboxInitialized) return;
|
||||
|
||||
const state = { root, activeIndex: -1, allOptions: [], options: [], visibleOptions: [], selected: new Map(), format: 'value', manualFilter: false };
|
||||
const state = { root, activeIndex: -1, allOptions: [], options: [], visibleOptions: [], selected: new Map(), format: 'value', manualFilter: false, skipOpenOnFocus: false };
|
||||
states.set(root, state);
|
||||
root.refresh = () => refreshCombobox(root);
|
||||
|
||||
@@ -366,14 +440,6 @@
|
||||
return;
|
||||
}
|
||||
|
||||
root.open = () => {
|
||||
if (state.popover.getAttribute('aria-hidden') === 'false') return;
|
||||
document.dispatchEvent(new CustomEvent('basecoat:popover', { detail: { source: root } }));
|
||||
root.refresh();
|
||||
filterOptions(state);
|
||||
state.popover.setAttribute('aria-hidden', 'false');
|
||||
state.input.setAttribute('aria-expanded', 'true');
|
||||
};
|
||||
root.close = (focusInput = false) => closePopover(state, focusInput);
|
||||
|
||||
root.select = (value) => selectValue(root, value);
|
||||
@@ -394,17 +460,34 @@
|
||||
root.selectNone = () => setSelected(root, []);
|
||||
}
|
||||
|
||||
const handleInputFocus = root.open;
|
||||
const handleInputClick = root.open;
|
||||
const handleInputFocus = () => {
|
||||
if (state.skipOpenOnFocus) return;
|
||||
openPopover(state);
|
||||
};
|
||||
const handleInputClick = () => openPopover(state);
|
||||
const handleInput = () => {
|
||||
root.open();
|
||||
openPopover(state);
|
||||
filterOptions(state);
|
||||
if (!state.isMultiple) {
|
||||
state.hiddenInput.value = '';
|
||||
state.selected.clear();
|
||||
syncSelectedOptions(state);
|
||||
syncTriggerValue(state);
|
||||
syncClearButton(state);
|
||||
}
|
||||
};
|
||||
const handleTriggerClick = () => {
|
||||
if (state.popover.getAttribute('aria-hidden') === 'false') {
|
||||
root.close(false);
|
||||
} else {
|
||||
openPopover(state);
|
||||
}
|
||||
};
|
||||
const handleClearClick = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
clearValue(root);
|
||||
};
|
||||
const handleInputKeydown = (event) => handleKeydown(event, root);
|
||||
const handleListboxMousemove = (event) => {
|
||||
const option = event.target.closest('[role="option"]');
|
||||
@@ -427,6 +510,8 @@
|
||||
state.input.addEventListener('click', handleInputClick);
|
||||
state.input.addEventListener('input', handleInput);
|
||||
state.input.addEventListener('keydown', handleInputKeydown);
|
||||
state.trigger?.addEventListener('click', handleTriggerClick);
|
||||
state.clearButton?.addEventListener('click', handleClearClick);
|
||||
state.listbox.addEventListener('mousemove', handleListboxMousemove);
|
||||
state.listbox.addEventListener('click', handleListboxClick);
|
||||
document.addEventListener('click', handleDocumentClick);
|
||||
@@ -437,6 +522,8 @@
|
||||
state.input.removeEventListener('click', handleInputClick);
|
||||
state.input.removeEventListener('input', handleInput);
|
||||
state.input.removeEventListener('keydown', handleInputKeydown);
|
||||
state.trigger?.removeEventListener('click', handleTriggerClick);
|
||||
state.clearButton?.removeEventListener('click', handleClearClick);
|
||||
state.listbox.removeEventListener('mousemove', handleListboxMousemove);
|
||||
state.listbox.removeEventListener('click', handleListboxClick);
|
||||
document.removeEventListener('click', handleDocumentClick);
|
||||
@@ -444,11 +531,11 @@
|
||||
state.chips?.querySelectorAll('.combobox-chip').forEach(chip => chip.remove());
|
||||
states.delete(root);
|
||||
delete root.refresh;
|
||||
delete root.open;
|
||||
delete root.close;
|
||||
delete root.select;
|
||||
delete root.selectByValue;
|
||||
delete root.setValue;
|
||||
delete root.clear;
|
||||
delete root.deselect;
|
||||
delete root.toggle;
|
||||
delete root.selectAll;
|
||||
@@ -457,6 +544,8 @@
|
||||
|
||||
state.popover.setAttribute('aria-hidden', 'true');
|
||||
state.input.setAttribute('aria-expanded', 'false');
|
||||
state.trigger?.setAttribute('aria-expanded', 'false');
|
||||
root.clear = () => clearValue(root);
|
||||
|
||||
Object.defineProperty(root, 'value', {
|
||||
configurable: true,
|
||||
|
||||
+3
-1
@@ -2,7 +2,9 @@
|
||||
const states = new WeakMap();
|
||||
|
||||
const isDisabled = (item) =>
|
||||
item.hasAttribute('disabled') || item.getAttribute('aria-disabled') === 'true';
|
||||
item.hasAttribute('disabled') ||
|
||||
item.getAttribute('aria-disabled') === 'true' ||
|
||||
item.getAttribute('data-disabled') === 'true';
|
||||
|
||||
const getElements = (container) => ({
|
||||
input: container.querySelector('header input'),
|
||||
|
||||
+12
-1
@@ -146,7 +146,18 @@
|
||||
const handleMenuMouseleave = () => setActiveItem(state, -1);
|
||||
const handleMenuClick = (event) => {
|
||||
const menuItem = event.target.closest('[role^="menuitem"]');
|
||||
if (menuItem && !isDisabled(menuItem)) root.close();
|
||||
if (!menuItem || isDisabled(menuItem)) return;
|
||||
|
||||
if (menuItem.getAttribute('role') === 'menuitemcheckbox') {
|
||||
menuItem.setAttribute('aria-checked', menuItem.getAttribute('aria-checked') !== 'true');
|
||||
} else if (menuItem.getAttribute('role') === 'menuitemradio') {
|
||||
const group = menuItem.closest('[role="group"], [role="menu"]');
|
||||
group?.querySelectorAll('[role="menuitemradio"]').forEach((item) => {
|
||||
if (!isDisabled(item)) item.setAttribute('aria-checked', item === menuItem ? 'true' : 'false');
|
||||
});
|
||||
}
|
||||
|
||||
root.close();
|
||||
};
|
||||
|
||||
const handleDocumentClick = (event) => {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
@param multiple {boolean} [optional] [default=false] - Enables multiple selection mode with chips.
|
||||
@param placeholder {string} [optional] - Placeholder text for the visible input.
|
||||
@param close_on_select {boolean} [optional] [default=false] - Closes the popover when selecting an option in multiple mode.
|
||||
@param clear {boolean} [optional] [default=false] - Shows a clear button in single-select mode.
|
||||
@param auto_highlight {boolean} [optional] [default=false] - Highlights the first matching option while filtering.
|
||||
@param format {string} [optional] [default="value"] - Hidden input serialization format: "value" or "object".
|
||||
@param main_attrs {object} [optional] - Additional HTML attributes for the root div.
|
||||
@@ -22,6 +23,7 @@
|
||||
multiple=false,
|
||||
placeholder=None,
|
||||
close_on_select=false,
|
||||
clear=false,
|
||||
auto_highlight=false,
|
||||
format="value",
|
||||
main_attrs={},
|
||||
@@ -65,7 +67,23 @@
|
||||
{% endfor %}
|
||||
>
|
||||
{% if multiple %}
|
||||
<div class="combobox-chips {{ input_attrs.class }}">
|
||||
<input
|
||||
type="text"
|
||||
role="combobox"
|
||||
class="{{ input_attrs.class }}"
|
||||
placeholder="{{ placeholder or '' }}"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="false"
|
||||
aria-controls="{{ id }}-listbox"
|
||||
{% for key, value in input_attrs %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% elif clear %}
|
||||
<div class="input-group {{ input_attrs.class }}">
|
||||
<input
|
||||
type="text"
|
||||
role="combobox"
|
||||
@@ -80,6 +98,14 @@
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
<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="{{ id }}-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>
|
||||
{% else %}
|
||||
<input
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
id="{{ id }}-menu"
|
||||
aria-orientation="vertical"
|
||||
data-empty="{{ empty_text }}"
|
||||
class="scrollbar-sm {{ menu_attrs.class }}"
|
||||
class="{{ menu_attrs.class }}"
|
||||
{% for key, value in menu_attrs %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
@@ -121,7 +121,7 @@
|
||||
id="{{ id }}-menu"
|
||||
aria-orientation="vertical"
|
||||
data-empty="{{ empty_text }}"
|
||||
class="scrollbar-sm {{ menu_attrs.class }}"
|
||||
class="{{ menu_attrs.class }}"
|
||||
{% for key, value in menu_attrs %}
|
||||
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
+50
-48
@@ -1,8 +1,7 @@
|
||||
---
|
||||
import "../css/landing.css";
|
||||
|
||||
import basecoatJsUrl from "basecoat-css/basecoat.min?url";
|
||||
import dropdownMenuJsUrl from "basecoat-css/dropdown-menu.min?url";
|
||||
import { CommandDialog } from "reallysimpledocs/components";
|
||||
import basecoatJsUrl from "basecoat-css/all.min?url";
|
||||
import StyleHead from "../../docs/src/site/StyleHead.astro";
|
||||
import vegaUrl from "../../docs/src/assets/styles/style-vega.css?url";
|
||||
import novaUrl from "../../docs/src/assets/styles/style-nova.css?url";
|
||||
import maiaUrl from "../../docs/src/assets/styles/style-maia.css?url";
|
||||
@@ -180,36 +179,7 @@ const sidebarSections = [
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<style id="docs-style-gate" is:inline>
|
||||
html[data-style-pending] body {
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
<script is:inline define:vars={{ styleUrls }}>
|
||||
(() => {
|
||||
const styleGateId = "docs-style-gate";
|
||||
const styleLinkId = "docs-style-variant";
|
||||
const getStoredStyle = () => {
|
||||
try {
|
||||
return localStorage.getItem("styleVariant") || "vega";
|
||||
} catch (_) {
|
||||
return "vega";
|
||||
}
|
||||
};
|
||||
const getStyleHref = (style) => {
|
||||
const href = styleUrls[style];
|
||||
return href?.startsWith("/docs/src/assets/styles/") ? `${href}?direct` : href;
|
||||
};
|
||||
const nextStyle = Object.prototype.hasOwnProperty.call(styleUrls, getStoredStyle()) ? getStoredStyle() : "vega";
|
||||
const href = getStyleHref(nextStyle);
|
||||
document.documentElement.dataset.styleVariant = nextStyle;
|
||||
if (!href) return;
|
||||
document.documentElement.setAttribute("data-style-pending", "");
|
||||
document.write(
|
||||
`<link id="${styleLinkId}" rel="stylesheet" href="${href}" onload="document.documentElement.removeAttribute('data-style-pending');document.getElementById('${styleGateId}')?.remove()" onerror="document.documentElement.removeAttribute('data-style-pending');document.getElementById('${styleGateId}')?.remove()">`,
|
||||
);
|
||||
})();
|
||||
</script>
|
||||
<StyleHead />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{title} | Basecoat</title>
|
||||
@@ -224,9 +194,9 @@ const sidebarSections = [
|
||||
<meta name="twitter:description" content={description} />
|
||||
<meta name="twitter:image" content={socialImage.src} />
|
||||
<script src={basecoatJsUrl} defer></script>
|
||||
<script src={dropdownMenuJsUrl} defer></script>
|
||||
</head>
|
||||
<body class="min-h-dvh overflow-x-clip bg-background text-foreground antialiased">
|
||||
<CommandDialog />
|
||||
<header class="bg-background sticky inset-x-0 top-0 isolate z-30 flex shrink-0 items-center gap-2">
|
||||
<div class="flex h-14 w-full items-center justify-between gap-2 px-4">
|
||||
<div class="flex min-w-0 items-center gap-1">
|
||||
@@ -242,6 +212,18 @@ const sidebarSections = [
|
||||
</div>
|
||||
|
||||
<div class="ml-auto flex min-w-0 flex-1 items-center justify-end gap-2">
|
||||
<div
|
||||
class="input-group h-8 w-full min-w-0 max-w-72 cursor-text sm:ml-auto"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
aria-label="Search docs"
|
||||
onclick="window.rsdOpenCommandSearch?.()"
|
||||
onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); window.rsdOpenCommandSearch?.(); }"
|
||||
>
|
||||
<input type="text" placeholder="Search..." readonly tabindex="-1" aria-hidden="true" />
|
||||
<span data-align="start" aria-hidden="true"><Fragment set:html={Search} /></span>
|
||||
<span data-align="end" aria-hidden="true"><kbd class="kbd">⌘K</kbd></span>
|
||||
</div>
|
||||
<select id="style-variant-select" class="select hidden h-8 w-28 shrink-0 text-sm leading-none sm:block" aria-label="Style pack">
|
||||
<option value="vega">Vega</option>
|
||||
<option value="nova">Nova</option>
|
||||
@@ -290,7 +272,7 @@ const sidebarSections = [
|
||||
|
||||
<section class="flex-1 p-0">
|
||||
<div class="w-full overflow-hidden">
|
||||
<div class="theme-neutral relative flex h-[980px] max-h-[calc(100svh-14rem)] min-h-[680px] w-full max-w-none flex-col gap-(--gap) overflow-hidden bg-muted p-12 pb-0! [--gap:--spacing(8)] min-[1900px]:h-[1120px] min-[1900px]:p-12 min-[1900px]:[--gap:--spacing(10)]! lg:p-6 lg:[--gap:--spacing(6)] dark:bg-background">
|
||||
<div class="relative flex h-[980px] max-h-[calc(100svh-14rem)] min-h-[680px] w-full max-w-none flex-col gap-(--gap) overflow-hidden bg-muted p-12 pb-0! [--gap:--spacing(8)] min-[1900px]:h-[1120px] min-[1900px]:p-12 min-[1900px]:[--gap:--spacing(10)]! lg:p-6 lg:[--gap:--spacing(6)] dark:bg-background" style="--chart-1: oklch(0.87 0 0); --chart-2: oklch(0.556 0 0); --color-chart-1: var(--chart-1); --color-chart-2: var(--chart-2);">
|
||||
<div class="relative z-10 mx-auto grid w-full grid-cols-1 gap-(--gap) [&_.card]:w-full md:max-w-3xl md:grid-cols-2 lg:max-w-none lg:grid-cols-3 xl:max-w-[1600px] min-[1400px]:grid-cols-4! 2xl:max-w-[1900px] min-[1900px]:grid-cols-5!">
|
||||
<div class="flex min-w-0 flex-col gap-(--gap)">
|
||||
<article class="card relative">
|
||||
@@ -392,7 +374,7 @@ const sidebarSections = [
|
||||
["Retirement", "$420,000", "65% achieved", "$273,000", "65%"],
|
||||
["Real Estate", "$85,000", "32% achieved", "$27,200", "32%"],
|
||||
].map(([label, amount, progress, value, width]) => (
|
||||
<div class="landing-demo-surface bg-muted/50 p-4">
|
||||
<div class="rounded-md [html[data-style-variant=nova]_&]:rounded-lg [html[data-style-variant=maia]_&]:rounded-2xl [html[data-style-variant=luma]_&]:rounded-2xl [html[data-style-variant=rhea]_&]:rounded-2xl [html[data-style-variant=lyra]_&]:rounded-none [html[data-style-variant=sera]_&]:rounded-none bg-muted/50 p-4">
|
||||
<p class="text-xs font-medium tracking-wider text-muted-foreground uppercase">{label}</p>
|
||||
<p class="mt-3 text-3xl font-semibold tabular-nums">{amount}</p>
|
||||
<div class="progress mt-3" role="progressbar" aria-label={`${label} savings progress`} aria-valuenow={parseInt(width, 10)} aria-valuemin="0" aria-valuemax="100"><span style={`width:${width}`}></span></div>
|
||||
@@ -418,7 +400,7 @@ const sidebarSections = [
|
||||
["May", 100],
|
||||
].map(([month, height]) => (
|
||||
<div class="flex h-full flex-1 flex-col justify-end gap-2">
|
||||
<div class="landing-demo-chart-bar min-h-2 bg-chart-2" style={`height:${height}%`}></div>
|
||||
<div class="rounded-t-md [html[data-style-variant=lyra]_&]:rounded-t-none [html[data-style-variant=sera]_&]:rounded-t-none min-h-2 bg-chart-2" style={`height:${height}%`}></div>
|
||||
<span class="text-center text-xs text-muted-foreground">{month}</span>
|
||||
</div>
|
||||
))}
|
||||
@@ -426,8 +408,8 @@ const sidebarSections = [
|
||||
</section>
|
||||
<section>
|
||||
<div class="grid w-full grid-cols-1 gap-3 xl:grid-cols-2">
|
||||
<div class="landing-demo-surface bg-muted/50 p-4"><p class="text-xs font-medium tracking-wider text-muted-foreground uppercase">Upcoming</p><p class="text-base font-semibold">May 2024</p><p class="text-sm text-muted-foreground">Scheduled</p></div>
|
||||
<div class="landing-demo-surface hidden bg-muted/50 p-4 xl:block"><p class="text-xs font-medium tracking-wider text-muted-foreground uppercase">Savings Plan</p><p class="text-base font-semibold">Accelerated</p><p class="text-sm text-muted-foreground">Recurring</p></div>
|
||||
<div class="rounded-md [html[data-style-variant=nova]_&]:rounded-lg [html[data-style-variant=maia]_&]:rounded-2xl [html[data-style-variant=luma]_&]:rounded-2xl [html[data-style-variant=rhea]_&]:rounded-2xl [html[data-style-variant=lyra]_&]:rounded-none [html[data-style-variant=sera]_&]:rounded-none bg-muted/50 p-4"><p class="text-xs font-medium tracking-wider text-muted-foreground uppercase">Upcoming</p><p class="text-base font-semibold">May 2024</p><p class="text-sm text-muted-foreground">Scheduled</p></div>
|
||||
<div class="rounded-md [html[data-style-variant=nova]_&]:rounded-lg [html[data-style-variant=maia]_&]:rounded-2xl [html[data-style-variant=luma]_&]:rounded-2xl [html[data-style-variant=rhea]_&]:rounded-2xl [html[data-style-variant=lyra]_&]:rounded-none [html[data-style-variant=sera]_&]:rounded-none hidden bg-muted/50 p-4 xl:block"><p class="text-xs font-medium tracking-wider text-muted-foreground uppercase">Savings Plan</p><p class="text-base font-semibold">Accelerated</p><p class="text-sm text-muted-foreground">Recurring</p></div>
|
||||
</div>
|
||||
</section>
|
||||
<footer><button class="btn w-full" type="button">View Full Report</button></footer>
|
||||
@@ -440,7 +422,7 @@ const sidebarSections = [
|
||||
<span class="badge" data-variant="outline"><span class="size-2 rounded-full bg-yellow-500"></span> Pending Setup</span>
|
||||
</header>
|
||||
<section class="flex flex-1 flex-col justify-end">
|
||||
<article class="item landing-demo-surface" data-variant="muted">
|
||||
<article class="item rounded-md [html[data-style-variant=nova]_&]:rounded-lg [html[data-style-variant=maia]_&]:rounded-2xl [html[data-style-variant=luma]_&]:rounded-2xl [html[data-style-variant=rhea]_&]:rounded-2xl [html[data-style-variant=lyra]_&]:rounded-none [html[data-style-variant=sera]_&]:rounded-none" data-variant="muted">
|
||||
<section class="gap-3">
|
||||
<div class="flex justify-between"><span class="text-muted-foreground">Net Royalties</span><span class="font-medium tabular-nums">$1,248.75</span></div>
|
||||
<div class="mt-3 flex justify-between"><span class="text-muted-foreground">Processing Fee</span><span class="font-medium tabular-nums">-$37.46</span></div>
|
||||
@@ -460,9 +442,9 @@ const sidebarSections = [
|
||||
<section>
|
||||
<div class="grid gap-2">
|
||||
{holdings.map(([name, shares, _amount, data]) => (
|
||||
<div class="landing-demo-surface flex items-center gap-3 bg-muted/50 p-3">
|
||||
<div class="rounded-md [html[data-style-variant=nova]_&]:rounded-lg [html[data-style-variant=maia]_&]:rounded-2xl [html[data-style-variant=luma]_&]:rounded-2xl [html[data-style-variant=rhea]_&]:rounded-2xl [html[data-style-variant=lyra]_&]:rounded-none [html[data-style-variant=sera]_&]:rounded-none flex items-center gap-3 bg-muted/50 p-3">
|
||||
<div class="mr-auto"><p class="text-sm font-medium">{name}</p><p class="text-sm text-muted-foreground">{shares}</p></div>
|
||||
<div class="hidden h-8 w-24 items-end gap-1 md:flex">{data.map((height) => <span class="landing-demo-chart-bar min-h-1 flex-1 bg-chart-2" style={`height:${height}%`}></span>)}</div>
|
||||
<div class="hidden h-8 w-24 items-end gap-1 md:flex">{data.map((height) => <span class="rounded-t-md [html[data-style-variant=lyra]_&]:rounded-t-none [html[data-style-variant=sera]_&]:rounded-t-none min-h-1 flex-1 bg-chart-2" style={`height:${height}%`}></span>)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -499,14 +481,14 @@ const sidebarSections = [
|
||||
<div class="field"><label for="email-address">Email Address</label><input id="email-address" class="input" type="email" placeholder="artist@studio.inc" /></div>
|
||||
<div class="field"><div class="flex items-center justify-between"><label for="current-password">Current Password</label><a href="#" class="text-xs font-medium tracking-wider text-muted-foreground uppercase hover:text-foreground">Forgot?</a></div><input id="current-password" class="input" type="password" placeholder="••••••••••••••••••••••••" /></div>
|
||||
</section>
|
||||
<footer class="flex-col gap-4"><button class="btn w-full" type="button"><Fragment set:html={Lock} /> Update Security</button><a href="#" class="item landing-demo-surface" data-variant="muted"><figure class="text-destructive"><Fragment set:html={Info} /></figure><section><h3>Danger Zone</h3><p class="line-clamp-1">Archive account and remove catalog</p></section><aside class="text-muted-foreground [&_svg]:size-4"><Fragment set:html={ChevronRight} /></aside></a></footer>
|
||||
<footer class="flex-col gap-4"><button class="btn w-full" type="button"><Fragment set:html={Lock} /> Update Security</button><a href="#" class="item rounded-md [html[data-style-variant=nova]_&]:rounded-lg [html[data-style-variant=maia]_&]:rounded-2xl [html[data-style-variant=luma]_&]:rounded-2xl [html[data-style-variant=rhea]_&]:rounded-2xl [html[data-style-variant=lyra]_&]:rounded-none [html[data-style-variant=sera]_&]:rounded-none" data-variant="muted"><figure class="text-destructive"><Fragment set:html={Info} /></figure><section><h3>Danger Zone</h3><p class="line-clamp-1">Archive account and remove catalog</p></section><aside class="text-muted-foreground [&_svg]:size-4"><Fragment set:html={ChevronRight} /></aside></a></footer>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="hidden min-w-0 flex-col gap-(--gap) md:flex">
|
||||
<article class="card relative">
|
||||
<section class="flex justify-center pt-6">
|
||||
<div class="landing-demo-surface border border-border bg-white p-4">
|
||||
<div class="rounded-md [html[data-style-variant=nova]_&]:rounded-lg [html[data-style-variant=maia]_&]:rounded-2xl [html[data-style-variant=luma]_&]:rounded-2xl [html[data-style-variant=rhea]_&]:rounded-2xl [html[data-style-variant=lyra]_&]:rounded-none [html[data-style-variant=sera]_&]:rounded-none border border-border bg-white p-4">
|
||||
<svg viewBox="0 0 21 21" class="size-40 text-black" role="img" aria-label="Connect device QR code" shape-rendering="crispEdges">
|
||||
<rect width="21" height="21" fill="white" />
|
||||
{qrRows.map((row, y) => [...row].map((cell, x) => cell === "1" && <rect x={x} y={y} width="1" height="1" />))}
|
||||
@@ -522,7 +504,7 @@ const sidebarSections = [
|
||||
<div class="field w-full"><label for="transfer-amount">Amount to Transfer</label><div class="input-group w-full"><span data-align="start">$</span><input id="transfer-amount" class="w-full" value="1,200.00" /></div></div>
|
||||
<div class="field w-full"><label for="from-account">From Account</label><select id="from-account" class="select w-full"><option>Main Checking (··8402) — $12,450.00</option></select></div>
|
||||
<div class="field w-full"><label for="to-account">To Account</label><select id="to-account" class="select w-full"><option>High Yield Savings (··1192) — $42,100.00</option></select></div>
|
||||
<article class="item landing-demo-surface" data-variant="muted">
|
||||
<article class="item rounded-md [html[data-style-variant=nova]_&]:rounded-lg [html[data-style-variant=maia]_&]:rounded-2xl [html[data-style-variant=luma]_&]:rounded-2xl [html[data-style-variant=rhea]_&]:rounded-2xl [html[data-style-variant=lyra]_&]:rounded-none [html[data-style-variant=sera]_&]:rounded-none" data-variant="muted">
|
||||
<section class="gap-3">
|
||||
<div class="flex justify-between"><span class="text-muted-foreground">Estimated arrival</span><span class="font-medium">Today, Apr 14</span></div><hr class="my-3 border-border" />
|
||||
<div class="flex justify-between"><span class="text-muted-foreground">Transaction fee</span><span class="font-medium tabular-nums">$0.00</span></div><hr class="my-3 border-border" />
|
||||
@@ -573,7 +555,7 @@ const sidebarSections = [
|
||||
["Scheduled transfers", "Set up a transfer to send at a later date."],
|
||||
["Recurring card payments", "Manage your repeated card transactions."],
|
||||
].map(([name, desc], index) => (
|
||||
<a href="#" class="item landing-demo-surface" data-variant="muted">
|
||||
<a href="#" class="item rounded-md [html[data-style-variant=nova]_&]:rounded-lg [html[data-style-variant=maia]_&]:rounded-2xl [html[data-style-variant=luma]_&]:rounded-2xl [html[data-style-variant=rhea]_&]:rounded-2xl [html[data-style-variant=lyra]_&]:rounded-none [html[data-style-variant=sera]_&]:rounded-none" data-variant="muted">
|
||||
<figure><Fragment set:html={index === 0 ? Settings : index === 1 ? Calendar : Refresh} /></figure>
|
||||
<section><h3>{name}</h3><p>{desc}</p></section>
|
||||
<aside class="text-muted-foreground [&_svg]:size-4"><Fragment set:html={ChevronRight} /></aside>
|
||||
@@ -630,7 +612,7 @@ const sidebarSections = [
|
||||
<section class="flex flex-col gap-4">
|
||||
<div class="flex h-[140px] w-full items-end gap-2" role="img" aria-label="Power usage by hour">
|
||||
{[32, 74, 82, 63, 89, 76, 100, 84].map((height, index) => (
|
||||
<div class="flex h-full flex-1 flex-col justify-end gap-1.5"><div class="landing-demo-chart-bar min-h-2 bg-chart-2" style={`height:${height}%`}></div><span class="text-center text-xs text-muted-foreground">{["6a", "8a", "10a", "12p", "2p", "4p", "6p", "8p"][index]}</span></div>
|
||||
<div class="flex h-full flex-1 flex-col justify-end gap-1.5"><div class="rounded-t-md [html[data-style-variant=lyra]_&]:rounded-t-none [html[data-style-variant=sera]_&]:rounded-t-none min-h-2 bg-chart-2" style={`height:${height}%`}></div><span class="text-center text-xs text-muted-foreground">{["6a", "8a", "10a", "12p", "2p", "4p", "6p", "8p"][index]}</span></div>
|
||||
))}
|
||||
</div>
|
||||
<hr class="border-border" />
|
||||
@@ -649,6 +631,26 @@ const sidebarSections = [
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="px-6 py-8 text-center text-sm text-muted-foreground">
|
||||
Built by
|
||||
<a
|
||||
href="https://x.com/hunvreus"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="underline underline-offset-2 decoration-muted-foreground/50 transition-all hover:decoration-foreground/50"
|
||||
>
|
||||
Ronan Berder</a
|
||||
>.
|
||||
<a
|
||||
href="https://github.com/sponsors/hunvreus"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="underline underline-offset-2 decoration-muted-foreground/50 transition-all hover:decoration-foreground/50"
|
||||
>
|
||||
Sponsor me</a
|
||||
>.
|
||||
</footer>
|
||||
|
||||
<script is:inline define:vars={{ styleUrls }}>
|
||||
(() => {
|
||||
const styleSelect = document.getElementById("style-variant-select");
|
||||
|
||||
Reference in New Issue
Block a user