Styling with utility classes
A utility class maps one small piece of CSS. Combine them to build any design without leaving C#.
Compose instead of names
A utility does one thing: set a background, a padding, a display mode, and so on. Instead of inventing a class name for every component, you chain a handful of utilities together.
Card title
A few chained utilities.
<div class="@Css.P(6).RoundedLg().Border().BorderColor(Color.Slate200).ShadowSm()
@Css.Background(Color.White) @Dark.Background(Color.Slate800)
@Css.Hover(h => h.ShadowLg())">
<p class="@Css.FontBold().Color(Color.Slate900) @Dark.Color(Color.White)">Card title</p>
<p class="@Css.TextSm().Color(Color.Slate500) @Dark.Color(Color.Slate400)">A few chained utilities.</p>
</div>
Entry points
Every chain starts with one of three roots. The root decides the variant prefix applied to the whole chain.
| Utility | Generated CSS |
|---|---|
Css |
no prefix, theme-neutral styles |
Dark |
theme-dark: prefix, applies inside the dark theme |
Theme("halloween") |
theme-halloween: prefix, any custom theme |
Chaining
Utility methods return the builder, so calls can be chained or spread over several statements. Each call registers a class immediately.
// One chain, many utilities
var card = FluentCssBuilder.Css
.P(6)
.RoundedLg()
.Background(Color.White)
.ShadowMd();
// Or spread over statements; they register in order
card = card.Color(Color.Slate900);
card = card.Hover(h => h.ShadowLg());
Using the result in Razor
On a plain HTML element you can call the builder inline. FluentCss returns an object whose
ToString() is the class attribute value.
Styled inline on an HTML element.
<p class="@Css.Background(Color.Slate100).Color(Color.Slate800).P(4).RoundedMd()
@Dark.Background(Color.Slate800).Color(Color.Slate100)">
Styled inline on an HTML element.
</p>
Blazor components need a variable
@* Does not compile on a component. *@
<NavLink class="@Css.Background(Color.Blue500).Color(Color.White)">Home</NavLink>
@* Precompute the class string first. *@
<NavLink class="@NavLinkClass">Home</NavLink>
@code {
private static string NavLinkClass =
Css.Background(Color.Blue500).Color(Color.White).ToString()!;
}
A common pattern is to keep reusable class strings in a static class. Because the builder works anywhere, the strings can be cached and shared.
using FluentCss.Core;
using static FluentCss.Core.FluentCssBuilder;
public static class MyStyles
{
public static string PrimaryButton { get; } =
Css.InlineBlock().Px(6).Py(3).RoundedMd().FontBold()
.Background(Color.Blue600).Color(Color.White)
.Hover(h => h.Background(Color.Blue700))
.Focus(f => f.Ring(3, Color.Blue300).OutlineHidden())
.ToString()!;
}
Inside a component the result can also be interpolated with other expressions:
<a href="/" class="@MyStyles.PrimaryButton">Home</a>
@* Mix literals, static styles and inline chains *@
<span class="extra @MyStyles.PrimaryButton @Css.Mb(4)">Mixed</span>
Deduplication
Registering the same utility twice only emits one CSS rule. The registry compares classes by their generated name, so the more often a utility is used the more the stylesheet is reused.
var a = new ClassRegistry();
a.Add(new UtilityClass("p-4", "padding", "1rem"));
a.Add(new UtilityClass("p-4", "padding", "1rem")); // ignored
// a.Css contains exactly one .p-4 rule