ee8797c142
Co-authored-by: Copilot <copilot@github.com>
183 lines
4.7 KiB
Markdown
183 lines
4.7 KiB
Markdown
# Badge
|
|
|
|
A small inline label pill. Used to indicate status, category, or count. Four variants cover most use-cases.
|
|
|
|
---
|
|
|
|
## HTML structure
|
|
|
|
```
|
|
span.{base classes + variant classes}
|
|
{text}
|
|
```
|
|
|
|
---
|
|
|
|
## CSS mechanics
|
|
|
|
| Class | Effect |
|
|
|---|---|
|
|
| `inline-flex items-center rounded-full` | Pill shape that sits inline with text |
|
|
| `px-2.5 py-0.5 text-xs font-semibold` | Compact size and bold label |
|
|
| `transition-colors` | Smooth color changes on hover |
|
|
| `focus:ring-2 focus:ring-ring focus:ring-offset-2` | Keyboard focus outline |
|
|
|
|
**Variants:**
|
|
|
|
| Variant | Classes |
|
|
|---|---|
|
|
| `default` | `bg-primary text-primary-foreground hover:bg-primary/80` |
|
|
| `secondary` | `bg-secondary text-secondary-foreground hover:bg-secondary/80` |
|
|
| `destructive` | `bg-destructive text-destructive-foreground hover:bg-destructive/80` |
|
|
| `outline` | `text-foreground border border-input hover:bg-accent` |
|
|
|
|
---
|
|
|
|
## Constructor signature
|
|
|
|
```csharp
|
|
public Badge(string text, string variant = "default")
|
|
```
|
|
|
|
| Parameter | Description |
|
|
|---|---|
|
|
| `text` | Label displayed inside the badge |
|
|
| `variant` | `"default"` / `"secondary"` / `"destructive"` / `"outline"` |
|
|
|
|
---
|
|
|
|
## Usage examples
|
|
|
|
### Basic badges
|
|
|
|
```csharp
|
|
new Badge("New")
|
|
new Badge("Beta", variant: "secondary")
|
|
new Badge("Error", variant: "destructive")
|
|
new Badge("Pending", variant: "outline")
|
|
```
|
|
|
|
### Status indicator in a table cell
|
|
|
|
```csharp
|
|
// Render to bytes and embed in table cell HTML
|
|
var writer = new System.Buffers.ArrayBufferWriter<byte>();
|
|
new Badge("Active", variant: "default").Render(new HtmxRenderContext(writer));
|
|
var badgeHtml = System.Text.Encoding.UTF8.GetString(writer.WrittenSpan);
|
|
|
|
new Table(
|
|
headers: new[] { "Name", "Status" },
|
|
rows: users.Select(u => new[] { u.DisplayName ?? "", badgeHtml }))
|
|
```
|
|
|
|
### Embedding in a page slot
|
|
|
|
```html
|
|
<!-- MyPage.htmx -->
|
|
<div class="flex items-center gap-2">
|
|
<span class="text-sm">Status:</span>
|
|
$$StatusBadge$$
|
|
</div>
|
|
```
|
|
|
|
```csharp
|
|
// MyPage.htmx.cs
|
|
public IHtmxComponent StatusBadge { get; }
|
|
|
|
public MyPage(string status)
|
|
{
|
|
StatusBadge = status == "active"
|
|
? new Badge("Active")
|
|
: new Badge("Inactive", variant: "secondary");
|
|
}
|
|
|
|
protected override void RenderStatusBadge(HtmxRenderContext ctx)
|
|
=> StatusBadge.Render(ctx.Next());
|
|
```
|
|
|
|
---
|
|
|
|
## Tips and tricks
|
|
|
|
- Badge does not have a click handler — wrap it in an `<a>` or a `Button` if you need interactivity.
|
|
- All four variants respond to focus, so a Badge embedded inside a focusable element will show a ring.
|
|
- For a count badge (e.g. `"3 new"`) just include the count in the text string.
|
|
- To render a Badge inside raw HTML strings (e.g. inside a `Table` cell or `Card` content), render it eagerly to a string in the constructor rather than relying on slot rendering.
|
|
|
|
---
|
|
|
|
## Complete page example
|
|
|
|
**`Templates/OrdersPage.htmx`**
|
|
```html
|
|
<div class="max-w-4xl mx-auto py-10">
|
|
<h1 class="text-2xl font-bold mb-6">Orders</h1>
|
|
$$OrdersTable$$
|
|
</div>
|
|
```
|
|
|
|
**`Templates/OrdersPage.htmx.cs`**
|
|
```csharp
|
|
namespace Htmx.ApiDemo.Templates;
|
|
|
|
public sealed class OrdersPage : OrdersPageBase
|
|
{
|
|
private readonly IHtmxComponent _table;
|
|
|
|
public OrdersPage(IEnumerable<Order> orders)
|
|
{
|
|
_table = new Components.Table(
|
|
headers: new[] { "Order", "Customer", "Amount", "Status" },
|
|
rows: orders.Select(o => new[]
|
|
{
|
|
System.Net.WebUtility.HtmlEncode(o.Id),
|
|
System.Net.WebUtility.HtmlEncode(o.CustomerName),
|
|
$"${o.Total:F2}",
|
|
BadgeHtml(o.Status),
|
|
}));
|
|
}
|
|
|
|
private static string BadgeHtml(string status)
|
|
{
|
|
var variant = status switch
|
|
{
|
|
"paid" => "default",
|
|
"pending" => "secondary",
|
|
"cancelled" => "destructive",
|
|
_ => "outline",
|
|
};
|
|
var buf = new System.Buffers.ArrayBufferWriter<byte>();
|
|
new Components.Badge(status, variant).Render(new HtmxRenderContext(buf));
|
|
return System.Text.Encoding.UTF8.GetString(buf.WrittenSpan);
|
|
}
|
|
|
|
protected override void RenderOrdersTable(HtmxRenderContext ctx)
|
|
=> _table.Render(ctx.Next());
|
|
}
|
|
```
|
|
|
|
**GET handler**
|
|
```csharp
|
|
[Handler]
|
|
[MapGet("/orders")]
|
|
public static partial class GetOrdersHandler
|
|
{
|
|
public record Query();
|
|
|
|
private static Task<IResult> HandleAsync(
|
|
Query _,
|
|
HttpContext ctx,
|
|
CancellationToken ct)
|
|
{
|
|
// Replace with real data source
|
|
var orders = new[]
|
|
{
|
|
new Order("ORD-001", "Alice Smith", 42.00m, "paid"),
|
|
new Order("ORD-002", "Bob Jones", 18.50m, "pending"),
|
|
new Order("ORD-003", "Carol White", 99.99m, "cancelled"),
|
|
};
|
|
return ctx.WriteHtmxPage(new OrdersPage(orders), title: "Orders");
|
|
}
|
|
}
|
|
```
|