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 PrimaryAa PrimaryInteractionPrimaryForegroundAa PrimarySoftAa PrimarySoftInteraction | Main call to action: the default filled button, the selected tab or navigation entry. |
Aa SecondaryAa SecondaryInteractionSecondaryForegroundAa SecondarySoftAa SecondarySoftInteraction | Neutral filled action, for a button that must read as a button without competing with Primary. |
Aa SuccessAa SuccessInteractionSuccessForegroundAa SuccessSoftAa SuccessSoftInteraction | Confirmation: success toast, valid state, positive badge. |
Aa InfoAa InfoInteractionInfoForegroundAa InfoSoftAa InfoSoftInteraction | Neutral information: info toast, informative badge. |
Aa WarningAa WarningInteractionWarningForegroundAa WarningSoftAa WarningSoftInteraction | Something needs attention but nothing is broken yet. |
Aa DangerAa DangerInteractionDangerForegroundAa DangerSoftAa 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.