Button
A polymorphic, accessible button primitive with loading and disabled states.
Basic Button
Renders a native <button> with no styling. All visuals come from Tailwind utility classes.
<BitBhButton OnClick="HandleBasicClick"
class="inline-flex items-center gap-2 rounded-lg border border-zinc-200 bg-white px-4 py-2 text-sm font-medium text-zinc-900 cursor-pointer transition-colors hover:bg-zinc-100 hover:border-zinc-300 active:bg-zinc-200 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600">
Click me
</BitBhButton>
<span class="text-sm text-zinc-500">Clicked @basicClickCount times</span>
@code {
private int basicClickCount;
private void HandleBasicClick() => basicClickCount++;
}Disabled State
Sets the native disabled attribute and emits data-disabled. The data-disabled: variant styles the disabled look.
<BitBhButton Disabled="true"
class="inline-flex items-center gap-2 rounded-lg border border-zinc-200 bg-white px-4 py-2 text-sm font-medium text-zinc-900 cursor-pointer transition-colors hover:bg-zinc-100 hover:border-zinc-300 active:bg-zinc-200 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 data-disabled:opacity-50 data-disabled:cursor-not-allowed data-disabled:pointer-events-none">
Can't click me
</BitBhButton>Loading State with Context
Uses RenderFragment<BitBhButtonContext> to drive a spinner from the button's own state.
<BitBhButton Loading="@isLoading" OnClick="HandleLoadingClick"
class="inline-flex items-center gap-2 rounded-lg border border-zinc-200 bg-white px-4 py-2 text-sm font-medium text-zinc-900 cursor-pointer transition-colors hover:bg-zinc-100 data-loading:opacity-70 data-loading:cursor-wait">
<Template Context="btn">
@if (btn.Loading)
{
<span class="inline-block h-4 w-4 animate-spin rounded-full border-2 border-current border-r-transparent"></span>
<span>Saving…</span>
}
else
{
<span>Save Changes</span>
}
</Template>
</BitBhButton>
@code {
private bool isLoading;
private async Task HandleLoadingClick()
{
isLoading = true;
await Task.Delay(2000);
isLoading = false;
}
}Polymorphic Rendering
Set As="a" to render as an anchor while keeping all button semantics. Non-native elements receive role="button", tabindex="0", and Enter/Space handling.
<BitBhButton As="a" href="https://github.com/bitfoundation/bitplatform/tree/develop/src/Utilities/Bheadless" target="_blank"
class="inline-flex items-center gap-2 rounded-lg border border-transparent bg-transparent px-4 py-2 text-sm font-medium text-blue-600 underline underline-offset-2 transition-colors hover:bg-blue-50 hover:text-blue-700">
I'm actually an <a> tag
</BitBhButton>
<BitBhButton As="span" OnClick="HandleBasicClick"
class="inline-flex items-center gap-2 rounded-lg border border-zinc-200 bg-white px-4 py-2 text-sm font-medium text-zinc-900 cursor-pointer transition-colors hover:bg-zinc-100">
I'm a <span> with role="button"
</BitBhButton>
@code {
private int basicClickCount;
private void HandleBasicClick() => basicClickCount++;
}Toggle Button
Combine BitBhButton with your own state to build a toggle. Pass aria-pressed via standard HTML attributes โ they merge automatically.
<BitBhButton OnClick="TogglePressed"
class="@(isPressed
? "inline-flex min-w-16 items-center justify-center gap-2 rounded-lg border border-blue-600 bg-blue-600 px-4 py-2 text-sm font-medium text-white cursor-pointer transition-colors hover:bg-blue-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
: "inline-flex min-w-16 items-center justify-center gap-2 rounded-lg border border-zinc-200 bg-white px-4 py-2 text-sm font-medium text-zinc-900 cursor-pointer transition-colors hover:bg-zinc-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600")"
aria-pressed="@isPressed.ToString().ToLowerInvariant()">
@(isPressed ? "ON" : "OFF")
</BitBhButton>
@code {
private bool isPressed;
private void TogglePressed() => isPressed = !isPressed;
}Styled via Data Attributes
Style entirely via the registered data-disabled: and data-loading: variants โ no component API for visuals.
<BitBhButton OnClick="HandleBasicClick"
class="inline-flex items-center gap-2 rounded-lg border border-transparent bg-zinc-900 px-4 py-2 text-sm font-medium text-white cursor-pointer data-disabled:opacity-40 data-disabled:cursor-not-allowed data-loading:opacity-70 data-loading:cursor-wait">
Styled via [data-*]
</BitBhButton>
<BitBhButton Disabled="true"
class="inline-flex items-center gap-2 rounded-lg border border-transparent bg-zinc-900 px-4 py-2 text-sm font-medium text-white cursor-pointer data-disabled:opacity-40 data-disabled:cursor-not-allowed data-loading:opacity-70 data-loading:cursor-wait">
Disabled via [data-disabled]
</BitBhButton>
<BitBhButton Loading="true"
class="inline-flex items-center gap-2 rounded-lg border border-transparent bg-zinc-900 px-4 py-2 text-sm font-medium text-white cursor-pointer data-disabled:opacity-40 data-disabled:cursor-not-allowed data-loading:opacity-70 data-loading:cursor-wait">
Loading via [data-loading]
</BitBhButton>
@code {
private int basicClickCount;
private void HandleBasicClick() => basicClickCount++;
}