Links
💅

CSS in JS

Compatibility with solutions like styled-components, emotion and TSS.
At build time react-dsfr parses the official dsfr.css files and spits out a typed JavaScript representation of the DSFR. In particular, its colors options and decisions, the spacing system and the breakpoints values.
This enables to write DSFR compliant CSS in JS code, since we are able to expose function that are the equivalent of the DSFR utility classes.
Native
TSS (recommended)
styled
You can use the style props on native react components but you won't be able to use the fr.breakpoint utility that enable to write responsive code.
import { fr } from "@codegouvfr/react-dsfr";
export type Props = {
className?: string;
};
export const MyComponent =(props: Props) => {
const { className } = props;
return (
<div
className={className}
style={{
padding: fr.spacing("10v"),
//SEE: https://components.react-dsfr.codegouv.studio/?path=/docs/%F0%9F%8E%A8-color-helper--page
backgroundColor: fr.colors.decisions.background.alt.blueFrance.active
}}
>
<span
className={fr.cx("fr-p-1v")}
style={{
...fr.spacing("margin", { "topBottom": "3v" })
}}
>
Hello World
</span>
</div>
);
};
tss-react
TSS React
Dynamic CSS-in-TS syle engine
# Dependencies to install even if never used directly:
yarn add tss-react @emotion/react
import { useState } from "react";
import { tss } from "tss-react/dsfr";
//NOTE: If you get "SyntaxError: Cannot use import statement outside a module" add tss-react here in your next.config.js: https://github.com/garronej/react-dsfr-next-demo/blob/43ecfa03d5416f2446b6867af65c7e3c7e7e41ef/next.config.js#L14
export type Props = {
className?: string;
};
export const MyComponent =(props: Props) => {
const { className } = props;
const [counter, setCounter]= useState(0)
const { classes, cx } = useStyles({
isClicked: couter > 0
});
return (
<div
className={cx(classes.root, className)}
onClick={()=> setCounter(counter+1)}
>
<span className={cx(fr.cx("fr-p-1v"), classes.innerText)} >
Hello World
</span>
</div>
);
};
MyComponent.displayName = MyComponent.name;
const useStyles = tss
.withName(MyComponent.name)
.withParams<{ isClicked: boolean; }>()
.create(({ isClicked })=> ({
root: {
padding: fr.spacing("10v"),
//SEE: https://components.react-dsfr.codegouv.studio/?path=/docs/%F0%9F%8E%A8-color-helper--page
backgroundColor: fr.colors.decisions.background.active.redMarianne.default,
"&:hover": {
//Rules that apply when the mouse is hover
backgroundColor: fr.colors.decisions.background.active.redMarianne.hover
},
[fr.breakpoints.up("md")]: {
//Rules that applies only when the screen is md or up
},
border: !isClicked ? undefined : `1px solid ${fr.colors.decisions.border.active.blueFrance.default}`
},
innerText: {
...fr.spacing("margin", { topBottom: "3v" })
}
}));
You can also use TSS to apply global styles:
import { GlobalStyles } from "tss-react";
import { fr } from "@codegouvfr/react-dsfr";
function App(){
return (
<>
<GlobalStyles
styles={{
html: {
overflowY: "scroll"
},
body: {
margin: 0,
borderWidth: 20,
borderStyle: "solid",
borderColor: fr.colors.decisions.border.actionHigh.success.default
}
}}
/>
{/*...rest of the app...*/}
</>
);
}
Advantages of tss-react over other CSS in JS solutions
  • I'm the author of TSS, it gets premium integration and support.
  • I made tss-react in coordination the MUI team. (TSS is documented in the MUI documentation here and here) so it works very well with it. Besides, getting MUI to correctly SSR in a Next.js setup is complicated (see the reference repo). With the help of TSS, it's much easier.
styled-component and @emotion/styled are equivalent API-wise so I give the example with Emotion since it has a better MUI integration.
index.tsx
import { ThemeProvider } from '@emotion/react'
import { useIsDark } from "@codegouvfr/react-dsfr/useIsDark";
function Root(){
const { isDark } = useIsDark();
return (
<ThemeProvider theme={{ isDark }}>
<App />
</ThemeProvider>
);
}
import styled from '@emotion/styled'
import { fr } from "@codegouvfr/react-dsfr";
export type Props = {
className?: string;
};
export function MyComponentNotStyled(props: Props){
const { className } = props;
return (
<div className={className}>
<span className={fr.cx("fr-p-1v")}>
Hello World
</span>
</div>
);
}
export const MyComponent = MyComponentNotStyled`
padding: ${fr.spacing("10v")};
background-color: ${fr.colors.decisions.background.alt.blueFrance.active};
/*
You can also use HEX values of colors instead of var(--xxx) with:
background-color: ${({ theme: { isDark } })=> fr.colors.getHex({ isDark }).decisions.background.alt.blueFrance.active};
*/
${fr.breakpoints.up("md")}: {
background-color: ${fr.colors.decisions.background.alt.beigeGrisGalet.active};
}
& > span {
margin-top: ${fr.spacing("3v")};
margin-bottom: ${fr.spacing("3v")};
}
`;You can use the style props on native react components but you won't be able to use the fr.spacing utility that enable to write responsive code.

spacing

For ensuring the spacing between elements is consistent throughout the website.
This tool is build using this file that is automatically generated from dsfr.css
import { fr } from "@codegouvfr/react-dsfr";
function MyComponent() {
return (
<div
style={{
marginTop: fr.spacing("2v"),
...fr.spacing("padding", { topBottom: "5w", left: 5 })
}}
/>
);
}
The above code is equivalent to:
import { fr } from "@codegouvfr/react-dsfr";
function MyComponent() {
return (
<div
style={{
marginTop: fr.spacing("2v"),
paddingTop: fr.spacing("5w"),
paddingBottom: fr.spacing("5w"),
paddingLeft: 5
}}
/>
);
}
Which is in turn equivalent to:
import { fr } from "@codegouvfr/react-dsfr";
function MyComponent() {
return (
<div
style={{
marginTop: "0.5rem",
paddingTop: "2.5rem",
paddingBottom: "2.5rem",
paddingLeft: 5
}}
/>
);
}
You can read the returned value in em just by hovering the spacing function call

breakpoints

For writing responsive UIs with media query (@media).
This tool is build using this file that is automatically generated from dsfr.css
import { useStyles } from "tss-react/dsfr";
import { fr } from "@codegouvfr/react-dsfr";
function MyComponent() {
const { css, theme } = useStyles();
return (
<div
className={css({
width: "100px",
height: "100px"
backgroundColor: theme.decisions.background.flat.info.default,
// On screen larger than MD the background color
// will be colors.decisions.background.alt.blueFrance.default.
[fr.breakpoints.up("md")]: {
backgroundColor: theme.decisions.background.alt.blueFrance.default
},
maxWidth: fr.breakpoints.values.xl
})}
/>
);
}
This tool generates @media query for you that matches the DSFR breakpoints

colors

Using the theme object that holds the colors decisions and options.
This is made possible by options.ts and decisions.ts files automatically generated from dsfr.css
There is a tool at your disposal to help you pick your colors.
The React agnostic way:
import { fr } from "@codegouvfr/react-dsfr";
const isDark = false;
const lightTheme = fr.getColors(isDark);
lightTheme.decisions.background.flat.info.default // #0063cb
lightTheme.options.blueFrance._850_200.default // #cacafb
lightTheme.isDark // false
With a hook that returns the theme object for the color scheme (light/dark) that is currently active:
import { useColors } from "@codegouvfr/react-dsfr/useColors";
function MyComponent(){
const theme = useColors();
//"#518fff" in darlk mode, "#0063cb" in light mode
console.log(theme.decisions.background.flat.info.default);
// "#313178" in darkMode, "#cacafb" in light mode
console.log(theme.options.blueFrance._850_200.default);
console.log(theme.isDark ? "App in dark mode": "App in light mode");
}
You can also access the theme object using the preconfigured tss-react adapter:
import { useStyles } from "tss-react/dsfr";
function MyComponent(){
const { theme } = useStyles();
// ...
}
And with makeStyles (recommended approach):
import { makeStyles } from "tss-react/dsfr";
type Props = {
calssName?: string;
};
function MyComponent(props){
const { className }= props;
const { classes, cx } = useStyles();
return (
<div classes={cx(classes.root, className)}>
// ...
</div>
);
}
const useStyles = makeStyles()(theme => ({
root: {
backgroundColor: theme.decisions.background.flat.info.default
}
}));

useIsDark()

You can access the active mode (isDark: true/false) in the theme object. However, if you want to manually switch the mode, you can use setIsDark(true/false) .
Consider using the <Display /> component instead of trying to manually manage the active mode.
import { useIsDark } from "@codegouvfr/react-dsfr/useIsDark";
function MyComponent(){
const { isDark, setIsDark } = useIsDark();
//isDark is a boolean that is true if the App is currently in dark mode.
//Calling setIsDark(true) will switch the app in dark mode.
//calling setIsDark("system") will set to whatever mode is signaled as prefered
//by the user browser
}

useBreakpointsValuesPx()

It returns the values in pixel of the different breakpoint ("xs", "md", "lg", "xl") based on the current root font size.
It can be used to do stuffs like this, geting the number of column of a responsive layout in JavaScript:
import { useBreakpointsValues } from "@codegouvfr/react-dsfr/useBreakpointsValues";
import { useWindowInnerSize } from "powerhooks/useWindowInnerSize";
function useColumnCount(){
const { breakpointsValues } = useBreakpointsValues();
const { windowInnerWidth } = useWindowInnerSize();
const columnCount = (() => {
if (windowInnerWidth < breakpointsValues.md) {
return 1;
}
if (windowInnerWidth < breakpointsValues.xl) {
return 2;
}
return 3;
})();
return collumnCount;
}
Be carefull though, favor using fr.breakpoints over client size mesurement and computation.
On the backend you can't know ahead of time the size of the screen of your users so this kind of approach will result in a flickering in SSR setups.
For example, your backend has no clue the size of the device making the request so it renders for a 1080p screen but the device making the request was, in fact, an iPhone and the first print is fully broken, the app becomes usable only after hydration.
Long story short, use this only if you are building an SPA.