@@ -0,0 +1,182 @@
|
||||
# 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");
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user