feat(htmx-css): ToggleGroup support + padding/primary parity
Generator (scripts/generate-htmx-css.ts): track `viaVariants` per slot so
slots that compose another component's variant system (e.g. ToggleGroupItem
via toggleVariants) inherit the referenced CVA's base + variant rules under
their own selector. Previously toggle-group-item's CSS contained only its
override classes, shipping with no padding/height/hover/active state.
Toggle (components/ui/toggle.tsx):
- data-[state=on] now uses bg-primary (orange) instead of bg-accent (grey),
matching every other "commit" affordance in the palette.
- Horizontal padding aligned with Button: px-4/px-3/px-6 per size, plus
has-[>svg]:px-* for icon-only toggles.
ToggleGroup (components/ui/toggle-group.tsx): drop min-w-0 flex-1 shrink-0
from the item override. Items now size to content instead of being clamped
into equal narrow columns where longer labels overflowed the bg box.
Showcase: add ToggleGroup section to the React page (component-matrix.tsx)
and 1:1 HTMX mirror (public/htmx.html) with a new JS bridge branch for
single/multi-select. compare-all.sh extended with the new section; 22/22
pass at ≥99.97%.
Docs: GAPS.md captures the generator gap, overflow root cause, color
rationale, and padding parity with before/after numbers.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -736,6 +736,69 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toggle Group -->
|
||||
<div id="sub-toggle-group">
|
||||
<h4 class="font-sans text-sm font-semibold uppercase tracking-wide text-muted-foreground mb-4">
|
||||
Toggle Group
|
||||
</h4>
|
||||
<div class="border border-border rounded-md p-6 bg-card">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<p class="font-sans text-xs text-muted-foreground mb-2">Single, outline</p>
|
||||
<div data-slot="toggle-group" data-variant="outline" data-type="single" role="group" aria-label="Theme">
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-state="off" data-value="system" aria-label="System">System</button>
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-state="on" data-value="light" aria-label="Light">Light</button>
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-state="off" data-value="dark" aria-label="Dark">Dark</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-sans text-xs text-muted-foreground mb-2">Single, default</p>
|
||||
<div data-slot="toggle-group" data-type="single" role="group" aria-label="Layout">
|
||||
<button type="button" data-slot="toggle-group-item" data-state="off" data-value="list" aria-label="List">List</button>
|
||||
<button type="button" data-slot="toggle-group-item" data-state="on" data-value="grid" aria-label="Grid">Grid</button>
|
||||
<button type="button" data-slot="toggle-group-item" data-state="off" data-value="board" aria-label="Board" disabled data-disabled="">Board</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-sans text-xs text-muted-foreground mb-2">Multiple</p>
|
||||
<div data-slot="toggle-group" data-variant="outline" data-type="multiple" role="group" aria-label="Text formatting">
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-state="on" data-value="bold" aria-label="Bold">Bold</button>
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-state="off" data-value="italic" aria-label="Italic">Italic</button>
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-state="off" data-value="underline" aria-label="Underline">Underline</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<p class="font-sans text-xs text-muted-foreground mb-2">Small</p>
|
||||
<div data-slot="toggle-group" data-variant="outline" data-type="single" role="group" aria-label="Theme (sm)">
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-size="sm" data-state="off" data-value="system">System</button>
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-size="sm" data-state="on" data-value="light">Light</button>
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-size="sm" data-state="off" data-value="dark">Dark</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-sans text-xs text-muted-foreground mb-2">Default</p>
|
||||
<div data-slot="toggle-group" data-variant="outline" data-type="single" role="group" aria-label="Theme (default)">
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-state="off" data-value="system">System</button>
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-state="on" data-value="light">Light</button>
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-state="off" data-value="dark">Dark</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-sans text-xs text-muted-foreground mb-2">Large</p>
|
||||
<div data-slot="toggle-group" data-variant="outline" data-type="single" role="group" aria-label="Theme (lg)">
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-size="lg" data-state="off" data-value="system">System</button>
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-size="lg" data-state="on" data-value="light">Light</button>
|
||||
<button type="button" data-slot="toggle-group-item" data-variant="outline" data-size="lg" data-state="off" data-value="dark">Dark</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tooltips -->
|
||||
<div id="sub-tooltips">
|
||||
<h4 class="font-sans text-sm font-semibold uppercase tracking-wide text-muted-foreground mb-4">
|
||||
@@ -993,6 +1056,19 @@
|
||||
el.setAttribute('aria-checked', next === 'checked' ? 'true' : 'false');
|
||||
const thumb = el.querySelector('[data-slot="switch-thumb"]');
|
||||
if (thumb) thumb.setAttribute('data-state', next);
|
||||
} else if (slot === 'toggle-group-item') {
|
||||
const group = el.closest('[data-slot="toggle-group"]');
|
||||
if (!group) return;
|
||||
const multi = group.getAttribute('data-type') === 'multiple';
|
||||
const next = el.getAttribute('data-state') === 'on' ? 'off' : 'on';
|
||||
if (multi) {
|
||||
el.setAttribute('data-state', next);
|
||||
} else {
|
||||
// Single-select: activate this, deactivate siblings. Don't deselect the active one.
|
||||
group.querySelectorAll('[data-slot="toggle-group-item"]').forEach((it) => {
|
||||
it.setAttribute('data-state', it === el ? 'on' : 'off');
|
||||
});
|
||||
}
|
||||
} else if (slot === 'tabs-trigger') {
|
||||
const list = el.closest('[data-slot="tabs-list"]');
|
||||
const tabs = el.closest('[data-slot="tabs"]');
|
||||
|
||||
Reference in New Issue
Block a user