Khamsin 0.3.4
See the version list below for details.
dotnet add package Khamsin --version 0.3.4
NuGet\Install-Package Khamsin -Version 0.3.4
<PackageReference Include="Khamsin" Version="0.3.4" />
<PackageVersion Include="Khamsin" Version="0.3.4" />
<PackageReference Include="Khamsin" />
paket add Khamsin --version 0.3.4
#r "nuget: Khamsin, 0.3.4"
#:package Khamsin@0.3.4
#addin nuget:?package=Khamsin&version=0.3.4
#tool nuget:?package=Khamsin&version=0.3.4
Khamsin
Khamsin is a build-time utility-CSS engine for Blazor. Components expose a dedicated Khamsin
parameter; the engine generates a minimal static stylesheet from the classes your app and its used
components actually contain, and merges library defaults with consumer overrides at runtime using
the same registry.
It is written from scratch in C#. It does not run Node, npm or another CSS toolchain, and it uses the widely documented Tailwind-compatible class syntax without being affiliated with Tailwind CSS.
Compatibility
The Tailwind utility and variant API — v3.4 plus the v4 core (masks, 3D transforms, container
queries, negation, v4 shadows, attribute shorthands) — with an optional khamsin.json theme
config. The JS toolchain, config file, plugins and @apply are intentionally replaced by the
closed build-time model. See docs/utilities.md,
docs/config.md and docs/tailwind-compatibility.md.
<PrimaryButton Khamsin="@("px-6")">Save</PrimaryButton>
@inherits KhamsinComponentBase
<button class="@(Defaults + Khamsin)">@ChildContent</button>
@code {
private static readonly KhamsinClass Defaults = "bg-ocean-600 px-4 py-2 rounded-md text-white";
}
The consumer's px-6 wins over the default px-4 at runtime; the stylesheet contains both because
both are statically reachable.
Install
<PackageReference Include="Khamsin" Version="0.3.0" />
The package carries the runtime (lib/net8.0), the analyzer and manifest generator
(analyzers/dotnet/cs), the MSBuild task (tasks/net8.0) and the
buildTransitive props/targets. Reference it from every project whose components use Khamsin classes;
libraries get manifests, the executable or web project gets the stylesheet.
The generated stylesheet also bundles the theme, a Tailwind-compatible preflight, and any CSS listed
under css in khamsin.json, including stylesheets contributed by referenced libraries. Listed
stylesheets are incremental inputs: editing or deleting one regenerates the stylesheet even when no
source file changed.
Serve the generated file like any other static asset:
<link rel="stylesheet" href="khamsin.css" />
For Blazor Server you can serve it from an endpoint instead, which uses an embedded copy with a content hash and a one-year immutable cache policy:
app.MapKhamsinCss();
How the build works
- The source generator reads
.razorfiles asAdditionalFilesand emits a[KhamsinManifest(Classes = "…", Uses = new[] { typeof(A) })]part for every component, plus one assembly-level manifest forKxliterals in helper classes. Generic components (@typeparam) keep their manifest on the real generic type and are referenced as open generics such astypeof(Picker<>). - After
CoreCompile, the MSBuild task opens the consumer assembly and finds the components it renders throughRenderTreeBuilder.OpenComponent<T>metadata. It also scans copy-local implementation assemblies, because reference assemblies omit method bodies; this recovers manifests and component edges from packages, including older packages whose generic components carry manifests on a non-generic stub. - It follows each component's
Usestransitively through referenced assemblies, supplemented by per-typeOpenComponent<T>edges read from IL, adds the consumer's own sources, and writesobj/<configuration>/<tfm>/khamsin.csscontaining only those classes. Components from a library that the app never renders contribute nothing. - The file is registered as a static web asset (and copied to the output for other project types).
The generated theme also registers Khamsin's internal --khamsin-* composition variables with
@property { syntax: "*"; inherits: false }. A transform, filter or mask set on a parent therefore
does not leak into descendants that compose the same variable; a var(…, fallback) alone cannot
prevent inheritance once the variable is set.
Output is minified by default. Setting KhamsinMinify to false writes indented CSS with one
declaration per line; the class set and ordering are identical in both forms.
The task is incremental: a no-op build skips it, and identical CSS is not rewritten.
MSBuild properties
| Property | Default | Purpose |
|---|---|---|
KhamsinEmitCss |
true for web/executable projects, false otherwise |
Emit the stylesheet for this project. |
KhamsinMinify |
true |
Write minified CSS; set false for indented output while debugging. |
KhamsinCssFileName |
khamsin.css |
File name registered as a static asset. |
KhamsinCssOutputPath |
$(IntermediateOutputPath)$(KhamsinCssFileName) |
Override the generated file location. |
KhamsinRegisterStaticWebAsset |
true |
Register the file with the static web asset pipeline. |
KhamsinAddRazorAdditionalFiles |
true |
Feed **/*.razor to the generator. |
KhamsinConfig |
khamsin.json |
Theme/token configuration file; see docs/config.md. |
KhamsinTaskAssembly |
set by the package | Task assembly path; used by repository samples. |
The closed class set
Every class that can render must be a literal in the source:
<div Khamsin="p-4 md:p-6">…</div>
Dynamic choices go through the literal-based helpers, which are equally extractable:
<div Khamsin="@Kx.When(compact, "p-2", "p-4")">…</div>
<div Khamsin="@Kx.Variant(size, ("sm", "px-2"), ("lg", "px-6"))">…</div>
Runtime values use CSS custom properties, not string building:
<div Khamsin="w-(--panel)" style="--panel: 18rem;">…</div>
For clsx-style composition there is Cn.Of(...), which accepts strings, booleans, condition
tuples, nested collections and KhamsinClass values, filters to the truthy parts and resolves
conflicts last-wins — every string in it must still be a literal so the build can extract it:
<div class="@Cn.Of("p-4", compact ? "p-2" : "p-8", (primary, "bg-ocean-600"))">…</div>
Interpolation, concatenation and variables flowing into Khamsin or Kx.* fail the build with
KHM001. See docs/utilities.md for the supported classes and variants and
docs/analyzer-rules.md for the diagnostics.
Repository layout
Khamsin.Core/ netstandard2.0 engine: parser, registry, theme, emitter, merge
Khamsin.Generators/ netstandard2.0 incremental generator, analyzer and code fix
Khamsin.Build/ net8.0 MSBuild task
Khamsin.Blazor/ net8.0 runtime: KhamsinClass, Kx, merge cache, MapKhamsinCss
Khamsin/ packaging-only metapackage
Khamsin.Core.Tests/ engine tests
Khamsin.Generators.Tests/ generator, analyzer and manifest tests
Khamsin.Blazor.Tests/ runtime tests
Khamsin.Build.Tests/ MSBuild integration tests
Sample.Lib/ sample Razor class library
Sample.App/ sample Blazor Server app
docs/ utilities, config, Tailwind compatibility and migration, analyzer rules
Every project lives in the repository root and is listed in Khamsin.slnx.
Building and testing
dotnet build Khamsin.slnx
dotnet test Khamsin.slnx
dotnet pack Khamsin/Khamsin.csproj -c Release
Two environment notes:
- The test projects target
net8.0and referenceMicrosoft.AspNetCore.App. On a machine without the ASP.NET Core 8.0 runtime, run the tests withDOTNET_ROLL_FORWARD=LatestMajor.
Coming from a Tailwind build? docs/tailwind-compatibility.md lists the differences and the migration steps.
License
Apache License 2.0 — see LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
This package has no dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Khamsin:
| Package | Downloads |
|---|---|
|
WaxUI
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
Bundles custom CSS into khamsin.css: khamsin.json's css files and referenced libraries' stylesheets are emitted in the base layer between theme and utilities, so consumers link one stylesheet, including correct merging of referenced library configs. Emits a Tailwind-compatible preflight in the base layer, extracts class attributes from render fragments declared in @code blocks, accepts fully qualified Khamsin.Cn/Khamsin.Kx calls, adds visible/invisible utilities and animate-in fade defaults, and fixes negative fraction utilities (the minus sign was dropped from -translate-x-1/2 style values) plus the attribute emitter, the metadata reader and the internal @property registrations that had gone missing.