# Card A bordered box for grouping related content — like a physical card you might hold in your hand. It has three distinct zones: a header (title + subtitle), a body (your content), and a footer (usually actions). --- ## Quick example ```csharp new Card( content: "

Your subscription renews on July 1.

", title: "Billing", description: "Current plan: Pro") ``` --- ## All the options ```csharp public Card( string content, string title = "", string description = "", string footer = "", string extraClasses = "") ``` | Parameter | What it does | |---|---| | `content` | The body of the card — always shown. Raw HTML. | | `title` | Optional bold heading at the top of the card. | | `description` | Optional smaller subtitle below the title. | | `footer` | Optional section at the bottom, typically holding action buttons. Raw HTML. | | `extraClasses` | Additional Tailwind classes on the outer `div` — useful for `max-w-sm`, `col-span-2`, etc. | The header section (title + description) is omitted entirely when both are empty. Same for the footer. --- ## Real-world examples ### A stats card on a dashboard ```csharp new Card( title: "Total Users", description: "All registered accounts", content: $"

{userCount:N0}

") ``` ### A confirmation card with footer buttons Buttons and other components need to be pre-rendered to HTML strings when used inside `content` or `footer`: ```csharp string ToHtml(IHtmxComponent c) { var w = new System.Buffers.ArrayBufferWriter(); c.Render(new HtmxRenderContext(w)); return System.Text.Encoding.UTF8.GetString(w.WrittenSpan); } new Card( title: "Delete account", description: "This action cannot be undone.", content: "

All your data will be permanently removed.

", footer: ToHtml(new Button("Cancel", variant: "outline")) + ToHtml(new Button("Delete", variant: "destructive", type: "submit"))) ``` ### A grid of cards Cards are most commonly placed in a CSS grid in the page template: ```html
$$Card1$$ $$Card2$$ $$Card3$$
``` ### Constrained width (e.g. a login card) ```csharp new Card( content: "...login form HTML...", title: "Welcome back", description: "Sign in to your account", extraClasses: "max-w-sm mx-auto") ``` --- ## How it works Card uses CSS variables (`bg-card`, `text-card-foreground`, `border-border`) which automatically adapt to dark mode. The header and footer sections are skipped entirely in the rendered HTML when they are not needed — they do not leave empty divs behind. All strings passed to `content` and `footer` are raw HTML. HTML-encode any user-supplied values before passing them in. --- ## Complete page example **`Templates/DashboardPage.htmx`** ```html

Dashboard

$$UsersCard$$ $$RevenueCard$$ $$OrdersCard$$
``` **`Templates/DashboardPage.htmx.cs`** ```csharp namespace Htmx.ApiDemo.Templates; public sealed class DashboardPage : DashboardPageBase { private readonly IHtmxComponent _users; private readonly IHtmxComponent _revenue; private readonly IHtmxComponent _orders; public DashboardPage(DashboardStats stats) { _users = new Components.Card( title: "Total users", description: "Registered accounts", content: $"

{stats.UserCount:N0}

"); _revenue = new Components.Card( title: "Revenue", description: "This month", content: $"

${stats.MonthlyRevenue:N2}

"); _orders = new Components.Card( title: "Open orders", description: "Awaiting fulfillment", content: $"

{stats.OpenOrders}

", footer: """View all"""); } protected override void RenderUsersCard(HtmxRenderContext ctx) => _users.Render(ctx.Next()); protected override void RenderRevenueCard(HtmxRenderContext ctx) => _revenue.Render(ctx.Next()); protected override void RenderOrdersCard(HtmxRenderContext ctx) => _orders.Render(ctx.Next()); } ``` **GET handler** ```csharp [Handler] [MapGet("/dashboard")] public static partial class GetDashboardHandler { public record Query(); private static Task HandleAsync( Query _, HttpContext ctx, CancellationToken ct) { var stats = new DashboardStats(UserCount: 1_204, MonthlyRevenue: 48_320.50m, OpenOrders: 37); return ctx.WriteHtmxPage(new DashboardPage(stats), title: "Dashboard"); } } public record DashboardStats(int UserCount, decimal MonthlyRevenue, int OpenOrders); ```