Compare commits

..
Author SHA1 Message Date
grugbraindev 2f1cb006b0 Style vertical separators in base CSS
Add a low-specificity base rule so <hr aria-orientation="vertical"> renders
as a 1px inline-start line that stretches to its row, matching shadcn/ui's
vertical Separator. Rewrite the Separator docs to use it without utility
classes, note role="none" for decorative separators, and add a changelog
entry.
2026-10-23 11:25:11 +08:00
grugbraindev 0482aa37c5 Merge remote-tracking branch 'origin/main' into docs/separator 2026-10-23 11:22:48 +08:00
Stefan Gründelandgrugbraindev 192f167c17 Fix tooltip ellipsis across style packs (#205)
Co-authored-by: grugbraindev <178141691+grugbraindev@users.noreply.github.com>
2026-10-23 09:52:36 +08:00
Stefan Gründel 64d219fdc6 Fix combobox Escape handling inside dialogs (#204) 2026-10-23 09:51:21 +08:00
grugbraindev a662fcb5b1 Document the separator
Basecoat has no Separator component: an <hr> already renders as a 1px line in the border color. Add a docs page with horizontal, vertical, menu, list and RTL examples that mirror shadcn/ui's Separator docs.
2026-10-23 09:34:53 +08:00
18 changed files with 189 additions and 22 deletions
+10 -1
View File
@@ -1,6 +1,15 @@
# Changelog
## Unreleased
## [Unreleased]
### Added
- Added vertical separators: `<hr aria-orientation="vertical">` renders as a 1px line that fills its row.
### 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.
## [1.0.2] - 2026-07-06
+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,6 +85,8 @@ 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
@@ -0,0 +1,134 @@
# 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,6 +53,14 @@ 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,6 +94,7 @@
"components/radio-group",
"components/select",
"components/scroll-area",
"components/separator",
"components/sidebar",
"components/skeleton",
"components/slider",
+10 -1
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,4 +133,13 @@
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;
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
@apply relative;
&:before {
@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)];
@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)];
}
&:hover:before {
+1 -1
View File
@@ -1324,7 +1324,7 @@
/* Tooltip */
[data-tooltip]::before {
@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;
@apply bg-foreground text-background rounded-xl px-3 py-1.5 text-xs;
}
[data-tooltip][data-arrow]::after {
+1 -1
View File
@@ -1327,7 +1327,7 @@
/* Tooltip */
[data-tooltip]::before {
@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;
@apply bg-foreground text-background rounded-none px-3 py-1.5 text-xs;
}
[data-tooltip][data-arrow]::after {
+1 -1
View File
@@ -1312,7 +1312,7 @@
/* Tooltip */
[data-tooltip]::before {
@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;
@apply bg-foreground text-background rounded-2xl px-3 py-1.5 text-xs;
}
[data-tooltip][data-arrow]::after {
+1 -1
View File
@@ -1321,7 +1321,7 @@
/* Tooltip */
[data-tooltip]::before {
@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;
@apply bg-foreground text-background rounded-md px-3 py-1.5 text-xs;
}
[data-tooltip][data-arrow]::after {
+1 -1
View File
@@ -1335,7 +1335,7 @@
/* Tooltip */
[data-tooltip]::before {
@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;
@apply bg-foreground text-background rounded-md px-3 py-1.5 text-xs;
}
[data-tooltip][data-arrow]::after {
+1 -1
View File
@@ -1329,7 +1329,7 @@
/* Tooltip */
[data-tooltip]::before {
@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;
@apply bg-foreground text-background rounded-xl px-3 py-1.5 text-xs;
}
[data-tooltip][data-arrow]::after {
+1 -1
View File
@@ -1345,7 +1345,7 @@
/* Tooltip */
[data-tooltip]::before {
@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;
@apply bg-foreground text-background rounded-none px-3 py-1.5 text-xs;
}
[data-tooltip][data-arrow]::after {
+1 -1
View File
@@ -1334,7 +1334,7 @@
/* Tooltip */
[data-tooltip]::before {
@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;
@apply bg-foreground text-background rounded-md px-3 py-1.5 text-xs;
}
[data-tooltip][data-arrow]::after {
+5 -1
View File
@@ -390,7 +390,11 @@
}
if (event.key === 'Escape') {
root.close(true);
if (isOpen) {
event.preventDefault();
event.stopPropagation();
root.close(true);
}
return;
}