# Card A styled container with optional header (title + description) and footer sections. The body content is always rendered; header and footer are conditionally included. --- ## HTML structure ``` div.rounded-lg.border.border-border.bg-card.text-card-foreground.shadow-sm.{extraClasses} div.flex.flex-col.space-y-1.5.p-6 ← header (omitted when no title/description) h3.text-2xl.font-semibold ← title p.text-sm.text-muted-foreground ← description div.p-6.pt-0 ← content (always present) {content} div.flex.items-center.p-6.pt-0 ← footer (omitted when empty) {footer} ``` --- ## CSS mechanics | Class | Effect | |---|---| | `bg-card text-card-foreground` | Pulls from CSS variables — dark mode works automatically | | `rounded-lg border border-border shadow-sm` | Subtle rounded box with border and drop shadow | | `p-6 pt-0` on content | Full padding except top (header provides the top spacing) | | `space-y-1.5` on header | Controlled gap between title and description | --- ## Constructor signature ```csharp public Card( string content, string title = "", string description = "", string footer = "", string extraClasses = "") ``` | Parameter | Description | |---|---| | `content` | Raw HTML for the card body (always rendered) | | `title` | Optional heading in the header area | | `description` | Optional subheading below the title | | `footer` | Optional raw HTML in the footer area | | `extraClasses` | Additional Tailwind classes on the outer `div` | --- ## Usage examples ### Simple content card ```csharp new Card(content: "

Your subscription renews on July 1.

") ``` ### Card with title and description ```csharp new Card( content: "

Manage your billing details and invoices.

", title: "Billing", description: "Your current plan: Pro") ``` ### Card with footer actions ```csharp new Card( content: "

Are you sure you want to cancel your account?

", title: "Delete account", description: "This action cannot be undone.", footer: """ """) ``` ### Constrained width ```csharp new Card( content: "

Hello world

", title: "Welcome", extraClasses: "max-w-sm mx-auto") ``` ### Embedding a component as content ```csharp // Render a Badge to a string then embed in the card body var writer = new System.Buffers.ArrayBufferWriter(); new Badge("Active").Render(new HtmxRenderContext(writer)); var badgeHtml = System.Text.Encoding.UTF8.GetString(writer.WrittenSpan); new Card( content: $"

Status:

{badgeHtml}", title: "Account") ``` --- ## Tips and tricks - `content`, `footer`, title, and description are inserted as raw HTML — HTML-encode any user-supplied strings before passing them in. - Use `extraClasses` to set max-width, margin, or custom background without subclassing. - If you need a completely custom header layout, omit `title` and `description` and build the header HTML in `content`, adding `p-6` padding yourself. - Cards can be placed in a CSS grid: `
`. - Cards can be placed in a CSS grid: `
`. --- ## 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); ```