Compare commits

..
Author SHA1 Message Date
grugbraindev b74987150d Replace the .form migration snippet that fails in Tailwind v4
Tailwind v4 can't @apply Basecoat's component classes, so the 1.0.0 migration snippet failed with "Cannot apply unknown utility class". Show the .field wrapper and the per-element classes instead.
2026-10-23 09:30:06 +08:00
36 changed files with 670 additions and 1947 deletions
-38
View File
@@ -1,38 +0,0 @@
name: CI
on:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
cache-dependency-path: |
package-lock.json
site/package-lock.json
- run: npm ci
# Package: build, check every export exists and the JS parses, list the tarball.
- run: npm run check
# Docs site: build it and check llms.txt links to the .md exports.
- run: npm run check:site
+33
View File
@@ -0,0 +1,33 @@
name: Deploy site on release
# Cloudflare Workers Builds deploys the site from the `release` branch.
# Publishing a GitHub release moves `release` to the released tag.
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: Tag to deploy (for example 1.1.0)
required: true
type: string
permissions:
contents: write
jobs:
move-release-branch:
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' || github.event.release.prerelease == false
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Point release branch at tag
env:
TAG: ${{ inputs.tag || github.event.release.tag_name }}
run: |
SHA=$(git rev-parse "refs/tags/$TAG^{commit}")
git push --force origin "$SHA:refs/heads/release"
+72
View File
@@ -0,0 +1,72 @@
name: Announce release on Discord
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: Release tag to announce
required: true
type: string
permissions:
contents: read
jobs:
announce:
runs-on: ubuntu-latest
steps:
- name: Send Discord announcement
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
RELEASE_TAG_INPUT: ${{ inputs.tag }}
REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
run: |
test -n "$DISCORD_WEBHOOK_URL"
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
test -n "$RELEASE_TAG_INPUT"
curl --fail-with-body \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GH_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"$GITHUB_API_URL/repos/$REPO/releases/tags/$RELEASE_TAG_INPUT" \
> release.json
else
jq '.release' "$GITHUB_EVENT_PATH" > release.json
fi
if [ "$(jq -r '.prerelease' release.json)" = "true" ]; then
echo "Skipping prerelease."
exit 0
fi
jq -n \
--arg repo "$REPO" \
--arg name "$(jq -r '.name // ""' release.json)" \
--arg tag "$(jq -r '.tag_name' release.json)" \
--arg url "$(jq -r '.html_url' release.json)" \
--arg body "$(jq -r '.body // ""' release.json)" \
'($body | if length > 1500 then .[:1500] + "...\n\n[Read more](" + $url + ")" else . end) as $description |
{
username: "Basecoat Releases",
allowed_mentions: { parse: [] },
embeds: [{
title: ($repo + " " + $tag),
url: $url,
description: ((if $name != "" then "**" + $name + "**\n\n" else "" end) + $description),
color: 5793266
}]
}' > payload.json
curl --fail-with-body \
--retry 3 \
--retry-all-errors \
--retry-delay 2 \
-H "Content-Type: application/json" \
-X POST \
-d @payload.json \
"$DISCORD_WEBHOOK_URL"
-277
View File
@@ -1,277 +0,0 @@
name: Release
# Publishing a GitHub release (not a prerelease):
# 1. publish: checks the tag is on main and matches package.json, runs the checks,
# then stages the version on npm with `npm stage publish`, using trusted publishing
# (OIDC, no token). A maintainer then approves it on npmjs.com (Staged Packages tab)
# or with `npm stage approve <stage-id>`, with 2FA. Until then the version is not
# installable.
# 2. deploy-site: waits until the version is live on npm (polls every 30 seconds for
# up to about 5h50m), then points the `release` branch at the tag. Cloudflare
# Workers Builds deploys basecoatui.com from `release`, so the docs never describe
# a version npm does not serve. If you approve later than that, re-run the failed
# jobs once approved (deploy-site, then announce).
# 3. announce: posts the release to Discord (DISCORD_WEBHOOK_URL secret), only after
# deploy-site succeeded: the version is on npm and `release` has moved.
#
# Running this workflow manually never publishes to npm. Pick a mode:
# deploy move `release` to the tag (redeploy or roll back the site),
# only if that version is already on npm. No Discord post.
# announce post the tag's GitHub release to Discord again. No deploy.
# deploy and announce both, announcing only if the deploy succeeded.
#
# Prereleases are ignored by every job.
#
# npm trusted publisher settings (allow `npm stage publish` only): repository
# hunvreus/basecoat, workflow release.yml, no environment. Renaming this file breaks
# publishing.
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: Release tag (for example 1.1.0). Never publishes to npm.
required: true
type: string
mode:
description: "deploy: move the site to this tag (version must be on npm). announce: post the release to Discord again. deploy and announce: both."
required: true
type: choice
default: deploy
options:
- deploy
- announce
- deploy and announce
permissions: {}
concurrency:
group: release
cancel-in-progress: false
jobs:
publish:
runs-on: ubuntu-latest
if: github.event_name == 'release' && github.event.release.prerelease == false
permissions:
contents: read
id-token: write
env:
TAG: ${{ github.event.release.tag_name }}
steps:
- uses: actions/checkout@v7
with:
ref: refs/tags/${{ github.event.release.tag_name }}
persist-credentials: false
fetch-depth: 0
- name: Check the tag is on main
run: |
git fetch --no-tags origin main
if ! git merge-base --is-ancestor HEAD FETCH_HEAD; then
echo "::error::Tag $TAG is not on main. Release from a commit on main."
exit 1
fi
- uses: actions/setup-node@v7
with:
node-version: 24
registry-url: https://registry.npmjs.org
# No dependency cache in a publishing job (cache poisoning).
package-manager-cache: false
- name: Ensure npm supports staged publishing (>= 11.15.0)
run: |
if ! node -e 'const [a,b]=process.argv[1].split(".").map(Number);process.exit(a>11||(a===11&&b>=15)?0:1)' "$(npm --version)"; then
npm install -g npm@^11.15.0
fi
npm --version
- name: Check package.json version matches the tag
id: version
run: |
NAME=$(node -p 'require("./package.json").name')
VERSION=$(node -p 'require("./package.json").version')
if [ "$VERSION" != "${TAG#v}" ]; then
echo "::error::package.json version is $VERSION but the release tag is $TAG. Bump package.json and tag that commit."
exit 1
fi
echo "name=$NAME" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Check whether this version is already on npm
id: npm
env:
NAME: ${{ steps.version.outputs.name }}
VERSION: ${{ steps.version.outputs.version }}
run: |
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' "https://registry.npmjs.org/$NAME/$VERSION")
case "$STATUS" in
200)
echo "::notice::$NAME@$VERSION is already on npm. Skipping publish."
echo "published=true" >> "$GITHUB_OUTPUT" ;;
404)
echo "published=false" >> "$GITHUB_OUTPUT" ;;
*)
echo "::error::Unexpected HTTP $STATUS from the npm registry."
exit 1 ;;
esac
- name: Install dependencies
if: steps.npm.outputs.published == 'false'
run: npm ci
# Builds dist/ and templates/, checks every export exists, and lists the tarball.
# `npm stage publish` builds again through the prepack script.
- name: Run checks
if: steps.npm.outputs.published == 'false'
run: npm run check
# Short-lived OIDC tokens cannot run `npm stage list`, so an already-staged version
# is detected from the registry's conflict error instead: staged and published
# versions share one version index, so re-staging the same version is refused.
- name: Stage on npm
if: steps.npm.outputs.published == 'false'
run: |
set +e
npm stage publish --provenance 2>&1 | tee "$RUNNER_TEMP/stage.log"
STATUS=${PIPESTATUS[0]}
set -e
if [ "$STATUS" -eq 0 ]; then
echo "::notice::Staged. Approve it on npmjs.com (Staged Packages tab) or with npm stage approve, using 2FA. The site deploys once it is live."
elif grep -Eqi 'E409|EPUBLISHCONFLICT|previously published|already (been )?(staged|published|exists)|cannot (publish|stage) over' "$RUNNER_TEMP/stage.log"; then
echo "::notice::This version is already staged or published on npm. Skipping."
else
exit "$STATUS"
fi
deploy-site:
runs-on: ubuntu-latest
needs: publish
if: >-
always() &&
(needs.publish.result == 'success' || needs.publish.result == 'skipped') &&
((github.event_name == 'release' && github.event.release.prerelease == false) ||
(github.event_name == 'workflow_dispatch' && inputs.mode != 'announce'))
# GitHub-hosted jobs stop at 6 hours. The wait below gives up before that.
timeout-minutes: 355
permissions:
contents: write
env:
TAG: ${{ inputs.tag || github.event.release.tag_name }}
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Check the tag is on main
id: tag
run: |
SHA=$(git rev-parse --verify "refs/tags/$TAG^{commit}")
if ! git merge-base --is-ancestor "$SHA" origin/main; then
echo "::error::Tag $TAG is not on main. Only tags on main can go live."
exit 1
fi
{
echo "sha=$SHA"
echo "name=$(git show "$SHA:package.json" | jq -r .name)"
echo "version=$(git show "$SHA:package.json" | jq -r .version)"
} >> "$GITHUB_OUTPUT"
- name: Wait until the version is live on npm
env:
NAME: ${{ steps.tag.outputs.name }}
VERSION: ${{ steps.tag.outputs.version }}
# Manual runs do not wait: the version must already be on npm.
MAX_WAIT: ${{ github.event_name == 'release' && '21000' || '0' }}
run: |
while :; do
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' "https://registry.npmjs.org/$NAME/$VERSION" || echo 000)
if [ "$STATUS" = "200" ]; then
echo "$NAME@$VERSION is live on npm."
exit 0
fi
if [ "$SECONDS" -ge "$MAX_WAIT" ]; then
if [ "$MAX_WAIT" = "0" ]; then
echo "::error::$NAME@$VERSION is not on npm (HTTP $STATUS). A manual run only deploys versions that are already published."
else
echo "::error::$NAME@$VERSION is still not live on npm after $((MAX_WAIT / 60)) minutes. Approve the staged package on npmjs.com (Staged Packages tab), then re-run this deploy-site job (Re-run failed jobs). If you rejected it, nothing needs to happen: the site was not deployed."
fi
exit 1
fi
echo "Waiting for $NAME@$VERSION on npm (HTTP $STATUS). Approve the staged package on npmjs.com. Checking again in 30 seconds."
sleep 30
done
- name: Point release branch at tag
env:
SHA: ${{ steps.tag.outputs.sha }}
run: git push --force origin "$SHA:refs/heads/release"
# Same message as the old discord-release.yml.
announce:
runs-on: ubuntu-latest
needs: deploy-site
if: >-
always() &&
((needs.deploy-site.result == 'success' &&
((github.event_name == 'release' && github.event.release.prerelease == false) ||
(github.event_name == 'workflow_dispatch' && inputs.mode == 'deploy and announce'))) ||
(github.event_name == 'workflow_dispatch' && inputs.mode == 'announce'))
permissions:
contents: read
steps:
- name: Send Discord announcement
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
RELEASE_TAG_INPUT: ${{ inputs.tag }}
REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
run: |
test -n "$DISCORD_WEBHOOK_URL"
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
test -n "$RELEASE_TAG_INPUT"
curl --fail-with-body \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GH_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"$GITHUB_API_URL/repos/$REPO/releases/tags/$RELEASE_TAG_INPUT" \
> release.json
else
jq '.release' "$GITHUB_EVENT_PATH" > release.json
fi
if [ "$(jq -r '.prerelease' release.json)" = "true" ]; then
echo "Skipping prerelease."
exit 0
fi
jq -n \
--arg repo "$REPO" \
--arg name "$(jq -r '.name // ""' release.json)" \
--arg tag "$(jq -r '.tag_name' release.json)" \
--arg url "$(jq -r '.html_url' release.json)" \
--arg body "$(jq -r '.body // ""' release.json)" \
'($body | if length > 1500 then .[:1500] + "...\n\n[Read more](" + $url + ")" else . end) as $description |
{
username: "Basecoat Releases",
allowed_mentions: { parse: [] },
embeds: [{
title: ($repo + " " + $tag),
url: $url,
description: ((if $name != "" then "**" + $name + "**\n\n" else "" end) + $description),
color: 5793266
}]
}' > payload.json
curl --fail-with-body \
--retry 3 \
--retry-all-errors \
--retry-delay 2 \
-H "Content-Type: application/json" \
-X POST \
-d @payload.json \
"$DISCORD_WEBHOOK_URL"
+1 -38
View File
@@ -1,43 +1,6 @@
# Changelog
## [Unreleased]
## [1.1.0] - 2026-10-23
### Upgrading
- Toast `title`, `description`, `action.label` and `cancel.label` are now escaped as text by default. If you pass HTML in any of them, add `html: true` to your `toaster.toast()` call.
### Added
- Added vertical separators: `<hr aria-orientation="vertical">` renders as a 1px line that fills its row.
- Added single-file `.bundle.css` builds (`dist/basecoat.bundle.css` and `dist/basecoat-{style}.bundle.css`) with every Basecoat import inlined, so the standalone Tailwind CLI can use Basecoat without npm. Based on the standalone CLI setup proposed by [@ytsruh](https://github.com/ytsruh) in [#150](https://github.com/hunvreus/basecoat/pull/150).
- Added an `html: true` option to `toaster.toast()` for callers that need to pass raw HTML in the title, description or action labels.
### Changed
- Toast `title`, `description`, `action.label` and `cancel.label` are now escaped as text by default, matching Sonner. Pass `html: true` to keep the previous raw-HTML behavior.
- Changed the default `--font-sans` to start with `"Geist"`, the family name used by Google Fonts, Bunny Fonts and Fontsource, with `"Geist Sans"` kept as a fallback. The customization docs now install `@fontsource/geist`.
- Updated the docs dependency to `reallysimpledocs@^1.0.14`, so `llms.txt` links to the Markdown version of each page.
- Documented how to use Tailwind utilities with Basecoat's theme colors alongside the CDN build and the Tailwind Play CDN.
### Fixed
- Prevented Escape from dismissing an enclosing dialog while closing an open Combobox popup.
- Fixed long Tooltip text clipping instead of showing an ellipsis across all style packs.
- Locked page scroll while a modal Dialog, Alert Dialog, Command Dialog or Drawer is open.
- Kept a parent Popover, Dropdown Menu, Select or Combobox open when a nested one opens inside it. Sibling components still close each other.
- Scoped Sidebar navigation styles to the sidebar's direct child `nav` (`.sidebar > nav`), so other `nav` elements inside a sidebar, such as a Tabs list, are no longer fixed and stacked.
- Stopped Dialogs and Command Dialogs from closing when a press starts inside the dialog and the drag ends on the backdrop. Updated the Nunjucks and Jinja templates and the docs examples. Based on [#104](https://github.com/hunvreus/basecoat/pull/104) by [@PhilipdeRijk](https://github.com/PhilipdeRijk).
- Fixed iOS auto-zoom when tapping the Combobox input, the Combobox chips input, the Command search input, native Selects, and Input Group inputs and textareas by using `text-base` below the `md` breakpoint, like regular inputs. The compact Lyra and Mira styles keep their smaller sizes.
- Fixed Slider thumb hover and focus rings, the RTL Sidebar submenu chevron, and some dark mode and tooltip arrow styles not applying in the unminified CDN files, because the rules were nested under pseudo-elements.
- Fixed CDN `.min.css` builds occasionally dropping rules (such as hidden Combobox, Command and Select options) by minifying with esbuild instead of Tailwind's minifier.
- Stopped the Toast docs example from pushing its fragment URL into the browser history with HTMX.
- Replaced the `.form` migration snippet in the 1.0.0 notes, which fails in Tailwind v4, with markup that works.
### Contributors
- Thanks to [@ytsruh](https://github.com/ytsruh), [@PhilipdeRijk](https://github.com/PhilipdeRijk), [@sgruendel](https://github.com/sgruendel), [@alexander-posztos](https://github.com/alexander-posztos) and [@Jezda1337](https://github.com/Jezda1337) for their contributions to this release.
## Unreleased
## [1.0.2] - 2026-07-06
+12 -26
View File
@@ -2,14 +2,13 @@
- Support development by sponsoring the author: https://github.com/sponsors/hunvreus
- Submit pull requests (PRs) against the `main` branch.
- Branches:
- `main` is the default branch. All work lands here, and https://main-basecoat.hunvreus.workers.dev previews it.
- `release` is what https://basecoatui.com deploys. Only the release workflow moves it (see below).
- For branches, we have:
- `main` is our production and default branch. This is what we deploy to https://basecoatui.com and publish to https://www.npmjs.com/package/basecoat-css.
- New features are worked in `feature/name-of-the-feature` branches.
- Issues are addressed in `issue/123-main-issue` branches.
- When ready, open a PR against `main`.
- Keep changes focused: one feature or fix per PR.
- Test locally before submitting: `npm run check` (package) and `npm run check:site` (docs site). CI runs both on every PR.
- Test locally before submitting.
- Follow existing code style.
## Cloudflare Workers deploys
@@ -25,32 +24,19 @@ Workers Builds settings:
- Build command: `npm run docs:build`
- Deploy command: `npx wrangler deploy`
- Non-production branch deploy command: `npx wrangler versions upload`
- Production branch: `release`
- Production branch: `main`
- Enable builds for non-production branches for branch preview URLs.
## Releasing
Thanks for helping!
## Releases and the docs site
The site at basecoatui.com deploys from the `release` branch, not `main`, so the docs always match the version on npm. Nobody commits to `release` directly.
To cut a release:
To release:
1. Bump the version on `main`: `npm version 1.1.0 --no-git-tag-version`, then commit (for example "Prepare 1.1.0 release") and push.
2. Tag that commit with the same version, with no `v` prefix, and push: `git tag 1.1.0 && git push origin 1.1.0`.
3. Publish a GitHub release for the tag: `gh release create 1.1.0 --generate-notes --latest --verify-tag`.
4. Approve the staged version on npmjs.com, in the package's Staged Packages tab, or with `npm stage approve <stage-id>`. Both ask for 2FA.
1. Bump the version in `package.json`, commit, tag (for example `1.1.0`) and push.
2. Run `npm publish`.
3. Publish a GitHub release for the tag, for example `gh release create 1.1.0 --generate-notes`.
Publishing the release runs `.github/workflows/release.yml`:
- **publish** checks that the tag is on `main` and matches the version in `package.json`, runs `npm run check`, and stages the version on npm with `npm stage publish`, using trusted publishing. A staged version is not installable until a maintainer approves it. If the version is already staged or published, this step is skipped.
- **deploy-site** waits until the version is live on npm, then points `release` at the tag, and Cloudflare builds and deploys the site. It checks every 30 seconds for up to about 5h50m. If you approve later than that, the job fails with a message: after approving, use Re-run failed jobs on that run, which re-runs deploy-site and then announce. If staging fails or you reject the version, the site is not deployed.
- **announce** posts the release notes to Discord, only after deploy-site succeeded. By then the version is installable from npm and `release` has moved, so Cloudflare is building the new site (that takes a few minutes).
Do not run `npm publish` by hand. Versions reach npm only through this workflow and your approval. The workflow needs no npm token and adds a provenance statement linking each version to the commit it was built from. Prereleases are not staged, deployed or announced.
You can also run the Release workflow manually from the Actions tab, with a tag and a mode. A manual run never publishes to npm.
- `deploy` (default) redeploys or rolls back the site: it moves `release` to the tag, only if that version is already on npm. It doesn't post to Discord.
- `announce` posts the tag's GitHub release to Discord again, for example after a failed post. It doesn't deploy.
- `deploy and announce` does both, and announces only if the deploy succeeded.
Thanks for helping!
Publishing the release runs `.github/workflows/deploy-site.yml`, which points `release` at the tag. Cloudflare then builds and deploys the site. To redeploy or roll back, run that workflow manually with the tag you want live.
+2 -487
View File
@@ -1,463 +1,20 @@
{
"name": "basecoat-css",
"version": "1.1.0",
"version": "1.0.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "basecoat-css",
"version": "1.1.0",
"version": "1.0.2",
"license": "MIT",
"devDependencies": {
"@tailwindcss/cli": "^4.1.17",
"esbuild": "0.28.2",
"prettier": "^3.5.3",
"tailwindcss": "^4.1.17",
"terser": "^5.42.0"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
"integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
"integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
"integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
"integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
"integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
"integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
"integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
"integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
"integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
"integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
"integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
"integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
"integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
"integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
"integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
"integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
"integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
"integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
"integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
"integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
"integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
"integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
"integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
"integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
"integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
"integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -1171,48 +728,6 @@
"node": ">=10.13.0"
}
},
"node_modules/esbuild": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
"integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.28.2",
"@esbuild/android-arm": "0.28.2",
"@esbuild/android-arm64": "0.28.2",
"@esbuild/android-x64": "0.28.2",
"@esbuild/darwin-arm64": "0.28.2",
"@esbuild/darwin-x64": "0.28.2",
"@esbuild/freebsd-arm64": "0.28.2",
"@esbuild/freebsd-x64": "0.28.2",
"@esbuild/linux-arm": "0.28.2",
"@esbuild/linux-arm64": "0.28.2",
"@esbuild/linux-ia32": "0.28.2",
"@esbuild/linux-loong64": "0.28.2",
"@esbuild/linux-mips64el": "0.28.2",
"@esbuild/linux-ppc64": "0.28.2",
"@esbuild/linux-riscv64": "0.28.2",
"@esbuild/linux-s390x": "0.28.2",
"@esbuild/linux-x64": "0.28.2",
"@esbuild/netbsd-arm64": "0.28.2",
"@esbuild/netbsd-x64": "0.28.2",
"@esbuild/openbsd-arm64": "0.28.2",
"@esbuild/openbsd-x64": "0.28.2",
"@esbuild/openharmony-arm64": "0.28.2",
"@esbuild/sunos-x64": "0.28.2",
"@esbuild/win32-arm64": "0.28.2",
"@esbuild/win32-ia32": "0.28.2",
"@esbuild/win32-x64": "0.28.2"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+1 -4
View File
@@ -1,6 +1,6 @@
{
"name": "basecoat-css",
"version": "1.1.0",
"version": "1.0.2",
"description": "Tailwind CSS for Basecoat components",
"author": {
"name": "hunvreus",
@@ -123,8 +123,6 @@
"scripts": {
"build": "node ./scripts/build.js",
"prepack": "node ./scripts/build.js",
"check": "npm run build && node ./scripts/check.js && npm pack --dry-run --ignore-scripts",
"check:site": "npm run docs:build && node ./scripts/check.js site",
"docs:build": "npm run build && npm --prefix site clean-install --progress=false && npm --prefix site run build",
"docs:dev": "npm --prefix site run dev",
"docs:preview": "npm run docs:build && npm --prefix site run preview",
@@ -134,7 +132,6 @@
},
"devDependencies": {
"@tailwindcss/cli": "^4.1.17",
"esbuild": "0.28.2",
"prettier": "^3.5.3",
"tailwindcss": "^4.1.17",
"terser": "^5.42.0"
+1 -13
View File
@@ -3,16 +3,10 @@ import path from 'path';
import { fileURLToPath } from 'url';
import { exec } from 'child_process';
import { promisify } from 'util';
import { transform } from 'esbuild';
import { generateCssEntrypoints } from './generate-css-entrypoints.js';
const execPromise = promisify(exec);
// Tailwind CSS v4 browser baseline (https://tailwindcss.com/docs/compatibility).
// Minified CDN CSS uses esbuild because Tailwind's --minify (Lightning CSS) can
// randomly drop rules (parcel-bundler/lightningcss#1235).
const cssTargets = ['chrome111', 'safari16.4', 'firefox128'];
// Resolve project root, assuming this script is in <project_root>/scripts/
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@@ -220,13 +214,7 @@ async function build() {
await execPromise(`npx tailwindcss -i "${cdnCssSrc}" -o "${cssDistCdnPath}"`);
console.log(`Generated non-minified CSS: ${cssDistCdnPath}`);
const { code, warnings } = await transform(await fs.readFile(cssDistCdnPath, 'utf-8'), {
loader: 'css',
minify: true,
target: cssTargets,
});
if (warnings.length) throw new Error(`esbuild warnings for ${cssDistCdnPath}: ${warnings.map((w) => w.text).join('; ')}`);
await fs.writeFile(cssDistCdnMinPath, code);
await execPromise(`npx tailwindcss -i "${cdnCssSrc}" -o "${cssDistCdnMinPath}" --minify`);
console.log(`Generated minified CSS: ${cssDistCdnMinPath}`);
}
-173
View File
@@ -1,173 +0,0 @@
// Cheap sanity checks. Run after a build.
// node scripts/check.js package: every export target exists, JS parses, min CSS keeps all selectors
// node scripts/check.js site docs site: site/dist built, llms.txt links to .md exports
import fs from 'fs';
import path from 'path';
import { execFileSync } from 'child_process';
import { fileURLToPath } from 'url';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const errors = [];
function checkPackage() {
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
for (const [key, target] of Object.entries(pkg.exports)) {
const file = path.join(root, target);
if (target.includes('*')) {
const dir = path.dirname(file.slice(0, file.indexOf('*')) + 'x');
if (!fs.existsSync(dir) || fs.readdirSync(dir).length === 0) {
errors.push(`export "${key}": no files in ${path.relative(root, dir)}`);
}
} else if (!fs.existsSync(file) || fs.statSync(file).size === 0) {
errors.push(`export "${key}": ${target} is missing or empty`);
}
}
const jsDir = path.join(root, 'dist', 'js');
for (const name of fs.existsSync(jsDir) ? fs.readdirSync(jsDir) : []) {
if (!name.endsWith('.js')) continue;
try {
execFileSync(process.execPath, ['--check', path.join(jsDir, name)], { stdio: 'pipe' });
} catch (error) {
const message = error.stderr.toString().split('\n').find((line) => /Error/.test(line));
errors.push(`dist/js/${name} does not parse: ${message || 'syntax error'}`);
}
}
}
// Minified CDN CSS must keep every selector of its unminified source.
// Selectors are flattened (nesting resolved, :is() lists expanded) and normalized
// (::after/:after, attribute quotes, combinator spacing, from/0%) before comparing.
function splitTop(str) {
const out = [];
let depth = 0, quote = null, cur = '';
for (let i = 0; i < str.length; i++) {
const c = str[i];
if (quote) { cur += c; if (c === '\\') cur += str[++i] ?? ''; else if (c === quote) quote = null; continue; }
if (c === '"' || c === "'") { quote = c; cur += c; continue; }
if (c === '(' || c === '[') depth++;
else if (c === ')' || c === ']') depth--;
if (c === ',' && depth === 0) { out.push(cur); cur = ''; } else cur += c;
}
out.push(cur);
return out.map((s) => s.trim()).filter(Boolean);
}
function parseCss(css) {
css = css.replace(/\/\*[\s\S]*?\*\//g, '');
const root = { children: [] };
const stack = [root];
let buf = '', quote = null, paren = 0;
for (let i = 0; i < css.length; i++) {
const c = css[i];
if (quote) { buf += c; if (c === '\\') buf += css[++i] ?? ''; else if (c === quote) quote = null; continue; }
if (c === '"' || c === "'") { quote = c; buf += c; continue; }
if (c === '(') paren++;
else if (c === ')') paren--;
if (paren > 0) { buf += c; continue; }
if (c === '{') { const node = { prelude: buf.trim(), children: [] }; stack.at(-1).children.push(node); stack.push(node); buf = ''; }
else if (c === '}') { if (buf.trim()) stack.at(-1).children.push({ decl: true }); stack.pop(); buf = ''; }
else if (c === ';') { if (buf.trim()) stack.at(-1).children.push({ decl: true }); buf = ''; }
else buf += c;
}
return root;
}
function normalizeSelector(s) {
return s
.replace(/::(before|after|first-line|first-letter)\b/g, ':$1')
.replace(/\[\s*([^\]=~|^$*\s]+)\s*([~|^$*]?=)\s*(["'])((?:\\.|(?!\3).)*)\3\s*(\s[is])?\s*\]/g, '[$1$2$4$5]')
.replace(/\s+/g, ' ')
.replace(/\s*([>+~,])\s*/g, '$1')
.replace(/\(\s+/g, '(')
.replace(/\s+\)/g, ')')
.replace(/^from$/, '0%')
.replace(/^to$/, '100%')
.trim();
}
function expandIs(sel) {
const i = sel.indexOf(':is(');
if (i < 0) return [sel];
let depth = 0, j = i + 3;
for (; j < sel.length; j++) { if (sel[j] === '(') depth++; else if (sel[j] === ')' && --depth === 0) break; }
const inner = splitTop(sel.slice(i + 4, j));
const attached = i > 0 && !/[\s>+~(,]/.test(sel[i - 1]);
if (attached && inner.some((part) => /[\s>+~]/.test(part.replace(/\([^)]*\)/g, '')))) {
return expandIs(sel.slice(j + 1)).map((rest) => sel.slice(0, j + 1) + rest);
}
return inner.flatMap((part) => expandIs(sel.slice(0, i) + part + sel.slice(j + 1)));
}
function flatSelectors(css) {
const keys = new Set();
const normParams = (p) => p.replace(/\s+/g, ' ').replace(/\s*([():,<>=]+)\s*/g, '$1').replace(/"/g, "'").trim().toLowerCase();
const walk = (node, ctx, parents) => {
for (const child of node.children) {
if (!child.children) continue;
const pre = child.prelude;
if (pre.startsWith('@')) {
const [, name, params] = pre.match(/^@([\w-]+)\s*([\s\S]*)$/);
const at = `@${name} ${normParams(params)}`;
if (/^(media|supports|layer|container|keyframes|-webkit-keyframes|scope|starting-style)$/.test(name)) {
// Declarations directly inside a nested at-rule belong to the parent selectors.
if (parents && child.children.some((c) => c.decl)) for (const p of parents) keys.add(`${[...ctx, at].join(' | ')} || ${p}`);
walk(child, [...ctx, at], name.endsWith('keyframes') ? null : parents);
} else keys.add([...ctx, at].join(' | '));
continue;
}
const own = splitTop(pre);
const sels = (parents ? parents.flatMap((p) => own.map((s) => (s.includes('&') ? s.replace(/&/g, p) : `${p} ${s}`))) : own)
.map(normalizeSelector).flatMap(expandIs).map(normalizeSelector);
if (child.children.some((c) => c.decl)) for (const s of sels) keys.add(`${ctx.join(' | ')} || ${s}`);
walk(child, ctx, sels);
}
};
walk(parseCss(css), [], null);
return keys;
}
function checkMinifiedCss() {
const dist = path.join(root, 'dist');
for (const name of fs.existsSync(dist) ? fs.readdirSync(dist) : []) {
if (!name.endsWith('.cdn.css')) continue;
const minName = name.replace(/\.css$/, '.min.css');
const minFile = path.join(dist, minName);
if (!fs.existsSync(minFile)) { errors.push(`dist/${minName} is missing`); continue; }
const min = flatSelectors(fs.readFileSync(minFile, 'utf8'));
const missing = [...flatSelectors(fs.readFileSync(path.join(dist, name), 'utf8'))].filter((key) => !min.has(key));
if (missing.length) errors.push(`dist/${minName} dropped ${missing.length} selector(s) from dist/${name}, e.g. ${missing.slice(0, Number(process.env.SHOW || 3)).join('; ')}`);
}
}
function checkSite() {
const dist = path.join(root, 'site', 'dist');
for (const file of ['index.html', 'llms.txt', 'llms-full.txt']) {
if (!fs.existsSync(path.join(dist, file))) errors.push(`site/dist/${file} is missing`);
}
const llms = path.join(dist, 'llms.txt');
if (!fs.existsSync(llms)) return;
const links = [...fs.readFileSync(llms, 'utf8').matchAll(/^- \[[^\]]*\]\(([^)]+)\)/gm)].map((m) => m[1]);
if (links.length === 0) errors.push('site/dist/llms.txt has no links');
for (const link of links) {
if (!link.endsWith('.md')) errors.push(`site/dist/llms.txt links to ${link}, not a .md export`);
}
}
const target = process.argv[2] || 'package';
if (target === 'package') {
checkPackage();
checkMinifiedCss();
}
else if (target === 'site') checkSite();
else {
console.error(`Unknown target "${target}". Use "package" or "site".`);
process.exit(2);
}
if (errors.length) {
console.error(`${target} check failed:\n- ${errors.join('\n- ')}`);
process.exit(1);
}
console.log(`${target} check passed.`);
+203 -5
View File
@@ -10,7 +10,7 @@
"htmx.org": "^2.0.10",
"lucide-static": "^1.17.0",
"nunjucks": "^3.2.4",
"reallysimpledocs": "^1.0.14"
"reallysimpledocs": "^1.0.12"
},
"devDependencies": {
"astro": "^6.4.6",
@@ -19,7 +19,7 @@
},
"..": {
"name": "basecoat-css",
"version": "1.1.0",
"version": "1.0.1",
"license": "MIT",
"devDependencies": {
"@tailwindcss/cli": "^4.1.17",
@@ -812,6 +812,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -834,6 +835,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -856,6 +858,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -872,6 +875,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -888,6 +892,7 @@
"cpu": [
"arm"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -904,6 +909,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -920,6 +926,7 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -936,6 +943,7 @@
"cpu": [
"riscv64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -952,6 +960,7 @@
"cpu": [
"s390x"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -968,6 +977,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -984,6 +994,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1000,6 +1011,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1016,6 +1028,7 @@
"cpu": [
"arm"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1038,6 +1051,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1060,6 +1074,7 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1082,6 +1097,7 @@
"cpu": [
"riscv64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1104,6 +1120,7 @@
"cpu": [
"s390x"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1126,6 +1143,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1148,6 +1166,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1170,6 +1189,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1192,6 +1212,7 @@
"cpu": [
"wasm32"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
"optional": true,
"dependencies": {
@@ -1211,6 +1232,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1230,6 +1252,7 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1249,6 +1272,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -2415,6 +2439,20 @@
"resolved": "..",
"link": true
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/blake3-wasm": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz",
@@ -2428,6 +2466,20 @@
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
"license": "ISC"
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ccount": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
@@ -2478,6 +2530,32 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/ci-info": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz",
@@ -3082,6 +3160,20 @@
}
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/flattie": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz",
@@ -3147,6 +3239,20 @@
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==",
"license": "ISC"
},
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"license": "ISC",
"optional": true,
"peer": true,
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -3469,6 +3575,20 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-decimal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
@@ -3494,6 +3614,31 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-hexadecimal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
@@ -3537,6 +3682,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/is-plain-obj": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
@@ -5346,10 +5502,38 @@
"integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==",
"license": "MIT"
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/readdirp/node_modules/picomatch": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/reallysimpledocs": {
"version": "1.0.14",
"resolved": "https://registry.npmjs.org/reallysimpledocs/-/reallysimpledocs-1.0.14.tgz",
"integrity": "sha512-TRiCxZZ3j6+AacD3HzeYr1+6soUdJN3ZkNtrdYON2OtiqlLc5qb0GHV1c4k+49l9z4/20FQ0qGH2r0YXoWwOQg==",
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/reallysimpledocs/-/reallysimpledocs-1.0.12.tgz",
"integrity": "sha512-nZIvrHdDcr2TImpT3TlhjLb3420xikeyXUqOuuvfosuEPgbfTYvnfg9MpgLvOciQLCjsuSGdpQzNrdW5MTVurQ==",
"license": "MIT",
"dependencies": {
"@astrojs/markdown-remark": "^7.2.0",
@@ -6047,6 +6231,20 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/trim-lines": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+1 -1
View File
@@ -15,7 +15,7 @@
"htmx.org": "^2.0.10",
"lucide-static": "^1.17.0",
"nunjucks": "^3.2.4",
"reallysimpledocs": "^1.0.14"
"reallysimpledocs": "^1.0.12"
},
"devDependencies": {
"astro": "^6.4.6",
+4 -4
View File
@@ -24,28 +24,28 @@ const styleUrls = {
<style is:inline>
@font-face {
font-family: "Geist";
font-family: "Geist Sans";
src: url("/assets/fonts/geist/geist-sans-latin-400-normal.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Geist";
font-family: "Geist Sans";
src: url("/assets/fonts/geist/geist-sans-latin-500-normal.woff2") format("woff2");
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Geist";
font-family: "Geist Sans";
src: url("/assets/fonts/geist/geist-sans-latin-600-normal.woff2") format("woff2");
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Geist";
font-family: "Geist Sans";
src: url("/assets/fonts/geist/geist-sans-latin-700-normal.woff2") format("woff2");
font-weight: 700;
font-style: normal;
-2
View File
@@ -85,8 +85,6 @@ Combobox is input-first. The visible input filters the list; the hidden input st
</Step>
</Steps>
Pressing Escape in the input closes an open suggestions popup without dismissing an enclosing dialog. Once the popup is closed, press Escape again to dismiss the dialog.
### HTML structure
<dl>
-134
View File
@@ -1,134 +0,0 @@
# Separator
<Callout title="No dedicated component" icon="circle-alert">
There is no dedicated Separator component in Basecoat.
</Callout>
<Preview class="w-full max-w-sm">
<div class="flex flex-col gap-4 text-sm">
<div class="flex flex-col gap-1.5">
<div class="leading-none font-medium">Basecoat</div>
<div class="text-muted-foreground">Tailwind components for any web stack</div>
</div>
<hr role="separator" />
<div>A set of components you can drop into any HTML page, styled like shadcn/ui.</div>
</div>
</Preview>
## Usage
<Steps>
<Step title="Include CSS">
Import Tailwind and one full Basecoat style bundle.
```css
@import "tailwindcss";
@import "basecoat-css/vega.css";
```
Or import only the base CSS, which styles horizontal and vertical separators.
```css
@import "tailwindcss";
@import "basecoat-css/base.css";
```
Separator is a native `<hr>` and has no dedicated component CSS file.
Using CDN or bundler imports? See the [Installation page](/installation).
</Step>
<Step title="Add your separator HTML">
Use an `<hr>` with `role="separator"`. It renders as a 1px line in the `border` color, matching shadcn/ui's Separator.
```html
<hr role="separator" />
```
If the separator is purely decorative, use `role="none"` instead so assistive technologies skip it. This is what shadcn/ui's `decorative` prop does.
```html
<hr role="none" />
```
</Step>
</Steps>
## Examples
### Vertical
Add `aria-orientation="vertical"` for a vertical separator. It fills the height of its row.
<Preview>
<div class="flex h-5 items-center gap-4 text-sm">
<div>Blog</div>
<hr role="separator" aria-orientation="vertical" />
<div>Docs</div>
<hr role="separator" aria-orientation="vertical" />
<div>Source</div>
</div>
</Preview>
### Menu
Vertical separators between menu items with descriptions.
<Preview>
<div class="flex items-center gap-2 text-sm md:gap-4">
<div class="flex flex-col gap-1">
<span class="font-medium">Settings</span>
<span class="text-xs text-muted-foreground">Manage preferences</span>
</div>
<hr role="separator" aria-orientation="vertical" />
<div class="flex flex-col gap-1">
<span class="font-medium">Account</span>
<span class="text-xs text-muted-foreground">Profile & security</span>
</div>
<hr role="separator" aria-orientation="vertical" class="hidden md:block" />
<div class="hidden flex-col gap-1 md:flex">
<span class="font-medium">Help</span>
<span class="text-xs text-muted-foreground">Support & docs</span>
</div>
</div>
</Preview>
### List
Horizontal separators between list items.
<Preview class="w-full max-w-sm">
<div class="flex flex-col gap-2 text-sm">
<dl class="flex items-center justify-between">
<dt>Item 1</dt>
<dd class="text-muted-foreground">Value 1</dd>
</dl>
<hr role="separator" />
<dl class="flex items-center justify-between">
<dt>Item 2</dt>
<dd class="text-muted-foreground">Value 2</dd>
</dl>
<hr role="separator" />
<dl class="flex items-center justify-between">
<dt>Item 3</dt>
<dd class="text-muted-foreground">Value 3</dd>
</dl>
</div>
</Preview>
### RTL
Separators follow document direction. Vertical separators use a logical inline-start border, so they need no changes.
<Preview class="w-full max-w-sm">
<div dir="rtl" class="flex flex-col gap-4 text-sm">
<div class="flex flex-col gap-1.5">
<div class="leading-none font-medium">Basecoat</div>
<div class="text-muted-foreground">مكونات Tailwind لأي بيئة ويب</div>
</div>
<hr role="separator" />
<div>مجموعة من المكونات يمكنك إضافتها إلى أي صفحة HTML، بتصميم مثل shadcn/ui.</div>
</div>
</Preview>
-8
View File
@@ -53,14 +53,6 @@ Basecoat tooltips are CSS-only and text-only. This intentionally differs from sh
<button class="btn" data-variant="outline" data-tooltip="Default tooltip">Default</button>
</Preview>
### Long text
Tooltips stay on one line and truncate with an ellipsis when the text exceeds the maximum width. This intentionally differs from shadcn/ui's multiline tooltip content.
<Preview>
<button class="btn" data-variant="outline" data-tooltip="A tooltip with enough text to exceed the default maximum width and demonstrate truncation">Hover</button>
</Preview>
### Sides
<Preview>
+6 -6
View File
@@ -45,12 +45,12 @@ Learn more in the [shadcn/ui theming docs](https://ui.shadcn.com/docs/theming).
## Fonts
Basecoat does not ship web font files by default. Its font tokens prefer [Geist](https://fonts.google.com/specimen/Geist) and [Geist Mono](https://fonts.google.com/specimen/Geist+Mono) when those fonts are available, then fall back to the full Tailwind default sans and mono stacks.
Basecoat does not ship web font files by default. Its font tokens prefer [Geist Sans](https://fonts.google.com/specimen/Geist) and [Geist Mono](https://fonts.google.com/specimen/Geist+Mono) when those fonts are available, then fall back to the full Tailwind default sans and mono stacks.
Install the fonts with Fontsource:
```bash
npm install @fontsource/geist @fontsource/geist-mono
npm install @fontsource/geist-sans @fontsource/geist-mono
```
Then import the font files. Basecoat's default font tokens already reference Geist with full system fallbacks:
@@ -58,10 +58,10 @@ Then import the font files. Basecoat's default font tokens already reference Gei
```css
@import "tailwindcss";
@import "basecoat-css/sera";
@import "@fontsource/geist/400.css";
@import "@fontsource/geist/500.css";
@import "@fontsource/geist/600.css";
@import "@fontsource/geist/700.css";
@import "@fontsource/geist-sans/400.css";
@import "@fontsource/geist-sans/500.css";
@import "@fontsource/geist-sans/600.css";
@import "@fontsource/geist-sans/700.css";
@import "@fontsource/geist-mono/400.css";
@import "@fontsource/geist-mono/500.css";
@import "@fontsource/geist-mono/600.css";
-1
View File
@@ -94,7 +94,6 @@
"components/radio-group",
"components/select",
"components/scroll-area",
"components/separator",
"components/sidebar",
"components/skeleton",
"components/slider",
-26
View File
@@ -70,32 +70,6 @@ Or load the Basecoat runtime once, then only the component scripts you use.
| `dist/js/basecoat.min.js` | Runtime only. Load once before individual component scripts. |
| `dist/js/{component}.min.js` | Individual JavaScript component file. |
### Tailwind utilities with the CDN
The CDN stylesheets only include the utilities Basecoat uses, so classes like `bg-primary` or `text-muted-foreground` are not available. If you add the [Tailwind Play CDN](https://tailwindcss.com/docs/installation/play-cdn) for utilities, map Basecoat's theme tokens to Tailwind colors and restore the border color after Tailwind's preflight. Load this after the Basecoat stylesheet.
```html
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<style type="text/tailwindcss">
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-border: var(--border);
}
@layer base {
*, ::before, ::after { border-color: var(--border); }
}
</style>
```
Add any other tokens you use the same way, for example `--color-destructive: var(--destructive);`. The Play CDN is meant for prototypes. For production, use the [Tailwind CLI with a bundle file](#npm) instead.
## NPM
<Steps>
+1 -10
View File
@@ -84,7 +84,7 @@
}
@theme {
--font-sans: "Geist", "Geist Sans", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-sans: "Geist Sans", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
@@ -133,13 +133,4 @@
body {
@apply bg-background text-foreground overscroll-none;
}
hr:where([aria-orientation='vertical']) {
flex-shrink: 0;
align-self: stretch;
inline-size: 1px;
block-size: auto;
margin: 0;
border-block-start-width: 0;
border-inline-start-width: 1px;
}
}
-14
View File
@@ -1,19 +1,5 @@
/* Dialog */
@keyframes dialog-scroll-lock {
from, to {
scrollbar-gutter: stable;
}
}
@layer components {
/* Lock page scroll while a modal dialog is open. The scroll timeline only
reserves the scrollbar gutter if the page can scroll, so nothing shifts. */
html:has(:is(.dialog, .alert-dialog, .command-dialog, .drawer):modal) {
overflow: hidden;
animation: dialog-scroll-lock linear both;
animation-timeline: scroll(self);
}
.dialog,
.alert-dialog {
@apply bg-transparent p-0 text-inherit opacity-0 transition-all transition-discrete backdrop:opacity-0 backdrop:transition-all backdrop:transition-discrete;
+7 -10
View File
@@ -8,7 +8,7 @@
&[aria-hidden=false] {
@apply max-md:bg-black/50 max-md:fixed max-md:inset-0 max-md:z-40;
}
> nav {
nav {
@apply fixed inset-y-0 z-50 flex w-(--sidebar-mobile-width) flex-col transition-transform duration-300 ease-in-out md:w-(--sidebar-width);
}
& + * {
@@ -16,14 +16,14 @@
}
&:not([data-side]),
&[data-side=left] {
> nav {
nav {
@apply left-0 border-r;
}
& + * {
@apply relative md:ml-(--sidebar-width);
}
&[aria-hidden=true] {
> nav {
nav {
@apply -translate-x-full;
}
& + * {
@@ -32,14 +32,14 @@
}
}
&[data-side=right] {
> nav {
nav {
@apply right-0 border-l;
}
& + * {
@apply relative md:mr-(--sidebar-width);
}
&[aria-hidden=true] {
> nav {
nav {
@apply translate-x-full;
}
& + * {
@@ -47,7 +47,7 @@
}
}
}
> nav {
nav {
> header,
> footer {
@apply flex flex-col;
@@ -79,10 +79,7 @@
&:not([open]) {
> summary {
&::after {
@apply -rotate-90;
}
&:where(:dir(rtl), [dir='rtl'], [dir='rtl'] *)::after {
@apply rotate-90;
@apply -rotate-90 rtl:rotate-90;
}
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
@apply relative;
&:before {
@apply absolute block z-[60] w-fit max-w-xs origin-(--transform-origin) scale-95 truncate invisible opacity-0 transition-all pointer-events-none content-[attr(data-tooltip)];
@apply absolute z-[60] w-fit max-w-xs origin-(--transform-origin) scale-95 truncate invisible opacity-0 transition-all pointer-events-none content-[attr(data-tooltip)];
}
&:hover:before {
+43 -102
View File
@@ -217,7 +217,7 @@
/* Native Select */
.field > select,
select.select {
@apply bg-input/50 border-transparent placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-9 w-fit rounded-3xl border py-1 ps-3 pe-8 text-base md:text-sm transition-[color,box-shadow,background-color] focus-visible:ring-3 aria-invalid:ring-3 data-[size=sm]:h-8;
@apply bg-input/50 border-transparent placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-9 w-fit rounded-3xl border py-1 ps-3 pe-8 text-sm transition-[color,box-shadow,background-color] focus-visible:ring-3 aria-invalid:ring-3 data-[size=sm]:h-8;
@apply bg-[image:var(--chevron-down-icon-50)] bg-no-repeat bg-position-[center_right_0.625rem] bg-size-[1rem];
}
@@ -373,7 +373,7 @@
}
.command > header input {
@apply ps-1.5 text-base md:text-sm placeholder:text-muted-foreground;
@apply ps-1.5 text-sm placeholder:text-muted-foreground;
}
.command [role='menu'] {
@@ -464,10 +464,6 @@
@apply bg-input/50 border-transparent focus-within:border-ring focus-within:ring-ring/30 has-[[aria-invalid=true]]:ring-destructive/20 dark:has-[[aria-invalid=true]]:ring-destructive/40 has-[[aria-invalid=true]]:border-destructive dark:has-[[aria-invalid=true]]:border-destructive/50 min-h-9 gap-1.5 rounded-3xl border bg-clip-padding px-1.5 py-1.5 text-sm transition-[color,box-shadow,background-color] focus-within:ring-3 has-[[aria-invalid=true]]:ring-3;
}
.combobox-chips > input[role='combobox'] {
@apply text-base md:text-sm;
}
.combobox-chip {
@apply bg-input text-foreground dark:bg-input/60 h-5.5 gap-1 rounded-3xl px-2 text-xs font-medium has-[>.combobox-chip-remove]:pe-0;
}
@@ -791,11 +787,7 @@
}
:is(.field > input[type='radio'], .input[type='radio']):checked:before {
@apply bg-primary-foreground size-2;
}
html.dark :is(.field > input[type='radio'], .input[type='radio']):checked:before {
@apply size-2.5;
@apply bg-primary-foreground size-2 dark:size-2.5;
}
/* Switch */
@@ -813,11 +805,7 @@
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):before {
@apply bg-background rounded-full shadow-sm;
}
html:not(.dark) :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):before {
@apply bg-clip-padding;
@apply bg-background rounded-full shadow-sm not-dark:bg-clip-padding;
}
html.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not(:checked):before {
@@ -960,7 +948,7 @@
@apply rounded-3xl;
}
.input-group > :is(input, select, [data-control]) {
@apply px-3 py-1 text-base md:text-sm;
@apply px-3 py-1 text-sm;
}
.input-group:not([data-orientation='vertical']):has(> :is([data-align='start'], [data-align='inline-start'])) > :is(input, select, [data-control]) {
@@ -972,7 +960,7 @@
}
.input-group > textarea {
@apply px-3 py-2.5 text-base md:text-sm;
@apply px-3 py-2.5 text-sm;
}
.input-group > :is(span, [role='group'], header, footer),
@@ -1194,92 +1182,92 @@
}
/* Sidebar */
.sidebar > nav {
.sidebar nav {
@apply bg-sidebar text-sidebar-foreground;
}
.sidebar > nav > header,
.sidebar > nav > footer {
.sidebar nav > header,
.sidebar nav > footer {
@apply gap-2 p-2 [--radius:var(--radius-xl)];
}
.sidebar > nav > section {
.sidebar nav > section {
@apply gap-2 [--radius:var(--radius-xl)];
}
.sidebar > nav [role=separator] {
.sidebar nav [role=separator] {
@apply border-sidebar-border mx-2;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply p-2;
}
.sidebar > nav h3 {
.sidebar nav h3 {
@apply text-sidebar-foreground/70 ring-sidebar-ring h-8 rounded-xl px-3 text-xs font-medium focus-visible:ring-2 [&>svg]:size-4 transition-[margin,opacity] duration-200 ease-linear;
}
.sidebar > nav > section > [role=group] > ul {
.sidebar nav > section > [role=group] > ul {
@apply gap-0.5;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply text-sm;
}
.sidebar > nav li > :is(a, button),
.sidebar > nav li > details > summary {
.sidebar nav li > :is(a, button),
.sidebar nav li > details > summary {
@apply ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground gap-2 rounded-xl px-3 py-2 text-sm focus-visible:ring-2 text-left focus-visible:outline-hidden data-[open]:hover:bg-sidebar-accent data-[open]:hover:text-sidebar-accent-foreground [&>svg]:shrink-0 [&>svg]:size-4;
}
.sidebar > nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar > nav li > details > summary:is([aria-current=page], [data-active=true]) {
.sidebar nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar nav li > details > summary:is([aria-current=page], [data-active=true]) {
@apply bg-sidebar-accent text-sidebar-accent-foreground font-medium;
}
.sidebar > nav li > :is(a, button):not([data-variant]),
.sidebar > nav li > details > summary:not([data-variant]),
.sidebar > nav li > :is(a, button)[data-variant=default],
.sidebar > nav li > details > summary[data-variant=default] {
.sidebar nav li > :is(a, button):not([data-variant]),
.sidebar nav li > details > summary:not([data-variant]),
.sidebar nav li > :is(a, button)[data-variant=default],
.sidebar nav li > details > summary[data-variant=default] {
@apply hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button)[data-variant=outline],
.sidebar > nav li > details > summary[data-variant=outline] {
.sidebar nav li > :is(a, button)[data-variant=outline],
.sidebar nav li > details > summary[data-variant=outline] {
@apply bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:shadow-[0_0_0_1px_var(--sidebar-accent)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button):not([data-size]),
.sidebar > nav li > details > summary:not([data-size]),
.sidebar > nav li > :is(a, button)[data-size=default],
.sidebar > nav li > details > summary[data-size=default] {
.sidebar nav li > :is(a, button):not([data-size]),
.sidebar nav li > details > summary:not([data-size]),
.sidebar nav li > :is(a, button)[data-size=default],
.sidebar nav li > details > summary[data-size=default] {
@apply h-9 text-sm;
}
.sidebar > nav li > :is(a, button)[data-size=sm],
.sidebar > nav li > details > summary[data-size=sm] {
.sidebar nav li > :is(a, button)[data-size=sm],
.sidebar nav li > details > summary[data-size=sm] {
@apply h-8 text-xs;
}
.sidebar > nav li > :is(a, button)[data-size=lg],
.sidebar > nav li > details > summary[data-size=lg] {
.sidebar nav li > :is(a, button)[data-size=lg],
.sidebar nav li > details > summary[data-size=lg] {
@apply h-14 px-3 text-sm;
}
.sidebar > nav li > details > summary::after {
.sidebar nav li > details > summary::after {
@apply size-4;
}
.sidebar > nav li > details::details-content {
.sidebar nav li > details::details-content {
@apply px-3.5;
}
.sidebar > nav ul ul {
.sidebar nav ul ul {
@apply border-sidebar-border translate-x-px gap-1 border-s px-2.5 py-0.5 rtl:-translate-x-px;
}
.sidebar > nav ul ul > li > :is(a, button),
.sidebar > nav ul ul > li > details > summary {
.sidebar nav ul ul > li > :is(a, button),
.sidebar nav ul ul > li > details > summary {
@apply text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground h-7 gap-2 rounded-xl px-3 text-sm focus-visible:ring-2 [&>svg]:size-4;
}
/* Progress */
@@ -1336,19 +1324,11 @@
/* Tooltip */
[data-tooltip]::before {
@apply bg-foreground text-background rounded-xl px-3 py-1.5 text-xs;
@apply bg-foreground text-background inline-flex items-center gap-1.5 rounded-xl px-3 py-1.5 text-xs has-[.kbd]:pe-1.5 [&_.kbd]:relative [&_.kbd]:isolate [&_.kbd]:z-50 [&_.kbd]:rounded-lg;
}
[data-tooltip][data-arrow]::after {
@apply bg-foreground absolute z-[60] size-2.5 rotate-45 rounded-[2px];
}
[data-tooltip][data-arrow][data-side='left']::after {
@apply translate-x-[-1.5px];
}
[data-tooltip][data-arrow][data-side='right']::after {
@apply translate-x-[1.5px];
@apply bg-foreground absolute z-[60] size-2.5 rotate-45 rounded-[2px] data-[side=left]:translate-x-[-1.5px] data-[side=right]:translate-x-[1.5px];
}
/* Toast */
@@ -1382,54 +1362,15 @@
}
:is(.field > input[type='range'], .input[type='range'])::-webkit-slider-thumb {
@apply ring-black/10 ring-1 h-4 w-6 rounded-full bg-white shadow-md transition-[color,box-shadow,background-color] -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)];
@apply ring-black/10 not-dark:bg-clip-padding ring-1 h-4 w-6 rounded-full bg-white shadow-md transition-[color,box-shadow,background-color] hover:ring-4 hover:ring-ring/30 focus-visible:ring-4 focus-visible:ring-ring/30 -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)] focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-moz-range-thumb {
@apply ring-black/10 ring-1 h-4 w-6 rounded-full bg-white shadow-md transition-[color,box-shadow,background-color];
@apply ring-black/10 not-dark:bg-clip-padding ring-1 h-4 w-6 rounded-full bg-white shadow-md transition-[color,box-shadow,background-color] hover:ring-4 hover:ring-ring/30 focus-visible:ring-4 focus-visible:ring-ring/30 focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-ms-thumb {
@apply ring-black/10 ring-1 h-4 w-6 rounded-full bg-white shadow-md transition-[color,box-shadow,background-color];
}
/* States go before the thumb pseudo-element so they still apply with native CSS nesting. */
html:not(.dark) :is(.field > input[type='range'], .input[type='range'])::-webkit-slider-thumb {
@apply bg-clip-padding;
}
html:not(.dark) :is(.field > input[type='range'], .input[type='range'])::-moz-range-thumb {
@apply bg-clip-padding;
}
html:not(.dark) :is(.field > input[type='range'], .input[type='range'])::-ms-thumb {
@apply bg-clip-padding;
}
@media (hover: hover) {
:is(.field > input[type='range'], .input[type='range']):hover::-webkit-slider-thumb {
@apply ring-4 ring-ring/30;
}
:is(.field > input[type='range'], .input[type='range']):hover::-moz-range-thumb {
@apply ring-4 ring-ring/30;
}
:is(.field > input[type='range'], .input[type='range']):hover::-ms-thumb {
@apply ring-4 ring-ring/30;
}
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-webkit-slider-thumb {
@apply ring-4 ring-ring/30 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-moz-range-thumb {
@apply ring-4 ring-ring/30 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-ms-thumb {
@apply ring-4 ring-ring/30 outline-hidden;
@apply ring-black/10 not-dark:bg-clip-padding ring-1 h-4 w-6 rounded-full bg-white shadow-md transition-[color,box-shadow,background-color] hover:ring-4 hover:ring-ring/30 focus-visible:ring-4 focus-visible:ring-ring/30 focus-visible:outline-hidden;
}
}
+36 -75
View File
@@ -1185,92 +1185,92 @@
}
/* Sidebar */
.sidebar > nav {
.sidebar nav {
@apply bg-sidebar text-sidebar-foreground;
}
.sidebar > nav > header,
.sidebar > nav > footer {
.sidebar nav > header,
.sidebar nav > footer {
@apply gap-2 p-2;
}
.sidebar > nav > section {
.sidebar nav > section {
@apply gap-0;
}
.sidebar > nav [role=separator] {
.sidebar nav [role=separator] {
@apply border-sidebar-border mx-2;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply p-2;
}
.sidebar > nav h3 {
.sidebar nav h3 {
@apply text-sidebar-foreground/70 ring-sidebar-ring h-8 rounded-none px-2 text-xs focus-visible:ring-2 [&>svg]:size-4 transition-[margin,opacity] duration-200 ease-linear;
}
.sidebar > nav > section > [role=group] > ul {
.sidebar nav > section > [role=group] > ul {
@apply gap-0;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply text-xs;
}
.sidebar > nav li > :is(a, button),
.sidebar > nav li > details > summary {
.sidebar nav li > :is(a, button),
.sidebar nav li > details > summary {
@apply ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground gap-2 rounded-none p-2 text-xs focus-visible:ring-2 text-left focus-visible:outline-hidden data-[open]:hover:bg-sidebar-accent data-[open]:hover:text-sidebar-accent-foreground [&>svg]:shrink-0 [&>svg]:size-4;
}
.sidebar > nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar > nav li > details > summary:is([aria-current=page], [data-active=true]) {
.sidebar nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar nav li > details > summary:is([aria-current=page], [data-active=true]) {
@apply bg-sidebar-accent text-sidebar-accent-foreground font-medium;
}
.sidebar > nav li > :is(a, button):not([data-variant]),
.sidebar > nav li > details > summary:not([data-variant]),
.sidebar > nav li > :is(a, button)[data-variant=default],
.sidebar > nav li > details > summary[data-variant=default] {
.sidebar nav li > :is(a, button):not([data-variant]),
.sidebar nav li > details > summary:not([data-variant]),
.sidebar nav li > :is(a, button)[data-variant=default],
.sidebar nav li > details > summary[data-variant=default] {
@apply hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button)[data-variant=outline],
.sidebar > nav li > details > summary[data-variant=outline] {
.sidebar nav li > :is(a, button)[data-variant=outline],
.sidebar nav li > details > summary[data-variant=outline] {
@apply bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:shadow-[0_0_0_1px_var(--sidebar-accent)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button):not([data-size]),
.sidebar > nav li > details > summary:not([data-size]),
.sidebar > nav li > :is(a, button)[data-size=default],
.sidebar > nav li > details > summary[data-size=default] {
.sidebar nav li > :is(a, button):not([data-size]),
.sidebar nav li > details > summary:not([data-size]),
.sidebar nav li > :is(a, button)[data-size=default],
.sidebar nav li > details > summary[data-size=default] {
@apply h-8 text-xs;
}
.sidebar > nav li > :is(a, button)[data-size=sm],
.sidebar > nav li > details > summary[data-size=sm] {
.sidebar nav li > :is(a, button)[data-size=sm],
.sidebar nav li > details > summary[data-size=sm] {
@apply h-7 text-xs;
}
.sidebar > nav li > :is(a, button)[data-size=lg],
.sidebar > nav li > details > summary[data-size=lg] {
.sidebar nav li > :is(a, button)[data-size=lg],
.sidebar nav li > details > summary[data-size=lg] {
@apply h-12 text-xs;
}
.sidebar > nav li > details > summary::after {
.sidebar nav li > details > summary::after {
@apply size-4;
}
.sidebar > nav li > details::details-content {
.sidebar nav li > details::details-content {
@apply px-3.5;
}
.sidebar > nav ul ul {
.sidebar nav ul ul {
@apply border-sidebar-border translate-x-px gap-1 border-s px-2.5 py-0.5 rtl:-translate-x-px;
}
.sidebar > nav ul ul > li > :is(a, button),
.sidebar > nav ul ul > li > details > summary {
.sidebar nav ul ul > li > :is(a, button),
.sidebar nav ul ul > li > details > summary {
@apply text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground h-7 gap-2 rounded-none px-2 text-xs focus-visible:ring-2 [&>svg]:size-4;
}
/* Progress */
@@ -1327,7 +1327,7 @@
/* Tooltip */
[data-tooltip]::before {
@apply bg-foreground text-background rounded-none px-3 py-1.5 text-xs;
@apply bg-foreground text-background inline-flex items-center gap-1.5 rounded-none px-3 py-1.5 text-xs has-[.kbd]:pe-1.5 [&_.kbd]:relative [&_.kbd]:isolate [&_.kbd]:z-50 [&_.kbd]:rounded-none;
}
[data-tooltip][data-arrow]::after {
@@ -1365,54 +1365,15 @@
}
:is(.field > input[type='range'], .input[type='range'])::-webkit-slider-thumb {
@apply border-ring ring-ring/50 relative size-3 rounded-none border bg-white transition-[color,box-shadow] -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)];
@apply border-ring ring-ring/50 relative size-3 rounded-none border bg-white transition-[color,box-shadow] hover:ring-1 focus-visible:ring-1 active:ring-1 -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)] focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-moz-range-thumb {
@apply border-ring ring-ring/50 relative size-3 rounded-none border bg-white transition-[color,box-shadow];
@apply border-ring ring-ring/50 relative size-3 rounded-none border bg-white transition-[color,box-shadow] hover:ring-1 focus-visible:ring-1 active:ring-1 focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-ms-thumb {
@apply border-ring ring-ring/50 relative size-3 rounded-none border bg-white transition-[color,box-shadow];
}
/* States go before the thumb pseudo-element so they still apply with native CSS nesting. */
@media (hover: hover) {
:is(.field > input[type='range'], .input[type='range']):hover::-webkit-slider-thumb {
@apply ring-1;
}
:is(.field > input[type='range'], .input[type='range']):hover::-moz-range-thumb {
@apply ring-1;
}
:is(.field > input[type='range'], .input[type='range']):hover::-ms-thumb {
@apply ring-1;
}
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-webkit-slider-thumb {
@apply ring-1 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-moz-range-thumb {
@apply ring-1 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-ms-thumb {
@apply ring-1 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):active::-webkit-slider-thumb {
@apply ring-1;
}
:is(.field > input[type='range'], .input[type='range']):active::-moz-range-thumb {
@apply ring-1;
}
:is(.field > input[type='range'], .input[type='range']):active::-ms-thumb {
@apply ring-1;
@apply border-ring ring-ring/50 relative size-3 rounded-none border bg-white transition-[color,box-shadow] hover:ring-1 focus-visible:ring-1 active:ring-1 focus-visible:outline-hidden;
}
}
+41 -80
View File
@@ -209,7 +209,7 @@
/* Native Select */
.field > select,
select.select {
@apply border-input bg-input/30 placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-9 w-fit rounded-4xl border py-1 ps-3 pe-8 text-base md:text-sm transition-colors focus-visible:ring-[3px] aria-invalid:ring-[3px] data-[size=sm]:h-8;
@apply border-input bg-input/30 placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-9 w-fit rounded-4xl border py-1 ps-3 pe-8 text-sm transition-colors focus-visible:ring-[3px] aria-invalid:ring-[3px] data-[size=sm]:h-8;
@apply bg-[image:var(--chevron-down-icon-50)] bg-no-repeat bg-position-[center_right_0.875rem] bg-size-[1rem];
}
@@ -365,7 +365,7 @@
}
.command > header input {
@apply ps-1.5 text-base md:text-sm placeholder:text-muted-foreground;
@apply ps-1.5 text-sm placeholder:text-muted-foreground;
}
.command [role='menu'] {
@@ -456,10 +456,6 @@
@apply bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 has-[[aria-invalid=true]]:ring-destructive/20 dark:has-[[aria-invalid=true]]:ring-destructive/40 has-[[aria-invalid=true]]:border-destructive dark:has-[[aria-invalid=true]]:border-destructive/50 min-h-9 gap-1.5 rounded-4xl border bg-clip-padding px-1.5 py-1.5 text-sm transition-colors focus-within:ring-[3px] has-[[aria-invalid=true]]:ring-[3px];
}
.combobox-chips > input[role='combobox'] {
@apply text-base md:text-sm;
}
.combobox-chip {
@apply bg-muted-foreground/10 text-foreground h-5.5 gap-1 rounded-4xl px-2 text-xs font-medium has-[>.combobox-chip-remove]:pe-0;
}
@@ -940,7 +936,7 @@
@apply rounded-2xl;
}
.input-group > :is(input, select, [data-control]) {
@apply px-3 py-1 text-base md:text-sm;
@apply px-3 py-1 text-sm;
}
.input-group:not([data-orientation='vertical']):has(> :is([data-align='start'], [data-align='inline-start'])) > :is(input, select, [data-control]) {
@@ -952,7 +948,7 @@
}
.input-group > textarea {
@apply px-3 py-2 text-base md:text-sm;
@apply px-3 py-2 text-sm;
}
.input-group > :is(span, [role='group'], header, footer),
@@ -1174,92 +1170,92 @@
}
/* Sidebar */
.sidebar > nav {
.sidebar nav {
@apply bg-sidebar text-sidebar-foreground;
}
.sidebar > nav > header,
.sidebar > nav > footer {
.sidebar nav > header,
.sidebar nav > footer {
@apply gap-2 p-2 [--radius:var(--radius-xl)];
}
.sidebar > nav > section {
.sidebar nav > section {
@apply gap-2 [--radius:var(--radius-xl)];
}
.sidebar > nav [role=separator] {
.sidebar nav [role=separator] {
@apply border-sidebar-border mx-2;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply p-2;
}
.sidebar > nav h3 {
.sidebar nav h3 {
@apply text-sidebar-foreground/70 ring-sidebar-ring h-8 rounded-md px-3 text-xs font-medium focus-visible:ring-2 [&>svg]:size-4 transition-[margin,opacity] duration-200 ease-linear;
}
.sidebar > nav > section > [role=group] > ul {
.sidebar nav > section > [role=group] > ul {
@apply gap-1;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply text-sm;
}
.sidebar > nav li > :is(a, button),
.sidebar > nav li > details > summary {
.sidebar nav li > :is(a, button),
.sidebar nav li > details > summary {
@apply ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground gap-2 rounded-lg px-3 py-2 text-sm focus-visible:ring-2 text-left focus-visible:outline-hidden data-[open]:hover:bg-sidebar-accent data-[open]:hover:text-sidebar-accent-foreground [&>svg]:shrink-0 [&>svg]:size-4;
}
.sidebar > nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar > nav li > details > summary:is([aria-current=page], [data-active=true]) {
.sidebar nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar nav li > details > summary:is([aria-current=page], [data-active=true]) {
@apply bg-sidebar-accent text-sidebar-accent-foreground font-medium;
}
.sidebar > nav li > :is(a, button):not([data-variant]),
.sidebar > nav li > details > summary:not([data-variant]),
.sidebar > nav li > :is(a, button)[data-variant=default],
.sidebar > nav li > details > summary[data-variant=default] {
.sidebar nav li > :is(a, button):not([data-variant]),
.sidebar nav li > details > summary:not([data-variant]),
.sidebar nav li > :is(a, button)[data-variant=default],
.sidebar nav li > details > summary[data-variant=default] {
@apply hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button)[data-variant=outline],
.sidebar > nav li > details > summary[data-variant=outline] {
.sidebar nav li > :is(a, button)[data-variant=outline],
.sidebar nav li > details > summary[data-variant=outline] {
@apply bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:shadow-[0_0_0_1px_var(--sidebar-accent)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button):not([data-size]),
.sidebar > nav li > details > summary:not([data-size]),
.sidebar > nav li > :is(a, button)[data-size=default],
.sidebar > nav li > details > summary[data-size=default] {
.sidebar nav li > :is(a, button):not([data-size]),
.sidebar nav li > details > summary:not([data-size]),
.sidebar nav li > :is(a, button)[data-size=default],
.sidebar nav li > details > summary[data-size=default] {
@apply h-9 text-sm;
}
.sidebar > nav li > :is(a, button)[data-size=sm],
.sidebar > nav li > details > summary[data-size=sm] {
.sidebar nav li > :is(a, button)[data-size=sm],
.sidebar nav li > details > summary[data-size=sm] {
@apply h-8 text-xs;
}
.sidebar > nav li > :is(a, button)[data-size=lg],
.sidebar > nav li > details > summary[data-size=lg] {
.sidebar nav li > :is(a, button)[data-size=lg],
.sidebar nav li > details > summary[data-size=lg] {
@apply h-14 px-3 text-sm;
}
.sidebar > nav li > details > summary::after {
.sidebar nav li > details > summary::after {
@apply size-4;
}
.sidebar > nav li > details::details-content {
.sidebar nav li > details::details-content {
@apply px-3.5;
}
.sidebar > nav ul ul {
.sidebar nav ul ul {
@apply border-sidebar-border translate-x-px gap-1 border-s px-2.5 py-0.5 rtl:-translate-x-px;
}
.sidebar > nav ul ul > li > :is(a, button),
.sidebar > nav ul ul > li > details > summary {
.sidebar nav ul ul > li > :is(a, button),
.sidebar nav ul ul > li > details > summary {
@apply text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground h-7 gap-2 rounded-md px-2 text-sm focus-visible:ring-2 [&>svg]:size-4;
}
/* Progress */
@@ -1316,19 +1312,11 @@
/* Tooltip */
[data-tooltip]::before {
@apply bg-foreground text-background rounded-2xl px-3 py-1.5 text-xs;
@apply bg-foreground text-background inline-flex items-center gap-1.5 rounded-2xl px-3 py-1.5 text-xs has-[.kbd]:pe-1.5 [&_.kbd]:relative [&_.kbd]:isolate [&_.kbd]:z-50 [&_.kbd]:rounded-4xl;
}
[data-tooltip][data-arrow]::after {
@apply bg-foreground absolute z-[60] size-2.5 rotate-45 rounded-[2px];
}
[data-tooltip][data-arrow][data-side='left']::after {
@apply translate-x-[-1.5px];
}
[data-tooltip][data-arrow][data-side='right']::after {
@apply translate-x-[1.5px];
@apply bg-foreground absolute z-[60] size-2.5 rotate-45 rounded-[2px] data-[side=left]:translate-x-[-1.5px] data-[side=right]:translate-x-[1.5px];
}
/* Toast */
@@ -1362,42 +1350,15 @@
}
:is(.field > input[type='range'], .input[type='range'])::-webkit-slider-thumb {
@apply border-primary ring-ring/50 size-4 rounded-4xl border bg-white shadow-sm transition-colors -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)];
@apply border-primary ring-ring/50 size-4 rounded-4xl border bg-white shadow-sm transition-colors hover:ring-4 focus-visible:ring-4 -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)] focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-moz-range-thumb {
@apply border-primary ring-ring/50 size-4 rounded-4xl border bg-white shadow-sm transition-colors;
@apply border-primary ring-ring/50 size-4 rounded-4xl border bg-white shadow-sm transition-colors hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-ms-thumb {
@apply border-primary ring-ring/50 size-4 rounded-4xl border bg-white shadow-sm transition-colors;
}
/* States go before the thumb pseudo-element so they still apply with native CSS nesting. */
@media (hover: hover) {
:is(.field > input[type='range'], .input[type='range']):hover::-webkit-slider-thumb {
@apply ring-4;
}
:is(.field > input[type='range'], .input[type='range']):hover::-moz-range-thumb {
@apply ring-4;
}
:is(.field > input[type='range'], .input[type='range']):hover::-ms-thumb {
@apply ring-4;
}
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-webkit-slider-thumb {
@apply ring-4 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-moz-range-thumb {
@apply ring-4 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-ms-thumb {
@apply ring-4 outline-hidden;
@apply border-primary ring-ring/50 size-4 rounded-4xl border bg-white shadow-sm transition-colors hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden;
}
}
+36 -75
View File
@@ -1179,92 +1179,92 @@
}
/* Sidebar */
.sidebar > nav {
.sidebar nav {
@apply bg-sidebar text-sidebar-foreground;
}
.sidebar > nav > header,
.sidebar > nav > footer {
.sidebar nav > header,
.sidebar nav > footer {
@apply gap-2 p-2;
}
.sidebar > nav > section {
.sidebar nav > section {
@apply gap-0;
}
.sidebar > nav [role=separator] {
.sidebar nav [role=separator] {
@apply border-sidebar-border mx-2;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply px-2 py-1;
}
.sidebar > nav h3 {
.sidebar nav h3 {
@apply text-sidebar-foreground/70 ring-sidebar-ring h-8 rounded-md px-2 text-xs focus-visible:ring-2 [&>svg]:size-4 transition-[margin,opacity] duration-200 ease-linear;
}
.sidebar > nav > section > [role=group] > ul {
.sidebar nav > section > [role=group] > ul {
@apply gap-px;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply text-xs;
}
.sidebar > nav li > :is(a, button),
.sidebar > nav li > details > summary {
.sidebar nav li > :is(a, button),
.sidebar nav li > details > summary {
@apply ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground gap-2 rounded-[calc(var(--radius-sm)+2px)] p-2 text-xs focus-visible:ring-2 text-left focus-visible:outline-hidden data-[open]:hover:bg-sidebar-accent data-[open]:hover:text-sidebar-accent-foreground [&>svg]:shrink-0 [&>svg]:size-4;
}
.sidebar > nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar > nav li > details > summary:is([aria-current=page], [data-active=true]) {
.sidebar nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar nav li > details > summary:is([aria-current=page], [data-active=true]) {
@apply bg-sidebar-accent text-sidebar-accent-foreground font-medium;
}
.sidebar > nav li > :is(a, button):not([data-variant]),
.sidebar > nav li > details > summary:not([data-variant]),
.sidebar > nav li > :is(a, button)[data-variant=default],
.sidebar > nav li > details > summary[data-variant=default] {
.sidebar nav li > :is(a, button):not([data-variant]),
.sidebar nav li > details > summary:not([data-variant]),
.sidebar nav li > :is(a, button)[data-variant=default],
.sidebar nav li > details > summary[data-variant=default] {
@apply hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button)[data-variant=outline],
.sidebar > nav li > details > summary[data-variant=outline] {
.sidebar nav li > :is(a, button)[data-variant=outline],
.sidebar nav li > details > summary[data-variant=outline] {
@apply bg-muted/20 dark:bg-muted/30 border-input shadow-[0_0_0_1px_var(--sidebar-border)] hover:shadow-[0_0_0_1px_var(--sidebar-accent)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button):not([data-size]),
.sidebar > nav li > details > summary:not([data-size]),
.sidebar > nav li > :is(a, button)[data-size=default],
.sidebar > nav li > details > summary[data-size=default] {
.sidebar nav li > :is(a, button):not([data-size]),
.sidebar nav li > details > summary:not([data-size]),
.sidebar nav li > :is(a, button)[data-size=default],
.sidebar nav li > details > summary[data-size=default] {
@apply h-8 text-xs;
}
.sidebar > nav li > :is(a, button)[data-size=sm],
.sidebar > nav li > details > summary[data-size=sm] {
.sidebar nav li > :is(a, button)[data-size=sm],
.sidebar nav li > details > summary[data-size=sm] {
@apply h-7 text-xs;
}
.sidebar > nav li > :is(a, button)[data-size=lg],
.sidebar > nav li > details > summary[data-size=lg] {
.sidebar nav li > :is(a, button)[data-size=lg],
.sidebar nav li > details > summary[data-size=lg] {
@apply h-12 text-xs;
}
.sidebar > nav li > details > summary::after {
.sidebar nav li > details > summary::after {
@apply size-4;
}
.sidebar > nav li > details::details-content {
.sidebar nav li > details::details-content {
@apply px-3.5;
}
.sidebar > nav ul ul {
.sidebar nav ul ul {
@apply border-sidebar-border translate-x-px gap-1 border-s px-2.5 py-0.5 rtl:-translate-x-px;
}
.sidebar > nav ul ul > li > :is(a, button),
.sidebar > nav ul ul > li > details > summary {
.sidebar nav ul ul > li > :is(a, button),
.sidebar nav ul ul > li > details > summary {
@apply text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground h-7 gap-2 rounded-md px-2 text-xs focus-visible:ring-2 [&>svg]:size-4;
}
/* Progress */
@@ -1321,7 +1321,7 @@
/* Tooltip */
[data-tooltip]::before {
@apply bg-foreground text-background rounded-md px-3 py-1.5 text-xs;
@apply bg-foreground text-background inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs has-[.kbd]:pe-1.5 [&_.kbd]:relative [&_.kbd]:isolate [&_.kbd]:z-50 [&_.kbd]:rounded-sm;
}
[data-tooltip][data-arrow]::after {
@@ -1359,54 +1359,15 @@
}
:is(.field > input[type='range'], .input[type='range'])::-webkit-slider-thumb {
@apply border-ring ring-ring/30 relative size-3 rounded-md border bg-white transition-[color,box-shadow] -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)];
@apply border-ring ring-ring/30 relative size-3 rounded-md border bg-white transition-[color,box-shadow] hover:ring-2 focus-visible:ring-2 active:ring-2 -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)] focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-moz-range-thumb {
@apply border-ring ring-ring/30 relative size-3 rounded-md border bg-white transition-[color,box-shadow];
@apply border-ring ring-ring/30 relative size-3 rounded-md border bg-white transition-[color,box-shadow] hover:ring-2 focus-visible:ring-2 active:ring-2 focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-ms-thumb {
@apply border-ring ring-ring/30 relative size-3 rounded-md border bg-white transition-[color,box-shadow];
}
/* States go before the thumb pseudo-element so they still apply with native CSS nesting. */
@media (hover: hover) {
:is(.field > input[type='range'], .input[type='range']):hover::-webkit-slider-thumb {
@apply ring-2;
}
:is(.field > input[type='range'], .input[type='range']):hover::-moz-range-thumb {
@apply ring-2;
}
:is(.field > input[type='range'], .input[type='range']):hover::-ms-thumb {
@apply ring-2;
}
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-webkit-slider-thumb {
@apply ring-2 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-moz-range-thumb {
@apply ring-2 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-ms-thumb {
@apply ring-2 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):active::-webkit-slider-thumb {
@apply ring-2;
}
:is(.field > input[type='range'], .input[type='range']):active::-moz-range-thumb {
@apply ring-2;
}
:is(.field > input[type='range'], .input[type='range']):active::-ms-thumb {
@apply ring-2;
@apply border-ring ring-ring/30 relative size-3 rounded-md border bg-white transition-[color,box-shadow] hover:ring-2 focus-visible:ring-2 active:ring-2 focus-visible:outline-hidden;
}
}
+40 -83
View File
@@ -205,7 +205,7 @@
/* Native Select */
.field > select,
select.select {
@apply border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-8 w-fit rounded-lg border bg-transparent py-1 ps-2.5 pe-8 text-base md:text-sm transition-colors focus-visible:ring-3 aria-invalid:ring-3 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-[size=sm]:py-0.5;
@apply border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-8 w-fit rounded-lg border bg-transparent py-1 ps-2.5 pe-8 text-sm transition-colors focus-visible:ring-3 aria-invalid:ring-3 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-[size=sm]:py-0.5;
@apply bg-[image:var(--chevron-down-icon-50)] bg-no-repeat bg-position-[center_right_0.625rem] bg-size-[1rem];
}
@@ -361,7 +361,7 @@
}
.command > header input {
@apply ps-1.5 text-base md:text-sm placeholder:text-muted-foreground;
@apply ps-1.5 text-sm placeholder:text-muted-foreground;
}
.command [role='menu'] {
@@ -452,10 +452,6 @@
@apply dark:bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 has-[[aria-invalid=true]]:ring-destructive/20 dark:has-[[aria-invalid=true]]:ring-destructive/40 has-[[aria-invalid=true]]:border-destructive dark:has-[[aria-invalid=true]]:border-destructive/50 min-h-8 gap-1 rounded-lg border bg-transparent bg-clip-padding px-1 py-1 text-sm transition-colors focus-within:ring-3 has-[[aria-invalid=true]]:ring-3;
}
.combobox-chips > input[role='combobox'] {
@apply text-base md:text-sm;
}
.combobox-chip {
@apply bg-muted text-foreground h-5.25 gap-1 rounded-sm px-1.5 text-xs font-medium has-[>.combobox-chip-remove]:pe-0;
}
@@ -955,7 +951,7 @@
}
.input-group > :is(input, select, [data-control]) {
@apply px-2.5 py-1 text-base md:text-sm;
@apply px-2.5 py-1 text-sm;
}
.input-group:not([data-orientation='vertical']):has(> :is([data-align='start'], [data-align='inline-start'])) > :is(input, select, [data-control]) {
@@ -967,7 +963,7 @@
}
.input-group > textarea {
@apply px-2.5 py-2 text-base md:text-sm;
@apply px-2.5 py-2 text-sm;
}
.input-group > :is(span, [role='group'], header, footer),
@@ -1197,92 +1193,92 @@
}
/* Sidebar */
.sidebar > nav {
.sidebar nav {
@apply bg-sidebar text-sidebar-foreground;
}
.sidebar > nav > header,
.sidebar > nav > footer {
.sidebar nav > header,
.sidebar nav > footer {
@apply gap-2 p-2;
}
.sidebar > nav > section {
.sidebar nav > section {
@apply gap-0;
}
.sidebar > nav [role=separator] {
.sidebar nav [role=separator] {
@apply border-sidebar-border mx-2;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply p-2;
}
.sidebar > nav h3 {
.sidebar nav h3 {
@apply text-sidebar-foreground/70 ring-sidebar-ring h-8 rounded-md px-2 text-xs font-medium focus-visible:ring-2 [&>svg]:size-4 transition-[margin,opacity] duration-200 ease-linear;
}
.sidebar > nav > section > [role=group] > ul {
.sidebar nav > section > [role=group] > ul {
@apply gap-0;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply text-sm;
}
.sidebar > nav li > :is(a, button),
.sidebar > nav li > details > summary {
.sidebar nav li > :is(a, button),
.sidebar nav li > details > summary {
@apply ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground gap-2 rounded-md p-2 text-sm focus-visible:ring-2 text-left focus-visible:outline-hidden data-[open]:hover:bg-sidebar-accent data-[open]:hover:text-sidebar-accent-foreground [&>svg]:shrink-0 [&>svg]:size-4;
}
.sidebar > nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar > nav li > details > summary:is([aria-current=page], [data-active=true]) {
.sidebar nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar nav li > details > summary:is([aria-current=page], [data-active=true]) {
@apply bg-sidebar-accent text-sidebar-accent-foreground font-medium;
}
.sidebar > nav li > :is(a, button):not([data-variant]),
.sidebar > nav li > details > summary:not([data-variant]),
.sidebar > nav li > :is(a, button)[data-variant=default],
.sidebar > nav li > details > summary[data-variant=default] {
.sidebar nav li > :is(a, button):not([data-variant]),
.sidebar nav li > details > summary:not([data-variant]),
.sidebar nav li > :is(a, button)[data-variant=default],
.sidebar nav li > details > summary[data-variant=default] {
@apply hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button)[data-variant=outline],
.sidebar > nav li > details > summary[data-variant=outline] {
.sidebar nav li > :is(a, button)[data-variant=outline],
.sidebar nav li > details > summary[data-variant=outline] {
@apply bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:shadow-[0_0_0_1px_var(--sidebar-accent)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button):not([data-size]),
.sidebar > nav li > details > summary:not([data-size]),
.sidebar > nav li > :is(a, button)[data-size=default],
.sidebar > nav li > details > summary[data-size=default] {
.sidebar nav li > :is(a, button):not([data-size]),
.sidebar nav li > details > summary:not([data-size]),
.sidebar nav li > :is(a, button)[data-size=default],
.sidebar nav li > details > summary[data-size=default] {
@apply h-8 text-sm;
}
.sidebar > nav li > :is(a, button)[data-size=sm],
.sidebar > nav li > details > summary[data-size=sm] {
.sidebar nav li > :is(a, button)[data-size=sm],
.sidebar nav li > details > summary[data-size=sm] {
@apply h-7 text-xs;
}
.sidebar > nav li > :is(a, button)[data-size=lg],
.sidebar > nav li > details > summary[data-size=lg] {
.sidebar nav li > :is(a, button)[data-size=lg],
.sidebar nav li > details > summary[data-size=lg] {
@apply h-12 text-sm;
}
.sidebar > nav li > details > summary::after {
.sidebar nav li > details > summary::after {
@apply size-4;
}
.sidebar > nav li > details::details-content {
.sidebar nav li > details::details-content {
@apply px-3.5;
}
.sidebar > nav ul ul {
.sidebar nav ul ul {
@apply border-sidebar-border translate-x-px gap-1 border-s px-2.5 py-0.5 rtl:-translate-x-px;
}
.sidebar > nav ul ul > li > :is(a, button),
.sidebar > nav ul ul > li > details > summary {
.sidebar nav ul ul > li > :is(a, button),
.sidebar nav ul ul > li > details > summary {
@apply text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground h-7 gap-2 rounded-md px-2 text-sm focus-visible:ring-2 [&>svg]:size-4;
}
/* Progress */
@@ -1339,7 +1335,7 @@
/* Tooltip */
[data-tooltip]::before {
@apply bg-foreground text-background rounded-md px-3 py-1.5 text-xs;
@apply bg-foreground text-background inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs has-[.kbd]:pe-1.5 [&_.kbd]:relative [&_.kbd]:isolate [&_.kbd]:z-50 [&_.kbd]:rounded-sm;
}
[data-tooltip][data-arrow]::after {
@@ -1377,54 +1373,15 @@
}
:is(.field > input[type='range'], .input[type='range'])::-webkit-slider-thumb {
@apply border-ring ring-ring/50 relative size-3 rounded-full border bg-white transition-[color,box-shadow] -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)];
@apply border-ring ring-ring/50 relative size-3 rounded-full border bg-white transition-[color,box-shadow] hover:ring-3 focus-visible:ring-3 active:ring-3 -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)] focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-moz-range-thumb {
@apply border-ring ring-ring/50 relative size-3 rounded-full border bg-white transition-[color,box-shadow];
@apply border-ring ring-ring/50 relative size-3 rounded-full border bg-white transition-[color,box-shadow] hover:ring-3 focus-visible:ring-3 active:ring-3 focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-ms-thumb {
@apply border-ring ring-ring/50 relative size-3 rounded-full border bg-white transition-[color,box-shadow];
}
/* States go before the thumb pseudo-element so they still apply with native CSS nesting. */
@media (hover: hover) {
:is(.field > input[type='range'], .input[type='range']):hover::-webkit-slider-thumb {
@apply ring-3;
}
:is(.field > input[type='range'], .input[type='range']):hover::-moz-range-thumb {
@apply ring-3;
}
:is(.field > input[type='range'], .input[type='range']):hover::-ms-thumb {
@apply ring-3;
}
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-webkit-slider-thumb {
@apply ring-3 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-moz-range-thumb {
@apply ring-3 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-ms-thumb {
@apply ring-3 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):active::-webkit-slider-thumb {
@apply ring-3;
}
:is(.field > input[type='range'], .input[type='range']):active::-moz-range-thumb {
@apply ring-3;
}
:is(.field > input[type='range'], .input[type='range']):active::-ms-thumb {
@apply ring-3;
@apply border-ring ring-ring/50 relative size-3 rounded-full border bg-white transition-[color,box-shadow] hover:ring-3 focus-visible:ring-3 active:ring-3 focus-visible:outline-hidden;
}
}
+43 -102
View File
@@ -217,7 +217,7 @@
/* Native Select */
.field > select,
select.select {
@apply bg-input/50 border-transparent placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-8 w-fit rounded-2xl border py-1 ps-2.5 pe-8 text-base md:text-sm transition-[color,box-shadow] duration-200 focus-visible:ring-3 aria-invalid:ring-3 data-[size=sm]:h-7;
@apply bg-input/50 border-transparent placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-8 w-fit rounded-2xl border py-1 ps-2.5 pe-8 text-sm transition-[color,box-shadow] duration-200 focus-visible:ring-3 aria-invalid:ring-3 data-[size=sm]:h-7;
@apply bg-[image:var(--chevron-down-icon-50)] bg-no-repeat bg-position-[center_right_0.625rem] bg-size-[1rem];
}
@@ -373,7 +373,7 @@
}
.command > header input {
@apply ps-1.5 text-base md:text-sm placeholder:text-muted-foreground;
@apply ps-1.5 text-sm placeholder:text-muted-foreground;
}
.command [role='menu'] {
@@ -464,10 +464,6 @@
@apply bg-input/50 border-transparent focus-within:border-ring focus-within:ring-ring/30 has-[[aria-invalid=true]]:ring-destructive/20 dark:has-[[aria-invalid=true]]:ring-destructive/40 has-[[aria-invalid=true]]:border-destructive dark:has-[[aria-invalid=true]]:border-destructive/50 min-h-8 gap-1 rounded-2xl border bg-clip-padding px-1 py-1 text-sm transition-[color,box-shadow] duration-200 focus-within:ring-3 has-[[aria-invalid=true]]:ring-3;
}
.combobox-chips > input[role='combobox'] {
@apply text-base md:text-sm;
}
.combobox-chip {
@apply bg-input text-foreground dark:bg-input/60 h-5.25 gap-1 rounded-2xl px-1.5 text-xs font-medium has-[>.combobox-chip-remove]:pe-0.5;
}
@@ -791,11 +787,7 @@
}
:is(.field > input[type='radio'], .input[type='radio']):checked:before {
@apply bg-primary-foreground size-2;
}
html.dark :is(.field > input[type='radio'], .input[type='radio']):checked:before {
@apply size-2.5;
@apply bg-primary-foreground size-2 dark:size-2.5;
}
/* Switch */
@@ -813,11 +805,7 @@
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):before {
@apply bg-background rounded-2xl shadow-sm;
}
html:not(.dark) :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):before {
@apply bg-clip-padding;
@apply bg-background rounded-2xl shadow-sm not-dark:bg-clip-padding;
}
html.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not(:checked):before {
@@ -957,7 +945,7 @@
@apply opacity-50;
}
.input-group > :is(input, select, [data-control]) {
@apply px-2.5 py-1 text-base md:text-sm;
@apply px-2.5 py-1 text-sm;
}
.input-group:not([data-orientation='vertical']):has(> :is([data-align='start'], [data-align='inline-start'])) > :is(input, select, [data-control]) {
@@ -969,7 +957,7 @@
}
.input-group > textarea {
@apply px-2.5 py-2 text-base md:text-sm;
@apply px-2.5 py-2 text-sm;
}
.input-group > :is(span, [role='group'], header, footer),
@@ -1199,92 +1187,92 @@
}
/* Sidebar */
.sidebar > nav {
.sidebar nav {
@apply bg-sidebar text-sidebar-foreground;
}
.sidebar > nav > header,
.sidebar > nav > footer {
.sidebar nav > header,
.sidebar nav > footer {
@apply gap-2 p-2 [--radius:var(--radius-xl)];
}
.sidebar > nav > section {
.sidebar nav > section {
@apply gap-2 [--radius:var(--radius-xl)];
}
.sidebar > nav [role=separator] {
.sidebar nav [role=separator] {
@apply border-sidebar-border mx-2;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply p-2;
}
.sidebar > nav h3 {
.sidebar nav h3 {
@apply text-sidebar-foreground/70 ring-sidebar-ring h-8 rounded-xl px-3 text-xs font-medium focus-visible:ring-3 [&>svg]:size-4 transition-[margin,opacity] duration-200 ease-linear;
}
.sidebar > nav > section > [role=group] > ul {
.sidebar nav > section > [role=group] > ul {
@apply gap-0.5;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply text-sm;
}
.sidebar > nav li > :is(a, button),
.sidebar > nav li > details > summary {
.sidebar nav li > :is(a, button),
.sidebar nav li > details > summary {
@apply ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground gap-2 rounded-xl px-3 py-2 text-sm duration-200 focus-visible:ring-3 whitespace-nowrap has-[>svg:first-child]:ps-2.5 has-[>svg:last-child]:pe-2.5 text-left focus-visible:outline-hidden data-[open]:hover:bg-sidebar-accent data-[open]:hover:text-sidebar-accent-foreground [&>svg]:shrink-0 [&>svg]:size-4;
}
.sidebar > nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar > nav li > details > summary:is([aria-current=page], [data-active=true]) {
.sidebar nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar nav li > details > summary:is([aria-current=page], [data-active=true]) {
@apply bg-sidebar-accent text-sidebar-accent-foreground font-medium;
}
.sidebar > nav li > :is(a, button):not([data-variant]),
.sidebar > nav li > details > summary:not([data-variant]),
.sidebar > nav li > :is(a, button)[data-variant=default],
.sidebar > nav li > details > summary[data-variant=default] {
.sidebar nav li > :is(a, button):not([data-variant]),
.sidebar nav li > details > summary:not([data-variant]),
.sidebar nav li > :is(a, button)[data-variant=default],
.sidebar nav li > details > summary[data-variant=default] {
@apply hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button)[data-variant=outline],
.sidebar > nav li > details > summary[data-variant=outline] {
.sidebar nav li > :is(a, button)[data-variant=outline],
.sidebar nav li > details > summary[data-variant=outline] {
@apply bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:shadow-[0_0_0_1px_var(--sidebar-accent)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button):not([data-size]),
.sidebar > nav li > details > summary:not([data-size]),
.sidebar > nav li > :is(a, button)[data-size=default],
.sidebar > nav li > details > summary[data-size=default] {
.sidebar nav li > :is(a, button):not([data-size]),
.sidebar nav li > details > summary:not([data-size]),
.sidebar nav li > :is(a, button)[data-size=default],
.sidebar nav li > details > summary[data-size=default] {
@apply h-8 text-sm;
}
.sidebar > nav li > :is(a, button)[data-size=sm],
.sidebar > nav li > details > summary[data-size=sm] {
.sidebar nav li > :is(a, button)[data-size=sm],
.sidebar nav li > details > summary[data-size=sm] {
@apply h-7 text-xs;
}
.sidebar > nav li > :is(a, button)[data-size=lg],
.sidebar > nav li > details > summary[data-size=lg] {
.sidebar nav li > :is(a, button)[data-size=lg],
.sidebar nav li > details > summary[data-size=lg] {
@apply h-12 px-3 text-sm;
}
.sidebar > nav li > details > summary::after {
.sidebar nav li > details > summary::after {
@apply size-4;
}
.sidebar > nav li > details::details-content {
.sidebar nav li > details::details-content {
@apply px-3.5;
}
.sidebar > nav ul ul {
.sidebar nav ul ul {
@apply border-sidebar-border translate-x-px gap-1 border-s px-2.5 py-0.5 rtl:-translate-x-px;
}
.sidebar > nav ul ul > li > :is(a, button),
.sidebar > nav ul ul > li > details > summary {
.sidebar nav ul ul > li > :is(a, button),
.sidebar nav ul ul > li > details > summary {
@apply text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground h-7 gap-2 rounded-xl px-3 text-sm focus-visible:ring-3 [&>svg]:size-4;
}
/* Progress */
@@ -1341,19 +1329,11 @@
/* Tooltip */
[data-tooltip]::before {
@apply bg-foreground text-background rounded-xl px-3 py-1.5 text-xs;
@apply bg-foreground text-background inline-flex items-center gap-1.5 rounded-xl px-3 py-1.5 text-xs has-[.kbd]:pe-1.5 [&_.kbd]:relative [&_.kbd]:isolate [&_.kbd]:z-50 [&_.kbd]:rounded-lg;
}
[data-tooltip][data-arrow]::after {
@apply bg-foreground absolute z-[60] size-2.5 rotate-45 rounded-[2px];
}
[data-tooltip][data-arrow][data-side='left']::after {
@apply translate-x-[-1.5px];
}
[data-tooltip][data-arrow][data-side='right']::after {
@apply translate-x-[1.5px];
@apply bg-foreground absolute z-[60] size-2.5 rotate-45 rounded-[2px] data-[side=left]:translate-x-[-1.5px] data-[side=right]:translate-x-[1.5px];
}
/* Toast */
@@ -1387,54 +1367,15 @@
}
:is(.field > input[type='range'], .input[type='range'])::-webkit-slider-thumb {
@apply ring-black/10 ring-1 size-4 rounded-2xl bg-white shadow-md transition-[color,box-shadow] duration-200 -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)];
@apply ring-black/10 not-dark:bg-clip-padding ring-1 size-4 rounded-2xl bg-white shadow-md transition-[color,box-shadow] duration-200 hover:ring-4 hover:ring-ring/30 focus-visible:ring-4 focus-visible:ring-ring/30 -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)] focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-moz-range-thumb {
@apply ring-black/10 ring-1 size-4 rounded-2xl bg-white shadow-md transition-[color,box-shadow] duration-200;
@apply ring-black/10 not-dark:bg-clip-padding ring-1 size-4 rounded-2xl bg-white shadow-md transition-[color,box-shadow] duration-200 hover:ring-4 hover:ring-ring/30 focus-visible:ring-4 focus-visible:ring-ring/30 focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-ms-thumb {
@apply ring-black/10 ring-1 size-4 rounded-2xl bg-white shadow-md transition-[color,box-shadow] duration-200;
}
/* States go before the thumb pseudo-element so they still apply with native CSS nesting. */
html:not(.dark) :is(.field > input[type='range'], .input[type='range'])::-webkit-slider-thumb {
@apply bg-clip-padding;
}
html:not(.dark) :is(.field > input[type='range'], .input[type='range'])::-moz-range-thumb {
@apply bg-clip-padding;
}
html:not(.dark) :is(.field > input[type='range'], .input[type='range'])::-ms-thumb {
@apply bg-clip-padding;
}
@media (hover: hover) {
:is(.field > input[type='range'], .input[type='range']):hover::-webkit-slider-thumb {
@apply ring-4 ring-ring/30;
}
:is(.field > input[type='range'], .input[type='range']):hover::-moz-range-thumb {
@apply ring-4 ring-ring/30;
}
:is(.field > input[type='range'], .input[type='range']):hover::-ms-thumb {
@apply ring-4 ring-ring/30;
}
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-webkit-slider-thumb {
@apply ring-4 ring-ring/30 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-moz-range-thumb {
@apply ring-4 ring-ring/30 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-ms-thumb {
@apply ring-4 ring-ring/30 outline-hidden;
@apply ring-black/10 not-dark:bg-clip-padding ring-1 size-4 rounded-2xl bg-white shadow-md transition-[color,box-shadow] duration-200 hover:ring-4 hover:ring-ring/30 focus-visible:ring-4 focus-visible:ring-ring/30 focus-visible:outline-hidden;
}
}
+40 -71
View File
@@ -221,7 +221,7 @@
/* Native Select */
.field > select,
select.select {
@apply border-transparent border-b-input bg-transparent placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground focus-visible:border-b-ring aria-invalid:border-b-destructive dark:aria-invalid:border-b-destructive/50 h-10 w-fit rounded-none border py-2 ps-0 pe-8 text-base md:text-sm transition-[color,border-color] data-[size=sm]:h-9;
@apply border-transparent border-b-input bg-transparent placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground focus-visible:border-b-ring aria-invalid:border-b-destructive dark:aria-invalid:border-b-destructive/50 h-10 w-fit rounded-none border py-2 ps-0 pe-8 text-sm transition-[color,border-color] data-[size=sm]:h-9;
@apply bg-[image:var(--chevron-down-icon-50)] bg-no-repeat bg-position-[center_right_0] bg-size-[0.875rem];
}
@@ -377,7 +377,7 @@
}
.command > header input {
@apply h-9 ps-1.5 pe-2 text-base md:text-sm placeholder:text-muted-foreground;
@apply h-9 ps-1.5 pe-2 text-sm placeholder:text-muted-foreground;
}
.command [role='menu'] {
@@ -468,10 +468,6 @@
@apply border-transparent border-b-input bg-transparent focus-within:border-b-ring has-[[aria-invalid=true]]:border-b-destructive dark:has-[[aria-invalid=true]]:border-b-destructive/50 min-h-10 gap-1.5 rounded-none border bg-clip-padding px-0 py-1.5 text-sm transition-[color,border-color];
}
.combobox-chips > input[role='combobox'] {
@apply text-base md:text-sm;
}
.combobox-chip {
@apply bg-muted text-foreground h-5.5 gap-1 rounded-none px-2 text-xs font-medium has-[>.combobox-chip-remove]:pe-0;
}
@@ -974,7 +970,7 @@
@apply opacity-50;
}
.input-group > :is(input, select, [data-control]) {
@apply px-0 py-1 text-base md:text-sm;
@apply px-0 py-1 text-sm;
}
.input-group:not([data-orientation='vertical']):has(> :is([data-align='start'], [data-align='inline-start'])) > :is(input, select, [data-control]) {
@@ -986,7 +982,7 @@
}
.input-group > textarea {
@apply px-0 py-2.5 text-base md:text-sm;
@apply px-0 py-2.5 text-sm;
}
.input-group > :is(span, [role='group'], header, footer),
@@ -1207,92 +1203,92 @@
}
/* Sidebar */
.sidebar > nav {
.sidebar nav {
@apply bg-sidebar text-sidebar-foreground;
}
.sidebar > nav > header,
.sidebar > nav > footer {
.sidebar nav > header,
.sidebar nav > footer {
@apply gap-2 p-2 [--radius:0];
}
.sidebar > nav > section {
.sidebar nav > section {
@apply gap-2 [--radius:0];
}
.sidebar > nav [role=separator] {
.sidebar nav [role=separator] {
@apply border-sidebar-border mx-2;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply p-2;
}
.sidebar > nav h3 {
.sidebar nav h3 {
@apply text-sidebar-foreground/70 ring-sidebar-ring h-8 rounded-none px-3 text-xs font-semibold uppercase tracking-wider focus-visible:ring-2 [&>svg]:size-3.5 transition-[margin,opacity] duration-200 ease-linear;
}
.sidebar > nav > section > [role=group] > ul {
.sidebar nav > section > [role=group] > ul {
@apply gap-0.5;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply text-sm;
}
.sidebar > nav li > :is(a, button),
.sidebar > nav li > details > summary {
.sidebar nav li > :is(a, button),
.sidebar nav li > details > summary {
@apply ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground gap-2 rounded-none px-3 py-2 text-sm focus-visible:ring-2 text-left focus-visible:outline-hidden data-[open]:hover:bg-sidebar-accent data-[open]:hover:text-sidebar-accent-foreground [&>svg]:shrink-0 [&>svg]:size-3.5;
}
.sidebar > nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar > nav li > details > summary:is([aria-current=page], [data-active=true]) {
.sidebar nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar nav li > details > summary:is([aria-current=page], [data-active=true]) {
@apply bg-sidebar-accent text-sidebar-accent-foreground font-medium;
}
.sidebar > nav li > :is(a, button):not([data-variant]),
.sidebar > nav li > details > summary:not([data-variant]),
.sidebar > nav li > :is(a, button)[data-variant=default],
.sidebar > nav li > details > summary[data-variant=default] {
.sidebar nav li > :is(a, button):not([data-variant]),
.sidebar nav li > details > summary:not([data-variant]),
.sidebar nav li > :is(a, button)[data-variant=default],
.sidebar nav li > details > summary[data-variant=default] {
@apply hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button)[data-variant=outline],
.sidebar > nav li > details > summary[data-variant=outline] {
.sidebar nav li > :is(a, button)[data-variant=outline],
.sidebar nav li > details > summary[data-variant=outline] {
@apply bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:shadow-[0_0_0_1px_var(--sidebar-accent)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button):not([data-size]),
.sidebar > nav li > details > summary:not([data-size]),
.sidebar > nav li > :is(a, button)[data-size=default],
.sidebar > nav li > details > summary[data-size=default] {
.sidebar nav li > :is(a, button):not([data-size]),
.sidebar nav li > details > summary:not([data-size]),
.sidebar nav li > :is(a, button)[data-size=default],
.sidebar nav li > details > summary[data-size=default] {
@apply h-9 text-sm;
}
.sidebar > nav li > :is(a, button)[data-size=sm],
.sidebar > nav li > details > summary[data-size=sm] {
.sidebar nav li > :is(a, button)[data-size=sm],
.sidebar nav li > details > summary[data-size=sm] {
@apply h-8 text-xs;
}
.sidebar > nav li > :is(a, button)[data-size=lg],
.sidebar > nav li > details > summary[data-size=lg] {
.sidebar nav li > :is(a, button)[data-size=lg],
.sidebar nav li > details > summary[data-size=lg] {
@apply h-14 px-3 text-sm;
}
.sidebar > nav li > details > summary::after {
.sidebar nav li > details > summary::after {
@apply size-3.5;
}
.sidebar > nav li > details::details-content {
.sidebar nav li > details::details-content {
@apply px-3.5;
}
.sidebar > nav ul ul {
.sidebar nav ul ul {
@apply border-sidebar-border translate-x-px gap-1 border-s px-2.5 py-0.5 rtl:-translate-x-px;
}
.sidebar > nav ul ul > li > :is(a, button),
.sidebar > nav ul ul > li > details > summary {
.sidebar nav ul ul > li > :is(a, button),
.sidebar nav ul ul > li > details > summary {
@apply text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground h-7 gap-2 rounded-none px-3 text-sm focus-visible:ring-2 [&>svg]:size-3.5;
}
/* Progress */
@@ -1349,7 +1345,7 @@
/* Tooltip */
[data-tooltip]::before {
@apply bg-foreground text-background rounded-none px-3 py-1.5 text-xs;
@apply bg-foreground text-background inline-flex items-center gap-1.5 rounded-none px-3 py-1.5 text-xs has-[.kbd]:pe-1.5 [&_.kbd]:relative [&_.kbd]:isolate [&_.kbd]:z-50 [&_.kbd]:rounded-none;
}
[data-tooltip][data-arrow]::after {
@@ -1387,42 +1383,15 @@
}
:is(.field > input[type='range'], .input[type='range'])::-webkit-slider-thumb {
@apply size-3 border-none bg-primary transition-colors -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)];
@apply size-3 border-none bg-primary transition-colors hover:ring-2 hover:ring-ring/30 focus-visible:ring-2 focus-visible:ring-ring/30 -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)] focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-moz-range-thumb {
@apply size-3 border-none bg-primary transition-colors;
@apply size-3 border-none bg-primary transition-colors hover:ring-2 hover:ring-ring/30 focus-visible:ring-2 focus-visible:ring-ring/30 focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-ms-thumb {
@apply size-3 border-none bg-primary transition-colors;
}
/* States go before the thumb pseudo-element so they still apply with native CSS nesting. */
@media (hover: hover) {
:is(.field > input[type='range'], .input[type='range']):hover::-webkit-slider-thumb {
@apply ring-2 ring-ring/30;
}
:is(.field > input[type='range'], .input[type='range']):hover::-moz-range-thumb {
@apply ring-2 ring-ring/30;
}
:is(.field > input[type='range'], .input[type='range']):hover::-ms-thumb {
@apply ring-2 ring-ring/30;
}
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-webkit-slider-thumb {
@apply ring-2 ring-ring/30 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-moz-range-thumb {
@apply ring-2 ring-ring/30 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-ms-thumb {
@apply ring-2 ring-ring/30 outline-hidden;
@apply size-3 border-none bg-primary transition-colors hover:ring-2 hover:ring-ring/30 focus-visible:ring-2 focus-visible:ring-ring/30 focus-visible:outline-hidden;
}
}
+40 -71
View File
@@ -205,7 +205,7 @@
/* Native Select */
.field > select,
select.select {
@apply border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-9 w-fit rounded-md border bg-transparent py-1 ps-2.5 pe-8 text-base md:text-sm shadow-xs transition-[color,box-shadow] focus-visible:ring-3 aria-invalid:ring-3 data-[size=sm]:h-8;
@apply border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-9 w-fit rounded-md border bg-transparent py-1 ps-2.5 pe-8 text-sm shadow-xs transition-[color,box-shadow] focus-visible:ring-3 aria-invalid:ring-3 data-[size=sm]:h-8;
@apply bg-[image:var(--chevron-down-icon-50)] bg-no-repeat bg-position-[center_right_0.625rem] bg-size-[1rem];
}
@@ -361,7 +361,7 @@
}
.command > header input {
@apply ps-1.5 text-base md:text-sm placeholder:text-muted-foreground;
@apply ps-1.5 text-sm placeholder:text-muted-foreground;
}
.command [role='menu'] {
@@ -452,10 +452,6 @@
@apply dark:bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 has-[[aria-invalid=true]]:ring-destructive/20 dark:has-[[aria-invalid=true]]:ring-destructive/40 has-[[aria-invalid=true]]:border-destructive dark:has-[[aria-invalid=true]]:border-destructive/50 min-h-9 gap-1.5 rounded-md border bg-transparent bg-clip-padding px-1.5 py-1.5 text-sm shadow-xs transition-[color,box-shadow] focus-within:ring-3 has-[[aria-invalid=true]]:ring-3;
}
.combobox-chips > input[role='combobox'] {
@apply text-base md:text-sm;
}
.combobox-chip {
@apply bg-muted text-foreground h-5.5 gap-1 rounded-sm px-1.5 text-xs font-medium has-[>.combobox-chip-remove]:pe-0;
}
@@ -954,7 +950,7 @@
@apply opacity-50;
}
.input-group > :is(input, select, [data-control]) {
@apply px-2.5 py-1 text-base md:text-sm;
@apply px-2.5 py-1 text-sm;
}
.input-group:not([data-orientation='vertical']):has(> :is([data-align='start'], [data-align='inline-start'])) > :is(input, select, [data-control]) {
@@ -966,7 +962,7 @@
}
.input-group > textarea {
@apply px-2.5 py-2 text-base md:text-sm;
@apply px-2.5 py-2 text-sm;
}
.input-group > :is(span, [role='group'], header, footer),
@@ -1196,92 +1192,92 @@
}
/* Sidebar */
.sidebar > nav {
.sidebar nav {
@apply bg-sidebar text-sidebar-foreground;
}
.sidebar > nav > header,
.sidebar > nav > footer {
.sidebar nav > header,
.sidebar nav > footer {
@apply gap-2 p-2;
}
.sidebar > nav > section {
.sidebar nav > section {
@apply gap-2;
}
.sidebar > nav [role=separator] {
.sidebar nav [role=separator] {
@apply border-sidebar-border mx-2;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply p-2;
}
.sidebar > nav h3 {
.sidebar nav h3 {
@apply text-sidebar-foreground/70 ring-sidebar-ring h-8 rounded-md px-2 text-xs font-medium focus-visible:ring-2 [&>svg]:size-4 transition-[margin,opacity] duration-200 ease-linear;
}
.sidebar > nav > section > [role=group] > ul {
.sidebar nav > section > [role=group] > ul {
@apply gap-1;
}
.sidebar > nav > section > [role=group] {
.sidebar nav > section > [role=group] {
@apply text-sm;
}
.sidebar > nav li > :is(a, button),
.sidebar > nav li > details > summary {
.sidebar nav li > :is(a, button),
.sidebar nav li > details > summary {
@apply ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground gap-2 rounded-md p-2 text-sm focus-visible:ring-2 text-left focus-visible:outline-hidden data-[open]:hover:bg-sidebar-accent data-[open]:hover:text-sidebar-accent-foreground [&>svg]:shrink-0 [&>svg]:size-4;
}
.sidebar > nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar > nav li > details > summary:is([aria-current=page], [data-active=true]) {
.sidebar nav li > :is(a, button):is([aria-current=page], [data-active=true]),
.sidebar nav li > details > summary:is([aria-current=page], [data-active=true]) {
@apply bg-sidebar-accent text-sidebar-accent-foreground font-medium;
}
.sidebar > nav li > :is(a, button):not([data-variant]),
.sidebar > nav li > details > summary:not([data-variant]),
.sidebar > nav li > :is(a, button)[data-variant=default],
.sidebar > nav li > details > summary[data-variant=default] {
.sidebar nav li > :is(a, button):not([data-variant]),
.sidebar nav li > details > summary:not([data-variant]),
.sidebar nav li > :is(a, button)[data-variant=default],
.sidebar nav li > details > summary[data-variant=default] {
@apply hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button)[data-variant=outline],
.sidebar > nav li > details > summary[data-variant=outline] {
.sidebar nav li > :is(a, button)[data-variant=outline],
.sidebar nav li > details > summary[data-variant=outline] {
@apply bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:shadow-[0_0_0_1px_var(--sidebar-accent)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground;
}
.sidebar > nav li > :is(a, button):not([data-size]),
.sidebar > nav li > details > summary:not([data-size]),
.sidebar > nav li > :is(a, button)[data-size=default],
.sidebar > nav li > details > summary[data-size=default] {
.sidebar nav li > :is(a, button):not([data-size]),
.sidebar nav li > details > summary:not([data-size]),
.sidebar nav li > :is(a, button)[data-size=default],
.sidebar nav li > details > summary[data-size=default] {
@apply h-8 text-sm;
}
.sidebar > nav li > :is(a, button)[data-size=sm],
.sidebar > nav li > details > summary[data-size=sm] {
.sidebar nav li > :is(a, button)[data-size=sm],
.sidebar nav li > details > summary[data-size=sm] {
@apply h-7 text-xs;
}
.sidebar > nav li > :is(a, button)[data-size=lg],
.sidebar > nav li > details > summary[data-size=lg] {
.sidebar nav li > :is(a, button)[data-size=lg],
.sidebar nav li > details > summary[data-size=lg] {
@apply h-12 text-sm;
}
.sidebar > nav li > details > summary::after {
.sidebar nav li > details > summary::after {
@apply size-4;
}
.sidebar > nav li > details::details-content {
.sidebar nav li > details::details-content {
@apply px-3.5;
}
.sidebar > nav ul ul {
.sidebar nav ul ul {
@apply border-sidebar-border translate-x-px gap-1 border-s px-2.5 py-0.5 rtl:-translate-x-px;
}
.sidebar > nav ul ul > li > :is(a, button),
.sidebar > nav ul ul > li > details > summary {
.sidebar nav ul ul > li > :is(a, button),
.sidebar nav ul ul > li > details > summary {
@apply text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground h-7 gap-2 rounded-md px-2 text-sm focus-visible:ring-2 [&>svg]:size-4;
}
/* Progress */
@@ -1338,7 +1334,7 @@
/* Tooltip */
[data-tooltip]::before {
@apply bg-foreground text-background rounded-md px-3 py-1.5 text-xs;
@apply bg-foreground text-background inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs has-[.kbd]:pe-1.5 [&_.kbd]:relative [&_.kbd]:isolate [&_.kbd]:z-50 [&_.kbd]:rounded-sm;
}
[data-tooltip][data-arrow]::after {
@@ -1376,42 +1372,15 @@
}
:is(.field > input[type='range'], .input[type='range'])::-webkit-slider-thumb {
@apply border-primary ring-ring/50 size-4 rounded-full border bg-white shadow-sm transition-[color,box-shadow] -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)];
@apply border-primary ring-ring/50 size-4 rounded-full border bg-white shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 -mt-[calc((var(--slider-thumb-size)-var(--slider-track-height))/2)] focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-moz-range-thumb {
@apply border-primary ring-ring/50 size-4 rounded-full border bg-white shadow-sm transition-[color,box-shadow];
@apply border-primary ring-ring/50 size-4 rounded-full border bg-white shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden;
}
:is(.field > input[type='range'], .input[type='range'])::-ms-thumb {
@apply border-primary ring-ring/50 size-4 rounded-full border bg-white shadow-sm transition-[color,box-shadow];
}
/* States go before the thumb pseudo-element so they still apply with native CSS nesting. */
@media (hover: hover) {
:is(.field > input[type='range'], .input[type='range']):hover::-webkit-slider-thumb {
@apply ring-4;
}
:is(.field > input[type='range'], .input[type='range']):hover::-moz-range-thumb {
@apply ring-4;
}
:is(.field > input[type='range'], .input[type='range']):hover::-ms-thumb {
@apply ring-4;
}
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-webkit-slider-thumb {
@apply ring-4 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-moz-range-thumb {
@apply ring-4 outline-hidden;
}
:is(.field > input[type='range'], .input[type='range']):focus-visible::-ms-thumb {
@apply ring-4 outline-hidden;
@apply border-primary ring-ring/50 size-4 rounded-full border bg-white shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden;
}
}
+2 -6
View File
@@ -390,11 +390,7 @@
}
if (event.key === 'Escape') {
if (isOpen) {
event.preventDefault();
event.stopPropagation();
root.close(true);
}
root.close(true);
return;
}
@@ -507,7 +503,7 @@
if (!root.contains(event.target)) root.close(false);
};
const handleDocumentPopover = (event) => {
if (!root.contains(event.detail.source)) root.close(false);
if (event.detail.source !== root) root.close(false);
};
state.input.addEventListener('focus', handleInputFocus);
+1 -1
View File
@@ -165,7 +165,7 @@
};
const handleDocumentPopover = (event) => {
if (!root.contains(event.detail.source)) root.close(false);
if (event.detail.source !== root) root.close(false);
};
state.trigger.addEventListener('click', handleTriggerClick);
+1 -1
View File
@@ -60,7 +60,7 @@
};
const handleDocumentPopover = (event) => {
if (!popoverComponent.contains(event.detail.source)) {
if (event.detail.source !== popoverComponent) {
closePopover(false);
}
};
+1 -1
View File
@@ -373,7 +373,7 @@
if (!root.contains(event.target)) root.close(false);
};
const handleDocumentPopover = (event) => {
if (!root.contains(event.detail.source)) root.close(false);
if (event.detail.source !== root) root.close(false);
};
state.trigger.addEventListener('keydown', handleTriggerKeydown);
+1 -1
View File
@@ -30,7 +30,7 @@
const handleClick = (event) => {
const target = event.target;
const nav = sidebarComponent.querySelector(':scope > nav');
const nav = sidebarComponent.querySelector('nav');
const isMobile = window.innerWidth < breakpoint;
if (isMobile && target.closest('a, button') && !target.closest('[data-keep-mobile-sidebar-open]')) {