Theming

Moka.Red's theming system is built entirely on CSS custom properties. MokaThemeProvider generates a set of --moka-* variables as an inline style on a wrapping <div class="moka-root"> element, making every token available to child components without any runtime style injection or JavaScript.

MokaThemeProvider

Wrap your application (or a subtree) in MokaThemeProvider to activate theming:

<MokaThemeProvider>
    <MokaButton>Hello from themed context</MokaButton>
</MokaThemeProvider>

By default the provider applies the built-in light theme. Every --moka-* variable falls back to a value defined in moka.css when MokaThemeProvider is absent, so components remain functional without it.

Parameters

Parameter Type Default Description
Theme MokaTheme MokaTheme.Light The active theme record
ChildContent RenderFragment Content to wrap

Light and Dark Themes

Built-in Dark Theme

Pass Theme="MokaTheme.Dark" to switch the built-in palette to its dark variant:

<MokaThemeProvider Theme="MokaTheme.Dark">
    <MokaButton>Dark themed button</MokaButton>
</MokaThemeProvider>

The dark palette swaps the primary colour from #d32f2f to #ef5350 and adjusts all surface, background, and text tokens accordingly.

Toggling at Runtime

Bind to a boolean state field to support a user-controlled toggle:

<MokaThemeProvider Theme="_isDark ? MokaTheme.Dark : MokaTheme.Light">
    <MokaToolbar>
        <MokaButton OnClick="() => _isDark = !_isDark">Toggle Theme</MokaButton>
    </MokaToolbar>
</MokaThemeProvider>

@code {
    private bool _isDark;
}

Custom Themes

A theme is composed of three records:

MokaTheme
  ├── MokaPalette   — colour tokens
  ├── MokaTypography — font family, sizes, weights, line heights
  └── MokaSpacing   — spacing scale and border radius

Creating a Custom Palette

MokaPalette exposes a static factory method and with-expression support:

// Use the built-in light palette as a starting point
var palette = MokaPalette.Light with
{
    Primary     = "#1565c0",   // deep blue
    PrimaryDark = "#003c8f",
    Secondary   = "#6a1b9a",   // purple
    Success     = "#2e7d32",
    Error       = "#c62828",
};

Creating a Custom Theme

var myTheme = new MokaTheme
{
    Palette    = palette,
    Typography = MokaTypography.Default with { FontSizeBase = "14px" },
    Spacing    = MokaSpacing.Default,
};

Applying the Theme

Pass the theme instance to MokaThemeProvider:

<MokaThemeProvider Theme="_theme">
    <MokaButton Color="MokaColor.Primary">Custom Blue Theme</MokaButton>
</MokaThemeProvider>

@code {
    private static readonly MokaTheme _theme = new()
    {
        Palette = MokaPalette.Light with
        {
            Primary     = "#1565c0",
            PrimaryDark = "#003c8f",
        },
    };
}

Storing Themes in a Service

For larger apps, define themes centrally and inject them where needed:

// Services/AppThemeService.cs
public sealed class AppThemeService
{
    public MokaTheme Light { get; } = new()
    {
        Palette = MokaPalette.Light with { Primary = "#1565c0" },
    };

    public MokaTheme Dark { get; } = new()
    {
        Palette = MokaPalette.Dark with { Primary = "#42a5f5" },
    };
}
// Program.cs
builder.Services.AddSingleton<AppThemeService>();
@inject AppThemeService Themes

<MokaThemeProvider Theme="_isDark ? Themes.Dark : Themes.Light">
    @Body
</MokaThemeProvider>

@code {
    private bool _isDark;
}

CSS Custom Properties

Every token MokaThemeProvider emits follows a --moka-{category}-{name} naming convention. You can consume these properties in your own CSS anywhere inside .moka-root.

Colour Tokens

Property Description
--moka-color-primary Primary brand colour
--moka-color-primary-dark Darker shade of primary (hover states)
--moka-color-on-primary Text colour on primary backgrounds
--moka-color-secondary Secondary brand colour
--moka-color-error Error / destructive colour
--moka-color-warning Warning colour
--moka-color-success Success / confirmation colour
--moka-color-info Informational colour
--moka-color-surface Card and panel background
--moka-color-background Page background
--moka-color-on-surface Primary text colour
--moka-color-on-surface-variant Secondary/muted text colour
--moka-color-outline Border and divider colour
--moka-color-outline-variant Subtle border colour

Typography Tokens

Property Description
--moka-font-family Base font stack
--moka-font-size-base Base font size (default 13px)
--moka-font-size-sm Small text
--moka-font-size-lg Large text
--moka-font-weight-normal Regular weight (400)
--moka-font-weight-medium Medium weight (500)
--moka-font-weight-bold Bold weight (700)
--moka-line-height-base Base line height

Spacing Tokens

Property Description
--moka-spacing-xs Extra-small spacing unit
--moka-spacing-sm Small spacing unit
--moka-spacing-md Medium spacing unit (base)
--moka-spacing-lg Large spacing unit
--moka-spacing-xl Extra-large spacing unit
--moka-radius-sm Small border radius
--moka-radius-md Medium border radius
--moka-radius-lg Large border radius
--moka-radius-full Pill / fully-rounded radius

Elevation / Shadow Tokens

Property Description
--moka-shadow-0 No shadow (elevation 0)
--moka-shadow-1 Subtle lift (elevation 1)
--moka-shadow-2 Medium lift (elevation 2)
--moka-shadow-3 High lift (elevation 3)
--moka-shadow-4 Highest lift (elevation 4)
--moka-shadow-popup Dropdown/popup shadow
--moka-shadow-popup-lg Large popup shadow (context menu, popover)
--moka-shadow-modal Dialog/modal shadow
--moka-shadow-subtle Toolbar/appbar shadow

Shadow tokens automatically adjust intensity for dark themes (stronger shadows on dark backgrounds).

Component Height Tokens

Property Default Description
--moka-height-statusbar 22px StatusBar height
--moka-height-toolbar 40px Toolbar height
--moka-height-toolbar-dense 32px Dense toolbar height

Using Tokens in Your Own CSS

/* MyComponent.razor.css */
.my-panel {
    background: var(--moka-color-surface);
    border: 1px solid var(--moka-color-outline);
    border-radius: var(--moka-radius-md);
    padding: var(--moka-spacing-md);
    font-size: var(--moka-font-size-base);
    color: var(--moka-color-on-surface);
}

.my-panel__title {
    font-weight: var(--moka-font-weight-medium);
    color: var(--moka-color-primary);
}

Theme Toggle Example

A complete light/dark toggle with theme persistence via localStorage:

@inject IJSRuntime JS

<MokaThemeProvider Theme="_isDark ? MokaTheme.Dark : MokaTheme.Light">
    <MokaCard Style="max-width: 400px; margin: 2rem auto;">
        <div style="display: flex; align-items: center; justify-content: space-between;">
            <span>Dark Mode</span>
            <MokaButton OnClick="ToggleTheme">Toggle</MokaButton>
        </div>

        <MokaButton Color="MokaColor.Primary" Style="width: 100%;">Primary Action</MokaButton>
        <MokaButton Color="MokaColor.Secondary" Variant="MokaVariant.Outlined" Style="width: 100%;">Secondary Action</MokaButton>
    </MokaCard>
</MokaThemeProvider>

@code {
    private bool _isDark;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            var stored = await JS.InvokeAsync<string>("localStorage.getItem", "moka-theme");
            _isDark = stored == "dark";
            StateHasChanged();
        }
    }

    private async Task ToggleTheme()
    {
        _isDark = !_isDark;
        await JS.InvokeVoidAsync("localStorage.setItem", "moka-theme", _isDark ? "dark" : "light");
    }
}

Summary

Scenario Approach
Default light theme <MokaThemeProvider> with no parameters
Built-in dark theme Theme="MokaTheme.Dark"
Runtime toggle Theme="_isDark ? MokaTheme.Dark : MokaTheme.Light"
Custom brand colours MokaPalette.Light with { Primary = "..." }
Use tokens in CSS var(--moka-color-primary) etc. inside .moka-root
Last updated: 2026-04-11
Was this page helpful?