# CSS in JS

At build time `react-dsfr` parses the official [dsfr.css](https://unpkg.com/browse/@gouvfr/dsfr/dist/dsfr/dsfr.css) files and spits out a typed JavaScript representation of the DSFR. In particular, its [colors options](https://unpkg.com/browse/@codegouvfr/react-dsfr/src/fr/generatedFromCss/colorOptions.ts) and [decisions](https://unpkg.com/browse/@codegouvfr/react-dsfr/src/fr/generatedFromCss/getColorDecisions.ts), the [spacing system](https://unpkg.com/browse/@codegouvfr/react-dsfr/src/fr/generatedFromCss/spacing.ts) and the [breakpoints values](https://unpkg.com/browse/@codegouvfr/react-dsfr/src/fr/generatedFromCss/breakpoints.ts).

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.

{% hint style="success" %}
Checkout [the color selection tool](https://components.react-dsfr.codegouv.studio/?path=/docs/%F0%9F%8E%A8-color-helper--page).
{% endhint %}

{% tabs %}
{% tab title="Native" %}
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.

<pre class="language-tsx"><code class="lang-tsx"><strong>import { fr } from "@codegouvfr/react-dsfr";
</strong>
export type Props = {
    className?: string;
};

export const MyComponent =(props: Props) => {

    const { className } = props;
    
    return (
	&#x3C;div 
	    className={className}
<strong>	    style={{
</strong><strong>	        padding: fr.spacing("10v"),
</strong><strong>		//SEE: https://components.react-dsfr.codegouv.studio/?path=/docs/%F0%9F%8E%A8-color-helper--page
</strong><strong>	        backgroundColor: fr.colors.decisions.background.alt.blueFrance.active
</strong><strong>	    }}
</strong>	>
	    &#x3C;span 
<strong>	        className={fr.cx("fr-p-1v")}
</strong>	        style={{
<strong>	            ...fr.spacing("margin", { "topBottom": "3v" })
</strong>	        }}
	    >
	        Hello World
	    &#x3C;/span>
	&#x3C;/div>
    );

};

</code></pre>

{% endtab %}

{% tab title="TSS (recommended)" %}
**tss-react**

{% embed url="<https://tss-react.dev>" %}
Dynamic CSS-in-TS syle engine
{% endembed %}

```bash
# Dependencies to install even if never used directly:
yarn add tss-react @emotion/react
```

```tsx
import { useState } from "react";
import { tss } from "tss-react";
//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:

```tsx
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...*/}
        </>
    );

}
```

{% hint style="info" %}
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](https://mui.com/material-ui/migration/migrating-from-jss/#2-use-tss-react) and [here](https://mui.com/material-ui/guides/interoperability/#jss-tss)) so it works very well with it. Besides, getting MUI to correctly SSR in a Next.js setup is complicated ([see the reference repo](https://github.com/mui/material-ui/tree/HEAD/examples/nextjs-with-typescript)). With the help of TSS, [it's much easier](https://docs.tss-react.dev/ssr/next.js#single-emotion-cache).
  {% endhint %}
  {% endtab %}

{% tab title="styled" %}
{% embed url="<https://styled-components.com/>" %}

{% hint style="info" %}
[styled-component](https://styled-components.com/) and [@emotion/styled](https://emotion.sh/docs/styled) are equivalent API-wise so I give the example with Emotion since it has a better MUI integration.
{% endhint %}

```tsx
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};
  ${fr.breakpoints.up("md")}: {
    background-color: ${fr.colors.decisions.background.alt.beigeGrisGalet.active};
  }
  & > span {
    margin-top: ${fr.spacing("3v")};
    margin-bottom: ${fr.spacing("3v")};
  }
`;
```

Optionally, if you want to have access to `isDark` in your styles, but this is not nessesary because fr.colors uses CSS variables by default.

```tsx
import { ThemeProvider } from '@emotion/react'
import { useIsDark } from "@codegouvfr/react-dsfr/useIsDark";

function Root(){

    const { isDark } = useIsDark();

    return (
        <ThemeProvider theme={{ isDark }}>
            <App />
        </ThemeProvider>
    );

}

// Then you'll be able to do the following to have the 
// hex values of the colors instead of var(--xxx).

export const MyComponent = MyComponentNotStyled`
  background-color: ${({ theme: { isDark } })=> fr.colors.getHex({ isDark }).decisions.background.alt.blueFrance.active};
`;
```

{% endtab %}
{% endtabs %}

### spacing

For ensuring the spacing between elements is consistent throughout the website.

{% hint style="info" %}
This tool is build using [this file](https://unpkg.com/browse/@codegouvfr/react-dsfr/src/fr/generatedFromCss/spacing.ts) that is automatically generated from [dsfr.css](https://unpkg.com/browse/@gouvfr/dsfr/dist/dsfr/dsfr.css)
{% endhint %}

<pre class="language-tsx"><code class="lang-tsx"><strong>import { fr } from "@codegouvfr/react-dsfr";
</strong>
function MyComponent() {

    return (
        &#x3C;div 
            style={{ 
                marginTop: fr.spacing("2v"),
                ...fr.spacing("padding", { topBottom: "5w", left: 5 })
            }}
        />
    );

}
</code></pre>

The above code is equivalent to:

```tsx
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:

```tsx
import { fr } from "@codegouvfr/react-dsfr";

function MyComponent() {

    return (
        <div 
            style={{ 
                marginTop: "0.5rem",
                paddingTop: "2.5rem",
                paddingBottom: "2.5rem",
                paddingLeft: 5
            }}
        />
    );

}
```

<figure><img src="https://312678300-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fcccd1rMVr8ooPvsgugcw%2Fuploads%2FTwO5eneA4yFtq1Fm7XLQ%2Fimage.png?alt=media&#x26;token=7071b00a-c4e5-4bbf-be28-62a81f1c6550" alt=""><figcaption><p>You can read the returned value in em just by hovering the spacing function call</p></figcaption></figure>

### breakpoints

For writing responsive UIs with media query (`@media`).

{% hint style="info" %}
This tool is build using [this file](https://unpkg.com/browse/@codegouvfr/react-dsfr/src/fr/generatedFromCss/breakpoints.ts) that is automatically generated from [dsfr.css](https://unpkg.com/browse/@gouvfr/dsfr/dist/dsfr/dsfr.css)
{% endhint %}

```tsx
import { useStyles } from "tss-react";
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
            })}
        />
    );

}
```

<figure><img src="https://312678300-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fcccd1rMVr8ooPvsgugcw%2Fuploads%2F3tjyZeKG5DWmh7CP3gcP%2Fimage.png?alt=media&#x26;token=936dda11-d16b-42bb-8e05-2af81aadacc1" alt=""><figcaption><p>This tool generates @media query for you that matches the DSFR breakpoints</p></figcaption></figure>

### colors

Using the `theme` object that holds the colors decisions and options.

{% hint style="success" %}
📣📣📣📣📣📣\
There is [a tool](https://components.react-dsfr.codegouv.studio/?path=/docs/%F0%9F%8E%A8-color-helper--page) at your disposal to help you pick your colors.\
Use it! It's great!\
📣📣📣📣📣📣📣
{% endhint %}

#### Using CSS variables (recommended)

This approad is React agnostic and yield the best performances. &#x20;

<pre class="language-typescript"><code class="lang-typescript"><strong>import { fr } from "@codegouvfr/react-dsfr";
</strong>
function MyComponent(){

  return (
    &#x3C;div style={{
      // The recommended method, using CSS variables: 
<strong>      backgroundColor: fr.colors.decisions.background.default.grey.default 
</strong>      // This is: backgroundColor: "var(--background-default-grey)"
    }} />
  );

}
</code></pre>

#### Using HEX color code

Some third party libraries might require you to provide explicit value as colors. &#x20;

When CSS variable references doesn't work you can do: &#x20;

<pre class="language-tsx"><code class="lang-tsx"><strong>import { fr } from "@codegouvfr/react-dsfr";
</strong><strong>import { useIsDark } from "@codegouvfr/react-dsfr/useIsDark";
</strong>
function MyComponent(){

<strong>  const { isDark } = useIsDark();
</strong>
  return (
    &#x3C;div style={{
<strong>      backgroundColor: fr.colors.getHex({ isDark }).decisionsbackground.default.grey.default
</strong>      // This is backgroundColor: "#161616" when isDark is true
      // and     backgroundColor: "#ffffff" when isDark is false
    }} />
  );

}
</code></pre>

### 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)` .

{% hint style="info" %}
Consider using the [\<Display />](https://components.react-dsfr.codegouv.studio/?path=/docs/components-display--default) component instead of trying to manually manage the active mode.
{% endhint %}

```tsx
import { 
   useIsDark, 
   getIsDark // Let you access the current value of isDark outside of React (client side only)
} 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


}
```

If you want to use the isDark value in your styles: &#x20;

<pre class="language-tsx"><code class="lang-tsx">import { tss } from "tss-react";
import { useIsDark } from "@codegouvfr/react-dsfr/useIsDark";

function MyComponent(props){

    const { className } = props;
    
    const { isDark } = useIsDark();
    const { classes, cx } = useStyles({ isDark });
    
    return (
        &#x3C;div classNames={cx(classes.root, className)}>
           // ...
        &#x3C;/div>
    );
    
}

const useStyles = tss
<strong>  .withParams&#x3C;{ isDark: boolean; }>()
</strong><strong>  .create(({ isDark })=> ({
</strong>    root: { /* ... */ }
  }));
</code></pre>

### 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:

```tsx
import { useBreakpointsValuesPx } from "@codegouvfr/react-dsfr/useBreakpointsValuesPx";
import { useWindowInnerSize } from "@codegouvfr/react-dsfr/tools/useWindowInnerSize";

function useColumnCount(){

        const { breakpointsValues } = useBreakpointsValuesPx();

        const { windowInnerWidth } = useWindowInnerSize();

        const columnCount = (() => {
            if (windowInnerWidth < breakpointsValues.md) {
                return 1;
            }

            if (windowInnerWidth < breakpointsValues.xl) {
                return 2;
            }

            return 3;
        })();
        
        return collumnCount;

}
```

{% hint style="warning" %}
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.
{% endhint %}
