apps/public-docsite-v9/src/Concepts/StylingComponents.mdx
import { Meta, Source } from '@storybook/addon-docs/blocks';
<Meta title="Concepts/Developer/Styling Components" />Visit the Styling handbook for a comprehensive styling guide, as this article only introduces basics to get you started quickly.
To style Fluent UI React v9 components makeStyles is used. makeStyles comes from Griffel a homegrown CSS-in-JS implementation which generates atomic CSS classes.
Get started by simply importing:
import { makeStyles } from '@fluentui/react-components';
To style a component, you need to call makeStyles in a module scope to create a React hook to be used inside a component.
The makeStyles call accepts an object of items where each key is a unique identifier and each value is an object with styles. The call returns an object with classes mapped to these unique identifiers.
import { makeStyles } from '@fluentui/react-components';
const useStyles = makeStyles({
root: { color: 'red' },
});
function Component() {
const classes = useStyles();
return <div className={classes.root} />;
}
There are cases where you need to merge classes from multiple useStyles calls.
useStyles classesTo properly merge the classes, you need to use mergeClasses() function, which performs merge and deduplication of atomic classes generated by makeStyles().
When mergeClasses() is called, it merges all classes from first to last - the latter argument overwrites the previous ones (similar to Object.assign()).
import { makeStyles, mergeClasses } from '@fluentui/react-components';
const useStyles = makeStyles({
blueBold: {
color: 'blue',
fontWeight: 'bold',
},
red: {
color: 'red',
},
});
function Component() {
const classes = useStyles();
const first = mergeClasses(classes.blueBold, classes.red); // { color: 'red', fontWeight: 'bold' }
const second = mergeClasses(classes.red, classes.blueBold); // { color: 'blue', fontWeight: 'bold' }
}
Both makeStyles and mergeClasses are simple. They are unaware of component state and props. Developers should merge and apply appropriate parts of the useStyles call based on their requirements.
import { makeStyles } from '@fluentui/react-components';
const useStyles = makeStyles({
root: { color: 'red' },
rootPrimary: { color: 'blue' },
});
function Component(props) {
const classes = useStyles();
return <div className={mergeClasses('ui-component', classes.root, props.primary && classes.rootPrimary)} />;
}
The same approach is used to apply classes passed from parent component. Again, you need to use mergeClasses() to properly merge and deduplicate the classes. Note again, that the order of application is important in mergeClasses() so that parent classes overwrite component classes.
import { mergeClasses } from '@fluentui/react-components';
function Component(props) {
const classes = useStyles();
return <div className={mergeClasses(classes.root, props.className /* these definitions have higher precedence */)} />;
}
No matter what theme is used, the component styles are always the same. The only way to change the component styling is through theme tokens which can be used in style values.
import { makeStyles, tokens } from '@fluentui/react-components';
const useStyles = makeStyles({
root: { display: 'flex' },
rootPrimary: { color: tokens.colorNeutralForeground3 },
});
Those tokens are resolved to CSS variable usages. The FluentProvider component is responsible for setting the CSS variables in DOM and changing them when the theme changes. When the theme is switched, only the variables are changed, all styles remain the same.
For more details on, see Theming.
This section shows and describes anti-patterns which should never be used.
import { makeStyles, mergeClasses } from '@fluentui/react-components';
const useStyles = makeStyles({
root: { color: 'red' }, // Do not use colors directly as those are not theme-able. Always use colors from a theme
});
function Component(props) {
const classes = useStyles();
const wrongClasses = classes.root + ' ' + props.className; // Never concatenate class strings, always use mergeClasses()
const wrongClasses2 = mergeClasses(props.className, classes.root); // Incorrect order of classes - as the latest wins, props.className should be last to override the component styles
}
Griffel devtools chrome extension can be used to debug style overrides. It shows all griffel styles applied on the currently selected DOM element, including the styles that are overridden in mergeClasses.
Griffel's approach to styling comes with certain limitations. One of which is the lack of support for CSS shorthand properties. To work around this, Griffel provides a collection of shorthand functions to write css shorthand. Their usage is demonstrated in the next example.
To override an appearance of a FUI component, you use the exactly same approach - You call makeStyles/useStyles in your code and pass the resulting classes through props.
import { makeStyles, tokens, shorthands } from '@fluentui/react-components';
import { CallEndRegular } from '@fluentui/react-icons';
const useOverrides = makeStyles({
button: { color: tokens.colorNeutralForeground3 },
buttonIcon: {
...shorthands.border('1px', 'solid', tokens.colorNeutralStroke1),
},
link: { fontWeight: 'bold' },
});
function MyComponent() {
const overrides = useOverrides();
return (
<>
<Button className={overrides.button} icon={{ className: overrides.buttonIcon, children: <CallEndRegular /> }} />
<Link className={overrides.link} />
</>
);
}