Skip to content

penx/govuk-frontend-react

Repository files navigation

govuk-frontend-react

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:

govuk-react/govuk-react#76

CSS Modules

govuk-frontend does not 100% support being used as CSS Modules, though there the potential for this to be added in future:

alphagov/govuk-design-system-architecture#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).

Conventions

Common conventions used when deciding how to rewrite the nunjucks templates:

BEM

When looking at govuk-frontend BEM class names:

  • Blocks become exported components
  • Elements become subcomponents either inside the same file as the main component or in an elements subfolder, and exported as a property of the main component.
  • Modifiers become prop names of the component or subcomponent.

e.g. a class name of govuk-my-block__my-element--my-modifier implies:

  • There is a component <MyBlock /> available by calling import { MyBlock } from 'govuk-frontend-react' that sits at /src/{type}/my-block/index.js
  • There is a subcomponent <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:

  • There is a component <Radios /> available by calling import { Radios } from 'govuk-frontend-react' that sits at /src/components/radios/index.js
  • There is a subcomponent <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:

  • govuk-label-wrapper

Props

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

Create React App support

Works! Including:

  • Lazy loading/path splitting
  • Tree shaking of CSS

See https://github.com/penx/govuk-frontend-react-example

TODO:

I aim to complete the following as part of the PoC:

  • Button
  • Header compatible with React Router
  • Support for js-enabled class, used by Header, plus any associated JS required by Header
  • Input with Final Form and Formik examples.
  • Date Input with Final Form and Formik examples.
  • Radios
  • Tables
  • Error summary with Formik and Final Form examples, following govuk design system guidelines.
  • Reach router fixtures
  • Code coverage checks in CI
  • 100% code coverage
  • Meaningful unit tests
  • Use/match govuk-frontend template tests
  • Prettier
  • Flow
  • Create React App example with lazy loading/path splitting and tree shaking
  • Next.js example
  • Server side only example with form submit and display of form errors
  • Separate styling and templates, e.g. so the same templates could be used by plain CSS classes, CSS modules or CSSinJS classes passed in as props
  • Non CSS modules version
  • CSSinJS PoC
  • Compile all .module.scss to .module.css and include in npm package to prevent conflicts if using multiple versions
  • Review remaining TODO: and put in to GitHub issues
  • More stories/use cases for radio buttons

Other TODO:

  • Script to convert govuk-frontend attributes to prop types (or Flow types)
  • Export standalone templates, CSS includes and CSS modules separately, e.g. import { Button } from 'govuk-frontend-react' import { Button } from 'govuk-frontend-react-templates', import { Button } from 'govuk-frontend-react-modules'
  • Use Jest snapshots from govuk-frontend rather than manually copying
  • refactor render.js so that it is easier to scale
  • Greenkeeper and CI releases on template spec, feeding in to Greenkeeper on templates => automated flagging of new releases of govuk-frontend

Grey areas

Things that I'm not 100% on how to deal with:

  • custom CSS classes such as "width-2" class on Input being passed in as props but are actually CSS modules - could look up via a classNames object first?
  • should we allow shortcuts so that label={{children: 'Label'}} can just be specified as label="Label"? Or should we separate in to two props, label and labelProps?
  • should elements and classNames props use the context API to ensure ancestor elements can always access without prop drilldown?

govuk-frontend related issues

About

govuk-frontend as React components

Resources

Stars

Watchers

Forks

Packages

No packages published