Rename Cloudflare worker to basecoat

This commit is contained in:
Ronan Berder
2026-06-04 15:45:13 +08:00
parent 89b3942965
commit 1509cdadef
24 changed files with 130 additions and 70381 deletions
+31 -1
View File
@@ -2,8 +2,38 @@
## Unreleased
### Breaking Changes
- Removed the `.form` convenience selector for Basecoat 1.0. Use explicit component classes (`label`, `input`, `textarea`, `select`) or compose fields with `.field` / `.fieldset`.
- To keep the previous wrapper behavior, define it in your own Tailwind CSS:
- Changed Combobox markup and behavior to an input-first structure. The visible input now filters options, the hidden input stores the submitted value, single select stores the selected value, and multiple select stores a JSON array.
- Changed Command markup to the migrated Basecoat structure: `.command-dialog` wraps `.command`, the search input lives in the command header, and items use role-based menu markup with `role="menuitem"`.
- Removed Combobox-specific search-header behavior from Select. Use the dedicated Combobox component for editable/filterable selection.
- Reworked style loading for style packs. Non-default styles are standalone bundles and should not be loaded on top of the default/Vega bundle.
### Added
- Added standalone style packs: Vega, Nova, Maia, Lyra, Mira, Luma, Sera, and Rhea.
- Added dedicated Empty component styles and docs.
- Added dedicated Item component styles and docs.
- Added dedicated Input Group component styles and docs.
- Added dedicated Spinner docs and examples using `animate-spin` and `size-4` patterns.
- Added style-specific package entrypoints such as `basecoat-css/nova` and styleless base entrypoints for custom themes.
### Changed
- Reworked component CSS so shared component files own structure and behavior hooks while style-pack files own visual treatment.
- Updated Button, Button Group, Input, Textarea, Select, Combobox, Command, Dialog, Dropdown Menu, Popover, Field, Tabs, Table, Card, Alert, Badge, Kbd, Label, Skeleton, Radio, Switch, Empty, Item, and Input Group toward current shadcn/ui styles.
- Split Native Select documentation from custom Select documentation.
- Updated docs to use Basecoat-specific HTML usage instead of React/shadcn composition APIs.
- Updated docs site styling, navigation, fonts, and style switcher for the 1.0 style system.
### Removed
- Removed the old Form component page. Form layout should now be composed with Field, Fieldset, Input, Textarea, Select, Native Select, Checkbox, Radio, and Switch.
### Migration Notes
To keep the previous `.form` wrapper behavior, define it in your own Tailwind CSS:
```css
.form label { @apply label; }
+46
View File
@@ -17,6 +17,52 @@
@apply visible opacity-100;
}
.docs-search-trigger {
@apply hidden h-8 w-full min-w-0 max-w-72 cursor-text items-center gap-x-2 text-muted-foreground outline-none transition-colors md:flex;
svg {
@apply size-4 shrink-0;
}
.kbd {
@apply -me-1.5 ms-auto;
}
}
:root:not([data-style-variant]),
:root[data-style-variant='nova'] {
.docs-search-trigger {
@apply border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 rounded-lg border bg-transparent px-2.5 py-1 text-sm focus-visible:ring-3;
}
}
:root[data-style-variant='vega'] .docs-search-trigger {
@apply border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 rounded-md border bg-transparent px-2.5 py-1 text-sm shadow-xs focus-visible:ring-3;
}
:root[data-style-variant='maia'] .docs-search-trigger {
@apply border-input bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 rounded-4xl border px-3 py-1 text-sm focus-visible:ring-[3px];
}
:root[data-style-variant='lyra'] .docs-search-trigger {
@apply border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 rounded-none border bg-transparent px-2.5 py-1 text-xs focus-visible:ring-1;
}
:root[data-style-variant='mira'] .docs-search-trigger {
@apply border-input bg-input/20 dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/30 rounded-md border px-2 py-0.5 text-xs/relaxed focus-visible:ring-2;
}
:root[data-style-variant='luma'] .docs-search-trigger {
@apply bg-input/50 focus-visible:border-ring focus-visible:ring-ring/30 rounded-3xl border border-transparent px-3 py-1 text-sm focus-visible:ring-3;
}
:root[data-style-variant='sera'] .docs-search-trigger {
@apply focus-visible:border-b-ring rounded-none border border-transparent border-b-input bg-transparent px-0 py-1 text-sm transition-[color,border-color];
}
:root[data-style-variant='rhea'] .docs-search-trigger {
@apply bg-input/50 focus-visible:border-ring focus-visible:ring-ring/30 rounded-2xl border border-transparent px-2.5 py-1 text-sm duration-200 focus-visible:ring-3;
}
.content {
pre {
@apply grid max-h-[650px] overflow-y-auto rounded-lg bg-muted/40 text-sm;
+3 -3
View File
@@ -33,12 +33,12 @@
<button
type="button"
class="relative text-muted-foreground bg-background dark:bg-input/30 border-input hidden h-8 w-full min-w-0 max-w-72 items-center rounded-md border px-3 py-1 gap-x-2 text-sm outline-none cursor-text focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] md:flex"
class="docs-search-trigger"
onclick="document.getElementById('command-search').showModal(); document.getElementById('command-search').querySelector('header input')?.focus();"
>
{% lucide "search", { "class": "size-4" } %}
{% lucide "search" %}
Search the docs...
<kbd class="kbd ml-auto -mr-1.5">⌘K</kbd>
<kbd class="kbd">⌘K</kbd>
</button>
<select class="select h-8 leading-none" id="theme-select" aria-label="Theme">
@@ -7,19 +7,18 @@
</header>
<div class="p-4">
<div class="flex flex-wrap items-center gap-2">
<div class="flex flex-wrap items-center gap-2">
<button class="btn-outline" hx-trigger="click" hx-disinherit="hx-select hx-target hx-swap" hx-get="/fragments/toast/success" hx-target="#toaster" hx-swap="beforeend">
<button class="btn-outline" hx-trigger="click" hx-select="unset" hx-get="/fragments/toast/success" hx-target="#toaster" hx-swap="beforeend">
Success
</button>
<button class="btn-outline" hx-trigger="click" hx-disinherit="hx-select hx-target hx-swap" hx-get="/fragments/toast/error" hx-target="#toaster" hx-swap="beforeend">
<button class="btn-outline" hx-trigger="click" hx-select="unset" hx-get="/fragments/toast/error" hx-target="#toaster" hx-swap="beforeend">
Error
</button>
<button class="btn-outline" hx-trigger="click" hx-disinherit="hx-select hx-target hx-swap" hx-get="/fragments/toast/info" hx-target="#toaster" hx-swap="beforeend">
<button class="btn-outline" hx-trigger="click" hx-select="unset" hx-get="/fragments/toast/info" hx-target="#toaster" hx-swap="beforeend">
Info
</button>
<button class="btn-outline" hx-trigger="click" hx-disinherit="hx-select hx-target hx-swap" hx-get="/fragments/toast/warning" hx-target="#toaster" hx-swap="beforeend">
<button class="btn-outline" hx-trigger="click" hx-select="unset" hx-get="/fragments/toast/warning" hx-target="#toaster" hx-swap="beforeend">
Warning
</button>
</div>
</div>
</section>
</section>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -33,7 +33,7 @@ toc:
{% set code_server_side %}<button
class="btn-outline"
hx-trigger="click"
hx-disinherit="hx-select hx-target hx-swap"
hx-select="unset"
hx-get="/fragments/toast/success"
hx-target="#toaster"
hx-swap="beforeend"
@@ -278,4 +278,4 @@ toc:
{ tab: "toaster()", panel: code_block(raw_code_toaster, "jinja", class="") },
{ tab: "toast()", panel: code_block(raw_code_toast, "jinja", class="") }
] %}
{{ tabs(id="macros", tabsets=tabsets) }}
{{ tabs(id="macros", tabsets=tabsets) }}
+23 -23
View File
@@ -90,28 +90,28 @@ layout: layouts/layout.njk
<h2>Cookie Settings</h2>
<p>Manage your cookie settings here.</p>
</header>
<section class="text-sm grid gap-6">
<label class="label flex items-center justify-between gap-2 text-sm font-normal normal-case tracking-normal">
<div class="flex flex-col gap-0.5">
<div class="font-medium">Strictly Necessary</div>
<div class="text-muted-foreground">These cookies are essential in order to use the website and use its features.</div>
</div>
<input type="checkbox" role="switch" class="input" checked />
</label>
<label class="label flex items-center justify-between gap-2 text-sm font-normal normal-case tracking-normal">
<div class="flex flex-col gap-0.5">
<div class="font-medium">Functional Cookies</div>
<div class="text-muted-foreground">These cookies allow the website to provide personalized functionality.</div>
</div>
<input type="checkbox" role="switch" class="input" />
</label>
<label class="label flex items-center justify-between gap-2 text-sm font-normal normal-case tracking-normal">
<div class="flex flex-col gap-0.5">
<div class="font-medium">Performance Cookies</div>
<div class="text-muted-foreground">These cookies help to improve the performance of the website.</div>
</div>
<input type="checkbox" role="switch" class="input" />
</label>
<section class="grid gap-6">
<div role="group" class="field" data-orientation="horizontal">
<section>
<label for="cookie-necessary">Strictly Necessary</label>
<p>These cookies are essential in order to use the website and use its features.</p>
</section>
<input id="cookie-necessary" type="checkbox" role="switch" class="input" checked aria-label="Strictly Necessary" />
</div>
<div role="group" class="field" data-orientation="horizontal">
<section>
<label for="cookie-functional">Functional Cookies</label>
<p>These cookies allow the website to provide personalized functionality.</p>
</section>
<input id="cookie-functional" type="checkbox" role="switch" class="input" aria-label="Functional Cookies" />
</div>
<div role="group" class="field" data-orientation="horizontal">
<section>
<label for="cookie-performance">Performance Cookies</label>
<p>These cookies help to improve the performance of the website.</p>
</section>
<input id="cookie-performance" type="checkbox" role="switch" class="input" aria-label="Performance Cookies" />
</div>
</section>
<footer>
<button type="button" class="btn-outline w-full">Save preferences</button>
@@ -333,4 +333,4 @@ layout: layouts/layout.njk
</div>
</div>
</section>
</div>
</div>
+3 -3
View File
@@ -1,7 +1,7 @@
/* Toasts */
@layer components {
.toaster {
@apply pointer-events-none fixed bottom-0 z-50 flex w-full flex-col-reverse p-4 sm:max-w-90;
@apply pointer-events-none fixed bottom-0 z-50 flex w-full flex-col-reverse gap-4 p-4 sm:max-w-90;
&:not([data-align]),
&[data-align='end'] {
@@ -14,7 +14,7 @@
@apply left-1/2 -translate-x-1/2;
}
.toast {
@apply pointer-events-auto mt-4 grid w-full grid-rows-[1fr] animate-[toast-up_0.3s_ease-in-out] transition-[grid-template-rows,opacity,margin] duration-300 ease-in-out;
@apply pointer-events-auto grid w-full grid-rows-[1fr] animate-[toast-up_0.3s_ease-in-out] transition-[grid-template-rows,opacity] duration-300 ease-in-out;
.toast-content {
@apply flex items-center gap-2.5 overflow-hidden p-3 text-[13px];
@@ -40,7 +40,7 @@
}
}
&[aria-hidden='true'] {
@apply m-0 grid-rows-[0fr] overflow-hidden border-0 p-0 opacity-0;
@apply grid-rows-[0fr] overflow-hidden border-0 p-0 opacity-0;
.toast-content {
@apply border-0;
+1 -1
View File
@@ -730,7 +730,7 @@
/* Card */
.card {
@apply bg-card text-card-foreground ring-foreground/5 dark:ring-foreground/10 gap-6 rounded-4xl py-2 text-sm shadow-md ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 [&>img:first-child]:rounded-t-4xl [&>img:last-child]:rounded-b-4xl;
@apply bg-card text-card-foreground ring-foreground/5 dark:ring-foreground/10 gap-6 overflow-hidden rounded-4xl py-6 text-sm shadow-md ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 [&>img:first-child]:rounded-t-4xl [&>img:last-child]:rounded-b-4xl;
}
.card > header {
+1 -1
View File
@@ -714,7 +714,7 @@
/* Card */
.card {
@apply ring-foreground/10 bg-card text-card-foreground gap-4 rounded-none py-4 text-xs/relaxed ring-1 has-[>footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-2 data-[size=sm]:py-3 data-[size=sm]:has-[>footer]:pb-0 [&>img:first-child]:rounded-none [&>img:last-child]:rounded-none;
@apply ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-none py-4 text-xs/relaxed ring-1 has-[>footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-2 data-[size=sm]:py-3 data-[size=sm]:has-[>footer]:pb-0 [&>img:first-child]:rounded-none [&>img:last-child]:rounded-none;
}
.card > header {
+1 -1
View File
@@ -718,7 +718,7 @@
/* Card */
.card {
@apply ring-foreground/10 bg-card text-card-foreground gap-6 rounded-2xl py-2 text-sm ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 [&>img:first-child]:rounded-t-xl [&>img:last-child]:rounded-b-xl;
@apply ring-foreground/10 bg-card text-card-foreground gap-6 overflow-hidden rounded-2xl py-6 text-sm ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 [&>img:first-child]:rounded-t-xl [&>img:last-child]:rounded-b-xl;
}
.card > header {
+1 -1
View File
@@ -722,7 +722,7 @@
/* Card */
.card {
@apply ring-foreground/10 bg-card text-card-foreground gap-4 rounded-lg py-4 text-xs/relaxed ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 [&>img:first-child]:rounded-t-lg [&>img:last-child]:rounded-b-lg;
@apply ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-lg py-4 text-xs/relaxed ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 [&>img:first-child]:rounded-t-lg [&>img:last-child]:rounded-b-lg;
}
.card > header {
+1 -1
View File
@@ -718,7 +718,7 @@
/* Card */
.card {
@apply ring-foreground/10 bg-card text-card-foreground gap-4 rounded-xl py-4 text-sm ring-1 has-[>footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-[>footer]:pb-0 [&>img:first-child]:rounded-t-xl [&>img:last-child]:rounded-b-xl;
@apply ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-xl py-4 text-sm ring-1 has-[>footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-[>footer]:pb-0 [&>img:first-child]:rounded-t-xl [&>img:last-child]:rounded-b-xl;
}
.card > header {
+1 -1
View File
@@ -730,7 +730,7 @@
/* Card */
.card {
@apply bg-card text-card-foreground ring-foreground/5 dark:ring-foreground/10 gap-5 rounded-[min(var(--radius-4xl),24px)] py-5 text-sm shadow-sm ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 [&>img:first-child]:rounded-t-[min(var(--radius-4xl),24px)] [&>img:last-child]:rounded-b-[min(var(--radius-4xl),24px)];
@apply bg-card text-card-foreground ring-foreground/5 dark:ring-foreground/10 gap-5 overflow-hidden rounded-[min(var(--radius-4xl),24px)] py-5 text-sm shadow-sm ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 [&>img:first-child]:rounded-t-[min(var(--radius-4xl),24px)] [&>img:last-child]:rounded-b-[min(var(--radius-4xl),24px)];
}
.card > header {
+1 -1
View File
@@ -738,7 +738,7 @@
/* Card */
.card {
@apply bg-card text-card-foreground ring-foreground/5 gap-8 py-8 text-sm shadow-sm ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-5 data-[size=sm]:py-5 [&>img:first-child]:rounded-none [&>img:last-child]:rounded-none;
@apply bg-card text-card-foreground ring-foreground/5 gap-8 overflow-hidden py-8 text-sm shadow-sm ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-5 data-[size=sm]:py-5 [&>img:first-child]:rounded-none [&>img:last-child]:rounded-none;
}
.card > header {
+1 -1
View File
@@ -718,7 +718,7 @@
/* Card */
.card {
@apply ring-foreground/10 bg-card text-card-foreground gap-6 rounded-xl py-2 text-sm shadow-xs ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 [&>img:first-child]:rounded-t-xl [&>img:last-child]:rounded-b-xl;
@apply ring-foreground/10 bg-card text-card-foreground gap-6 overflow-hidden rounded-xl py-6 text-sm shadow-xs ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 [&>img:first-child]:rounded-t-xl [&>img:last-child]:rounded-b-xl;
}
.card > header {