Align button styles across all eight variants

This commit is contained in:
Ronan Berder
2026-06-01 15:10:35 +08:00
parent a54d5fb4eb
commit 317c490e0c
66 changed files with 3735 additions and 7629 deletions
+66 -1
View File
@@ -14,7 +14,10 @@ Basecoat is a vanilla JavaScript/HTML/CSS port of shadcn/ui components. It reuse
## What to edit (and what not)
- Edit sources only:
- `src/css/basecoat.css` – All component CSS classes (Tailwind + custom)
- `src/css/base/base.css` – Shared base layer, tokens, and semantic utility classes
- `src/css/components/*.css` – Component CSS classes (Tailwind + custom)
- `src/css/styles/style-*.css` – Style-pack overrides (`vega`, `nova`, `maia`, `lyra`, `mira`, `luma`, `sera`, `rhea`)
- `src/css/basecoat.css` – Aggregate CSS entrypoint
- `src/js/*.js` – Individual component JS files (kebab-case, ESM) + `basecoat.js` (component registry)
- `src/nunjucks/*.njk` and `src/jinja/*.html.jinja` – Component template macros
- `docs/src/components/*.njk` – Component documentation pages with examples
@@ -31,6 +34,67 @@ Basecoat is a vanilla JavaScript/HTML/CSS port of shadcn/ui components. It reuse
- `packages/cli` – CLI; distributed assets live under `dist/`.
- Docs site: `docs/src` (Eleventy); output goes to `_site/`.
## Style system
- Basecoat keeps its own component class API (`btn`, `dialog`, `popover`, etc.). Do not introduce shadcn `cn-*` classes into Basecoat source.
- Shadcn/ui style packs are used as reference material, but must be mapped onto Basecoat markup and selectors.
- Style-pack architecture is one file per style:
- `src/css/styles/style-vega.css`
- `src/css/styles/style-nova.css`
- `src/css/styles/style-maia.css`
- `src/css/styles/style-lyra.css`
- `src/css/styles/style-mira.css`
- `src/css/styles/style-luma.css`
- `src/css/styles/style-sera.css`
- `src/css/styles/style-rhea.css`
- Default aggregate and CDN behavior should resolve to Vega unless explicitly changed.
- Non-default styles should be selected by importing the relevant style file or using a style-specific bundled asset, not by introducing shadcn internal selector conventions.
- During migration, each component must be reviewed in two layers:
- shared/base component CSS
- style-pack CSS overrides
- Do not assume existing Basecoat component CSS is still the right baseline. Reassess shared rules vs style-owned rules component by component.
- When Basecoat differs from upstream shadcn/ui, classify the difference as either intentional or drift, and prefer explicit decisions over silent divergence.
## Component migration workflow
For each component migration, follow this sequence:
1. Review the current Basecoat component CSS and markup/templates first.
2. Review the latest upstream shadcn/ui base component source for the equivalent component.
3. Review the relevant sections in all eight upstream style packs (`vega`, `nova`, `maia`, `lyra`, `mira`, `luma`, `sera`, `rhea`).
4. Decide what belongs in shared Basecoat component CSS versus what belongs in style-pack CSS.
5. Keep Basecoat selectors and markup. Do not port shadcn `cn-*` selectors directly.
6. Remove outdated Basecoat visual rules from the shared component CSS if those rules are now style-owned.
7. Implement or update the component section in:
- `src/css/components/<component>.css`
- `src/css/styles/style-vega.css`
- `src/css/styles/style-nova.css`
- `src/css/styles/style-maia.css`
- `src/css/styles/style-lyra.css`
- `src/css/styles/style-mira.css`
- `src/css/styles/style-luma.css`
- `src/css/styles/style-sera.css`
- `src/css/styles/style-rhea.css`
8. Document any non-trivial difference from upstream as either:
- intentional
- drift fixed
- deferred
9. Update the migration tracker entry for the component.
10. Run `npm run build` after each component migration.
11. Manually verify the component in docs for:
- light and dark mode
- all supported variants and sizes
- interaction states (`hover`, `focus`, `disabled`, `aria-invalid`, open/selected states when relevant)
Per-component file checklist:
1. Basecoat component CSS: `src/css/components/<component>.css`
2. Basecoat templates/macros if relevant: `src/nunjucks/*.njk`, `src/jinja/*.html.jinja`
3. Basecoat docs page: `docs/src/components/<component>.njk`
4. Upstream base component: `../_sandbox/shadcn-ui/apps/v4/registry/bases/base/ui/<component>.tsx`
5. Upstream styles: `../_sandbox/shadcn-ui/apps/v4/registry/styles/style-{vega,nova,maia,lyra,mira,luma,sera,rhea}.css`
6. Basecoat styles: `src/css/styles/style-{vega,nova,maia,lyra,mira,luma,sera,rhea}.css`
## Conventions
- ESM everywhere (`import`/`export`).
@@ -72,6 +136,7 @@ Examples:
## Common tasks
- Update a component's CSS/JS: Edit `src/css/basecoat.css` and/or `src/js/*.js`.
- Update a component's CSS/JS: Edit the relevant file in `src/css/components/*.css`, `src/css/styles/style-*.css`, and/or `src/js/*.js`.
- Update a component template: Edit `src/nunjucks/*.njk` and/or `src/jinja/*.html.jinja`.
- Add a new component: Create JS, CSS, templates, and docs page under `docs/src/components/*.njk`.
- Update navigation: Edit files in `docs/src/_includes/partials/`.
+3
View File
@@ -0,0 +1,3 @@
@import "tailwindcss" source(none);
@import "../../src/css/base/base.css";
@import "../../src/css/styles/style-luma.css";
+3
View File
@@ -0,0 +1,3 @@
@import "tailwindcss" source(none);
@import "../../src/css/base/base.css";
@import "../../src/css/styles/style-lyra.css";
+3
View File
@@ -0,0 +1,3 @@
@import "tailwindcss" source(none);
@import "../../src/css/base/base.css";
@import "../../src/css/styles/style-maia.css";
+3
View File
@@ -0,0 +1,3 @@
@import "tailwindcss" source(none);
@import "../../src/css/base/base.css";
@import "../../src/css/styles/style-mira.css";
+3
View File
@@ -0,0 +1,3 @@
@import "tailwindcss" source(none);
@import "../../src/css/base/base.css";
@import "../../src/css/styles/style-nova.css";
+3
View File
@@ -0,0 +1,3 @@
@import "tailwindcss" source(none);
@import "../../src/css/base/base.css";
@import "../../src/css/styles/style-rhea.css";
+3
View File
@@ -0,0 +1,3 @@
@import "tailwindcss" source(none);
@import "../../src/css/base/base.css";
@import "../../src/css/styles/style-sera.css";
+24 -1
View File
@@ -36,6 +36,17 @@
}
})();
</script>
{# Style variant #}
<script>
(function() {
try {
const storedStyle = localStorage.getItem('styleVariant') || 'vega';
document.documentElement.dataset.styleVariant = storedStyle;
} catch (event) {
console.error('Could not apply style variant from localStorage', event);
}
})();
</script>
{# Meta #}
<meta charset="utf-8">
<meta http-equiv="Content-Language" content="en">
@@ -68,6 +79,18 @@
<meta name="twitter:creator" content="{{ site.author.x }}">
{# CSS #}
<link rel="stylesheet" href="/assets/styles.css">
<link rel="stylesheet" id="style-variant-stylesheet">
<script>
(() => {
const styleLink = document.getElementById('style-variant-stylesheet');
const styleVariant = document.documentElement.dataset.styleVariant || 'vega';
if (styleVariant === 'vega') {
styleLink.removeAttribute('href');
} else {
styleLink.href = `/assets/style-${styleVariant}.css`;
}
})();
</script>
{# JS #}
<script src="/assets/js/basecoat.js?v={{ pkg.version }}" defer></script>
<script src="/assets/js/command.js?v={{ pkg.version }}" defer></script>
@@ -109,4 +132,4 @@
{% from "toast.njk" import toaster %}
{{ toaster() }}
</body>
</html>
</html>
+39 -1
View File
@@ -38,6 +38,44 @@
});
})();
</script>
<select
class="select h-8 leading-none"
id="style-select"
>
<option value="vega">Vega</option>
<option value="nova">Nova</option>
<option value="maia">Maia</option>
<option value="lyra">Lyra</option>
<option value="mira">Mira</option>
<option value="luma">Luma</option>
<option value="sera">Sera</option>
<option value="rhea">Rhea</option>
</select>
<script>
(() => {
const styleSelect = document.getElementById('style-select');
const styleLink = document.getElementById('style-variant-stylesheet');
const applyStyle = (styleName) => {
const nextStyle = styleName || 'vega';
document.documentElement.dataset.styleVariant = nextStyle;
if (nextStyle === 'vega') {
styleLink.removeAttribute('href');
} else {
styleLink.href = `/assets/style-${nextStyle}.css`;
}
};
const storedStyle = localStorage.getItem('styleVariant') || 'vega';
if (styleSelect) styleSelect.value = storedStyle;
applyStyle(storedStyle);
styleSelect.addEventListener('change', () => {
const newStyle = styleSelect.value || 'vega';
applyStyle(newStyle);
localStorage.setItem('styleVariant', newStyle);
});
})();
</script>
<button
type="button"
@@ -61,4 +99,4 @@
>
{% lucide "github" %}
</a>
</div>
</div>
+1 -1
View File
@@ -173,4 +173,4 @@
footer=footer,
content_attrs={ "class": "scrollbar [&_[data-new-link]::after]:content-['New'] [&_[data-new-link]::after]:ml-auto [&_[data-new-link]::after]:text-xs [&_[data-new-link]::after]:font-medium [&_[data-new-link]::after]:bg-sidebar-primary [&_[data-new-link]::after]:text-sidebar-primary-foreground [&_[data-new-link]::after]:px-2 [&_[data-new-link]::after]:py-0.5 [&_[data-new-link]::after]:rounded-md" },
menu=menu
) }}
) }}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+21 -2
View File
@@ -28,6 +28,10 @@ toc:
id: example-loading
- label: Large
id: example-large
- label: Extra Small
id: example-extra-small
- label: Extra Small Icon
id: example-extra-small-icon
- label: Small
id: example-small
---
@@ -60,9 +64,10 @@ toc:
<li>
<b>Sizes</b>:
<ul>
<li><code>btn-xs</code> for extra small buttons.</li>
<li><code>btn-sm</code> for small buttons.</li>
<li><code>btn-lg</code> for large buttons.</li>
<li>You can combine sizes with any variant, for example <code>btn-lg-destructive</code> or <code>btn-sm-icon-outline</code>.</li>
<li>You can combine sizes with any variant, for example <code>btn-lg-destructive</code>, <code>btn-xs-outline</code> or <code>btn-sm-icon-outline</code>.</li>
</ul>
</li>
</ul>
@@ -133,9 +138,23 @@ toc:
</button>{% endset %}
{{ code_preview("button-large", code) }}
<h3 id="example-extra-small"><a href="#example-extra-small">Extra Small</a></h3>
{% set code %}<button class="btn-xs-outline">
XS outline
</button>{% endset %}
{{ code_preview("button-extra-small", code) }}
<h3 id="example-extra-small-icon"><a href="#example-extra-small-icon">Extra Small Icon</a></h3>
{% set code %}<button class="btn-xs-icon-outline">
{% lucide "plus" %}
</button>{% endset %}
{{ code_preview("button-extra-small-icon", code) }}
<h3 id="example-small"><a href="#example-small">Small</a></h3>
{% set code %}<button class="btn-sm-icon-destructive">
{% lucide "trash" %}
</button>{% endset %}
{{ code_preview("button-small", code) }}
{{ code_preview("button-small", code) }}
+156
View File
@@ -0,0 +1,156 @@
# Basecoat Multi-Style Migration Handoff
This handoff is for resuming the `release/1.0.0` work on Basecoat's split CSS architecture and shadcn/ui v4 style-pack support.
## Ground Truth
- Local Basecoat repo: `/Users/hunvreus/Workspace/basecoat`
- Local shadcn/ui upstream repo: `/Users/hunvreus/Workspace/_sandbox/shadcn-ui`
- Upstream branch/commit verified during this pass: `main` at `460ad60d8`
- Upstream style registry: `../_sandbox/shadcn-ui/apps/v4/registry/styles.tsx`
- Current upstream styles: `vega`, `nova`, `maia`, `lyra`, `mira`, `luma`, `sera`, `rhea`
The earlier five-style assumption was wrong. Luma, Sera, and Rhea are current upstream styles and must be included in every component migration.
## Core Rules
- Keep Basecoat's own class API (`btn`, `btn-outline`, `dialog`, `popover`, etc.).
- Do not introduce shadcn/ui `cn-*` classes.
- Use upstream shadcn/ui as a style reference, not as selectors to copy directly.
- Translate upstream style intent to Basecoat selectors and markup.
- Component CSS should stay structural/shared when possible.
- Style-pack CSS owns visual differences: radius, density, spacing, typography, focus rings, shadows, and style-specific variant details.
- Default aggregate behavior remains Vega.
- Non-default styles are selected by importing or loading the relevant style file, not by requiring `.style-*` wrapper classes.
## Files To Check For Every Component
- Basecoat component CSS: `src/css/components/<component>.css`
- Basecoat templates/macros if relevant: `src/nunjucks/*.njk`, `src/jinja/*.html.jinja`
- Basecoat docs page: `docs/src/components/<component>.njk`
- Upstream base component: `../_sandbox/shadcn-ui/apps/v4/registry/bases/base/ui/<component>.tsx`
- Upstream styles: `../_sandbox/shadcn-ui/apps/v4/registry/styles/style-{vega,nova,maia,lyra,mira,luma,sera,rhea}.css`
- Basecoat styles: `src/css/styles/style-{vega,nova,maia,lyra,mira,luma,sera,rhea}.css`
## Required Process
1. Review current Basecoat CSS and markup/templates/docs.
2. Review upstream base component source.
3. Review the component section in all eight upstream style files.
4. Decide what belongs in shared component CSS versus each style file.
5. Remove outdated shared visual rules if those rules are now style-owned.
6. Implement the component section in all eight Basecoat style files.
7. Keep Basecoat selectors only.
8. Classify non-trivial differences as `intentional`, `drift fixed`, or `deferred`.
9. Update `migration-tracker.md`.
10. Run `npm run build`.
11. Run `npm run docs:build` when docs or docs style assets are touched.
12. Browser-check light/dark and all eight styles.
Do not mark a component done just because CSS compiles.
## What Has Been Done
- Split CSS source into:
- `src/css/base/base.css`
- `src/css/components/*.css`
- `src/css/styles/style-*.css`
- Kept `src/css/basecoat.css` as the backward-compatible default aggregate.
- Added `src/css/basecoat.all.css` as an explicit all-in-one Tailwind source.
- Updated `scripts/build.js` to copy split CSS folders into `packages/css/dist`.
- Split `select` and `native-select`.
- Kept `src/css/components/form.css` as a convenience aggregate rather than nesting under `components/form/*`.
- Added docs style switcher in `docs/src/_includes/partials/header.njk`.
- Added docs stylesheet swapping in `docs/src/_includes/layouts/base.njk`.
- Added docs style entrypoints for all non-default styles:
- `docs/css/style-nova.css`
- `docs/css/style-maia.css`
- `docs/css/style-lyra.css`
- `docs/css/style-mira.css`
- `docs/css/style-luma.css`
- `docs/css/style-sera.css`
- `docs/css/style-rhea.css`
- Added Basecoat style files for all eight upstream styles:
- `src/css/styles/style-vega.css`
- `src/css/styles/style-nova.css`
- `src/css/styles/style-maia.css`
- `src/css/styles/style-lyra.css`
- `src/css/styles/style-mira.css`
- `src/css/styles/style-luma.css`
- `src/css/styles/style-sera.css`
- `src/css/styles/style-rhea.css`
## Button Status
Button is the only component with style-pack sections currently present.
Files involved:
- `src/css/components/button.css`
- `src/css/styles/style-vega.css`
- `src/css/styles/style-nova.css`
- `src/css/styles/style-maia.css`
- `src/css/styles/style-lyra.css`
- `src/css/styles/style-mira.css`
- `src/css/styles/style-luma.css`
- `src/css/styles/style-sera.css`
- `src/css/styles/style-rhea.css`
- `docs/src/components/button.njk`
Current button work includes:
- Shared button CSS moved toward structural rules.
- Visual rules moved into style files.
- `xs` button and icon size classes added.
- Icon-aware padding mapped with Basecoat selectors using `:has(> svg:first-child)` and `:has(> svg:last-child)`.
- All eight style files have Button sections.
- Docs include `btn-xs-outline` and `btn-xs-icon-outline` examples.
Button should remain `in-progress` until browser-audited across light/dark and all eight styles. Button-group adjacency is separate and should be handled during `button-group`.
## Current Component Status
- `button`: in progress / pending visual audit
- Everything else: pending
No component besides Button should be considered migrated.
## Issues Encountered
- The original migration plan assumed only five styles. Upstream now has eight.
- The first button attempt mixed old Basecoat visual rules with new style-pack rules, causing broken rendering.
- Some docs style entrypoints need `src/css/base/base.css` imported first so Tailwind can resolve semantic utilities like `focus-visible:border-ring`.
- Style switching should remove the override stylesheet `href` for Vega instead of setting `href=""`.
- Sidebar dropdown width was a tangent. The user explicitly said not to modify generic CSS for that; handle width with a Tailwind class only when that task is active.
## Things Learned
- Shadcn/ui icon-aware button padding is CSS-owned, not React-owned.
- Upstream uses data hooks like `has-data-[icon=inline-start]:pl-*`.
- Basecoat should map that to its own markup, currently `:has(> svg:first-child)` and `:has(> svg:last-child)`.
- Blind copy is not viable because upstream uses `cn-*` class composition while Basecoat uses explicit classes like `btn-xs-icon-outline`.
## Build And Validation Commands
```sh
npm run build
npm run docs:build
npm run docs:dev
```
Use `npm run docs:dev` for manual browser review.
## Remaining Work
1. Run build validation after the latest Luma/Sera/Rhea wiring.
2. Browser-check `/components/button` for all eight styles in light and dark mode.
3. Update Button tracker status only after visual audit.
4. Migrate `button-group` next because it interacts with button radius and adjacency.
5. Continue component-by-component using `migration-tracker.md`.
## Current Risks
- Button may still have visual deltas from upstream until audited.
- Docs style override assets may duplicate base CSS because they import base utilities for Tailwind compilation.
- The package does not currently generate separate style-specific CDN bundles beyond split style exports.
- The worktree includes unrelated sidebar template changes; do not revert or fold them into style migration unless explicitly requested.
+41
View File
@@ -0,0 +1,41 @@
# Basecoat Style Migration Tracker
Track migration of Basecoat components against the latest shadcn/ui v4 component source and all eight style packs.
Upstream source root: `../_sandbox/shadcn-ui`
Upstream style files: `apps/v4/registry/styles/style-{vega,nova,maia,lyra,mira,luma,sera,rhea}.css`
Legend:
- `pending`: not started
- `in-progress`: actively being migrated
- `done`: style-pack coverage implemented for current scope
- `deferred`: intentionally postponed
| Component | Status | Basecoat CSS | Upstream Base | Upstream Styles | Notes |
| --- | --- | --- | --- | --- | --- |
| alert | pending | `src/css/components/alert.css` | `apps/v4/registry/bases/base/ui/alert.tsx` | `style-*.css` | |
| badge | pending | `src/css/components/badge.css` | `apps/v4/registry/bases/base/ui/badge.tsx` | `style-*.css` | |
| button | done | `src/css/components/button.css` | `apps/v4/registry/bases/base/ui/button.tsx` | all 8 `style-*.css` | Standalone button rules are mapped across all eight styles, including xs sizing and icon-aware padding. Drift fixed: Maia base radius now uses upstream `rounded-4xl`. Button-group adjacency details remain separate and should be handled during button-group migration. |
| button-group | pending | `src/css/components/button-group.css` | `apps/v4/registry/bases/base/ui/button-group.tsx` | `style-*.css` | |
| card | pending | `src/css/components/card.css` | `apps/v4/registry/bases/base/ui/card.tsx` | `style-*.css` | |
| checkbox | pending | `src/css/components/checkbox.css` | `apps/v4/registry/bases/base/ui/checkbox.tsx` | `style-*.css` | |
| collapsible | pending | `src/css/components/collapsible.css` | `apps/v4/registry/bases/base/ui/collapsible.tsx` | `style-*.css` | |
| command | pending | `src/css/components/command.css` | `apps/v4/registry/bases/base/ui/command.tsx` | `style-*.css` | |
| dialog | pending | `src/css/components/dialog.css` | `apps/v4/registry/bases/base/ui/dialog.tsx` | `style-*.css` | |
| dropdown-menu | pending | `src/css/components/dropdown-menu.css` | `apps/v4/registry/bases/base/ui/dropdown-menu.tsx` | `style-*.css` | |
| field | pending | `src/css/components/field.css` | `apps/v4/registry/bases/base/ui/field.tsx` | `style-*.css` | |
| input | pending | `src/css/components/input.css` | `apps/v4/registry/bases/base/ui/input.tsx` | `style-*.css` | |
| kbd | pending | `src/css/components/kbd.css` | `apps/v4/registry/bases/base/ui/kbd.tsx` | `style-*.css` | |
| label | pending | `src/css/components/label.css` | `apps/v4/registry/bases/base/ui/label.tsx` | `style-*.css` | |
| native-select | pending | `src/css/components/native-select.css` | `apps/v4/registry/bases/base/ui/native-select.tsx` | `style-*.css` | |
| popover | pending | `src/css/components/popover.css` | `apps/v4/registry/bases/base/ui/popover.tsx` | `style-*.css` | |
| radio | pending | `src/css/components/radio.css` | `apps/v4/registry/bases/base/ui/radio-group.tsx` | `style-*.css` | |
| range | pending | `src/css/components/range.css` | `apps/v4/registry/bases/base/ui/slider.tsx` | `style-*.css` | Consider moving slider fill sync into shared JS. |
| select | pending | `src/css/components/select.css` | `apps/v4/registry/bases/base/ui/select.tsx` | `style-*.css` | |
| sidebar | pending | `src/css/components/sidebar.css` | `apps/v4/registry/bases/base/ui/sidebar.tsx` | `style-*.css` | |
| switch | pending | `src/css/components/switch.css` | `apps/v4/registry/bases/base/ui/switch.tsx` | `style-*.css` | |
| table | pending | `src/css/components/table.css` | `apps/v4/registry/bases/base/ui/table.tsx` | `style-*.css` | |
| tabs | pending | `src/css/components/tabs.css` | `apps/v4/registry/bases/base/ui/tabs.tsx` | `style-*.css` | |
| textarea | pending | `src/css/components/textarea.css` | `apps/v4/registry/bases/base/ui/textarea.tsx` | `style-*.css` | |
| toast | pending | `src/css/components/toast.css` | `apps/v4/registry/bases/base/ui/sonner.tsx` | `style-*.css` | Needs review against current Basecoat toast structure. |
| tooltip | pending | `src/css/components/tooltip.css` | `apps/v4/registry/bases/base/ui/tooltip.tsx` | `style-*.css` | |
+3 -2
View File
@@ -34,8 +34,9 @@
"homepage": "https://github.com/hunvreus/basecoat#readme",
"scripts": {
"build": "node ./scripts/build.js",
"docs:build": "tailwindcss -i ./docs/css/styles.css -o ./docs/src/assets/styles.css --minify && eleventy",
"docs:dev:css": "tailwindcss -i ./docs/css/styles.css -o ./docs/src/assets/styles.css --watch",
"docs:build:css": "tailwindcss -i ./docs/css/styles.css -o ./docs/src/assets/styles.css --minify && tailwindcss -i ./docs/css/style-nova.css -o ./docs/src/assets/style-nova.css --minify && tailwindcss -i ./docs/css/style-maia.css -o ./docs/src/assets/style-maia.css --minify && tailwindcss -i ./docs/css/style-lyra.css -o ./docs/src/assets/style-lyra.css --minify && tailwindcss -i ./docs/css/style-mira.css -o ./docs/src/assets/style-mira.css --minify && tailwindcss -i ./docs/css/style-luma.css -o ./docs/src/assets/style-luma.css --minify && tailwindcss -i ./docs/css/style-sera.css -o ./docs/src/assets/style-sera.css --minify && tailwindcss -i ./docs/css/style-rhea.css -o ./docs/src/assets/style-rhea.css --minify",
"docs:build": "npm run docs:build:css && eleventy",
"docs:dev:css": "concurrently \"tailwindcss -i ./docs/css/styles.css -o ./docs/src/assets/styles.css --watch\" \"tailwindcss -i ./docs/css/style-nova.css -o ./docs/src/assets/style-nova.css --watch\" \"tailwindcss -i ./docs/css/style-maia.css -o ./docs/src/assets/style-maia.css --watch\" \"tailwindcss -i ./docs/css/style-lyra.css -o ./docs/src/assets/style-lyra.css --watch\" \"tailwindcss -i ./docs/css/style-mira.css -o ./docs/src/assets/style-mira.css --watch\" \"tailwindcss -i ./docs/css/style-luma.css -o ./docs/src/assets/style-luma.css --watch\" \"tailwindcss -i ./docs/css/style-sera.css -o ./docs/src/assets/style-sera.css --watch\" \"tailwindcss -i ./docs/css/style-rhea.css -o ./docs/src/assets/style-rhea.css --watch\"",
"docs:dev:eleventy": "ELEVENTY_ENV=prod eleventy --serve",
"docs:dev": "concurrently \"npm:docs:dev:css\" \"npm:docs:dev:eleventy\""
},
+10 -1
View File
@@ -58,6 +58,15 @@
"./tabs.min": "./dist/js/tabs.min.js",
"./toast": "./dist/js/toast.js",
"./toast.min": "./dist/js/toast.min.js",
"./all.css": "./dist/basecoat.all.css",
"./styles/vega": "./dist/styles/style-vega.css",
"./styles/nova": "./dist/styles/style-nova.css",
"./styles/maia": "./dist/styles/style-maia.css",
"./styles/lyra": "./dist/styles/style-lyra.css",
"./styles/mira": "./dist/styles/style-mira.css",
"./styles/luma": "./dist/styles/style-luma.css",
"./styles/sera": "./dist/styles/style-sera.css",
"./styles/rhea": "./dist/styles/style-rhea.css",
"./package.json": "./package.json"
}
}
}
+111
View File
@@ -0,0 +1,111 @@
# Basecoat Multi-Style + Theme Refactor Plan
## Goals
- Support the current shadcn/ui visual packs: `vega`, `nova`, `maia`, `lyra`, `mira`, `luma`, `sera`, `rhea`.
- Keep current CDN and Tailwind/NPM usage working.
- Keep Basecoat's own class API (`btn`, `dialog`, `popover`, etc.).
- Split CSS into base tokens/utilities, component structure, style-pack visuals, and aggregate outputs.
- Make custom token themes, including TweakCN-style themes, predictable by loading them after Basecoat CSS.
## Current Upstream Reference
- Local repo: `../_sandbox/shadcn-ui`
- Style registry: `../_sandbox/shadcn-ui/apps/v4/registry/styles.tsx`
- Style files: `../_sandbox/shadcn-ui/apps/v4/registry/styles/style-{vega,nova,maia,lyra,mira,luma,sera,rhea}.css`
- Base components: `../_sandbox/shadcn-ui/apps/v4/registry/bases/base/ui/<component>.tsx`
## Target CSS Architecture
1. `src/css/base/base.css`
- base layer, default token fallbacks, and semantic utilities needed by CDN users
2. `src/css/components/*.css`
- Basecoat component selectors and shared structural behavior
3. `src/css/styles/style-*.css`
- one file per style pack: `vega`, `nova`, `maia`, `lyra`, `mira`, `luma`, `sera`, `rhea`
- component visuals: size, spacing, radius, typography, shadows, focus rings, variant colors when style-owned
4. Aggregates
- `src/css/basecoat.css`: backward-compatible default aggregate, includes Vega
- `src/css/basecoat.all.css`: explicit all-in-one Tailwind source
- `src/css/basecoat.cdn.css`: CDN build entry
No `.style-*` wrapper is part of the Basecoat public contract for style selection. Users choose a style by importing a style file or using a style-specific generated asset. Docs may switch by swapping stylesheet URLs.
## Per-Component File Checklist
For every component, check these files before editing:
- Basecoat component CSS: `src/css/components/<component>.css`
- Basecoat templates/macros if relevant: `src/nunjucks/*.njk`, `src/jinja/*.html.jinja`
- Basecoat docs examples: `docs/src/components/<component>.njk`
- Upstream base component: `../_sandbox/shadcn-ui/apps/v4/registry/bases/base/ui/<component>.tsx`
- Upstream style packs: `../_sandbox/shadcn-ui/apps/v4/registry/styles/style-{vega,nova,maia,lyra,mira,luma,sera,rhea}.css`
- Basecoat style packs: `src/css/styles/style-{vega,nova,maia,lyra,mira,luma,sera,rhea}.css`
## Component Migration Workflow
1. Review current Basecoat CSS, markup, templates, and docs for the component.
2. Review the upstream base component and all eight upstream style files.
3. Decide what is shared structure versus style-pack-owned visual rules.
4. Remove outdated visual rules from shared component CSS when they are now style-owned.
5. Map upstream style intent onto Basecoat selectors. Do not port `cn-*` selectors.
6. Update the component section in all eight Basecoat style files.
7. Document non-trivial deltas as `intentional`, `drift fixed`, or `deferred`.
8. Update `migration-tracker.md`.
9. Run `npm run build`.
10. Run `npm run docs:build` when docs or docs style assets change.
11. Manually verify the docs page in light/dark and all eight styles.
Do not mark a component done because CSS compiles. It needs a visual/state check.
## Packaging Contract
- Keep `@import "basecoat-css";` as the default Vega-compatible import.
- Keep existing CDN files and JS bundle paths.
- Export split style files from `packages/css` as `basecoat-css/styles/<style>`.
- Keep the default CDN output equivalent to Vega unless a separate style-specific CDN asset is explicitly added.
- Custom themes should load after Basecoat styles so their `:root` and `.dark` tokens override defaults.
## Component Migration Order
1. `button`
2. `button-group`
3. `input`
4. `textarea`
5. `native-select`
6. `select`
7. `checkbox`
8. `radio`
9. `switch`
10. `range`
11. `label`
12. `field`
13. `card`
14. `alert`
15. `badge`
16. `table`
17. `tabs`
18. `popover`
19. `dropdown-menu`
20. `dialog`
21. `command`
22. `sidebar`
23. `toast`
24. `tooltip`
25. `collapsible`
26. `kbd`
## Current Status
- CSS split exists.
- Docs style switcher exists.
- Button is started and style sections exist for all eight styles.
- Button still needs a strict browser audit across all eight styles and light/dark.
- No other component should be considered migrated.
## Risks
- Selector drift between Basecoat markup and upstream shadcn/ui assumptions.
- Incorrectly leaving visual rules in shared component CSS.
- Bundle growth from style-specific assets.
- Docs style assets need base semantic utilities available during Tailwind compilation.
+13
View File
@@ -77,6 +77,7 @@ async function build() {
const srcDir = path.join(projectRoot, 'src');
const srcCssDir = path.join(srcDir, 'css');
const srcCssStylesDir = path.join(srcCssDir, 'styles');
const srcJsDir = path.join(srcDir, 'js');
const srcNunjucksDir = path.join(srcDir, 'nunjucks');
const srcJinjaDir = path.join(srcDir, 'jinja');
@@ -149,8 +150,20 @@ async function build() {
console.log('Building CSS package...');
await ensureDir(cssDistDir); // Ensure dist dir exists for css package
await fs.copyFile(path.join(srcCssDir, 'basecoat.css'), path.join(cssDistDir, 'basecoat.css'));
await fs.copyFile(path.join(srcCssDir, 'basecoat.all.css'), path.join(cssDistDir, 'basecoat.all.css'));
console.log(`Copied basecoat.css to ${cssDistDir}`);
// Copy split CSS folders used by basecoat.css imports.
const cssBaseSrcDir = path.join(srcCssDir, 'base');
const cssComponentsSrcDir = path.join(srcCssDir, 'components');
const cssBaseDistDir = path.join(cssDistDir, 'base');
const cssComponentsDistDir = path.join(cssDistDir, 'components');
const cssStylesDistDir = path.join(cssDistDir, 'styles');
await copyDirRecursive(cssBaseSrcDir, cssBaseDistDir);
await copyDirRecursive(cssComponentsSrcDir, cssComponentsDistDir);
await copyDirRecursive(srcCssStylesDir, cssStylesDistDir);
console.log(`Copied split CSS folders to ${cssDistDir}`);
// Create Tailwind CSS builds for the CSS package
const cdnCssSrc = path.join(srcCssDir, 'basecoat.cdn.css');
const cssDistCdnPath = path.join(cssDistDir, 'basecoat.cdn.css');
+150
View File
@@ -0,0 +1,150 @@
@custom-variant dark (&:is(.dark *));
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--sidebar-width: 16rem;
--sidebar-mobile-width: 18rem;
--scrollbar-track: transparent;
--scrollbar-thumb: rgba(0, 0, 0, 0.3);
--scrollbar-width: 6px;
--scrollbar-radius: 6px;
--chevron-down-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.556 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>'); /* --muted-foreground */
--chevron-down-icon-50: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.556 0 0 / 0.5)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>'); /* --muted-foreground + 50% opacity */
--check-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.556 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>'); /* --muted-foreground */
}
.dark {
--background: oklch(0.145 0 0);
--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-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.371 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.439 0 0);
--scrollbar-thumb: rgba(255, 255, 255, 0.3);
--chevron-down-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.708 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>'); /* --muted-foreground */
--chevron-down-icon-50: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.708 0 0 / 0.5)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>'); /* --muted-foreground + 50% opacity */
--check-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="oklch(0.708 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>');/* --muted-foreground */
color-scheme: dark;
}
@theme {
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
html {
@apply scroll-smooth;
}
body {
@apply bg-background text-foreground overscroll-none antialiased;
}
.scrollbar {
scrollbar-width: thin;
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
&::-webkit-scrollbar {
width: var(--scrollbar-width);
}
&::-webkit-scrollbar-track {
background: var(--scrollbar-track);
}
&::-webkit-scrollbar-thumb {
background: var(--scrollbar-thumb);
border-radius: var(--scrollbar-radius);
}
}
}
+1
View File
@@ -0,0 +1 @@
@import "./basecoat.css";
+30 -1311
View File
File diff suppressed because it is too large Load Diff
+35
View File
@@ -0,0 +1,35 @@
/* Alert */
@layer components {
.alert,
.alert-destructive {
@apply relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current;
> h2,
> h3,
> h4,
> h5,
> h6,
> strong,
> [data-title] {
@apply col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight;
}
> section {
@apply text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed;
ul {
@apply list-inside list-disc text-sm;
}
}
}
.alert {
@apply bg-card text-card-foreground;
}
.alert-destructive {
@apply text-destructive bg-card [&>svg]:text-current;
> section {
@apply text-destructive;
}
}
}
+24
View File
@@ -0,0 +1,24 @@
/* Badge */
@layer components {
.badge,
.badge-primary,
.badge-secondary,
.badge-destructive,
.badge-outline {
@apply inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden;
}
.badge,
.badge-primary {
@apply border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90;
}
.badge-secondary {
@apply border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90;
}
.badge-destructive {
@apply border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60;
}
.badge-outline {
@apply text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground;
}
}
+44
View File
@@ -0,0 +1,44 @@
/* Button Group */
@layer components {
.button-group {
@apply inline-flex w-fit items-stretch;
/* isolate */
> *:focus-visible,
> :is(.dropdown-menu, .popover, .select) > button:focus-visible {
@apply relative z-10;
}
> hr[role='separator'] {
@apply w-0 h-auto self-stretch border border-input shrink-0 m-0;
}
&:not([data-orientation='vertical']) {
> *:not(:first-child),
> :is(.dropdown-menu, .popover, .select):not(:first-child) > button {
@apply rounded-l-none border-l-0;
}
> *:not(:last-child),
> :is(.dropdown-menu, .popover, .select):not(:last-child) > button {
@apply rounded-r-none;
}
}
&[data-orientation='vertical'] {
@apply flex-col;
> hr[role='separator'] {
@apply w-auto h-px;
}
> *:not(:first-child),
> :is(.dropdown-menu, .popover, .select):not(:first-child) > button {
@apply rounded-t-none border-t-0;
}
> *:not(:last-child),
> :is(.dropdown-menu, .popover, .select):not(:last-child) > button {
@apply rounded-b-none;
}
}
}
}
+61
View File
@@ -0,0 +1,61 @@
/* Button */
@layer components {
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive,
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive,
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive,
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive,
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive,
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@apply inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 cursor-pointer;
}
}
+25
View File
@@ -0,0 +1,25 @@
/* Card */
@layer components {
.card {
@apply bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm;
> header {
@apply @container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6;
/* TODO: CLEAN has-data-[slot=card-action] */
h2 {
@apply leading-none font-semibold;
}
p {
@apply text-muted-foreground text-sm;
}
}
> section {
@apply px-6;
}
> footer {
@apply flex items-center px-6 [.border-t]:pt-6;
}
}
}
+13
View File
@@ -0,0 +1,13 @@
/* Checkbox */
@layer components {
:is(.form, .field) input[type='checkbox']:not([role='switch']),
.input[type='checkbox']:not([role='switch']) {
@apply appearance-none border-input dark:bg-input/30 checked:bg-primary dark:checked:bg-primary checked:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50;
&:checked:after {
@apply content-[''] block size-3.5 bg-primary-foreground;
@apply mask-[image:var(--check-icon)] mask-size-[0.875rem] mask-no-repeat mask-center;
}
}
}
+21
View File
@@ -0,0 +1,21 @@
/* Collapsible */
@layer components {
details {
&::details-content {
block-size: 0;
@apply block opacity-0 transition-discrete transition-all;
}
&[open]::details-content {
block-size: auto;
block-size: calc-size(auto, size);
@apply opacity-100;
}
summary {
@apply inline-flex items-center cursor-pointer;
}
}
details > summary::-webkit-details-marker {
display: none;
}
}
+109
View File
@@ -0,0 +1,109 @@
/* Command */
@layer components {
.command-dialog {
@apply inset-y-0 opacity-0 transition-all transition-discrete;
&:is([open],:popover-open) {
@apply opacity-100;
&::backdrop {
@apply opacity-100;
}
> * {
@apply scale-100;
}
@starting-style {
@apply opacity-0;
&::backdrop {
@apply opacity-0;
}
> * {
@apply scale-95;
}
}
}
&::backdrop {
@apply bg-black/50 opacity-0 transition-all transition-discrete;
}
> * {
@apply bg-background fixed top-[50%] left-[50%] z-50 flex flex-col w-full max-w-[calc(100%_-_2rem)] -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-lg border shadow-lg sm:max-w-lg max-h-[min(640px,calc(100%_-_2rem))];
@apply transition-all scale-95;
> button,
> form[method='dialog'] {
@apply absolute top-4 right-4;
}
> button,
> form[method='dialog'] > button {
@apply ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4;
}
}
.command {
> header input {
@apply h-12;
}
[role='menu'] {
@apply px-2;
[role='menuitem'] {
@apply py-3 px-2 [&_svg]:size-5;
}
[role='heading'] {
@apply px-2 py-1.5 font-medium text-muted-foreground;
}
}
}
}
.command {
> header {
@apply flex items-center border-b px-3 gap-2;
svg {
@apply size-4 shrink-0 opacity-50;
}
input {
@apply placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50;
}
}
[role='menu'] {
@apply max-h-[300px] overflow-y-auto overflow-x-hidden p-1;
[role='menuitem'] {
@apply aria-hidden:hidden relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none aria-disabled:opacity-50 aria-disabled:pointer-events-none disabled:opacity-50 disabled:pointer-events-none w-full truncate [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4;
&.active {
@apply bg-accent text-accent-foreground;
}
}
[role='heading'] {
@apply text-muted-foreground flex px-2 py-1.5 text-xs font-medium;
}
[role='group']:not(:has([role='menuitem']:not([aria-hidden='true']))) {
@apply hidden;
}
[role='separator'] {
@apply border-border -mx-1 my-1;
}
&:not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
@apply flex items-center justify-center py-6 px-3 text-sm truncate -m-1;
}
&[data-empty]:not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
@apply content-[attr(data-empty)];
}
&:not([data-empty]):not(:has([role='menuitem']:not([aria-hidden='true'])))::before {
@apply content-['No_results_found'];
}
}
&:not([data-command-initialized]) [role='menuitem'] {
@apply hover:bg-accent hover:text-accent-foreground;
}
&:has(> header input:not(:placeholder-shown)) [role='separator'] {
@apply hidden;
}
}
}
+61
View File
@@ -0,0 +1,61 @@
/* Dialog */
@layer components {
.dialog {
@apply inset-y-0 opacity-0 transition-all transition-discrete;
&:is([open],:popover-open) {
@apply opacity-100;
&::backdrop {
@apply opacity-100;
}
> * {
@apply scale-100;
}
@starting-style {
@apply opacity-0;
&::backdrop {
@apply opacity-0;
}
> * {
@apply scale-95;
}
}
}
&::backdrop {
@apply bg-black/50 opacity-0 transition-all transition-discrete;
}
> * {
@apply bg-background fixed top-[50%] left-[50%] z-50 flex flex-col w-full max-w-[calc(100%_-_2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-lg border p-6 shadow-lg sm:max-w-lg max-h-[calc(100%_-_2rem)];
@apply transition-all scale-95;
> header {
@apply flex flex-col gap-2 text-center sm:text-left;
> h2 {
@apply text-lg leading-none font-semibold;
}
> p {
@apply text-muted-foreground text-sm;
}
}
> section {
@apply flex-1 -mx-6 px-6;
}
> footer {
@apply flex flex-col-reverse gap-2 sm:flex-row sm:justify-end;
}
> button,
> form[method='dialog'] {
@apply absolute top-4 right-4;
}
> button,
> form[method='dialog'] > button {
@apply ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4;
}
}
}
}
+39
View File
@@ -0,0 +1,39 @@
/* Dropdown Menu */
@layer components {
.dropdown-menu {
@apply relative inline-flex;
[data-popover] {
@apply p-1;
min-width: anchor-size(width);
[role='menuitem'],
[role='menuitemcheckbox'],
[role='menuitemradio'] {
@apply aria-hidden:hidden [&_svg]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none [&_svg]:shrink-0 [&_svg]:size-4 aria-disabled:opacity-50 aria-disabled:pointer-events-none disabled:opacity-50 disabled:pointer-events-none w-full truncate;
&:not([aria-disabled='true']) {
@apply focus-visible:bg-accent focus-visible:text-accent-foreground;
}
&.active {
@apply bg-accent text-accent-foreground;
}
}
[role='menu'] [role='heading'] {
@apply flex px-2 py-1.5 text-sm font-medium;
}
[role='separator'] {
@apply border-border -mx-1 my-1;
}
}
&:not([data-dropdown-menu-initialized]) [data-popover] {
[role='menuitem'],
[role='menuitemcheckbox'],
[role='menuitemradio'] {
@apply hover:bg-accent hover:text-accent-foreground;
}
}
}
}
+44
View File
@@ -0,0 +1,44 @@
/* Field */
@layer components {
.fieldset {
@apply flex flex-col gap-6;
> legend {
@apply text-base font-medium mb-3;
}
}
.field {
@apply flex flex-col w-full gap-3 data-[invalid=true]:text-destructive [&>*]:w-full [&>.sr-only]:w-auto;
h2,
h3 {
@apply flex w-fit items-center gap-2 text-sm leading-snug font-medium;
}
[role="alert"] {
@apply text-sm text-destructive font-normal;
ul {
@apply ml-4 flex list-disc flex-col gap-1;
}
}
section {
@apply flex flex-1 flex-col gap-1.5 leading-snug;
}
&[data-orientation='horizontal'] {
@apply flex-row items-center [&>label]:flex-auto has-[>section]:items-start has-[>section]:[&>[type=checkbox],[type=radio]]:mt-px [&_p]:text-balance;
}
}
.fieldset legend + p,
.field > p,
.field section > p {
@apply text-muted-foreground text-sm leading-normal font-normal [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4;
}
.fieldset legend + p {
@apply -mt-1.5;
}
.field > p {
@apply last:mt-0 nth-last-2:-mt-1;
}
}
+9
View File
@@ -0,0 +1,9 @@
@import "./checkbox.css";
@import "./input.css";
@import "./label.css";
@import "./radio.css";
@import "./range.css";
@import "./native-select.css";
@import "./select.css";
@import "./switch.css";
@import "./textarea.css";
+34
View File
@@ -0,0 +1,34 @@
/* Input */
@layer components {
:is(.form, .field) input[type='text']:not(:is(.select [data-popover] > header input[role='combobox'], .command > header input, .dialog > * > header input)),
:is(.form, .field) input[type='email'],
:is(.form, .field) input[type='password'],
:is(.form, .field) input[type='number'],
:is(.form, .field) input[type='file'],
:is(.form, .field) input[type='tel'],
:is(.form, .field) input[type='url'],
:is(.form, .field) input[type='search']:not(:is(.select [data-popover] > header input[role='combobox'], .command > header input, .dialog > * > header input)),
:is(.form, .field) input[type='date'],
:is(.form, .field) input[type='datetime-local'],
:is(.form, .field) input[type='month'],
:is(.form, .field) input[type='week'],
:is(.form, .field) input[type='time'],
.input[type='text'],
.input[type='email'],
.input[type='password'],
.input[type='number'],
.input[type='file'],
.input[type='tel'],
.input[type='url'],
.input[type='search'],
.input[type='date'],
.input[type='datetime-local'],
.input[type='month'],
.input[type='week'],
.input[type='time'] {
@apply appearance-none file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm;
@apply focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px];
@apply aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive;
}
}
+7
View File
@@ -0,0 +1,7 @@
/* Kbd */
@layer components {
.kbd {
@apply bg-muted text-muted-foreground pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm px-1 font-sans text-xs font-medium select-none [&_svg:not([class*='size-'])]:size-3;
}
}
+13
View File
@@ -0,0 +1,13 @@
/* Label */
@layer components {
:is(.form, .field) label,
.label {
@apply flex items-center gap-2 text-sm leading-none font-medium select-none peer-disabled:pointer-events-none peer-disabled:opacity-50;
&:has(>*:disabled),
&:has(+*:disabled) {
@apply opacity-50 pointer-events-none;
}
}
}
+13
View File
@@ -0,0 +1,13 @@
/* Native Select */
@layer components {
:is(.form, .field) select,
select.select {
@apply appearance-none border-input 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:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent pl-3 pr-9 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 h-9;
@apply bg-[image:var(--chevron-down-icon-50)] bg-no-repeat bg-position-[center_right_0.75rem] bg-size-[1rem];
option,
optgroup {
@apply bg-popover text-popover-foreground;
}
}
}
+72
View File
@@ -0,0 +1,72 @@
/* Popover */
@layer components {
[data-popover] {
@apply absolute bg-popover text-popover-foreground overflow-x-hidden overflow-y-auto rounded-md border shadow-md z-50 visible opacity-100 scale-100 min-w-full w-max transition-all;
&[aria-hidden='true'] {
@apply invisible opacity-0 scale-95;
&:not([data-side]),
&[data-side='bottom'] {
@apply -translate-y-2;
}
&[data-side='top'] {
@apply translate-y-2;
}
&[data-side='left'] {
@apply translate-x-2;
}
&[data-side='right'] {
@apply -translate-x-2;
}
}
&:not([data-side]),
&[data-side='bottom'] {
@apply mt-1 top-full;
}
&[data-side='top'] {
@apply mb-1 bottom-full;
}
&[data-side='left'] {
@apply mr-1 right-full;
}
&[data-side='right'] {
@apply ml-1 left-full;
}
&:not([data-side]),
&[data-side='bottom'],
&[data-side='top'] {
&:not([data-align]),
&[data-align='start'] {
@apply left-0;
}
&[data-align='end'] {
@apply right-0;
}
&[data-align='center'] {
@apply left-1/2 -translate-x-1/2;
}
}
&[data-side='left'],
&[data-side='right'] {
&:not([data-align]),
&[data-align='start'] {
@apply top-0;
}
&[data-align='end'] {
@apply bottom-0;
}
&[data-align='center'] {
@apply top-1/2 -translate-y-1/2;
}
}
}
.popover {
@apply relative inline-flex;
[data-popover] {
@apply p-4;
}
}
}
+12
View File
@@ -0,0 +1,12 @@
/* Radio */
@layer components {
:is(.form, .field) input[type='radio'],
.input[type='radio'] {
@apply appearance-none border-input text-primary 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:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 relative;
&:checked:before {
@apply absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 content-[''] rounded-full size-2 bg-primary;
}
}
}
+49
View File
@@ -0,0 +1,49 @@
/* Range */
@layer components {
:is(.form, .field) input[type='range'],
.input[type='range'] {
@apply appearance-none flex items-center p-0 outline-none;
--slider-value: 20%;
&:hover,
&:focus-visible {
&::-webkit-slider-thumb {
@apply ring-4;
}
&::-moz-range-thumb {
@apply ring-4;
}
&::-ms-thumb {
@apply ring-4;
}
}
&::-webkit-slider-thumb {
@apply appearance-none border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm -mt-1.25;
}
&::-webkit-slider-runnable-track {
@apply appearance-none rounded-full h-1.5 w-full;
background: linear-gradient(to right, var(--primary) var(--slider-value), var(--muted) var(--slider-value));
}
&::-moz-range-thumb {
@apply appearance-none border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm -mt-1.25;
}
&::-moz-range-track {
@apply appearance-none rounded-full h-1.5 w-full;
background: linear-gradient(to right, var(--primary) var(--slider-value), var(--muted) var(--slider-value));
}
&::-ms-thumb {
@apply appearance-none border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm -mt-1.25;
}
&::-ms-track {
@apply appearance-none rounded-full h-1.5 w-full;
}
&::-ms-fill-lower {
@apply bg-primary rounded-full;
}
&::-ms-fill-upper {
@apply bg-muted rounded-full;
}
}
}
+64
View File
@@ -0,0 +1,64 @@
/* Select */
@layer components {
*:not(select).select {
@apply relative inline-flex;
& > button {
@apply justify-between font-normal;
&[aria-invalid='true'] {
@apply ring-destructive/20 dark:ring-destructive/40 border-destructive;
}
}
[data-popover] {
@apply p-1;
[role='option'] {
@apply aria-hidden:hidden [&_svg]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm pl-2 py-1.5 pr-7.5 text-sm outline-hidden select-none [&_svg]:shrink-0 [&_svg]:size-4 aria-disabled:opacity-50 aria-disabled:pointer-events-none disabled:opacity-50 disabled:pointer-events-none w-full truncate;
&[aria-selected='true'] {
@apply bg-[image:var(--check-icon)] bg-no-repeat bg-position-[center_right_0.5rem] bg-size-[0.875rem];
}
&.active,
&:focus-visible {
@apply bg-accent text-accent-foreground;
}
}
[role='listbox'] [role='heading'] {
@apply flex text-muted-foreground px-2 py-1.5 text-xs;
}
[role='listbox'] [role='group']:not(:has([role='option']:not([aria-hidden='true']))) {
@apply hidden;
}
[role='separator'] {
@apply border-border -mx-1 my-1;
}
> header {
@apply flex h-9 items-center gap-2 border-b px-3 -mx-1 -mt-1 mb-1;
svg {
@apply size-4 shrink-0 opacity-50;
}
input[role='combobox'] {
@apply placeholder:text-muted-foreground flex h-10 flex-1 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50 min-w-0;
}
}
[role='listbox']:not(:has([data-value]:not([aria-hidden='true'])))::before {
@apply flex items-center justify-center p-6 text-sm truncate;
}
[role='listbox'][data-empty]:not(:has([data-value]:not([aria-hidden='true'])))::before {
@apply content-[attr(data-empty)];
}
[role='listbox']:not([data-empty]):not(:has([data-value]:not([aria-hidden='true'])))::before {
@apply content-['No_results_found'];
}
}
[data-popover]:has(> header input:not(:placeholder-shown)) [role='separator'] {
@apply hidden;
}
&:not([data-select-initialized]) [data-popover] [role='option'] {
@apply hover:bg-accent hover:text-accent-foreground;
}
}
}
+122
View File
@@ -0,0 +1,122 @@
/* Sidebar */
@layer components {
.sidebar {
&:not([data-sidebar-initialized]) {
@apply max-md:hidden;
}
&:not([aria-hidden]),
&[aria-hidden=false] {
@apply max-md:bg-black/50 max-md:fixed max-md:inset-0 max-md:z-40;
}
nav {
@apply bg-sidebar text-sidebar-foreground flex flex-col w-(--sidebar-mobile-width) md:w-(--sidebar-width) fixed inset-y-0 z-50 transition-transform ease-in-out duration-300;
}
& + * {
@apply transition-[margin] ease-in-out duration-300;
}
&:not([data-side]),
&[data-side=left] {
nav {
@apply left-0 border-r;
}
& + * {
@apply relative md:ml-(--sidebar-width);
}
&[aria-hidden=true] {
nav {
@apply -translate-x-full;
}
& + * {
@apply md:ml-0;
}
}
}
&[data-side=right] {
nav {
@apply right-0 border-l;
}
& + * {
@apply relative md:mr-(--sidebar-width);
}
&[aria-hidden=true] {
nav {
@apply translate-x-full;
}
& + * {
@apply md:mr-0;
}
}
}
nav {
> header,
> footer {
@apply flex flex-col gap-2 p-2;
}
[role=separator] {
@apply border-sidebar-border mx-2 w-auto;
}
> section {
@apply flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto;
> [role=group] {
@apply relative flex w-full min-w-0 flex-col p-2;
}
h3 {
@apply text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0;
}
ul {
@apply flex w-full min-w-0 flex-col gap-1;
li {
@apply relative;
> a,
> details > summary {
@apply flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&[aria-current=page]]:bg-sidebar-accent [&[aria-current=page]]:font-medium [&[aria-current=page]]:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0;
&:not([data-variant]),
&[data-variant=default] {
@apply hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
&[data-variant=outline] {
@apply bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))];
}
&:not([data-size]),
&[data-size=default] {
@apply h-8 text-sm;
}
&[data-size=sm] {
@apply h-7 text-xs;
}
&[data-size=lg] {
@apply h-12 text-sm group-data-[collapsible=icon]:p-0!;
}
}
> details {
&:not([open]) {
> summary {
&::after {
@apply -rotate-90;
}
}
}
> summary {
&::after {
@apply content-[''] block size-3.5 bg-primary ml-auto transition-transform ease-linear;
@apply mask-[image:var(--chevron-down-icon)] mask-size-[1rem] mask-no-repeat mask-center;
}
}
&::details-content {
@apply px-3.5;
}
}
}
ul {
@apply border-sidebar-border flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5 w-full;
}
}
}
}
}
}
+12
View File
@@ -0,0 +1,12 @@
/* Switch */
@layer components {
:is(.form, .field) input[type='checkbox'][role='switch'],
.input[type='checkbox'][role='switch'] {
@apply appearance-none focus-visible:border-ring focus-visible:ring-ring/50 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50;
@apply bg-input dark:bg-input/80 checked:bg-primary dark:checked:bg-primary;
@apply before:content-[''] before:pointer-events-none before:block before:size-4 before:rounded-full before:ring-0 before:transition-all;
@apply before:bg-background dark:before:bg-foreground;
@apply dark:checked:before:bg-primary-foreground checked:before:ms-3.5;
}
}
+28
View File
@@ -0,0 +1,28 @@
/* Tables */
@layer components {
.table {
@apply w-full caption-bottom text-sm;
thead {
@apply [&_tr]:border-b;
}
tbody {
@apply [&_tr:last-child]:border-0;
}
tfoot {
@apply bg-muted/50 border-t font-medium [&>tr]:last:border-b-0;
}
tr {
@apply hover:bg-muted/50 border-b transition-colors;
}
th {
@apply text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px];
}
td {
@apply p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px];
}
caption {
@apply text-muted-foreground mt-4 text-sm;
}
}
}
+22
View File
@@ -0,0 +1,22 @@
/* Tabs */
@layer components {
.tabs {
@apply flex flex-col gap-2;
[role='tablist'] {
@apply bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px];
[role='tab'] {
@apply focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground dark:text-muted-foreground inline-flex h-[calc(100%_-_1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4;
&[aria-selected='true'] {
@apply bg-background dark:text-foreground dark:border-input dark:bg-input/30 shadow-sm;
}
}
}
[role='tabpanel'] {
@apply flex-1 outline-none;
}
}
}
+8
View File
@@ -0,0 +1,8 @@
/* Textarea */
@layer components {
:is(.form, .field) textarea,
.textarea {
@apply border-input placeholder:text-muted-foreground 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:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm;
}
}
+58
View File
@@ -0,0 +1,58 @@
/* Toasts */
@layer components {
.toaster {
@apply fixed bottom-0 p-4 pointer-events-none z-50 w-full sm:max-w-90 flex flex-col-reverse;
&:not([data-align]),
&[data-align='end'] {
@apply right-0;
}
&[data-align='start'] {
@apply left-0;
}
&[data-align='center'] {
@apply left-1/2 -translate-x-1/2;
}
.toast {
@apply pointer-events-auto w-full mt-4 animate-[toast-up_0.3s_ease-in-out] grid grid-rows-[1fr] transition-[grid-template-rows,opacity,margin] duration-300 ease-in-out;
.toast-content {
@apply text-popover-foreground text-[13px] bg-popover border shadow-lg rounded-lg overflow-hidden flex gap-2.5 p-3 items-center;
svg {
@apply size-4 shrink-0;
}
section {
h2 {
@apply font-medium tracking-tight;
}
p {
@apply text-muted-foreground break-all;
}
}
footer {
@apply ml-auto flex flex-col gap-2;
[data-toast-action],
[data-toast-cancel] {
@apply h-6 px-2.5 text-xs;
}
}
}
&[aria-hidden='true'] {
@apply grid-rows-[0fr] opacity-0 m-0 border-0 p-0 overflow-hidden;
.toast-content {
@apply border-0;
}
}
}
}
}
@keyframes toast-up {
from {
opacity: 0;
transform: translateY(100%);
}
}
+56
View File
@@ -0,0 +1,56 @@
/* Tooltip */
@layer components {
[data-tooltip] {
@apply relative;
&:before {
@apply absolute content-[attr(data-tooltip)] bg-foreground text-background z-[60] truncate max-w-xs w-fit rounded-md px-3 py-1.5 text-xs invisible opacity-0 scale-95 transition-all pointer-events-none;
}
&:hover:before {
@apply visible opacity-100 scale-100;
}
&:focus-visible:not(:hover):before {
@apply hidden;
}
&:not([data-side]),
&[data-side='top'] {
@apply before:bottom-full before:mb-1.5 before:translate-y-2 hover:before:translate-y-0 ;
}
&[data-side='bottom'] {
@apply before:top-full before:mt-1.5 before:-translate-y-2 hover:before:translate-y-0 ;
}
&:not([data-side]),
&[data-side='top'],
&[data-side='bottom'] {
&[data-align='start'] {
@apply before:left-0;
}
&[data-align='end'] {
@apply before:right-0;
}
&:not([data-align]),
&[data-align='center'] {
@apply before:left-1/2 before:-translate-x-1/2;
}
}
&[data-side='left'] {
@apply before:right-full before:mr-1.5 before:translate-x-2 hover:before:translate-x-0;
}
&[data-side='right'] {
@apply before:left-full before:ml-1.5 before:-translate-x-2 hover:before:translate-x-0;
}
&[data-side='left'],
&[data-side='right'] {
&[data-align='start'] {
@apply before:top-0;
}
&[data-align='end'] {
@apply before:bottom-0;
}
&:not([data-align]),
&[data-align='center'] {
@apply before:top-1/2 before:-translate-y-1/2;
}
}
}
}
+247
View File
@@ -0,0 +1,247 @@
/* Button */
@layer components {
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive,
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive,
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive,
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive,
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive,
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@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;
}
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive {
@apply h-9 gap-1.5 px-3;
&:has(> svg:first-child) {
@apply pl-2.5;
}
&:has(> svg:last-child) {
@apply pr-2.5;
}
}
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive {
@apply h-6 gap-1 px-2.5 text-xs [&_svg:not([class*='size-'])]:size-3;
&:has(> svg:first-child) {
@apply pl-2;
}
&:has(> svg:last-child) {
@apply pr-2;
}
}
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive {
@apply h-8 gap-1 px-3;
&:has(> svg:first-child) {
@apply pl-2;
}
&:has(> svg:last-child) {
@apply pr-2;
}
}
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive {
@apply h-10 gap-1.5 px-4;
&:has(> svg:first-child) {
@apply pl-3;
}
&:has(> svg:last-child) {
@apply pr-3;
}
}
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive {
@apply size-9;
}
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive {
@apply size-6 [&_svg:not([class*='size-'])]:size-3;
}
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive {
@apply size-8;
}
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@apply size-10;
}
.btn,
.btn-primary,
.btn-xs,
.btn-xs-primary,
.btn-sm,
.btn-sm-primary,
.btn-lg,
.btn-lg-primary,
.btn-icon,
.btn-icon-primary,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-lg-icon,
.btn-lg-icon-primary {
@apply bg-primary text-primary-foreground hover:bg-primary/80;
}
.btn-secondary,
.btn-xs-secondary,
.btn-sm-secondary,
.btn-lg-secondary,
.btn-icon-secondary,
.btn-xs-icon-secondary,
.btn-sm-icon-secondary,
.btn-lg-icon-secondary {
@apply bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground;
}
.btn-outline,
.btn-xs-outline,
.btn-sm-outline,
.btn-lg-outline,
.btn-icon-outline,
.btn-xs-icon-outline,
.btn-sm-icon-outline,
.btn-lg-icon-outline {
@apply border-border bg-background hover:bg-muted hover:text-foreground dark:bg-transparent dark:hover:bg-input/30 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-ghost,
.btn-xs-ghost,
.btn-sm-ghost,
.btn-lg-ghost,
.btn-icon-ghost,
.btn-xs-icon-ghost,
.btn-sm-icon-ghost,
.btn-lg-icon-ghost {
@apply hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-link,
.btn-xs-link,
.btn-sm-link,
.btn-lg-link,
.btn-icon-link,
.btn-xs-icon-link,
.btn-sm-icon-link,
.btn-lg-icon-link {
@apply text-primary underline-offset-4 hover:underline;
}
.btn-destructive,
.btn-xs-destructive,
.btn-sm-destructive,
.btn-lg-destructive,
.btn-icon-destructive,
.btn-xs-icon-destructive,
.btn-sm-icon-destructive,
.btn-lg-icon-destructive {
@apply bg-destructive/10 text-destructive hover:bg-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40;
}
}
+247
View File
@@ -0,0 +1,247 @@
/* Button */
@layer components {
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive,
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive,
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive,
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive,
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive,
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@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;
}
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive {
@apply h-8 gap-1.5 px-2.5;
&:has(> svg:first-child) {
@apply pl-2;
}
&:has(> svg:last-child) {
@apply pr-2;
}
}
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive {
@apply h-6 gap-1 px-2 text-xs rounded-none [&_svg:not([class*='size-'])]:size-3;
&:has(> svg:first-child) {
@apply pl-1.5;
}
&:has(> svg:last-child) {
@apply pr-1.5;
}
}
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive {
@apply h-7 gap-1 px-2.5 rounded-none [&_svg:not([class*='size-'])]:size-3.5;
&:has(> svg:first-child) {
@apply pl-1.5;
}
&:has(> svg:last-child) {
@apply pr-1.5;
}
}
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive {
@apply h-9 gap-1.5 px-2.5;
&:has(> svg:first-child) {
@apply pl-2;
}
&:has(> svg:last-child) {
@apply pr-2;
}
}
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive {
@apply size-8;
}
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive {
@apply size-6 rounded-none [&_svg:not([class*='size-'])]:size-3;
}
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive {
@apply size-7 rounded-none;
}
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@apply size-9;
}
.btn,
.btn-primary,
.btn-xs,
.btn-xs-primary,
.btn-sm,
.btn-sm-primary,
.btn-lg,
.btn-lg-primary,
.btn-icon,
.btn-icon-primary,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-lg-icon,
.btn-lg-icon-primary {
@apply bg-primary text-primary-foreground hover:bg-primary/80;
}
.btn-secondary,
.btn-xs-secondary,
.btn-sm-secondary,
.btn-lg-secondary,
.btn-icon-secondary,
.btn-xs-icon-secondary,
.btn-sm-icon-secondary,
.btn-lg-icon-secondary {
@apply bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground;
}
.btn-outline,
.btn-xs-outline,
.btn-sm-outline,
.btn-lg-outline,
.btn-icon-outline,
.btn-xs-icon-outline,
.btn-sm-icon-outline,
.btn-lg-icon-outline {
@apply border-border bg-background hover:bg-muted hover:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-ghost,
.btn-xs-ghost,
.btn-sm-ghost,
.btn-lg-ghost,
.btn-icon-ghost,
.btn-xs-icon-ghost,
.btn-sm-icon-ghost,
.btn-lg-icon-ghost {
@apply hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-link,
.btn-xs-link,
.btn-sm-link,
.btn-lg-link,
.btn-icon-link,
.btn-xs-icon-link,
.btn-sm-icon-link,
.btn-lg-icon-link {
@apply text-primary underline-offset-4 hover:underline;
}
.btn-destructive,
.btn-xs-destructive,
.btn-sm-destructive,
.btn-lg-destructive,
.btn-icon-destructive,
.btn-xs-icon-destructive,
.btn-sm-icon-destructive,
.btn-lg-icon-destructive {
@apply bg-destructive/10 text-destructive hover:bg-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40;
}
}
+247
View File
@@ -0,0 +1,247 @@
/* Button */
@layer components {
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive,
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive,
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive,
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive,
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive,
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@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;
}
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive {
@apply h-9 gap-1.5 px-3;
&:has(> svg:first-child) {
@apply pl-2.5;
}
&:has(> svg:last-child) {
@apply pr-2.5;
}
}
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive {
@apply h-6 gap-1 px-2.5 text-xs [&_svg:not([class*='size-'])]:size-3;
&:has(> svg:first-child) {
@apply pl-2;
}
&:has(> svg:last-child) {
@apply pr-2;
}
}
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive {
@apply h-8 gap-1 px-3;
&:has(> svg:first-child) {
@apply pl-2;
}
&:has(> svg:last-child) {
@apply pr-2;
}
}
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive {
@apply h-10 gap-1.5 px-4;
&:has(> svg:first-child) {
@apply pl-3;
}
&:has(> svg:last-child) {
@apply pr-3;
}
}
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive {
@apply size-9;
}
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive {
@apply size-6 [&_svg:not([class*='size-'])]:size-3;
}
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive {
@apply size-8;
}
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@apply size-10;
}
.btn,
.btn-primary,
.btn-xs,
.btn-xs-primary,
.btn-sm,
.btn-sm-primary,
.btn-lg,
.btn-lg-primary,
.btn-icon,
.btn-icon-primary,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-lg-icon,
.btn-lg-icon-primary {
@apply bg-primary text-primary-foreground hover:bg-primary/80;
}
.btn-secondary,
.btn-xs-secondary,
.btn-sm-secondary,
.btn-lg-secondary,
.btn-icon-secondary,
.btn-xs-icon-secondary,
.btn-sm-icon-secondary,
.btn-lg-icon-secondary {
@apply bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground;
}
.btn-outline,
.btn-xs-outline,
.btn-sm-outline,
.btn-lg-outline,
.btn-icon-outline,
.btn-xs-icon-outline,
.btn-sm-icon-outline,
.btn-lg-icon-outline {
@apply border-border bg-input/30 hover:bg-input/50 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-ghost,
.btn-xs-ghost,
.btn-sm-ghost,
.btn-lg-ghost,
.btn-icon-ghost,
.btn-xs-icon-ghost,
.btn-sm-icon-ghost,
.btn-lg-icon-ghost {
@apply hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-link,
.btn-xs-link,
.btn-sm-link,
.btn-lg-link,
.btn-icon-link,
.btn-xs-icon-link,
.btn-sm-icon-link,
.btn-lg-icon-link {
@apply text-primary underline-offset-4 hover:underline;
}
.btn-destructive,
.btn-xs-destructive,
.btn-sm-destructive,
.btn-lg-destructive,
.btn-icon-destructive,
.btn-xs-icon-destructive,
.btn-sm-icon-destructive,
.btn-lg-icon-destructive {
@apply bg-destructive/10 text-destructive hover:bg-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40;
}
}
+247
View File
@@ -0,0 +1,247 @@
/* Button */
@layer components {
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive,
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive,
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive,
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive,
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive,
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@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;
}
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive {
@apply h-7 gap-1 px-2 text-xs/relaxed;
&:has(> svg:first-child) {
@apply pl-1.5;
}
&:has(> svg:last-child) {
@apply pr-1.5;
}
}
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive {
@apply h-5 gap-1 px-2 text-[0.625rem] rounded-sm [&_svg:not([class*='size-'])]:size-2.5;
&:has(> svg:first-child) {
@apply pl-1.5;
}
&:has(> svg:last-child) {
@apply pr-1.5;
}
}
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive {
@apply h-6 gap-1 px-2 text-xs/relaxed [&_svg:not([class*='size-'])]:size-3;
&:has(> svg:first-child) {
@apply pl-1.5;
}
&:has(> svg:last-child) {
@apply pr-1.5;
}
}
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive {
@apply h-8 gap-1 px-2.5 text-xs/relaxed;
&:has(> svg:first-child) {
@apply pl-2;
}
&:has(> svg:last-child) {
@apply pr-2;
}
}
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive {
@apply size-7 [&_svg:not([class*='size-'])]:size-3.5;
}
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive {
@apply size-5 rounded-sm [&_svg:not([class*='size-'])]:size-2.5;
}
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive {
@apply size-6 [&_svg:not([class*='size-'])]:size-3;
}
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@apply size-8 [&_svg:not([class*='size-'])]:size-4;
}
.btn,
.btn-primary,
.btn-xs,
.btn-xs-primary,
.btn-sm,
.btn-sm-primary,
.btn-lg,
.btn-lg-primary,
.btn-icon,
.btn-icon-primary,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-lg-icon,
.btn-lg-icon-primary {
@apply bg-primary text-primary-foreground hover:bg-primary/80;
}
.btn-secondary,
.btn-xs-secondary,
.btn-sm-secondary,
.btn-lg-secondary,
.btn-icon-secondary,
.btn-xs-icon-secondary,
.btn-sm-icon-secondary,
.btn-lg-icon-secondary {
@apply bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground;
}
.btn-outline,
.btn-xs-outline,
.btn-sm-outline,
.btn-lg-outline,
.btn-icon-outline,
.btn-xs-icon-outline,
.btn-sm-icon-outline,
.btn-lg-icon-outline {
@apply border-border hover:bg-input/50 hover:text-foreground dark:border-input dark:bg-input/30 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-ghost,
.btn-xs-ghost,
.btn-sm-ghost,
.btn-lg-ghost,
.btn-icon-ghost,
.btn-xs-icon-ghost,
.btn-sm-icon-ghost,
.btn-lg-icon-ghost {
@apply hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-link,
.btn-xs-link,
.btn-sm-link,
.btn-lg-link,
.btn-icon-link,
.btn-xs-icon-link,
.btn-sm-icon-link,
.btn-lg-icon-link {
@apply text-primary underline-offset-4 hover:underline;
}
.btn-destructive,
.btn-xs-destructive,
.btn-sm-destructive,
.btn-lg-destructive,
.btn-icon-destructive,
.btn-xs-icon-destructive,
.btn-sm-icon-destructive,
.btn-lg-icon-destructive {
@apply bg-destructive/10 text-destructive hover:bg-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40;
}
}
+247
View File
@@ -0,0 +1,247 @@
/* Button */
@layer components {
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive,
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive,
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive,
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive,
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive,
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@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;
}
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive {
@apply h-8 gap-1.5 px-2.5;
&:has(> svg:first-child) {
@apply pl-2;
}
&:has(> svg:last-child) {
@apply pr-2;
}
}
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive {
@apply h-6 gap-1 px-2 text-xs rounded-[min(var(--radius-md),10px)] [&_svg:not([class*='size-'])]:size-3;
&:has(> svg:first-child) {
@apply pl-1.5;
}
&:has(> svg:last-child) {
@apply pr-1.5;
}
}
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive {
@apply h-7 gap-1 px-2.5 text-[0.8rem] rounded-[min(var(--radius-md),12px)] [&_svg:not([class*='size-'])]:size-3.5;
&:has(> svg:first-child) {
@apply pl-1.5;
}
&:has(> svg:last-child) {
@apply pr-1.5;
}
}
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive {
@apply h-9 gap-1.5 px-2.5;
&:has(> svg:first-child) {
@apply pl-2;
}
&:has(> svg:last-child) {
@apply pr-2;
}
}
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive {
@apply size-8;
}
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive {
@apply size-6 rounded-[min(var(--radius-md),10px)] [&_svg:not([class*='size-'])]:size-3;
}
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive {
@apply size-7 rounded-[min(var(--radius-md),12px)];
}
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@apply size-9;
}
.btn,
.btn-primary,
.btn-xs,
.btn-xs-primary,
.btn-sm,
.btn-sm-primary,
.btn-lg,
.btn-lg-primary,
.btn-icon,
.btn-icon-primary,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-lg-icon,
.btn-lg-icon-primary {
@apply bg-primary text-primary-foreground hover:bg-primary/80;
}
.btn-secondary,
.btn-xs-secondary,
.btn-sm-secondary,
.btn-lg-secondary,
.btn-icon-secondary,
.btn-xs-icon-secondary,
.btn-sm-icon-secondary,
.btn-lg-icon-secondary {
@apply bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground;
}
.btn-outline,
.btn-xs-outline,
.btn-sm-outline,
.btn-lg-outline,
.btn-icon-outline,
.btn-xs-icon-outline,
.btn-sm-icon-outline,
.btn-lg-icon-outline {
@apply border-border bg-background hover:bg-muted hover:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-ghost,
.btn-xs-ghost,
.btn-sm-ghost,
.btn-lg-ghost,
.btn-icon-ghost,
.btn-xs-icon-ghost,
.btn-sm-icon-ghost,
.btn-lg-icon-ghost {
@apply hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-link,
.btn-xs-link,
.btn-sm-link,
.btn-lg-link,
.btn-icon-link,
.btn-xs-icon-link,
.btn-sm-icon-link,
.btn-lg-icon-link {
@apply text-primary underline-offset-4 hover:underline;
}
.btn-destructive,
.btn-xs-destructive,
.btn-sm-destructive,
.btn-lg-destructive,
.btn-icon-destructive,
.btn-xs-icon-destructive,
.btn-sm-icon-destructive,
.btn-lg-icon-destructive {
@apply bg-destructive/10 text-destructive hover:bg-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40;
}
}
+247
View File
@@ -0,0 +1,247 @@
/* Button */
@layer components {
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive,
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive,
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive,
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive,
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive,
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@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;
}
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive {
@apply h-8 gap-1.5 px-3;
&:has(> svg:first-child) {
@apply pl-2.5;
}
&:has(> svg:last-child) {
@apply pr-2.5;
}
}
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive {
@apply h-6 gap-1 px-2.5 text-xs [&_svg:not([class*='size-'])]:size-3;
&:has(> svg:first-child) {
@apply pl-2;
}
&:has(> svg:last-child) {
@apply pr-2;
}
}
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive {
@apply h-7 gap-1 px-3;
&:has(> svg:first-child) {
@apply pl-2;
}
&:has(> svg:last-child) {
@apply pr-2;
}
}
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive {
@apply h-9 gap-1.5 px-4;
&:has(> svg:first-child) {
@apply pl-3;
}
&:has(> svg:last-child) {
@apply pr-3;
}
}
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive {
@apply size-8;
}
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive {
@apply size-6 [&_svg:not([class*='size-'])]:size-3;
}
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive {
@apply size-7;
}
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@apply size-9;
}
.btn,
.btn-primary,
.btn-xs,
.btn-xs-primary,
.btn-sm,
.btn-sm-primary,
.btn-lg,
.btn-lg-primary,
.btn-icon,
.btn-icon-primary,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-lg-icon,
.btn-lg-icon-primary {
@apply bg-primary text-primary-foreground hover:bg-primary/80;
}
.btn-secondary,
.btn-xs-secondary,
.btn-sm-secondary,
.btn-lg-secondary,
.btn-icon-secondary,
.btn-xs-icon-secondary,
.btn-sm-icon-secondary,
.btn-lg-icon-secondary {
@apply bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground;
}
.btn-outline,
.btn-xs-outline,
.btn-sm-outline,
.btn-lg-outline,
.btn-icon-outline,
.btn-xs-icon-outline,
.btn-sm-icon-outline,
.btn-lg-icon-outline {
@apply border-border bg-background hover:bg-muted hover:text-foreground dark:bg-transparent dark:hover:bg-input/30 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-ghost,
.btn-xs-ghost,
.btn-sm-ghost,
.btn-lg-ghost,
.btn-icon-ghost,
.btn-xs-icon-ghost,
.btn-sm-icon-ghost,
.btn-lg-icon-ghost {
@apply hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-link,
.btn-xs-link,
.btn-sm-link,
.btn-lg-link,
.btn-icon-link,
.btn-xs-icon-link,
.btn-sm-icon-link,
.btn-lg-icon-link {
@apply text-primary underline-offset-4 hover:underline;
}
.btn-destructive,
.btn-xs-destructive,
.btn-sm-destructive,
.btn-lg-destructive,
.btn-icon-destructive,
.btn-xs-icon-destructive,
.btn-sm-icon-destructive,
.btn-lg-icon-destructive {
@apply bg-destructive/10 text-destructive hover:bg-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40;
}
}
+247
View File
@@ -0,0 +1,247 @@
/* Button */
@layer components {
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive,
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive,
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive,
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive,
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive,
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@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;
}
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive {
@apply h-10 gap-1.5 px-6;
&:has(> svg:first-child) {
@apply pl-4;
}
&:has(> svg:last-child) {
@apply pr-4;
}
}
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive {
@apply h-7 gap-1 px-3 [&_svg:not([class*='size-'])]:size-3;
&:has(> svg:first-child) {
@apply pl-2;
}
&:has(> svg:last-child) {
@apply pr-2;
}
}
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive {
@apply h-9 gap-1 px-4;
&:has(> svg:first-child) {
@apply pl-3;
}
&:has(> svg:last-child) {
@apply pr-3;
}
}
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive {
@apply h-11 gap-1.5 px-8;
&:has(> svg:first-child) {
@apply pl-5;
}
&:has(> svg:last-child) {
@apply pr-5;
}
}
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive {
@apply size-10;
}
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive {
@apply size-7 [&_svg:not([class*='size-'])]:size-3;
}
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive {
@apply size-9;
}
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@apply size-11;
}
.btn,
.btn-primary,
.btn-xs,
.btn-xs-primary,
.btn-sm,
.btn-sm-primary,
.btn-lg,
.btn-lg-primary,
.btn-icon,
.btn-icon-primary,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-lg-icon,
.btn-lg-icon-primary {
@apply bg-primary text-primary-foreground hover:bg-primary/80;
}
.btn-secondary,
.btn-xs-secondary,
.btn-sm-secondary,
.btn-lg-secondary,
.btn-icon-secondary,
.btn-xs-icon-secondary,
.btn-sm-icon-secondary,
.btn-lg-icon-secondary {
@apply bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground;
}
.btn-outline,
.btn-xs-outline,
.btn-sm-outline,
.btn-lg-outline,
.btn-icon-outline,
.btn-xs-icon-outline,
.btn-sm-icon-outline,
.btn-lg-icon-outline {
@apply border-border bg-transparent hover:bg-muted hover:text-foreground dark:hover:bg-input/30 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-ghost,
.btn-xs-ghost,
.btn-sm-ghost,
.btn-lg-ghost,
.btn-icon-ghost,
.btn-xs-icon-ghost,
.btn-sm-icon-ghost,
.btn-lg-icon-ghost {
@apply hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-link,
.btn-xs-link,
.btn-sm-link,
.btn-lg-link,
.btn-icon-link,
.btn-xs-icon-link,
.btn-sm-icon-link,
.btn-lg-icon-link {
@apply text-primary underline underline-offset-4 hover:underline;
}
.btn-destructive,
.btn-xs-destructive,
.btn-sm-destructive,
.btn-lg-destructive,
.btn-icon-destructive,
.btn-xs-icon-destructive,
.btn-sm-icon-destructive,
.btn-lg-icon-destructive {
@apply bg-destructive/10 text-destructive hover:bg-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40;
}
}
+247
View File
@@ -0,0 +1,247 @@
/* Button */
@layer components {
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive,
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive,
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive,
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive,
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive,
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@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;
}
.btn,
.btn-primary,
.btn-secondary,
.btn-outline,
.btn-ghost,
.btn-link,
.btn-destructive {
@apply h-9 gap-1.5 px-2.5;
&:has(> svg:first-child) {
@apply pl-2;
}
&:has(> svg:last-child) {
@apply pr-2;
}
}
.btn-xs,
.btn-xs-primary,
.btn-xs-secondary,
.btn-xs-outline,
.btn-xs-ghost,
.btn-xs-link,
.btn-xs-destructive {
@apply h-6 gap-1 px-2 text-xs rounded-[min(var(--radius-md),8px)] [&_svg:not([class*='size-'])]:size-3;
&:has(> svg:first-child) {
@apply pl-1.5;
}
&:has(> svg:last-child) {
@apply pr-1.5;
}
}
.btn-sm,
.btn-sm-primary,
.btn-sm-secondary,
.btn-sm-outline,
.btn-sm-ghost,
.btn-sm-link,
.btn-sm-destructive {
@apply h-8 gap-1 px-2.5 rounded-[min(var(--radius-md),10px)];
&:has(> svg:first-child) {
@apply pl-1.5;
}
&:has(> svg:last-child) {
@apply pr-1.5;
}
}
.btn-lg,
.btn-lg-primary,
.btn-lg-secondary,
.btn-lg-outline,
.btn-lg-ghost,
.btn-lg-link,
.btn-lg-destructive {
@apply h-10 gap-1.5 px-2.5;
&:has(> svg:first-child) {
@apply pl-2;
}
&:has(> svg:last-child) {
@apply pr-2;
}
}
.btn-icon,
.btn-icon-primary,
.btn-icon-secondary,
.btn-icon-outline,
.btn-icon-ghost,
.btn-icon-link,
.btn-icon-destructive {
@apply size-9;
}
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-xs-icon-secondary,
.btn-xs-icon-outline,
.btn-xs-icon-ghost,
.btn-xs-icon-link,
.btn-xs-icon-destructive {
@apply size-6 rounded-[min(var(--radius-md),8px)] [&_svg:not([class*='size-'])]:size-3;
}
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-sm-icon-secondary,
.btn-sm-icon-outline,
.btn-sm-icon-ghost,
.btn-sm-icon-link,
.btn-sm-icon-destructive {
@apply size-8 rounded-[min(var(--radius-md),10px)];
}
.btn-lg-icon,
.btn-lg-icon-primary,
.btn-lg-icon-secondary,
.btn-lg-icon-outline,
.btn-lg-icon-ghost,
.btn-lg-icon-link,
.btn-lg-icon-destructive {
@apply size-10;
}
.btn,
.btn-primary,
.btn-xs,
.btn-xs-primary,
.btn-sm,
.btn-sm-primary,
.btn-lg,
.btn-lg-primary,
.btn-icon,
.btn-icon-primary,
.btn-xs-icon,
.btn-xs-icon-primary,
.btn-sm-icon,
.btn-sm-icon-primary,
.btn-lg-icon,
.btn-lg-icon-primary {
@apply bg-primary text-primary-foreground hover:bg-primary/80;
}
.btn-secondary,
.btn-xs-secondary,
.btn-sm-secondary,
.btn-lg-secondary,
.btn-icon-secondary,
.btn-xs-icon-secondary,
.btn-sm-icon-secondary,
.btn-lg-icon-secondary {
@apply bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground;
}
.btn-outline,
.btn-xs-outline,
.btn-sm-outline,
.btn-lg-outline,
.btn-icon-outline,
.btn-xs-icon-outline,
.btn-sm-icon-outline,
.btn-lg-icon-outline {
@apply border-border bg-background shadow-xs hover:bg-muted hover:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-ghost,
.btn-xs-ghost,
.btn-sm-ghost,
.btn-lg-ghost,
.btn-icon-ghost,
.btn-xs-icon-ghost,
.btn-sm-icon-ghost,
.btn-lg-icon-ghost {
@apply hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground;
}
.btn-link,
.btn-xs-link,
.btn-sm-link,
.btn-lg-link,
.btn-icon-link,
.btn-xs-icon-link,
.btn-sm-icon-link,
.btn-lg-icon-link {
@apply text-primary underline-offset-4 hover:underline;
}
.btn-destructive,
.btn-xs-destructive,
.btn-sm-destructive,
.btn-lg-destructive,
.btn-icon-destructive,
.btn-xs-icon-destructive,
.btn-sm-icon-destructive,
.btn-lg-icon-destructive {
@apply bg-destructive/10 text-destructive hover:bg-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40;
}
}
+1 -1
View File
@@ -128,7 +128,7 @@
<li>
<a
href="{{ item.url }}"
{{ 'aria-current="page"' if item.current }}
{% if item.current %}aria-current="page"{% endif %}
{% if item.attrs %}
{% for key, value in item.attrs.items() %}
{% if key != "href" and key != "aria-current" %}{{ key }}="{{ value }}"{% endif %}
+1 -1
View File
@@ -128,7 +128,7 @@
<li>
<a
href="{{ item.url }}"
{{ 'aria-current="page"' if item.current }}
{% if item.current %}aria-current="page"{% endif %}
{% if item.attrs %}
{% for key, value in item.attrs %}
{% if key != "href" and key != "aria-current" %}{{ key }}="{{ value }}"{% endif %}