Next.js & Fluent UI: Styling
Even though components from the Fluent UI library come with pre-defined styles, there are cases when it is necessary to add additional styles or customize the existing ones.
These are notes on how to style React-based application and its components using Next.js and Fluent UI React library.
Make styles
Fluent UI React library provides makeStyles
, which is a utility function to define styles in components. The function originates from Griffel, CSS-in-JS implementation that generates atomic CSS classes.
Usage
In Fluent UI, makeStyles
takes an object with unique keys and style values, returning a React hook for use inside a component.
To prevent unnecessary re-creation of styles during each render, define makeStyles
outside the component. It not only ensures improved performance but also maintains separation between styling logic and component logic.
import { makeStyles, shorthands, tokens } from "@fluentui/react-components";
// Define the useStyles hook outside of the component
const useStyles = makeStyles({
button: {
backgroundColor: tokens.colorBrandBackground,
...shorthands.borderRadius("5px"),
}
});
const CustomButton = ({ children }) => {
// Use the useStyles hook inside the component
const styles = useStyles();
return <button className={styles.button}>{children}</button>;
};
export default CustomButton;
In the provided example, shorthands
and tokens
are also imported from @fluentui/react-components
and used within the makeStyles
styling definition.
Fluent UI design tokens
In Fluent UI React library, tokens
are predefined values that represent the design system's core styles, such as colors, typography, and spacing.
Using tokens ensures a consistent look and feel throughout the application and makes it easier to apply global design changes.
In the example above, tokens.colorBrandBackground
is used to set the background color of the button. This ensures that the button's background color aligns with the design system's color scheme.
List of provided tokens:
CSS shorthands
Fluent UI's shorthands
is a set of functions designed to mimic CSS shorthand properties. As the library does not natively support CSS shorthand properties, using this collection of shorthand functions can help avoid issues.
const useStyles = makeStyles({
button: {
// This is not supported, and styles will not be inserted to DOM.
borderRadius: "5px",
// Use shorthand functions instead to avoid issues.
...shorthands.borderRadius("5px"),
}
});
As previously mentioned, Fluent UI utilizes Griffel for CSS-in-JS functionality. Consequently, the documentation for shorthands
functions is found on the Griffel documentation page.
Media queries
To apply media queries with makeStyles
, use nested objects within the style definition. Media query keys should follow standard CSS syntax, starting with @media
and specifying the desired conditions, such as min-width
or max-width
.
Inside the media query object, define the styles for those conditions.
import { makeStyles } from "@fluentui/react-components";
const useStyles = makeStyles({
container: {
display: "flex",
flexDirection: "column",
"@media (min-width: 768px)": {
flexDirection: "row",
},
},
});
const ResponsiveContainer = ({ children }) => {
const styles = useStyles();
return <div className={styles.container}>{children}</div>;
};
export default ResponsiveContainer;
In this example, the container
style definition includes a media query for screen widths larger than 768px. The flexDirection
is set to 'column'
by default, but when the screen width exceeds 768px, the flexDirection
changes to 'row'
.
This makes the ResponsiveContainer
component adaptable to different screen sizes by adjusting its layout based on the specified media query.
Nested selectors
Using nested selectors in makeStyles
should be done with caution, as they can cause "CSS rule explosion" and negatively impact performance.
Avoid using complex or deeply nested selectors, as they can create unique, non-reusable CSS rules, increase the bundle size, and make it harder to override styles. Instead, keep selectors simple and apply classes directly to the target element when possible.
Additionally, it's recommended to use JavaScript state rather than input pseudo-classes, as it produces fewer classes on an element and simplifies selectors for overrides. Input pseudo-classes are also limited to input elements, which can be a constraint in some cases.
Merge classes
Fluent UI provides mergeClasses
, which is a utility function for managing CSS classes in React-based application.
This function combines multiple class names into a single class. Its helps in extending or overriding existing styles, making it easier to customize components while maintaining their original design.
Usage
Import the function from @fluentui/react-components
:
import { mergeClasses } from "@fluentui/react-components";
mergeClasses
accepts multiple class arguments, in the form of strings or objects.
function CustomComponent() {
const styles = useStyles();
return (
<div className={mergeClasses(styles.base, styles.primary)}>
Content
</div>
);
}
Note, do not concatenate classnames, use mergeClasses()
instead.
// Incorrect usage
<div className={styles.base + ' ' + styles.primary} />
// Correct usage
<div className={mergeClasses(styles.base, styles.primary)} />
Concatenation is not recommended because it may produce wrong results.
Arguments order
In contrast to native CSS, the mergeClasses()
function's output depends on the order of the classes provided, which allows for better control over the priority of style overrides.
import { mergeClasses, makeStyles } from "@fluentui/react-components";
const useStyles = makeStyles({
base: {
fontSize: "18px"
},
additional: {
fontSize: "1.125rem"
}
});
function Component() {
const styles = useStyles();
const pxSize = mergeClasses(styles.additional, styles.base);
// Result: { fontSize: "18px" }
const remSize = mergeClasses(styles.base, styles.additional);
// Result: { fontSize: "1.125rem" }
}
Arguments order matters. It gives control over the precedence of styles when combining multiple classes. When classes are passed in a specific order, the styles from later classes can override the styles from earlier classes.
This is useful when customizing or extending components with additional styles, as it allows to modify or prioritize specific styles without changing the original class definitions.