Back to Fluentui

StylingComponents

apps/public-docsite-v9/src/Concepts/StylingComponents.mdx

4.40.2-hotfix26.3 KB
Original Source

import { Meta, Source } from '@storybook/addon-docs/blocks';

<Meta title="Concepts/Developer/Styling Components" />

Styling components

Visit the Styling handbook for a comprehensive styling guide, as this article only introduces basics to get you started quickly.

Getting started

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:

js
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.

jsx
import { makeStyles } from '@fluentui/react-components';

const useStyles = makeStyles({
  root: { color: 'red' },
});

function Component() {
  const classes = useStyles();

  return <div className={classes.root} />;
}

Merging component styles

There are cases where you need to merge classes from multiple useStyles calls.

⚠ It is not possible to simply concatenate useStyles classes

To 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()).

jsx
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' }
}

Mapping props and state to styles

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.

jsx
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)} />;
}

Applying classes passed from parent

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.

jsx
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 */)} />;
}

Applying theme to styles

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.

js
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.

Incorrect usages

This section shows and describes anti-patterns which should never be used.

js
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
}

Debugging 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.

Limitations

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.

Overriding FUI component styles

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.

jsx
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} />
    </>
  );
}