Themes and presets
Set the whole app's look in one place: IzzyTheme in App.razor's head takes a
preset, then your brand color, grays, radius, density and fonts over it. It renders on the server, so the theme is
there on first paint, with no script and in every render mode.
Set the theme
Put one IzzyTheme in the head, after the stylesheet:
<link rel="stylesheet" href="Assets["_content/IzzyBlazor.Core/izzyblazor.css"]" />
<IzzyTheme Preset="ThemePreset.Soft" Brand="#0a7d5a" Radius="ThemeRadius.Full" />Each setting beats the preset's own; leave it unset to take the preset's. With none at all you get the Standard preset in blue on neutral grays.
Presets
Use Preset for a starting set of defaults: density, shape, type and colors that suit a kind of app.
Preview
Standard
The default: 36px controls, a medium radius and hairline shadows.
Compact
For dense data and admin: 32px controls, tight rows, a small radius, borders only.
Soft
For consumer apps and portals: 40px controls, roomy, a large radius, gentle shadows.
Editorial
For reading and docs: serif headings over a plain sans, a large radius.
Code
@* The same card under each preset, described by its card. On part of a page a preset sets density,
shape and depth only, so each card also takes its preset's brand and gray, and the Editorial card
sets its fonts with Style (fonts are app-wide, so the preset alone wouldn't change them here). *@
<Grid Columns="new() { Base = 1, Sm = 2 }" Gap="4" Grow>
foreach (var set in Sets)
{
<IzzyTheme Preset="set.Preset" Brand="set.Brand" Gray="set.Gray" Style="set.Style">
<Card Title="set.Preset.ToString()" Description="set.Description" Height="Length.Full">
<Row Gap="2" Wrap Align="Align.Center">
<Button>Invite</Button>
<Button Variant="ButtonVariant.Outline" Accent="Accent.Neutral">Later</Button>
<Badge Status="Status.Success">Active</Badge>
</Row>
</Card>
</IzzyTheme>
}
</Grid>
{
private sealed record PresetSet(ThemePreset Preset, string Brand, string Gray, string Description, string? Style = null);
private static readonly PresetSet[] Sets =
[
new(ThemePreset.Standard, "blue", "neutral", "The default: 36px controls, a medium radius and hairline shadows."),
new(ThemePreset.Compact, "indigo", "slate", "For dense data and admin: 32px controls, tight rows, a small radius, borders only."),
new(ThemePreset.Soft, "teal", "neutral", "For consumer apps and portals: 40px controls, roomy, a large radius, gentle shadows."),
new(ThemePreset.Editorial, "ink", "stone", "For reading and docs: serif headings over a plain sans, a large radius.",
"font-family: \"IBM Plex Sans\", system-ui, sans-serif; --ib-font-heading: Newsreader, Georgia, \"Times New Roman\", serif"),
];
}On part of a page a preset doesn't bring its brand and gray, so each card sets them too. Fonts are app-wide, so Editorial's don't show here.
| Preset | For | Controls | Shape | Type | Brand | Gray |
|---|---|---|---|---|---|---|
Standard | Internal tools, line-of-business | 36px | Medium radius, hairline shadow | Inter | Blue | Neutral |
Compact | Admin, ERP, finance, data grids | 32px, tight rows | Small radius, borders only | Inter | Indigo | Slate |
Soft | Consumer apps, education, health | 40px, roomy | Large radius, gentle shadow | Inter | Teal | Neutral |
Editorial | Docs, publishing, knowledge bases | 36px | Large radius | IBM Plex Sans, Newsreader headings | Ink | Stone |
Editorial's fonts aren't served with IzzyBlazor: load them yourself, as under Fonts.
Your brand color
Use Brand with a hex color, or the name of a built-in one. It becomes the primary accent: primary
buttons, links, focus rings, the field halo, selected rows, switches and sliders.
Preview
Code
@* The built-in brand colors, each tuned so text holds 4.5:1 on its fill and on every gray. *@
<Stack Gap="3" Grow>
foreach (var brand in Names)
{
<IzzyTheme Brand="brand">
<Row Gap="3" Align="Align.Center" Wrap>
<Code Width="4.Rem">brand</Code>
<Button Size="ControlSize.Sm">Save</Button>
<Button Size="ControlSize.Sm" Variant="ButtonVariant.Soft">Preview</Button>
<Anchor Href="#">A link</Anchor>
<Badge Accent="Accent.Primary">New</Badge>
</Row>
</IzzyTheme>
}
</Stack>
{
private static readonly string[] Names = ["blue", "ink", "indigo", "violet", "teal", "green", "rose"];
}From a hex color IzzyBlazor works out the fill, the text on it, the color as text and a soft tint, in light and dark, each checked for contrast. In light mode the fill is your color, unless no text can reach 4.5:1 on it; then only its lightness moves. In dark mode a dark color is lifted so controls still show. To set the shades yourself, see Brand shades by hand.
Grays
Use Gray with neutral (the default), slate (cool) or stone
(warm, for serif headings), or a hex color for your light page. From a hex color the surfaces, fills, borders and
text are worked out in its hue, with the text checked for contrast and a dark set to match. A very light or dark
page color is kept light enough for text to read.
<IzzyTheme Gray="slate" />
@* or your light page color; app-wide *@
<IzzyTheme Gray="#f4efe6" />Radius and density
Use Radius to round everything in proportion: None, Sm, Md (the
default), Lg, or Full for pill-shaped controls. Use Density for control
heights, table rows and card padding: Compact (32px), Standard (36px) or
Comfortable (40px).
Fonts
Body text is Inter, served with IzzyBlazor: browsers download only the character sets a page uses, and nothing
once you name another font. Use BodyFont, HeadingFont and MonoFont for your
own fonts, and HeadingWeight for the headings' weight. Load any font other than Inter yourself:
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@400;600;700&display=swap" />
<IzzyTheme BodyFont="Sora, system-ui, sans-serif" HeadingWeight="700" />The Editorial preset uses IBM Plex Sans and Newsreader:
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=Newsreader:opsz,wght@6..72,500..600&display=swap" />App-wide or part of a page
Fonts, HeadingWeight, a hex Gray and Scrollbars (whether the page's own
scrollbar is themed too) are app-wide: set them on the IzzyTheme in the head. The rest
also work on part of a page; see Theme part of a page.
For a strict Content-Security-Policy, pass your nonce as Nonce: hex colors and fonts come in a small
style block.
Next steps
- Light and dark: the default scheme and a switch for readers.
- Tokens and customizing: exact values with CSS variables.
- IzzyTheme API: every parameter.