Skip to content
Home Theme Gallery

ASP.NET Core

Home-UI is server-rendered HTML plus two static files, so it drops into ASP.NET Core with no client build: a Razor layout loads the pair, a view writes the elements, and model binding reads their values on POST.

In Views/Shared/_Layout.cshtml, or the layout a Razor Page uses:

<!doctype html>
<html lang="en" class="@(Model.Theme ?? "light")">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="https://home-ui.harriverse.com/dist/latest/home-ui.css" />
<script type="module" src="https://home-ui.harriverse.com/dist/latest/home-ui.js"></script>
</head>
<body class="hui-surface">
@RenderBody()
</body>
</html>

Theme is "light" or "dark", read from the request server-side, so there is no flash of the wrong theme. hui-surface opts the page into the tokens; see Dark mode.

Those URLs load the current build from this site. For something you ship, download the files and serve them yourself - see Getting the files.

A component is configured entirely by attributes, so Razor writes them like any other tag:

<hui-field>
<span data-label>Project name</span>
<hui-input data-control name="Project"
value="@Model.Form.Project"
aria-invalid="@(Model.Errors.ContainsKey("Project") ? "true" : null)"></hui-input>
@if (Model.Errors.TryGetValue("Project", out var error))
{
<span data-error>@error</span>
}
</hui-field>

Razor omits an attribute whose value is null, so aria-invalid appears only when the field is invalid. hui-field wires the label and the error to the control for you.

Every form-associated control submits under its name, so ordinary model binding picks the values up with nothing special:

public class ProjectForm
{
public string? Project { get; set; }
public string? Volume { get; set; }
}
[BindProperty]
public ProjectForm Form { get; set; } = new();
public IActionResult OnPost()
{
if (!ModelState.IsValid)
return Page(); // re-render with the errors
Save(Form);
return RedirectToPage("./Index");
}

Volume, from hui-slider, arrives as a string exactly as a native field would.

Business validation is the server’s. On a failed POST, re-render the same view with the submitted values and an error per field; the template above then marks the control aria-invalid and wires the message to it. The client does not reconstruct that.

<form method="post">
@Html.AntiForgeryToken()
<!-- hui-field, hui-input and the rest -->
<hui-button type="submit">Save</hui-button>
</form>

hui-button type="submit" drives the ancestor form, so it can replace <button type="submit"> wherever you want the component’s styling.

To update part of the page, return a partial from an action and swap it in. Any technique works - fetch, HTMX, or Blazor’s interop; the element is created from the new markup and re-associates with its form. Keep endpoints, tokens and record ids on the server: there is no attribute for them, deliberately.