# Theme unification > Status: IN PROGRESS > Design contacts: Daisy Geng, Sunmin Cheng > Eng contacts: David Zearing, Levi Thomason > [Figma resources](https://www.figma.com/files/588096576863690753/project/8475751/%F0%9F%8E%A8Theme-and-Tokens) ## Overview Building themes for Fluent UI React v8 should be robust enough to build rich, customized applications and to re-theme them easily. Default themes should adhere to the Fluent Design system, but should be easily configurable through a decoupled theme provider. We should have clear documentation on how to define an application theme, and a clear future-proof theme contract which can be used in more than just v7, v0, or even React or a specific css framework. Today, v7 themes are relatively simplistic, short on documentation, and incomplete. We'll explore the shortcomings and identify areas of improvement. The v0 Teams theme provides more robust theming but also has some drawbacks, such as a dependency on css in js, and also lacks in clear documentation. We'll expore how we can improve this as we converge themes. Our goal is to move v7 customers to v8 and bring theming in v7 and v0 closer together towards an ideal, based on css variables. ## What's changing Fluent UI React v8 includes a normalized, official way to apply themes to your application: * A `ThemeProvider` component for providing the theme * `useTheme` hook for consuming theme values in javascript > QUESTION: Should we expose a non react way to grab the theme? Do we really want to depend on context then? >* `applyTheme` for imperative non-React specific application of the theme to a DOM element. ```tsx import { lightTheme, darkTheme } from '@fluentui/base-themes'; import { ThemeProvider } from '@fluentui/react-theme-provider'; const myTheme = { tokens: { palette: { brand: 'red' }, body: { background: '#fafafa' } } }; const App = () => { const [dark, setDark] = useState(false); return ( <ThemeProvider theme={[dark ? darkTheme : lightTheme, { brand: 'red' }]} > <Button content="Hello world!" onClick={{ () => setDark(!dark) }} /> </ThemeProvider> ); } ``` The theme object itself will have a number of improvements: * Full support for configuring color, typography, density, elevation, and borders in addition to per-component nuances. * Fully documented, consistent, and type safe. * Values provided via css variables for fast perf and interopability with any css technology. * Theme itself can be provided through type-safe javascript. With Fluent UI theming, you can build your components to be used on any of a variety of officially supported themes: * M365 (Teams, Outlook, Word, Excel, Powerpoint, OneNote) * SharePoint * Azure Portal * Github * Linkedin ## Open issues to resolve * High contrast - can we have a high contrast theme and avoid media queries? How can we bring the existing component styles forward to be hc theme friendly? ## Theme tokens Theme tokens provide a way to customize your application's look and feel. ```tsx import { githubLightTheme, githubDarkTheme } from '@fluentui/github-themes'; const App = () => ( <ThemeProvider theme={githubLightTheme}> <Button content="Hello world!"/> </ThemeProvider> ); ``` ### Naming conventions and vocabulary When a color's lumninance gets closer to the background color, it appears **softer**. When it gets farther, it appears **stronger**. Token sets can be broken into 3 groups: * **Design tokens** - Basic building blocks: palette, density, design units. These values will end up affecting many other values. * **Alias tokens** - a set of semantically named tokens which pull from design tokens. Example background1, foreground1 * **Component tokens** - tokens specific to a component type, which can refer to alias tokens. ### Theme: Color token naming guidance Numerical color ramps should always go from softer to stronger. Background colors will be always referred to as `background`. For example: `--body-background`. Foreground colors will always be prefixed with purpose and suffixed with `Color`. For example: `--button-contentColor`; #### Color system tokens #### Color token sets * TBD: Should borders be an exception `buttonBorder`, vs separate `buttonBorderColor` annd `buttonBorderWidth` values? ### Theme: ## Previous theme research and considerations ### v7 theming #### Theme loading mechanism Theming within Fabric is controlled through a theme object, provided through a variety of mechanisms due to the vast amount of user scenarios that we've accumulated over time: * `Customizer` React wrapper - uses React to provide theme object contextually. If a provider does not wrap context, global settings using `applySettings` will be used as default. * `Customizer.applySettings` static method - Provides a way to assign global default settings. Similar to `loadTheme`. * `loadTheme` - Loads a theme into a global context on the window object. Internally also called `loadTheme` api from `load-themed-styles`, which replaces tokens wihtin previously injected styles. Additionally, the `Fabric` component is used as a `DIV` surface to render background and foreground colors, font settings, and a few other normalizers. Customizer does not directly render an element, so it has been recommended to use the `Fabric` component within. ##### Pros * Partners with islands of React can still define the theme in a central place * `loadTheme` has no dependency on React and can be used for partners who want basic theming but use another framework or combo of many frameworks. ##### Cons * Too many ways to load a theme. * Fabric component has old product name * Fabric component separate from Customizer, requires extra layers. * Many things are specified in the theme interface, but not all components use the values, or the right values. Many component styles still refer to palette colors rather than semantic colors because there wasn't a value available. #### Theme shape The following top level properties are available on a theme object. See notes for recommendations: | Prop | Plan | Notes | |-|-|-| |[`palette: IPalette;`](#Palette)| TBD|| |[`semanticColors: ISemanticColors;`](#Semantic-colors)| TBD|| |[`fonts: IFontStyles;`](#Typography)| TBD|| |[`spacing: ISpacing;`](#Spacing)| TBD|| |[`effects: IEffects;`](#Effects)| TBD|| There are also additional values in the theme which have less to do with the theme, or should be considered for deprecation. | Prop | Plan | Notes | |-|-|-| |`rtl?: boolean`| TBD| We should handle RTL outside of theme, given we have not seen adequate examples of where the global RTL setting isn't sufficient. | |`isInverted: boolean;`| TBD| In a non-cssinjs world, this is less important at runtime evaluation. | |`disableGlobalClassNames: boolean;`| TBD| We should consider a better way outside of theming to provide component settings. | |`schemes?: { [P in ISchemeNames]?: IScheme };`| TBD| The concept of schemes should definitely be caried over, but we should refine the vocabulary. | #### Palette: Overview The color palette provides a set of "available" colors. The unfortunate problem with available colors is the inability to change them without creating accessibility issues. #### Palette: Brand coloring The `themePrimary` value defines the primary accent color, with `themeSecondary` and `themeTertiary` provide lighter shades to use for hover cases. The `Darker`/`Lighter` variants also provide triplet colors in lighter/darker tones. Theme values: | Value | Plan | Notes | |-|-|-| | `themeDarker` |TBD| | `themeDark` |TBD| | `themeDarkAlt` |TBD| | `themePrimary` |TBD| | `themeSecondary` |TBD| | `themeTertiary` |TBD| | `themeLight` |TBD| | `themeLighter` |TBD| | `themeLighterAlt` |TBD| #### Palette: Neutrals Neutrals have 8 base tones, going from dark (`neutralPrimary`) to light (`neutralQuaternaryAlt`) in light theme. There are also Dark and Light variants. Neutrals: | Value | Plan | Notes | |-|-|-| | `neutralDark` |TBD| | `neutralPrimary` |TBD| | `neutralPrimaryAlt` |TBD| | `neutralSecondary` |TBD| | `neutralSecondaryAlt` |TBD| | `neutralTertiary` |TBD| | `neutralTertiaryAlt` |TBD| | `neutralQuaternary` |TBD| | `neutralQuaternaryAlt` |TBD| | `neutralLight` |TBD| | `neutralLighter` |TBD| | `neutralLighterAlt` |TBD| Overall it is very difficult to understand or predict the color intent based on the names in terms of what is "median", "softest" or "strongest". There are also missing tonal variants for base colors. For example, danger buttons would not have enough red tones to use here. Some tones like "blue" have 4 values, while "teal" has 3, and "red" has 2. There is no symmetry or reasoning here for why some colors have more or less. Also "accent" color makes no sense in context of themePrimary. #### Palette: Color sets | Value | Plan | Notes | |-|-|-| | `black` |TBD| | `blackTranslucent40` |TBD| | `accent` |TBD| | `white` |TBD| | `whiteTranslucent40` |TBD| | `yellowDark` |TBD| | `yellow` |TBD| | `yellowLight` |TBD| | `orange` |TBD| | `orangeLight` |TBD| | `orangeLighter` |TBD| | `redDark` |TBD| | `red` |TBD| | `magentaDark` |TBD| | `magenta` |TBD| | `magentaLight` |TBD| | `purpleDark` |TBD| | `purple` |TBD| | `purpleLight` |TBD| | `blueDark` |TBD| | `blueMid` |TBD| | `blue` |TBD| | `blueLight` |TBD| | `tealDark` |TBD| | `teal` |TBD| | `tealLight` |TBD| | `greenDark` |TBD| | `green` |TBD| | `greenLight` |TBD| #### Semantic colors: Overview Note that each of the values are literal strings and are intended to contain a value. There are no lookups or relationships. This is done in the variants package, which has a `getVariant` helper to pull values based on a partial palette and assign semantic colors based on the result. In general, semantic colors are sporadic, don't follow a convention and have no extension policy. Therefore they become unpredictable to use, and tend to steer devs #### Semantic colors: Body/general purpose values | Value | Plan | Notes | |-|-|-| | `bodyBackground` | `--body-background` | | `bodyBackgroundHovered` | `--body-hovered-background` | Should this apply to body? It's unclear of the usage. | `bodyBackgroundChecked` | `--body-checked-` | | `bodyStandoutBackground` | TBD | | `bodyFrameBackground` | TBD | | `bodyFrameDivider` | TBD | | `bodyDivider` | TBD | | `disabledBackground` | TBD | What does this apply to? vs `buttonBackgroundDisabled` | `disabledBorder` | TBD | What does this apply to? | `warningHighlight` | TBD | What is a highlight? | `bodyText` | TBD | | | `bodyTextChecked` | TBD | | | `bodySubtext` | TBD | | | `disabledText` | TBD | | | `disabledBodyText` | TBD | | | `disabledSubtext` | TBD | | | `disabledBodySubtext` | TBD | | | `errorText` | TBD | | | `messageText` | TBD | | | `infoBackground` | TBD | | `errorBackground` | TBD | | `blockingBackground` | TBD | | `warningBackground` | TBD | | `severeWarningBackground` | TBD | | `successBackground` | TBD | | `infoIcon` | TBD | | `errorIcon` | TBD | | `blockingIcon` | TBD | | `warningIcon` | TBD | | `severeWarningIcon` | TBD | | `successIcon` | TBD | #### Semantic colors: Link values | Value | Plan | Notes | |-|-|-| | `actionLink` | TBD | | | `actionLinkHovered` | TBD | | | `link` | TBD | | | `linkHovered` | TBD | | | `messageLink` | TBD | | `messageLinkHovered` | TBD | #### Semantic colors: Focus values: Focus rectangles often has a default border color, but also an contrast colored inner stroke for accessibility contrast reasons. We are missing any sort of token for inner color. | Value | Plan | Notes | |-|-|-| | `focusBorder` | TBD | #### Semantic colors: Input values: | Value | Plan | Notes | |-|-|-| | `inputBorder` | TBD | | `smallInputBorder` | TBD | What is this used for? Inconsistent. | `inputBorderHovered` | TBD | | `inputBackground` | TBD | | `inputBackgroundChecked` | TBD | | `inputBackgroundCheckedHovered` | TBD | | `inputPlaceholderBackgroundChecked` | TBD | | `inputForegroundChecked` | TBD | | `inputFocusBorderAlt` | TBD | | `inputIconDisabled` | TBD | | `inputIcon` | TBD | | `inputIconHovered` | TBD | Button values: | Value | Plan | Notes | |-|-|-| | `buttonBackground` | TBD | | `buttonBackgroundChecked` | TBD | | `buttonBackgroundHovered` | TBD | | `buttonBackgroundCheckedHovered` | TBD | | `buttonBackgroundDisabled` | TBD | | `buttonBackgroundPressed` | TBD | | `buttonBorder` | TBD | | `buttonBorderDisabled` | TBD | | `primaryButtonBackground` | TBD | | `primaryButtonBackgroundHovered` | TBD | | `primaryButtonBackgroundPressed` | TBD | | `primaryButtonBackgroundDisabled` | TBD | | `primaryButtonBorder` | TBD | | `accentButtonBackground` | TBD | ??? Menu values: | Value | Plan | Notes | |-|-|-| | `menuBackground` | TBD | | `menuDivider` | TBD | | `menuIcon` | TBD | | `menuHeader` | TBD | | `menuItemBackgroundHovered` | TBD | | `menuItemBackgroundPressed` | TBD | | `menuItemText` | TBD | | `menuItemTextHovered` | TBD | | `menuItemBackgroundChecked` | TBD | List values: | Value | Plan | Notes | |-|-|-| | `listBackground` | TBD | | `listText` | TBD | | `listItemBackgroundHovered` | TBD | | `listItemBackgroundChecked` | TBD | | `listItemBackgroundCheckedHovered` | TBD | | `listHeaderBackgroundHovered` | TBD | | `listHeaderBackgroundPressed` | TBD | Unknown purpose values: | Value | Plan | Notes | |-|-|-| | `variantBorder` | TBD | | `variantBorderHovered` | TBD | | `defaultStateBackground` | TBD | ##### Typography Fonts in the theme do not take a variable value approach, but rather an `IStyles` css in js approach, unfortunately. Ths means that non-css-in-js systems will never be able to leverage the values in web code. Each value in the `fonts` section of the theme represent a font variant. This is the worst of all worlds: 1. You don't have fine tune control of the family, weight, or size independently. 2. The variants are not semanticly named (caption vs header) so changing a font may have unintended consequences, similar to palette colors causing accessibility issues when changed. | Value | Plan | Notes | |-|-|-| |`tiny: IRawStyle`| TBD | | |`xSmall: IRawStyle`| TBD | | |`small: IRawStyle`| TBD | | |`smallPlus: IRawStyle`| TBD | | |`medium: IRawStyle`| TBD | | |`mediumPlus: IRawStyle`| TBD | | |`large: IRawStyle`| TBD | | |`xLarge: IRawStyle`| TBD | | |`xLargePlus: IRawStyle`| TBD | | |`xxLarge: IRawStyle`| TBD | | |`xxLargePlus: IRawStyle`| TBD | | |`superLarge: IRawStyle`| TBD | | |`mega: IRawStyle`| TBD | | ##### Spacing and layout Spacing is currently not respected anywhere in the controls. This means adjusting these don't change anything. But also, it's unclear to a user what changing them means. | Value | Plan | Notes | |-|-|-| | `s2: string` | TBD | | `s1: string` | TBD | | `m: string` | TBD | | `l1: string` | TBD | | `l2: string` | TBD | Proposal that we have a base design unit to define the base: `designUnit: 4px` This can weave We could then have a set of semantic spacing units we are based off the central design unit: | Name | Value | |-|-| |`spacing1` |`calc(designUnit * 1)` // 4px | |`spacing2` | `calc(designUnit * 2)` // 8px | |`spacing4` | `calc(designUnit * 4)` // 16px | Questions: * In designing controls, why use these over hardcoded values? * Should these be split into horizontal/vertical variants? Are there examples where density would change on one axis differently from another? This value feeds into semantic spacing slots: ##### Effects | Value | Plan | Notes | |-|-|-| | `elevation4: string;` | TBD |Used for: cards, grid items | `elevation8: string;` | TBD |Used for: menus, command surfaces | `elevation16: string;` | TBD |Used for: search result dropdowns, hover cards, tooltips, help bubbles | `elevation64: string;` | TBD |Used for: Panels, Dialogs | `roundedCorner2: string;` | TBD |Used for: buttons | `roundedCorner4: string;` | TBD |Used for: cards | `roundedCorner6: string;` | TBD |Used for: surfaces ## v0 theming ### v0 Pros ### v7 Cons ## Issues to resolve from both * Documentation * Theme designer ## Proposed unification plan