FluentCss documentation

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

Blazor does not allow complex C# expressions in the attributes of a component. The inline chain below will not compile. Precompute the class string and pass a simple variable instead.
@* 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