apps/public-docsite-v9/src/Concepts/Migration/KeepingDesignConsistent.mdx
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="Concepts/Migration/Keeping Design Consistent" />Fluent 2 is the next version of the Fluent design language. Fluent UI React v9 uses the Fluent 2 design language for layout and style of components.
The design language defines a set of design tokens. Themes consist of a property/value pair for each token. Designers reference these tokens in the design specs detailing the layout and style of components.
The themes in v0 and v8 were specific to component, component part, and state. While this allowed for fine-grained control over each component's style, it led to an explosion of theme properties. Too many theme properties can make defining new themes an arduous chore, can make themes fragile when properties are added or removed, and can leave dead theme properties behind when components stop using them.
In v9, the theme properties are called design tokens. They are much more general purpose. They are partitioned by neutral vs. brand, usage (e.g. foreground, background, stroke), and state.
This topic covers ways you can maintain a consistent theme and style during migration.
The visual style differences between v9 and previous versions are slight. If your migration effort is small, you can migrate all at once, or if your users are OK with some inconsistency, you might decide to avoid extra effort and live with it.
For example, you can see differences between v8's and v9's Button components when you put them side-by-side. However, the design differences between v8's Input and v9's Button are difficult to detect.
You can reduce the visual friction between Fluent 1 and Fluent 2 designs during migration by migrating all instances of one component type to v9.
In v0/v8, you pass the ThemeProvider component a theme.
This puts the theme object on React's context.
Component style methods then reference the theme properties when building styles.
In v9, you pass the FluentProvider component a theme.
This defines a CSS variable for each design token.
Component CSS styles then reference the CSS variables.
When migrating to v9, you will have v0/v8 and v9 components side-by-side.
You can wrap both ThemeProvider and FluentProvider around components.
Both ThemeProvider and FluentProvider support nesting to define a theme or partial theme at different scopes.
import { ThemeProvider, Button as ButtonV8 } from '@fluentui/react';
import { FluentProvider, Button as ButtonV9, webLightTheme } from '@fluentui/react-components';
function App() {
return (
<ThemeProvider>
<FluentProvider theme={webLightTheme}>
<ButtonV8>Hello migration</ButtonV8>
<ButtonV9>Hello migration</ButtonV9>
</FluentProvider>
</ThemeProvider>
);
}
Fluent UI React v9 leverages the Griffel CSS-in-JS library.
The makeStyles and mergeClasses methods are exported by @fluentui/react-components package.
Read Styling Components for details.
Because FluentProvider defines design token values as CSS variables, you can reference them your own component styles.
Read Theming to see examples.
The v9 FluentProvider defines CSS variables consumed by components.
You can extend the Theme type with your own design tokens, set values when creating an instance of your theme, and then consume them in your own components.
See Extending themes with new tokens section in Theming.
While you have both old and new components together in your application, you may see some differences in the theme and styling of components. You can choose to live with the minor discrepancies during migration, or make everything look the same.
We recommend moving to v9's Fluent 2 design. It has improved accessibility and has consistent style across components.
If you want to make v8 components look like v9, you can pass the Fluent 2 theme for v8 to the ThemeProvider. For more information read the v8 Fluent 2 theme documentation.
In the v8 documentation for each component, you can select the Fluent 2 theme to see how it looks.
By passing a v8 theme to ThemeProvider that uses v9 colors, v8 will look more like v9.
Conversely, passing a v9 theme to FluentProvider that uses v8 colors will make v9 look more like v8.
We have developed some shims (code that helps with migration) that let you create a v9 theme from the default v8 theme, create a v8 theme from the webLightTheme or webDarkTheme v9 themes.
One of our key partner teams has developed a Fluent 2 theme for v8 that includes custom component styles to exactly match the Fluent 2 theme of v9.
Check them out in the /Migration Shims/Themes topics.
As detailed in the /Concepts/Developer/Styling Components topic, you can create styles with makeStyles and mergeClasses and then pass the className to any v9 component or component slot.Those styles will be applied last, allowing you to modify the default component style.
If you do create custom styles, we strongly recommend using design tokens. This ensures styles update with theme changes.
Fluent v9 has a powerful composition model using React hooks. Each component is separated into a hook that maps props to state, a hook that uses state to set className styles on each slot, and a render function that outputs each slot with applied props.
While you can create a wrapper component that renders a v9 component with custom styles applied, this often introduces more virtual DOM elements that may affect performance.
Instead, you can create a component that reuses the same hooks of the component. You can substitute your own hooks or call additional hooks. Because you are leveraging the same infrastructure v9 components use, no additional wrapper virtual DOM elements are created.
This example defines a new button component.
The props to state hooks and render method from Button are reused.
A new style hook is substituted for useButtonStyle.
import * as React from 'react';
import {
makeStyles,
tokens,
renderButton_unstable as renderButton,
useButton_unstable as useButton,
} from '@fluentui/react-components';
import type { ButtonProps, ButtonState, ForwardRefComponent } from '@fluentui/react-components';
const useStyles = makeStyles({
root: {
background: tokens.colorNeutralBackground2,
//...
},
});
// This is an example of a custom style hook
const useCustomButtonStyles = (state: ButtonState): ButtonState => {
const styles = useStyles();
state.root.className = styles.root;
//...
return state;
};
export const MyButton: ForwardRefComponent<ButtonProps> = React.forwardRef((props, ref) => {
const state = useButton(props, ref);
useCustomButtonStyles(state);
return renderButton(state);
}) as ForwardRefComponent<ButtonProps>;
This is the most advanced form of customization in Fluent UI React v9, but provides you complete control over components.
⚠ Avoid custom style hooks
Prefer creating custom themes, setting className, replacing slots, extending theme tokens, and component recomposition. This approach is meant as an escape-hatch when all other customizations are not enough for your use case.
**⚠ Be careful **
className.mergeClasses call as you may affect the performance.You can pass FluentProvider.customStyleHooks_unstable a set of custom style hooks. The custom style hooks are considered unstable and the API may change at any time.
Each custom style hook can update root or slot class names.
import { makeStyles, mergeClasses, FluentProvider } from '@fluentui/react-components';
import type { FluentProviderCustomStyleHooks, ButtonState } from '@fluentui/react-components';
const useMyButtonStyles = makeStyles({
root: {
//...
},
});
const myCustomStyles: FluentProviderCustomStyleHooks = {
useButtonStyles_unstable: state => {
const myButtonStyles = useMyButtonStyles();
const buttonState = state as ButtonState;
buttonState.root.className = mergeClasses(buttonState.root.className, myButtonStyles.root);
},
};
function App() {
return <FluentProvider customStyleHooks_unstable={myCustomStyles}></FluentProvider>;
}
To override existing styles, use makeStyles to create your custom styles and then call mergeClasses with state className and the your new styles.
To completely replace existing styles, overwrite the state className.
When FluentProvider are nested, custom style hooks will be shallow merged.