# Theme, tokens, and variants
## Overview
The Fluent UI theming system contains everything you need to skin your application to match your designer needs.
A theme consists of tokens, component settings, and stylesheets.
Tokens (css variables) provide values which can be fit into the styles. They are accessible via javascript and as css variables.
Component settings affect Fluent UI components, but we also provide guidance on how it can affect your own components.
Stylesheets can be provided in the theme as string content or as hrefs to be loaded on theme initialization.
## Usage
### Create a theme
```jsx
import { ThemeProvider } from '@fluentui/react-theme-provider';
import { createLightTheme } from '@fluentui/base-theme';
const theme = createLightTheme({
tokens: {
colors: {
brand: {
background: '#3f3'
}
}
}
});
```
### Apply the theme with ThemeProvider
### Use Fluent UI components and customize them in your theme
### Build components which use the theme
### Using a theme
```
import { ThemeProvider, createTheme } from '@fluentui/react-theme-provider';
import { baseTheme } from '@fluentui/base-theme';
const theme = createTheme(
baseTheme, // start from the base theme
{
// ...partial theme specific to your app
}
});
const App = () => (
<ThemeProvider theme={theme} applyToBody>
... content here.
</ThemeProvider>
)
```
### Tokens
A token is a value provided by the theme and used as a replacement value within styling.
A simple example of a theme:
```
theme = {
tokens: {
colors: {
brand: {
background: '#f00'
}
}
}
}
```
This translates into defining the variable: `--colors-brand-background` with the value of `#f00`.
This can also be accessed through React context using the `useTheme` hook, or `ThemeContext.Consumer` wrapper.
Variables can reference other variables in the theme by using css `var` notation:
```
theme = {
tokens: {
layout: {
baseUnit: '4px',
size1: 'var(--layout-baseUnit)' // 4px
}
}
}
```
Calculations using `calc` can also be made to derive numerical values:
```
theme = {
tokens: {
layout: {
baseUnit: '4px',
size4: 'calc(var(--layout-baseUnit) * 4)' // 16px
}
}
}
```
### Color tokens
Fluent UI provides a set of color groups. Each color group represents a particular background color concept and provides a set of purposeful color tokens to use in that context.
Standard color groups include:
| Name | Usage |
|-|-|
| `body` |
| `neutral` | Body sections, cards, secondary buttons |
| `brand` | Teaching areas, primary buttons |
| `danger` | Error message panes, danger buttons |
| `warning` | Warning message panes |
| `success` | Success message panes, success buttons |
| `info` | Information message panes |
>TODO: is this color set list complete?
>Daisy: Clarifying question, is this the global or alias? It might be nice to use flat color names for the secondary colors like Red, Yellow, Green as we use them interchangeably for different semantics. Red is used for 'Busy' presence status as well as Errors and Recording indicator, Green is used in both Available presence and Success.
>
>I wonder if the semantic meaning should not be tied at the color level but on a per component basis?
>(i.e. inside an Alert component there are Alert types of [Danger, Warning, Success, Info], inside a Text component there are Text types of [Success, Error, Primary, Secondary, Tertiary], inside a Badge/Status component there are Status types of [Available, Busy, Away, OOF] )
Each color group gives the designer/engineer a set of accessible options to choose from for background and foreground colors:
| Token | Example |
|-|-|
| `background`, `background2`, `background3` | Shades of the color, where `background` is the strongest, and `background2` become softer. |
| `foreground`, `foreground2`, `foreground3` | Accessible colors to be used on any of the backgrounds, where `foreground` is the strongest and the following variations become softer. |
> TODO: what is the complete of tokens per color group? Are there enough values to build all of our components?
Theme example:
```
theme = {
tokens: {
colors: {
brand: {
background: '',
background2: '',
background3: '',
foreground: '',
foreground2: '',
foreground3: '',
},
neutral: {...},
inverted: {...}
danger: {...},
success: {...},
info: {...},
}
}
}
```
Accessing these values works like other variables:
In css:
```css
.myRule {
background: var(--colors-brand-background);
}
```
In js:
```js
import { colors } from '@fluentui/base-theme';
mergeStyles({
root: {
background: colors.brand.background
}
});
```
### Colors in theme modes (light, dark, and high contrast)
Light, dark and high contrast themes can be easily created when components utilize Fluent theming correctly.
> TODO: is there an easy way to generate L/D/HC themes from a small set of inputs?
> Daisy: Ask from Rekha and Aneesha for HC themes has been to read directly from OS setting. (then we won't need a column for HC-white and HC-black, and it's just 1 col for HC OS values)
### Typography tokens
> TODO
### Density tokens
The amount of space between elements can change how an application feels; more relaxed or more functional. Fluent UI theming provides knobs to adjust application density as needed.
Layout tokens give you 64 tokens to work with to provide scaled layout values.
The `--layout-baseUnit` token controls the base unit to work with.
Spacing tokens gives you units to work with that can scale with the base values. (E.g. `--layout-size1`.)
By default each size multiplies its value times the base unit. This creates predictability in design the default app experience, but keeps things scalable.
Theme definition:
```
var theme = {
tokens: {
layout: {
baseUnit: '4px',
size1: 'calc(var(--layout-baseUnit) * 1)',
size2: 'calc(var(--layout-baseUnit) * 2)',
...
size64: 'calc(var(--layout-baseUnit) * 64)',
}
}
}
```
> QUESTION: do we need to split our horizontal and vertical values?
> Daisy: It would be nice to have a base scale at the global level, and then alias out to padding/margin, and height/width where it maps the density themes. One the design side, we'd like to just say 'Spacing - #' and on the engineer side there are variables available for padding left, right, top, and bottom or margin left, right, top and bottom. Historically when a designer is explicit about padding vs margin it ends up causing more confusion than solutions. For height/width, imagine the tokens might be ['Sizing-W-#', 'Sizing-H-#'] / or ['Width-#', Height-#']
>
>Justification for alias to different usecases:
>A divider or border radius may also reference this base scale but they do not change at the same rate across density themes than spacing or sizing would.
>
>We'll need an escape hatch for the density theme changes, where we can control if a component 'responds to density' or not across density themes.
>example usecase: we might not want the avatar inside chat pane to change size while we do want the avatar size to change inside Teams chat left rail'
Usage in css:
```css
.className {
padding: var(--layout-size1); /* 4px */
}
```
Usage in javascript:
```js
import { layout } from '@fluentui/base-theme';
mergeStyles({
padding: layout.size1
});
```
> TODO: when and when not to use spacing values guidance.
### Typography tokens
```
--ramps-fontSizes-1
--ramps-fontSizes-2
--ramps-fontSizes-3
--ramps-fontSizes-4
--ramps-fontSizes-5
--fonts-body-family:
--fonts-caption-size:
--fonts-body-family:
```
> Daisy: Current thinking for Type is to break out individual style attributes at the global level (i.e. Size, Font Family, Font Weight), then at alias assigned Semantic names such as 'Body Primary, 'Caption Primary' using various combinations of Global tokens
>
> On Web, we would use 'Segoe UI' for the 'Base' font family in the Global token, then on various Mobile platforms, swap out to 'Roboto' for 'Base' font family token for Android, swap out to 'SF Pro' for 'Base' font family token. Other nuances on font weight and size are needed as well, Fluent Mobile designers on point to define that.
### Elevation tokens
> Daisy: Shadow tokens are currently just a ramp of different elevation levels and each references a color token that handles Default and Dark theme color changes. So on design side, we would not need to explicitly call out which shadow to use in which theme. It is just 'Shadow-2' or 'Shadow-8'
>
> Todo on the design side, there is general ask for directional shadows as well, but we have not gotten to it yet. All shadows right now are casting top down.
>
> When we were prototyping the shadow between panels in the Teams silhouette, realized those were actually inner shadows and not shadows produced from canvas or from left rail. So we'll need to think through inner shadow tokens as well.
### Animation tokens
> Daisy: Casey Baker would be a great resource here :)
## Component settings
### Icons
### Variants
We use the term `variant` to define a collection of token values, represented by a css classname. A variant class can be represented as tokens:
```
.Button-primary {
--button-background: var(--colors-brand-background1);
--button-contentColor: var(--colors-brand-foreground1);
--button-hovered-background: var(--colors-brand-background2);
--button-pressed-background: var(--colors-brand-background3);
}
```
We use variants to build easily referencable color sets that can be applied to any element in the app.
Example set of `variant` definitions:
```
theme = {
variants: {
Button: {
base: { ...tokens },
flyout: { ...tokens },
primary: { ...tokens }
> []
> []
},
Toggle: {
base: { ...tokens }
},
Component1: {
variant1: { ...tokens }
},
}
};
```
Fluent UI components can opt into variants:
```
<ThemeProvider variant="neutral1">
<Button variant="flyout"></Button>
</ThemeProvider>
```
The `ThemeProvider` will apply the current active scheme to the underlying `div` container, defaulting to `base`. You can always opt a particular section of your application into an alternative scheme.
Internally, schemes translate into fixed class names which define css variables.
Example input:
```
theme = {
colors: {
brand: {
background: '',
fg1: '',
fg2: '',
fg3: '',
border: ''
hovered: {
background: '',
fg1:
fg2:
fg3:
border:
}
}
}
variants: {
button: {
base: {
background: 'red'
},
primary: {
background: var(--colors-brand-background),
background: var(--colors-brand-background-primary),
hovered: {
background: var(--colors-brand-hovered-background),
background: var(--colors-brand-background-strong1)
}
}
}
}
}
```
When rendered, the `Button` component will find the appropriate variants ( defaulting to `base`) within the theme, and identify if a classname is available for this configuration.
If not, there will be a one time cost of registering a classname with that configuration of tokens:
```
.button-base-1234 {
--background: red;
}
```
This class name will be injected on the instance `className` prop.
## Full token list
### Colors
Standard color groups include:
| Name | Usage |
|-|-|
| `body` | Used on the body.|
| `brand` | Used for primary buttons and teaching bubbles. |
| `input` | Used for inputs. |
| `inverted` | Used in groups like a suite header or a tooltip. |
Standard color states include:
| Name | Usage |
|-|-|
| `hovered` | When an interactive component is hovered. |
| `pressed` | When an interactive component is pressed. |
| `disabled` | When an interactive component is disabled. |
| `checked` | When an checked component is rested. |
| `checkedHovered` | When an checked component is hovered. |
| `checkedPressed` | When an checked component is pressed. |
| `checkedDisabled` | When an checked component is hovered. |
Colors per area and state (note the rested state would be blank, so for example `--colors-brand-background` would be the brand colored background):
| Name | Description |
|-|-|
| `--colors-{area}-{state}-background` | Background color applied to the area. |
| `--colors-{area}-{state}-background2` | Background color applied to the area. |
| `--colors-{area}-{state}-background3` | Background color applied to the area. |
| `--colors-{area}-{state}-background4` | Background color applied to the area. |
| `--colors-{area}-{state}-foreground` | |
| `--colors-{area}-{state}-foreground2` | |
| `--colors-{area}-{state}-foreground3` | |
| `--colors-{area}-{state}-brandForeground` | Link color. |
| `--colors-{area}-{state}-thumb` | Body thumb color for interactive elements like Switch and Slider. |
| `--colors-{area}-{state}-focus` | Body focus rectangle color. |
| `--colors-{area}-{state}-innerFocus` | Body inner focus rectangle color. |
| `--colors-{area}-{state}-stroke` | Body border color. |
| `--colors-{area}-{state}-stroke2` | Body softer border color. |
| `--colors-{area}-{state}-stroke3` | Body softest border color. |
| `--colors-{area}-{state}-shadowKey` | Shadow color of the close part of the shadow. Should be paired with shadowAmbient. |
| `--colors-{area}-{state}-shadowKey2` | |
| `--colors-{area}-{state}-shadowKey3` | |
| `--colors-{area}-{state}-shadowAmbient` | Shadow color the far part of the shadow. Should be paired with shadowKey. |
| `--colors-{area}-{state}-shadowAmbient2` | Body shadow color at medium elevation. |
| `--colors-{area}-{state}-shadowAmbient3` | Body shadow color at highest elevation.
--colors-body-background // white/black
--colors-body-brandForeground // purple
--colors-brand-background // purple
--colors-brand-brandForeground // white
--colors-brand-hovered-brandForeground // white
--colors-brand-pressed-brandForeground // white
--colors-body-background4
--colors-brandSoft-background
--colors-brand-background6
--colors-brandStrong-background
--colors-inverted-brandForeground