This site provides a machine-readable index at /llms.txt.

Skip to main content Skip to navigation

Recolor the site

When the site needs a different palette, a tweak to prose rules, or a chunk of site-wide CSS, the knobs below live on MonorailCssOptions. DocSiteOptions and BlogSiteOptions forward ColorScheme, ExtraStyles, and CustomCssFrameworkSettings directly, so most reskins do not need to leave the template. ContentPaths and other non-CSS capabilities still require the bare-AddPennington + AddMonorailCss path — see When is DocSite the right starting point?.

Assumptions

The ServiceConfiguration helpers referenced below are backed by examples/DocSiteKitchenSinkExample.


Options

Pick NamedColorScheme for a Tailwind-named palette

NamedColorScheme maps three MonorailCSS palette slots (primary, accent, base) onto named palettes from MonorailCss.Theme.ColorNames. The simplest re-skin is changing the three *ColorName values on the default options.

/// <summary>
/// A color scheme that uses named Tailwind colors.
/// </summary>
public class NamedColorScheme : IColorScheme
{
    /// <summary>
    /// Gets or sets the color name to map to "primary".
    /// </summary>
    public required ColorName PrimaryColorName { get; init; }
  
    /// <summary>
    /// Gets or sets the color name to map to "accent".
    /// </summary>
    public required ColorName AccentColorName { get; init; }
  
    /// <summary>
    /// Gets or sets the color name to map to "base".
    /// </summary>
    public required ColorName BaseColorName { get; init; }
  
    /// <summary>
    /// Gets or sets additional color mappings beyond the core slots.
    /// Key is the target slot name (e.g., "info", "warning"), value is the source color.
    /// </summary>
    public Dictionary<string, ColorName> AdditionalMappings { get; init; } = [];
  
    /// <inheritdoc />
    public Theme ApplyToTheme(Theme theme)
    {
        theme = theme.MapColorPalette(PrimaryColorName.Value, "primary")
             .MapColorPalette(AccentColorName.Value, "accent")
             .MapColorPalette(BaseColorName.Value, "base");
  
        foreach (var (slot, color) in AdditionalMappings)
            theme = theme.MapColorPalette(color.Value, slot);
  
        return theme;
    }
}

Pick AlgorithmicColorScheme for hue-driven palettes

AlgorithmicColorScheme synthesises primary and accent palettes from one PrimaryHue plus a ColorSchemeGenerator delegate (hue → accent hue), so the whole site repigments by changing a single number. The kitchen-sink helper below shows a plausible generator wired against ColorName.Zinc.

new()
{
    PrimaryHue = 220,
    ColorSchemeGenerator = primary => primary + 140,
    BaseColorName = ColorName.Zinc,
}

Assign the color scheme on the DocSite options

DocSiteOptions.ColorScheme is the forwarded knob — whichever IColorScheme is assigned becomes the seed for the generated stylesheet.

/// <summary>Color scheme driving the MonorailCSS theme. Defaults to the built-in DocSite palette when null.</summary>
public IColorScheme? ColorScheme { get; init; }

Override syntax-highlight colors with SyntaxTheme

MonorailCssOptions.SyntaxTheme holds the five Tailwind palettes used by .hljs-* token classes (keyword, string, variable, function, comment). It is independent of the brand ColorScheme, so code colors can stay consistent while the site reskins, or vice versa. SyntaxTheme.Default ships Sky / Emerald / Rose / Amber / Slate; replace the whole record to substitute your own.

/// <summary>
/// Color palette used by <c>.hljs-*</c> syntax-highlight token classes.
/// Each slot is a Tailwind color name whose shades (300-800) are consumed by light/dark theme rules.
/// </summary>
public sealed record SyntaxTheme
{
    /// <summary>Keywords, class names, literals, selector tags.</summary>
    public required ColorName Keyword { get; init; }
  
    /// <summary>String literals, numbers, regular expressions.</summary>
    public required ColorName String { get; init; }
  
    /// <summary>Variables, attribute names, symbols.</summary>
    public required ColorName Variable { get; init; }
  
    /// <summary>Function/method titles, parameters, built-ins.</summary>
    public required ColorName Function { get; init; }
  
    /// <summary>Comments and quotes. Usually the site's base color.</summary>
    public required ColorName Comment { get; init; }
  
    /// <summary>Default palette: Sky keywords, Emerald strings, Rose variables, Amber functions, Slate comments.</summary>
    public static SyntaxTheme Default { get; } = new()
    {
        Keyword = ColorName.Sky,
        String = ColorName.Emerald,
        Variable = ColorName.Rose,
        Function = ColorName.Amber,
        Comment = ColorName.Slate,
    };
}

Append site-wide rules with ExtraStyles

The ExtraStyles string is emitted verbatim above the generated utility stylesheet. It fits @font-face declarations, utility overrides, or one-off selectors that don't belong in a Razor component. The kitchen-sink helper below combines two font faces with a component-scoped tweak as a realistic reference.

"""
        @font-face {
            font-family: 'DocSiteKitchenSinkDisplay';
            font-style: normal;
            font-weight: 100 900;
            font-display: swap;
            src: url(/fonts/display.woff2) format('woff2');
        }
        @font-face {
            font-family: 'DocSiteKitchenSinkBody';
            font-style: normal;
            font-weight: 100 900;
            font-display: swap;
            src: url(/fonts/body.woff2) format('woff2');
        }
        article .feature-callout-demo { letter-spacing: 0.01em; }
        """

Pass it through on the DocSite options:

/// <summary>Additional CSS appended to the generated stylesheet.</summary>
public string? ExtraStyles { get; init; }

Tweak prose rules with CustomCssFrameworkSettings

DocSiteOptions.CustomCssFrameworkSettings mirrors the MonorailCssOptions delegate — it post-processes the CssFrameworkSettings after the DocSite theme is applied, so it fits prose tweaks, color maps, or apply directives without leaving DocSite. When ContentPaths (the glob list scanned at startup for classes used in non-HTML files) or other capabilities outside DocSite's scope are needed, drop to bare AddPennington + AddMonorailCss; see When is DocSite the right starting point? for the authoritative breakdown.

/// <summary>
/// Callback to further customize the MonorailCSS framework settings after
/// the DocSite theme has been applied. Mirrors
/// <see cref="Pennington.MonorailCss.MonorailCssOptions.CustomCssFrameworkSettings"/>.
/// </summary>
public Func<global::MonorailCss.CssFrameworkSettings, global::MonorailCss.CssFrameworkSettings>? CustomCssFrameworkSettings { get; init; }

Backing options type for the delegate signature and the bare-host escape:

/// <summary>
/// Options for configuring the Monorail CSS framework integration.
/// </summary>
public class MonorailCssOptions
{
    /// <summary>
    /// Gets or sets the color scheme for the site.
    /// The default is a NamedColorScheme with Blue (primary), Purple (accent), and Slate (base).
    /// </summary>
    public IColorScheme ColorScheme { get; init; } = new NamedColorScheme
    {
        PrimaryColorName = ColorName.Blue,
        AccentColorName = ColorName.Purple,
        BaseColorName = ColorName.Slate
    };
  
    /// <summary>
    /// Gets or sets the syntax-highlight color theme.
    /// Controls the Tailwind palettes used by <c>.hljs-*</c> token classes,
    /// independent of the site's brand <see cref="ColorScheme"/>.
    /// </summary>
    public SyntaxTheme SyntaxTheme { get; init; } = SyntaxTheme.Default;
  
    /// <summary>
    /// Gets or sets a function to customize the CSS framework settings.
    /// This allows for advanced customization of the MonorailCSS framework.
    /// </summary>
    public Func<CssFrameworkSettings, CssFrameworkSettings> CustomCssFrameworkSettings { get; init; } =
        settings => settings;
  
    /// <summary>
    /// Gets or sets any extra CSS styles to be included in the generated stylesheet.
    /// </summary>
    public string ExtraStyles { get; init; } = string.Empty;
  
    /// <summary>
    /// Gets or sets file paths (relative to the web root) to scan for CSS class usage at startup.
    /// Similar to Tailwind's <c>content</c> configuration — ensures classes used only in
    /// client-side JS or other non-HTML files are included in the generated stylesheet.
    /// </summary>
    public string[] ContentPaths { get; init; } = [];
}

For a bare AddPennington host the same knob sits on MonorailCssOptions directly; see the Lab's helper:

new()
    {
        ColorScheme = new NamedColorScheme
        {
            PrimaryColorName = ColorName.Sky,
            AccentColorName = ColorName.Emerald,
            BaseColorName = ColorName.Slate,
        },
        CustomCssFrameworkSettings = settings => settings with
        {
            Applies = settings.Applies
                .SetItem(".lab-tabs", "flex flex-col bg-base-50 border border-base-300 rounded-lg")
                .SetItem(".lab-tabs-list", "flex flex-row gap-2 border-b border-base-200 px-3 pt-2")
                .SetItem(".lab-tabs-button", "py-1.5 text-sm text-base-700 data-[selected=true]:text-primary-700 data-[selected=true]:border-b data-[selected=true]:border-primary-600")
                .SetItem(".lab-tabs-panel", "hidden data-[selected=true]:block px-3 py-3"),
        },
    }

Result

Every bg-primary-*, text-accent-*, border-base-* utility on the site resolves to the new palette on the next page load. Code-block tokens recolor independently when SyntaxTheme is set, and any rules from ExtraStyles appear at the top of /styles.css ahead of the generated utilities.

Verify

  • Run dotnet run and visit any page. Inspect a bg-primary-500 element; the rendered color matches the palette set above.
  • Fetch /styles.css and confirm the ExtraStyles block appears above the generated utility rules.
  • When ContentPaths is wired, add a class that only appears in a referenced non-HTML file (such as wwwroot/app.js) and verify it lands in /styles.css on the next reload.