govuk-frontend as React components.
This is proof of concept, showing how govuk-frontend can be used as CSS modules via a set of React components that is published to npm, in a way that is compatible with create-react-app, Next.js, Final Form, Formik, React Router and Reach Router, with support for tree shaking and code splitting/lazy loading.
See:
https://github.com/govuk-react/govuk-react/issues/76
govuk-frontend does not 100% support being used as CSS Modules, though there the potential for this to be added in future:
https://github.com/alphagov/govuk-design-system-architecture/pull/12
One aim of this project is to create a library that has all the features of govuk-react, but is powered by govuk-frontend. One of these features is path splitting or critical extraction of styles when used in a 'standard' React build system such as those used by create-react-app or next.js.
Path splitting for CSS is coming to next.js soon:
As raised in the RFC for this feature, critically extracting CSS is potentially error prone due to the potential to extract global styles. However, when using CSS Modules, next.js is able to analyse what styles are not global and extract these. As such, next.js will only provide path splitting and critical extraction for CSS Modules. This is a reasonable approach and means in order to achieve this we need to use govuk-frontend as a set of CSS Modules and then identify what is broken and perhaps manually fix it, or raise as an issue on govuk-frontend (see list of issues below).
Common conventions used when deciding how to rewrite the nunjucks templates:
When looking at govuk-frontend BEM class names:
e.g. a class name of govuk-my-block__my-element--my-modifier
implies:
<MyBlock />
available by calling import { MyBlock } from 'govuk-frontend-react'
that sits at /src/{type}/my-block/index.js
<MyBlock.MyElement />
that is defined either in /src/{type}/my-block/index.js
or in /src/{type}/my-block/elements/my-element.js
<MyBlock.MyElement />
accepts a prop myModifier
{type}
can be objects
or components
following govuk-frontend/ITCSS classification.e.g. a class name of govuk-radios__conditional--hidden
implies:
<Radios />
available by calling import { Radios } from 'govuk-frontend-react'
that sits at /src/components/radios/index.js
<Radios.Conditional />
that is defined either in /src/components/radios/index.js
or in /src/components/radios/elements/conditional.js
<Radios.Conditional />
accepts a prop hidden
TODO: props (modifiers) sent to component should be made available to overrding sub components (elements) - how to do this?
=> if you want to intercept the modifiers as props then provide custom elements/components (CSSinJS with styled function) => if you just want to provide className lookup then use classNames (CSS Modules)/CSSinJS with css function
BEM convention classname exceptions:
Where our React prop names differ from the nunjucks macro option names.
nunjucks | react |
---|---|
element | as |
attributes | rest props (where appropriate) |
text | children (where appropriate) |
html | children (where appropriate) |
describedBy | aria-describedby |
This conversion can be seen in more detail in tests/render.js
(though this file needs cleaning up at time of writing).
Works! Including:
See https://github.com/penx/govuk-frontend-react-example
I aim to complete the following as part of the PoC:
Other TODO:
import { Button } from 'govuk-frontend-react'
import { Button } from 'govuk-frontend-react-templates'
, import { Button } from 'govuk-frontend-react-modules'
Things that I'm not 100% on how to deal with:
label={{children: 'Label'}}
can just be specified as label="Label"
? Or should we separate in to two props, label
and labelProps
?elements
and classNames
props use the context API to ensure ancestor elements can always access without prop drilldown?