ee8797c142
Co-authored-by: Copilot <copilot@github.com>
208 lines
6.3 KiB
Markdown
208 lines
6.3 KiB
Markdown
# Checkbox
|
||
|
||
A styled checkbox input with an optional visible label. Uses the `accent-primary` Tailwind class so the checkmark color follows your primary theme color.
|
||
|
||
---
|
||
|
||
## HTML structure
|
||
|
||
```
|
||
div.flex.items-center.space-x-2
|
||
input[type=checkbox, id, name, value, class, $$Checked$$]
|
||
label[for={id}] ← omitted when label is empty
|
||
{label text}
|
||
```
|
||
|
||
---
|
||
|
||
## CSS mechanics
|
||
|
||
| Class | Effect |
|
||
|---|---|
|
||
| `accent-primary` | Checkmark color follows the `--color-primary` CSS variable |
|
||
| `h-4 w-4 rounded` | Consistent 16×16 size with slightly rounded corners |
|
||
| `cursor-pointer` | Pointer cursor on label |
|
||
| `text-sm font-medium leading-none peer-disabled:opacity-70` | Standard label styling |
|
||
|
||
---
|
||
|
||
## Constructor signature
|
||
|
||
```csharp
|
||
public Checkbox(
|
||
string id,
|
||
string label = "",
|
||
string name = "",
|
||
string value = "true",
|
||
bool @checked = false)
|
||
```
|
||
|
||
| Parameter | Description |
|
||
|---|---|
|
||
| `id` | Element id and the `for` attribute on the label |
|
||
| `label` | Visible text next to the checkbox; omit for a standalone checkbox |
|
||
| `name` | Form field name (required when used in a form) |
|
||
| `value` | Submitted value when checked (default: `"true"`) |
|
||
| `checked` | Pre-checked state |
|
||
|
||
---
|
||
|
||
## Usage examples
|
||
|
||
### Basic opt-in checkbox
|
||
|
||
```csharp
|
||
new Checkbox(
|
||
id: "newsletter",
|
||
label: "Subscribe to newsletter",
|
||
name: "newsletter")
|
||
```
|
||
|
||
### Pre-checked
|
||
|
||
```csharp
|
||
new Checkbox(
|
||
id: "remember",
|
||
label: "Remember me",
|
||
name: "rememberMe",
|
||
checked: true)
|
||
```
|
||
|
||
### No visible label
|
||
|
||
```csharp
|
||
new Checkbox(id: "select-all", name: "selectAll")
|
||
```
|
||
|
||
### Custom submitted value
|
||
|
||
```csharp
|
||
new Checkbox(
|
||
id: "agree",
|
||
label: "I agree to the terms",
|
||
name: "terms",
|
||
value: "accepted")
|
||
```
|
||
|
||
### Reading in a form handler
|
||
|
||
```csharp
|
||
public record Command(
|
||
[property: FromForm] string? Newsletter = null, // null when unchecked
|
||
[property: FromForm] string? RememberMe = null
|
||
);
|
||
|
||
bool wantsNewsletter = command.Newsletter == "true";
|
||
bool rememberUser = command.RememberMe == "true";
|
||
```
|
||
|
||
> Note: Unchecked checkboxes are not included in form data. Always use a nullable string or a default value of `null`.
|
||
|
||
---
|
||
|
||
## Tips and tricks
|
||
|
||
- Because HTML forms only submit checked checkboxes, pair a checkbox with a hidden input of the same name and value `"false"` if you need the unchecked state explicitly in your command.
|
||
- The label `for` attribute ties to the `id`, so clicking the label text toggles the checkbox — always set `id`.
|
||
- If you need multi-select (select multiple rows in a table), use the same `name` for all checkboxes; they will be submitted as a comma-separated list or multiple values depending on form binding.
|
||
- `accent-primary` is a modern CSS property — all current browsers support it.
|
||
- `accent-primary` is a modern CSS property — all current browsers support it.
|
||
|
||
---
|
||
|
||
## Complete page example
|
||
|
||
**`Templates/PreferencesPage.htmx`**
|
||
```html
|
||
<div class="max-w-md mx-auto py-10">
|
||
<h1 class="text-2xl font-bold mb-6">Preferences</h1>
|
||
<form method="post" action="/preferences">
|
||
$$AntiforgeryToken$$
|
||
<div class="space-y-4 mb-6">
|
||
$$NewsletterCheck$$
|
||
$$MarketingCheck$$
|
||
$$RememberCheck$$
|
||
</div>
|
||
$$SaveBtn$$
|
||
</form>
|
||
$$SuccessAlert$$
|
||
</div>
|
||
```
|
||
|
||
**`Templates/PreferencesPage.htmx.cs`**
|
||
```csharp
|
||
namespace Htmx.ApiDemo.Templates;
|
||
|
||
public sealed class PreferencesPage : PreferencesPageBase
|
||
{
|
||
private readonly IHtmxComponent _newsletter;
|
||
private readonly IHtmxComponent _marketing;
|
||
private readonly IHtmxComponent _remember;
|
||
private readonly IHtmxComponent _save;
|
||
private readonly IHtmxComponent _success;
|
||
private readonly byte[] _afToken;
|
||
|
||
public PreferencesPage(
|
||
IAntiforgery af,
|
||
HttpContext ctx,
|
||
UserPrefs? prefs = null,
|
||
bool saved = false)
|
||
{
|
||
var tokens = af.GetAndStoreTokens(ctx);
|
||
_afToken = $"""<input type="hidden" name="{tokens.FormFieldName}" value="{tokens.RequestToken}">""".ToUtf8Bytes();
|
||
|
||
_newsletter = new Components.Checkbox(
|
||
id: "newsletter", label: "Receive newsletter",
|
||
name: "newsletter", @checked: prefs?.Newsletter ?? false);
|
||
_marketing = new Components.Checkbox(
|
||
id: "marketing", label: "Receive marketing emails",
|
||
name: "marketing", @checked: prefs?.Marketing ?? false);
|
||
_remember = new Components.Checkbox(
|
||
id: "remember", label: "Keep me signed in",
|
||
name: "remember", @checked: prefs?.Remember ?? false);
|
||
_save = new Components.Button("Save preferences", type: "submit");
|
||
_success = saved
|
||
? new Components.Alert(title: "Preferences saved.")
|
||
: HtmxEmpty.Instance;
|
||
}
|
||
|
||
protected override void RenderAntiforgeryToken(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_afToken);
|
||
protected override void RenderNewsletterCheck(HtmxRenderContext ctx) => _newsletter.Render(ctx.Next());
|
||
protected override void RenderMarketingCheck(HtmxRenderContext ctx) => _marketing.Render(ctx.Next());
|
||
protected override void RenderRememberCheck(HtmxRenderContext ctx) => _remember.Render(ctx.Next());
|
||
protected override void RenderSaveBtn(HtmxRenderContext ctx) => _save.Render(ctx.Next());
|
||
protected override void RenderSuccessAlert(HtmxRenderContext ctx) => _success.Render(ctx.Next());
|
||
}
|
||
```
|
||
|
||
**POST handler**
|
||
```csharp
|
||
[Handler]
|
||
[MapPost("/preferences")]
|
||
public static partial class PostPreferencesHandler
|
||
{
|
||
public record Command(
|
||
[property: FromForm] string? Newsletter = null,
|
||
[property: FromForm] string? Marketing = null,
|
||
[property: FromForm] string? Remember = null);
|
||
|
||
private static Task<IResult> HandleAsync(
|
||
[AsParameters] Command cmd, HttpContext ctx, IAntiforgery af, CancellationToken ct)
|
||
{
|
||
var prefs = new UserPrefs(
|
||
Newsletter: cmd.Newsletter != null,
|
||
Marketing: cmd.Marketing != null,
|
||
Remember: cmd.Remember != null);
|
||
// Persist prefs…
|
||
return ctx.WriteHtmxPage(new PreferencesPage(af, ctx, prefs, saved: true), title: "Preferences");
|
||
}
|
||
}
|
||
|
||
public record UserPrefs(bool Newsletter, bool Marketing, bool Remember);
|
||
```
|
||
|
||
**`AppJsonSerializerContext.cs`**
|
||
```csharp
|
||
[JsonSerializable(typeof(PostPreferencesHandler.Command), TypeInfoPropertyName = "PreferencesCommand")]
|
||
```
|