mirror of
https://github.com/hunvreus/basecoat.git
synced 2026-11-11 04:38:18 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f1cb006b0 | ||
|
|
0482aa37c5 | ||
|
|
192f167c17 | ||
|
|
64d219fdc6 | ||
|
|
a662fcb5b1 |
+10
-1
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
"components/radio-group",
|
||||
"components/select",
|
||||
"components/scroll-area",
|
||||
"components/separator",
|
||||
"components/sidebar",
|
||||
"components/skeleton",
|
||||
"components/slider",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+6
-2
@@ -390,7 +390,11 @@
|
||||
}
|
||||
|
||||
if (event.key === 'Escape') {
|
||||
root.close(true);
|
||||
if (isOpen) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
root.close(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -503,7 +507,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);
|
||||
|
||||
@@ -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
@@ -60,7 +60,7 @@
|
||||
};
|
||||
|
||||
const handleDocumentPopover = (event) => {
|
||||
if (!popoverComponent.contains(event.detail.source)) {
|
||||
if (event.detail.source !== popoverComponent) {
|
||||
closePopover(false);
|
||||
}
|
||||
};
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user