Toggle / Toggle Group
Two-state buttons styled with aria-pressed: and data-[state=on]: utilities.
Toggle button
BitBhToggle is a native button with aria-pressed and data-state="on|off".
Space / Enter or a click flips it; use @bind-Pressed, DefaultPressed and Disabled.
Muted: no
<BitBhToggle aria-label="Bold"
class="inline-flex h-9 min-w-9 cursor-pointer items-center justify-center gap-1.5 rounded-lg border border-zinc-200 bg-white px-3 text-sm font-medium text-zinc-700 outline-none transition-colors enabled:hover:bg-zinc-100 focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 aria-pressed:border-blue-200 aria-pressed:bg-blue-50 aria-pressed:text-blue-700 enabled:aria-pressed:hover:bg-blue-100 disabled:cursor-not-allowed disabled:opacity-50"><strong>B</strong></BitBhToggle>
<BitBhToggle @bind-Pressed="muted"
class="inline-flex h-9 min-w-9 cursor-pointer items-center justify-center gap-1.5 rounded-lg border border-zinc-200 bg-white px-3 text-sm font-medium text-zinc-700 outline-none transition-colors enabled:hover:bg-zinc-100 focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 aria-pressed:border-blue-200 aria-pressed:bg-blue-50 aria-pressed:text-blue-700 enabled:aria-pressed:hover:bg-blue-100 disabled:cursor-not-allowed disabled:opacity-50">Mute</BitBhToggle>
<BitBhToggle DefaultPressed="true" Disabled="true"
class="inline-flex h-9 min-w-9 cursor-pointer items-center justify-center gap-1.5 rounded-lg border border-zinc-200 bg-white px-3 text-sm font-medium text-zinc-700 outline-none transition-colors enabled:hover:bg-zinc-100 focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 aria-pressed:border-blue-200 aria-pressed:bg-blue-50 aria-pressed:text-blue-700 enabled:aria-pressed:hover:bg-blue-100 disabled:cursor-not-allowed disabled:opacity-50">Pinned</BitBhToggle>
<p class="m-0 text-sm text-zinc-500">Muted: <strong class="font-semibold text-zinc-900">@(muted ? "yes" : "no")</strong></p>
@code {
private bool muted;
}State-aware content with Template
Template receives the bool pressed state. Keep the label constant and swap only the icon — aria-pressed conveys the state.
<BitBhToggle DefaultPressed="true"
class="inline-flex h-9 min-w-9 cursor-pointer items-center justify-center gap-1.5 rounded-lg border border-zinc-200 bg-white px-3 text-sm font-medium text-zinc-700 outline-none transition-colors enabled:hover:bg-zinc-100 focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 aria-pressed:border-blue-200 aria-pressed:bg-blue-50 aria-pressed:text-blue-700 enabled:aria-pressed:hover:bg-blue-100">
<Template Context="pressed">
<span class="text-base leading-none" aria-hidden="true">@(pressed ? "★" : "☆")</span>
Star
</Template>
</BitBhToggle>
<BitBhToggle class="inline-flex h-9 min-w-9 cursor-pointer items-center justify-center gap-1.5 rounded-lg border border-zinc-200 bg-white px-3 text-sm font-medium text-zinc-700 outline-none transition-colors enabled:hover:bg-zinc-100 focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 aria-pressed:border-blue-200 aria-pressed:bg-blue-50 aria-pressed:text-blue-700 enabled:aria-pressed:hover:bg-blue-100">
<Template Context="pressed">
<span class="text-base leading-none" aria-hidden="true">@(pressed ? "✓" : "+")</span>
Follow
</Template>
</BitBhToggle>Multiple: text formatting
Type="BitBhToggleGroupType.Multiple" is a role="group" of aria-pressed buttons bound with @bind-Values.
One tab stop; ← / → move focus, Home / End jump, Space / Enter toggle.
The quick brown fox jumps over the lazy dog.
Values: bold
<BitBhToggleGroup Type="BitBhToggleGroupType.Multiple" @bind-Values="textStyles"
aria-label="Text formatting"
class="inline-flex gap-1 rounded-[0.6rem] border border-zinc-200 bg-zinc-100 p-1">
<BitBhToggleGroupItem Value="bold" aria-label="Bold"
class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center gap-2 rounded-md px-2.5 text-sm font-medium text-zinc-600 outline-none transition hover:bg-zinc-200 hover:text-zinc-900 focus-visible:ring-2 focus-visible:ring-blue-600 data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-[state=on]:hover:bg-white"><strong>B</strong></BitBhToggleGroupItem>
<BitBhToggleGroupItem Value="italic" aria-label="Italic"
class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center gap-2 rounded-md px-2.5 text-sm font-medium text-zinc-600 outline-none transition hover:bg-zinc-200 hover:text-zinc-900 focus-visible:ring-2 focus-visible:ring-blue-600 data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-[state=on]:hover:bg-white"><em>I</em></BitBhToggleGroupItem>
<BitBhToggleGroupItem Value="underline" aria-label="Underline"
class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center gap-2 rounded-md px-2.5 text-sm font-medium text-zinc-600 outline-none transition hover:bg-zinc-200 hover:text-zinc-900 focus-visible:ring-2 focus-visible:ring-blue-600 data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-[state=on]:hover:bg-white"><u>U</u></BitBhToggleGroupItem>
<BitBhToggleGroupItem Value="strike" aria-label="Strikethrough"
class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center gap-2 rounded-md px-2.5 text-sm font-medium text-zinc-600 outline-none transition hover:bg-zinc-200 hover:text-zinc-900 focus-visible:ring-2 focus-visible:ring-blue-600 data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-[state=on]:hover:bg-white"><s>S</s></BitBhToggleGroupItem>
</BitBhToggleGroup>
<p class="m-0 w-full max-w-md rounded-lg border border-zinc-200 bg-white px-4 py-3 text-[0.95rem] text-zinc-800"
style="@PreviewStyle()">The quick brown fox jumps over the lazy dog.</p>
<p class="m-0 text-sm text-zinc-500">
Values: <strong class="font-semibold text-zinc-900">@(textStyles.Count == 0 ? "none" : string.Join(", ", textStyles))</strong>
</p>
@code {
private IReadOnlyCollection<string> textStyles = ["bold"];
private string PreviewStyle()
{
var decorations = textStyles
.Where(s => s is "underline" or "strike")
.Select(s => s == "strike" ? "line-through" : s)
.ToList();
return $"font-weight: {(textStyles.Contains("bold") ? 700 : 400)}; " +
$"font-style: {(textStyles.Contains("italic") ? "italic" : "normal")}; " +
$"text-decoration: {(decorations.Count > 0 ? string.Join(" ", decorations) : "none")}";
}
}Single: alignment
The default Single type is a radiogroup of radio items (aria-checked); the pressed item is the tab stop.
AllowEmpty="false" stops a click on the pressed item from clearing the value.
Headless components ship behavior and accessibility, and leave every pixel of the presentation to you — so this paragraph can be aligned however you like.
<BitBhToggleGroup @bind-Value="align" AllowEmpty="false" aria-label="Text alignment"
class="inline-flex gap-1 rounded-[0.6rem] border border-zinc-200 bg-zinc-100 p-1">
<BitBhToggleGroupItem Value="left" aria-label="Align left"
class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center rounded-md px-2.5 text-zinc-600 outline-none transition hover:bg-zinc-200 hover:text-zinc-900 focus-visible:ring-2 focus-visible:ring-blue-600 data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-[state=on]:hover:bg-white">
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path d="M2 3h12M2 6.5h8M2 10h12M2 13.5h8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" /></svg>
</BitBhToggleGroupItem>
<BitBhToggleGroupItem Value="center" aria-label="Align center"
class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center rounded-md px-2.5 text-zinc-600 outline-none transition hover:bg-zinc-200 hover:text-zinc-900 focus-visible:ring-2 focus-visible:ring-blue-600 data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-[state=on]:hover:bg-white">
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path d="M2 3h12M4 6.5h8M2 10h12M4 13.5h8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" /></svg>
</BitBhToggleGroupItem>
<BitBhToggleGroupItem Value="right" aria-label="Align right"
class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center rounded-md px-2.5 text-zinc-600 outline-none transition hover:bg-zinc-200 hover:text-zinc-900 focus-visible:ring-2 focus-visible:ring-blue-600 data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-[state=on]:hover:bg-white">
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path d="M2 3h12M6 6.5h8M2 10h12M6 13.5h8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" /></svg>
</BitBhToggleGroupItem>
<BitBhToggleGroupItem Value="justify" aria-label="Justify"
class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center rounded-md px-2.5 text-zinc-600 outline-none transition hover:bg-zinc-200 hover:text-zinc-900 focus-visible:ring-2 focus-visible:ring-blue-600 data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-[state=on]:hover:bg-white">
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path d="M2 3h12M2 6.5h12M2 10h12M2 13.5h12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" /></svg>
</BitBhToggleGroupItem>
</BitBhToggleGroup>
<p class="m-0 w-full max-w-md rounded-lg border border-zinc-200 bg-white px-4 py-3 text-[0.95rem] text-zinc-800"
style="text-align: @align">
Headless components ship behavior and accessibility, and leave every pixel of
the presentation to you — so this paragraph can be aligned however you like.
</p>
@code {
private string? align = "left";
}Vertical orientation
Orientation="BitBhRovingOrientation.Vertical" switches navigation to ↑ / ↓ and sets data-orientation="vertical".
Uncontrolled via DefaultValue; each item's Template draws a check mark.
<BitBhToggleGroup Orientation="BitBhRovingOrientation.Vertical" DefaultValue="week"
aria-label="Calendar view"
class="inline-flex gap-1 rounded-[0.6rem] border border-zinc-200 bg-zinc-100 p-1 data-orientation-vertical:min-w-36 data-orientation-vertical:flex-col">
@foreach (var view in views)
{
<BitBhToggleGroupItem Value="@view.ToLowerInvariant()"
class="inline-flex h-8 cursor-pointer items-center justify-start gap-2 rounded-md px-2.5 text-sm font-medium text-zinc-600 outline-none transition hover:bg-zinc-200 hover:text-zinc-900 focus-visible:ring-2 focus-visible:ring-blue-600 data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-[state=on]:hover:bg-white">
<Template Context="isOn">
<span class="inline-block w-4 text-blue-600" aria-hidden="true">@(isOn ? "✓" : "")</span>
@view
</Template>
</BitBhToggleGroupItem>
}
</BitBhToggleGroup>
@code {
private readonly string[] views = ["Day", "Week", "Month", "Year"];
}Disabled items
A disabled item gets the native disabled attribute (style with disabled: or data-disabled:) and arrow keys skip it. Disabled on the group disables every item.
<BitBhToggleGroup @bind-Value="size" aria-label="Size"
class="inline-flex gap-1 rounded-[0.6rem] border border-zinc-200 bg-zinc-100 p-1">
<BitBhToggleGroupItem Value="s" class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center rounded-md px-2.5 text-sm font-medium text-zinc-600 outline-none transition hover:bg-zinc-200 hover:text-zinc-900 focus-visible:ring-2 focus-visible:ring-blue-600 data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-[state=on]:hover:bg-white data-disabled:cursor-not-allowed data-disabled:opacity-40 data-disabled:hover:bg-transparent data-disabled:hover:text-zinc-600">S</BitBhToggleGroupItem>
<BitBhToggleGroupItem Value="m" class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center rounded-md px-2.5 text-sm font-medium text-zinc-600 outline-none transition hover:bg-zinc-200 hover:text-zinc-900 focus-visible:ring-2 focus-visible:ring-blue-600 data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-[state=on]:hover:bg-white data-disabled:cursor-not-allowed data-disabled:opacity-40 data-disabled:hover:bg-transparent data-disabled:hover:text-zinc-600">M</BitBhToggleGroupItem>
<BitBhToggleGroupItem Value="l" class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center rounded-md px-2.5 text-sm font-medium text-zinc-600 outline-none transition hover:bg-zinc-200 hover:text-zinc-900 focus-visible:ring-2 focus-visible:ring-blue-600 data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-[state=on]:hover:bg-white data-disabled:cursor-not-allowed data-disabled:opacity-40 data-disabled:hover:bg-transparent data-disabled:hover:text-zinc-600">L</BitBhToggleGroupItem>
<BitBhToggleGroupItem Value="xl" Disabled="true" class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center rounded-md px-2.5 text-sm font-medium text-zinc-600 outline-none transition hover:bg-zinc-200 hover:text-zinc-900 focus-visible:ring-2 focus-visible:ring-blue-600 data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-[state=on]:hover:bg-white data-disabled:cursor-not-allowed data-disabled:opacity-40 data-disabled:hover:bg-transparent data-disabled:hover:text-zinc-600">XL</BitBhToggleGroupItem>
</BitBhToggleGroup>
<BitBhToggleGroup DefaultValue="m" Disabled="true" aria-label="Size (sold out)"
class="inline-flex gap-1 rounded-[0.6rem] border border-zinc-200 bg-zinc-100 p-1 data-disabled:opacity-60">
<BitBhToggleGroupItem Value="s" class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center rounded-md px-2.5 text-sm font-medium text-zinc-600 outline-none data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-disabled:cursor-not-allowed">S</BitBhToggleGroupItem>
<BitBhToggleGroupItem Value="m" class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center rounded-md px-2.5 text-sm font-medium text-zinc-600 outline-none data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-disabled:cursor-not-allowed">M</BitBhToggleGroupItem>
<BitBhToggleGroupItem Value="l" class="inline-flex h-8 min-w-9 cursor-pointer items-center justify-center rounded-md px-2.5 text-sm font-medium text-zinc-600 outline-none data-[state=on]:bg-white data-[state=on]:text-blue-600 data-[state=on]:shadow-sm data-disabled:cursor-not-allowed">L</BitBhToggleGroupItem>
</BitBhToggleGroup>
@code {
private string? size = "m";
}