Design tokens

A theme is a collection of the keys below. Every control style reads them through DynamicResource, so a theme only has to redefine what it wants to change — anything it leaves out keeps the built-in value.

A theme registered with ThemeManager can define both colours and dimensions: it is merged right after Resources.xaml, so its Dimensions keys (and Colors.DisabledOpacity / MutedOpacity) override the built-in ones, and change with the theme. A dictionary merged after Resources.xaml still overrides every theme.

Keys are ComponentResourceKey statics rather than strings, so a renamed key breaks the build instead of silently falling back at runtime:

xmlns:joufflu="clr-namespace:Joufflu;assembly=Joufflu"

<!-- Always DynamicResource: a theme swap, or the customizer editing a
     dimension, has to reach controls already on screen. -->
<Border
    Background="{DynamicResource {x:Static joufflu:Brushes.DisplayBase0Brush}}"
    BorderBrush="{DynamicResource {x:Static joufflu:Brushes.DisplayBorder0Brush}}"
    BorderThickness="{DynamicResource {x:Static joufflu:Dimensions.DisplayThickness}}"
    CornerRadius="{DynamicResource {x:Static joufflu:Dimensions.DisplayRadius}}" />

<!-- The raw Color is there for what a brush cannot express -->
<GradientStop Offset="0" Color="{DynamicResource {x:Static joufflu:Colors.PrimaryColor}}" />

See Theme for swapping palettes at runtime, Customize theme for editing these values live, and Derived dimensions for scaling one per side or per corner.

Colors

Each colour exists twice: Joufflu.Colors.XColor holds the Color, Joufflu.Brushes.XBrush the SolidColorBrush built from it. Use the brush in the UI; the colour is there for gradients, animations and anything that needs the raw value.

Bases

Three colours a theme defines for the neutrals. The controls do not read them directly but through the three categories below, whose base falls back to them unless the theme gives a category a base of its own. Use them for an app’s own layout.

Key Used for
Base0 The page, and by default what an action is filled with, so on a display it reads as set into it. An action is not put directly on the page.
Base1 A card, and by default the base of a display and of an input.
Base2 A contrast inside a card, for an app’s own layout.

Categories

Every control belongs to one of three categories, each with the same four brushes. Only the base is a colour, and it is optional: a theme that leaves it out gets the global colour in brackets, through a ColorFallback. The three others are derived from it by mixing toward the Foreground, so the direction is right in a light and in a dark theme and no theme defines them.

Brush Used for
XBase The background.
XBorder0 The base mixed 10% toward Foreground: the border, and the parts that sit on the base (a header, a track, a tag).
XBorder1 The border mixed 10% toward Foreground: a border that must stand out from a XBorder0 background (the border of a CardSecondary), and the border of an interacted control.
XInteraction The base mixed 10% toward Foreground: the background of an interacted control.

An interaction turns the border to XBorder1 and the background to XInteraction. A part with no border at rest (a row, a menu item, a ghost button) keeps none. Disabled is DisabledOpacity, selected and checked are the primary.

Category Base Controls Interaction
Display DisplayBase0 (Base1), DisplayBase1 (Base2) Card, popup, tooltip, expander, group box, and the frame of a list, tree, grid, menu or tab control. Its items (rows, menu items, tabs, navigation entries) are actions without a fill at rest. DisplayBase1 is a contrast inside it (the header of a group box or a data grid), its own colour, not derived. None of its own: DisplayInteraction is there for an app’s informative hover.
Input InputBase0 (Base1), InputBase1 (Base2) Text box, combo box, check box, radio button, slider. InputBase1 is a contrast inside it, its own colour, not derived. On focus: the border; the background too, except where text is typed. No hover.
Action ActionBase0 (Base0) Button, toggle button. A grip (scroll bar thumb, grid splitter, slider thumb) turns ActionBorder1 when hovered or dragged. On hover. Pressed is a translation down, not a colour; no focus border.

A theme overrides a category by defining its base, for example inputs set apart from the cards, with their Border0, Border1 and Interaction brushes following:

<Color x:Key="{x:Static joufflu:Colors.InputBase0Color}">#FFFFFF</Color>

ColorFallback is public: a library of your own can declare an optional colour the same way (see Joufflu/Styles/Brushes.xaml).

Text

Key Used for
Foreground Default text and icon colour, on any base.

Accents

Six semantic families sharing one shape — the five keys of each are shown side by side below: X is the fill, XForeground the text and icons drawn on top of it: the two colours a theme defines. The others are brushes only, derived from the fill so no theme defines them. XInteractionBrush is the fill while hovered (and a checked solid toggle button): the fill mixed 10% toward its own XForeground, so a dark fill with light foreground lightens and a pale one darkens, in a light and in a dark theme alike. XSoftBrush is the fill at low opacity (soft buttons, outline hover) and XSoftInteractionBrush its hover, drawn with the fill as text, as a soft button is. Secondary is the exception: its fill is pale, made to sit under its foreground, so its soft tint and the text of its soft and outline buttons come from SecondaryForeground.

Aa
Primary
Aa
PrimaryInteraction
 
PrimaryForeground
Aa
PrimarySoft
Aa
PrimarySoftInteraction
Main call to action: the default filled button, the selected tab or navigation entry.
Aa
Secondary
Aa
SecondaryInteraction
 
SecondaryForeground
Aa
SecondarySoft
Aa
SecondarySoftInteraction
Neutral filled action, for a button that must read as a button without competing with Primary.
Aa
Success
Aa
SuccessInteraction
 
SuccessForeground
Aa
SuccessSoft
Aa
SuccessSoftInteraction
Confirmation: success toast, valid state, positive badge.
Aa
Info
Aa
InfoInteraction
 
InfoForeground
Aa
InfoSoft
Aa
InfoSoftInteraction
Neutral information: info toast, informative badge.
Aa
Warning
Aa
WarningInteraction
 
WarningForeground
Aa
WarningSoft
Aa
WarningSoftInteraction
Something needs attention but nothing is broken yet.
Aa
Danger
Aa
DangerInteraction
 
DangerForeground
Aa
DangerSoft
Aa
DangerSoftInteraction
Destructive action and validation errors — a delete button, the border of a field in error.

The swatches show the built-in Light palette; every theme redefines the same sixteen keys (Foreground, the three bases and the twelve accent colours). The sample gallery renders them live against whichever theme is selected.

State

Key Used for
DisabledOpacity Opacity applied to a whole control while IsEnabled is False (0.5). Disabled controls fade rather than switch to a dedicated palette, so a single key covers every control.
MutedOpacity Opacity of muted text: placeholders, hints, descriptions, captions (0.65). Muted text is dimmed rather than given its own colour, so it follows the foreground it sits in.

Dimensions

Joufflu.Dimensions holds the metrics. The values in parentheses are the built-in defaults.

Shape

What a Border takes, a Thickness or a CornerRadius. Bind it through Derive when you need only some sides or corners.

Key Type Used for
Thickness Thickness The global border (1).
Radius CornerRadius The global corner radius (4).
DisplayRadius, InputRadius, ActionRadius CornerRadius The radius of each category (4). The controls clamp each corner to half their height, so a large one gives a pill.
DisplayThickness, InputThickness, ActionThickness Thickness The border of each category (1).
DisplayPadding Thickness The padding inside a display (12). An action is padded by ActionPaddingXs…ActionPaddingLg, an input by InputPaddingXs…InputPaddingLg; a button or an input takes only their horizontal part, its height coming from its size, while a row, a menu item or a tab takes both.
DisplayPaddingSm Thickness The padding inside a compact display: a popup, a menu, a tooltip, a badge (4, a third of DisplayPadding in the customizer).

The global Radius and Thickness are for an app’s own layout: the controls read the category keys. Override a category key in a dictionary merged after Resources.xaml (or in a registered theme) to reshape that category alone:

<CornerRadius x:Key="{x:Static joufflu:Dimensions.ActionRadius}">16</CornerRadius>

Layout

Distances between things rather than inside them.

Key Type Used for
Spacing Thickness Standard gap between sibling elements and page padding (12). Feed it to Spacing.Gap.
TitleBarHeight double Height of the custom window title bar (30).
TitleBarHeightOffset Thickness Top only, used as a margin to push content below an overlapping title bar.

Control heights

Vertical size of single line controls. Sizing.Size picks one of the four; md is the default written in the base styles, so only xs/sm/lg appear in the size triggers.

Key Value Used for
HeightXs 24 Dense tables and toolbars.
HeightSm 28  
HeightMd 32 The default.
HeightLg 40 Prominent, touch friendly controls.
HeightEmbedded 20 0.625 of HeightMd, kept in sync by the theme customizer. The EmbeddedButton, a button inside an input (clear, open, step…).

Font sizes

A TextBlock picks one through Sizing.Size (xs to xl; md keeps the inherited body size), like the controls do, so restyling the scale restyles the text.

Key Value
FontSizeXs 11
FontSizeSm 12
FontSizeMd 13 (the default body size)
FontSizeLg 16
FontSizeXl 24

Paddings

Space inside a control, one Thickness per size. Two families: buttons and anything content shaped use ActionPaddingXs…ActionPaddingLg, text inputs use InputPadding*, whose halved horizontal keeps the caret closer to the border.

Key Value Input key Value
ActionPaddingXs 6,2 InputPaddingXs 3,2
ActionPaddingSm 9,3 InputPaddingSm 4.5,3
ActionPaddingMd 12,4 InputPaddingMd 6,4
ActionPaddingLg 18,6 InputPaddingLg 9,6

ActionPaddingMd and InputPaddingMd are the defaults.

See the Design tokens page in the sample gallery for the same reference with live swatches.