FluentCss 0.3.1
dotnet add package FluentCss --version 0.3.1
NuGet\Install-Package FluentCss -Version 0.3.1
<PackageReference Include="FluentCss" Version="0.3.1" />
<PackageVersion Include="FluentCss" Version="0.3.1" />
<PackageReference Include="FluentCss" />
paket add FluentCss --version 0.3.1
#r "nuget: FluentCss, 0.3.1"
#:package FluentCss@0.3.1
#addin nuget:?package=FluentCss&version=0.3.1
#tool nuget:?package=FluentCss&version=0.3.1
FluentCss
Utility-first CSS builder for .NET — generate Tailwind-inspired utility CSS classes on-the-fly from C# code.
⚠️ Pre-release software. FluentCss is in active development and has not reached a stable release. The public API — interface shapes, method names, generated class names and CSS output — may change between versions without a major-version bump. Pin an exact version and read the release notes before upgrading.
Using an AI coding agent? See the agent usage guide: AGENTS.md.
Overview
FluentCss lets you build CSS utility classes directly in .razor/.cshtml files using a fluent, chainable API. Instead of writing CSS by hand, you compose styles with type-safe method calls — and FluentCss automatically generates the CSS and class names.
- Fluent builder API — chain methods like
.Background(Color.Red500).Color(Color.White).P(4) - Full color palette — 22 color families (red, blue, green, slate, etc.), 11 shades each, plus black/white (taken from TailwindCss)
- Pseudo-classes —
Hover(...),Focus(...),Active(...), and customPseudo("...", ...) - Theming / dark mode —
FluentCssBuilder.DarkorFluentCssBuilder.Theme("dark") - Responsive design —
MinMd(...),MaxMd(...), and customMedia(...)queries - Automatic CSS output — classes are deduplicated and the final CSS is available as Css, build artifact or Razor component
- Blazor/Razor integration — drop
<CssStyles />into your layout and get live CSS injection
Packages
| Package | Target | Description |
|---|---|---|
| FluentCss.Core | netstandard2.0 |
Core builder, registry, and all CSS utilities |
| FluentCss | net10.0 |
Blazor component <CssStyles>, the MVC/Razor Pages tag helpers and the preflight stylesheet |
| FluentCss.Middleware | net10.0 |
ASP.NET Core middleware and the AddFluentCss() / UseFluentCss() extensions |
Installation
dotnet add package FluentCss # For Blazor and MVC/Razor Pages tag helpers
dotnet add package FluentCss.Middleware # For MVC/Razor Pages middleware
Nuget.org links: FluentCss, FluentCss.Middleware.
Quick Start
1. Blazor (Razor Components)
Add the following directives to your _Imports.razor for a cleaner syntax:
@using static FluentCss.Core.FluentCssBuilder
@using FluentCss.Core
Add <CssStyles /> ad preflight to your layout (e.g., App.razor or MainLayout.razor):
<link rel="stylesheet" href="_content/FluentCss/preflight.css" />
<CssStyles />
Preflight is licensed by Tailwind under MIT license.
Then build classes anywhere in your components using the shorthand @Css and @Dark:
<p class="
@Css.Background(Color.Slate400).Color(Color.Green400)
.Hover(h => h.Background(Color.Black).Color(Color.White))
@Css.MinMd(md => md.Color(Color.Green800)
.Hover(h => h.Color(Color.Gray400)))
@Dark.Background(Color.Black).Color(Color.White)
.Hover(h => h.Background(Color.Red300).Color(Color.Red800))">
Hello, world!
</p>
The generated HTML class attribute will be something like:
bg-slate-400 text-green-400 hover:bg-black hover:text-white max-md:text-green-800 max-md:hover:text-gray-400 dark:bg-black dark:text-white dark:hover:bg-red-300 dark:hover:text-red-800
And FluentCss automatically emits the corresponding CSS into the page.
Alternativly you can place styles inside a class:
using FluentCss.Core;
using static FluentCss.Core.FluentCssBuilder;
public static class MyStyles
{
public static string MyElement { get; } =
$"""
{Css.Background(Color.Slate400).Color(Color.Green400).Hover(h => h.Background(Color.Black).Color(Color.White))}
{Css.MinMd(md => md.Color(Color.Green800).Hover(h => h.Color(Color.Gray400)))}
{Dark.Background(Color.Black).Color(Color.White).Hover(h => h.Background(Color.Red300).Color(Color.Red800))}
""";
}
and use it as
<p class="@MyStyles.MyElement">
Test
</p>
⚠️ Important: The inline
@Css.Chain().Method()syntax only works on HTML elements (<div>,<p>,<a>, etc.). Blazor components (<NavLink>,<InputText>, etc.) do not support complex C# expressions in their attributes. For component attributes, precompute the class string in a@codeblock or a static class (as shown above) and pass a simple variable:@* ❌ Does NOT work on components *@ <NavLink class="@Css.Background(Color.Blue500).Color(Color.White)">...</NavLink> @* ✅ Works on components *@ <NavLink class="@NavLinkClass">...</NavLink> @code { private static string NavLinkClass = Css.Background(Color.Blue500).Color(Color.White).ToString(); }
Blazor without the component: A server-hosted Blazor app can also use the middleware and script-link path described in the next section. The tag helpers ship in the
FluentCsspackage next to<CssStyles>; the middleware itself is inFluentCss.Middleware. The script path keeps the stylesheet in sync after client-side DOM changes, which suits Blazor Server and Blazor Web Apps.
2. ASP.NET Core MVC / Razor Pages
Add the FluentCss package for the tag helpers and FluentCss.Middleware for the middleware, then register it in Program.cs. It serves the current hash at /fluentcss/hash and the stylesheet at /fluentcss/{hash}.css, with gzip and an ETag:
builder.Services.AddFluentCss();
...
app.UseRouting();
app.UseFluentCss(); // serve /fluentcss/hash and /fluentcss/{hash}.css
app.UseAuthorization();
Register the tag helper and the namespaces in _ViewImports.cshtml (or Pages/_ViewImports.cshtml for Razor Pages):
@using static FluentCss.Core.FluentCssBuilder
@using FluentCss.Core
@addTagHelper *, FluentCss
Finally add the tag helper and, optionally, the Tailwind CSS reset (preflight) to your layout:
<head>
...
<link rel="stylesheet" href="/_content/FluentCss/preflight.css" />
<fluent-css />
</head>
The tag helper renders the stylesheet <link> and a small script that keeps it in sync after client-side DOM changes, such as a partial view loaded over AJAX.
3. Non-Blazor / Any .NET App
using FluentCss.Core;
// Create a root builder (registers classes in the default registry)
var classes = FluentCssBuilder.Css
.Background(Color.Emerald500)
.Color(Color.White)
.Flex(FlexDirection.Row, Justify.Between, Align.Center)
.Px(6).Py(3)
.RoundedMd();
// Get the class string for your HTML
string classAttr = classes.ToString();
// => "bg-emerald-500 text-white flex flex-row justify-between items-center px-6 py-3 rounded-md"
// Get the auto-generated CSS
string css = ClassRegistry.DefaultRegistry.Css;
Builder API
Entry Points
FluentCssBuilder.Css // standard builder
FluentCssBuilder.Dark // dark theme builder (prefix: "dark:")
FluentCssBuilder.Theme(name) // custom theme builder (prefix: "theme-{name}:")
Layout
| Method | CSS |
|---|---|
.Block() |
display: block |
.InlineBlock() |
display: inline-block |
.Inline() |
display: inline |
.Flex() |
display: flex |
.Grid() |
display: grid |
.Hidden() |
display: none |
.Relative() |
position: relative |
.Absolute() |
position: absolute |
.Fixed() |
position: fixed |
.Sticky() |
position: sticky |
.Inset(n) |
inset (n × 0.25rem) |
.Top(n) / .Bottom(n) / .Left(n) / .Right(n) |
positional offsets |
.Z(n) |
z-index |
Flexbox
// Individual methods
.FlexRow() .FlexCol()
.FlexWrap() .ItemsCenter() .ItemsStart() .ItemsEnd()
.JustifyCenter() .JustifyBetween() .JustifyStart() .JustifyEnd()
.Gap(n) .GapX(n) .GapY(n)
.Grow() .Shrink()
// Composite method
.Flex(FlexDirection.Row, Justify.Center, Align.Center, gap: 4)
FlexDirection: Row, RowReverse, Col, ColReverse
Justify: Start, End, Center, Between, Around, Evenly
Align: Start, End, Center, Baseline, Stretch
FlexWrapValue: Wrap, NoWrap, WrapReverse
Grid
.GridCols(n) .GridRows(n)
.ColSpan(n) .RowSpan(n)
.ColStart(n)
Spacing
Spacing scale: n × 0.25rem. E.g., P(4) = 1rem.
| Method | CSS |
|---|---|
.P(n) / .Px(n) / .Py(n) |
padding / padding-inline / padding-block |
.Pt(n) / .Pr(n) / .Pb(n) / .Pl(n) |
directional padding |
.M(n) / .Mx(n) / .My(n) |
margin / margin-inline / margin-block |
.Mt(n) / .Mr(n) / .Mb(n) / .Ml(n) |
directional margin |
.MxAuto() / .MyAuto() |
auto margins |
.SpaceX(n) / .SpaceY(n) |
space between child elements |
.DivideX(n) / .DivideY(n) / .DivideColor(Color) |
borders between child elements |
Sizing
.W(n) .WFull() .WScreen() .WAuto()
.WFit() .WMin() .WMax()
.H(n) .HFull() .HScreen()
.HFit() .HMin() .HMax()
.MaxW(Size.Md) .MinW(Size.Sm)
.MaxH(Size.Lg) .MinH(Size.Zero)
Predefined sizes: Size.Sm, Size.Md, Size.Lg, Size.Xl, Size.Xxl, Size.Xxxl, Size.Full, Size.ScreenH, Size.ScreenW, Size.Zero, Size.Min, Size.Max, Size.Fit.
Typography
.TextXs() .TextSm() .TextBase()
.TextLg() .TextXl() .Text2Xl() .Text3Xl()
.Text4Xl() .Text5Xl() .Text6Xl() .Text7Xl() .Text8Xl() .Text9Xl()
.FontThin() .FontNormal() .FontBold()
.TextCenter() .TextLeft() .TextRight() .TextEnd()
.Underline() .NoUnderline()
.Italic() .NotItalic()
.Uppercase() .Lowercase() .Capitalize()
.Leading(n) .WhitespaceNoWrap()
.DecorationColor(Color.Indigo500) .Decoration(2)
.DecorationSolid() .DecorationWavy() .UnderlineOffset(4)
.AlignMiddle() .AlignTop() .AlignBaseline()
Colors
.Background(Color.Red500) // background-color
.Color(Color.White) // color
.BorderColor(Color.Slate200) // border-color
22 color families × 11 shades (50–950) + Black and White.
Examples: Color.Red50, Color.Blue700, Color.Emerald400, Color.Slate950.
Borders
.Border() // 1px solid
.Border(2) // 2px width
.Border(2, Color.Slate300, BorderStyle.Dashed)
.BorderSolid() .BorderDashed() .BorderDotted()
.Rounded() .RoundedSm() .RoundedMd() .RoundedLg() .RoundedFull()
.RoundedXl() .Rounded2Xl() .Rounded3Xl()
.RoundedT() .RoundedR() .RoundedB() .RoundedL() // two corners each
.RoundedTL() .RoundedTR() .RoundedBR() .RoundedBL() // one corner each
.Ring(2, Color.Blue200) // ring (box-shadow based outline)
.Outline(2) .OutlineHidden() .OutlineSolid() .OutlineDashed()
Effects & Utilities
.Shadow() .ShadowMd() .ShadowLg()
.Opacity(50) // 0-100 → opacity: 0.5
.CursorPointer()
.OverflowAuto() .OverflowHidden() .OverflowScroll()
.OverflowXAuto() .OverflowYAuto() .OverflowXHidden() .OverflowYHidden()
.Container() // full width + responsive max-widths
.ObjectCover() .ObjectContain()
.AspectSquare() .AspectVideo()
// Transitions
.Transition() .TransitionColors() .Duration(300) .EaseInOut()
// Transforms (set transform directly; they do not compose)
.Rotate(45) .Scale(50) .TranslateX(4) .OriginTop() .TransformNone()
// Filters (set filter directly; they do not compose)
.Blur() .Grayscale() .Brightness(50) .DropShadowLg() .FilterNone()
Pseudo-classes
.Hover(h => h.Background(Color.Blue600))
.Focus(f => f.Ring(2, Color.Blue300))
.Active(a => a.Background(Color.Blue700))
.Pseudo("nth-child(2)", x => x.Background(Color.Gray100))
Pseudo-elements use the same shape and emit a double colon:
.Placeholder(p => p.Color(Color.Neutral400)) // ::placeholder
.Before(b => b.Background(Color.Red500)) // ::before
.After(a => a.Underline()) // ::after
.Marker(m => m.Color(Color.Blue500)) // ::marker
.Selection(s => s.Background(Color.Blue200)) // ::selection
.File(f => f.RoundedMd()) // ::file-selector-button
.Pseudo("part(label)", ...), Backdrop(...), FirstLetter(...) and
FirstLine(...) cover the remaining pseudo-elements. Pseudo(...) decides
between one and two colons from the name; a leading : / :: forces it.
Media Queries (responsive)
FluentCssBuilder.Css
.Background(Color.Red500)
.MinMd(m => m.Background(Color.Blue500)) // @media (width > 48rem)
.MaxMd(m => m.Background(Color.Green500)); // @media (width < 48rem)
Custom media queries:
.Media("min-width: 1024px", "lg", builder =>
{
builder.Flex(FlexDirection.Row);
builder.P(8);
});
Theming / Dark Mode
// Use the dark theme entry point
string darkClasses = FluentCssBuilder.Dark
.Background(Color.Slate800)
.Color(Color.Slate100)
.ToString();
// => "dark:bg-slate-800 dark:text-slate-100"
// Or inline theming
string themeClasses = FluentCssBuilder.Theme("halloween")
.Background(Color.Orange500)
.ToString();
// => "theme-halloween:bg-orange-500"
Getting the CSS Output
The ClassRegistry aggregates all utility classes and generates the final CSS:
string css = ClassRegistry.DefaultRegistry.Css;
In Blazor, the <CssStyles /> component subscribes to ClassRegistry.DefaultRegistry.OnClassAdded and automatically updates the rendered <style> block in real time. It also implements IDisposable to clean up the event subscription.
How It Works
- You call fluent builder methods (e.g.,
.Background(Color.Red500)) - Each call creates a
UtilityClassinstance (CSS property + value + generated class name) - The class is added to a
ClassRegistry— duplicates are ignored - The registry builds a CSS string automatically
.ToString()on the builder returns the space-separated class names for yourclassattribute
Class Name Format
[media-prefix]:[theme]:[pseudo]:{utility}
Examples:
bg-red-500— plain utilityhover:bg-red-600— hover pseudodark:text-white— dark thememax-md:bg-black— responsive (max-md media prefix)dark:hover:bg-slate-700— combined theme + pseudo
License
BSD Zero Clause License (0BSD) — a public-domain-equivalent license with no restrictions.
Color values adapted stolen from Tailwind CSS (MIT licensed).
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- FluentCss.Core (>= 0.3.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.