Wildfish logo
Wildfish logo


14 May 2020Stuart MacKay

Government has never been so crispy

  • crispyforms
  • django
  • oss
Government has never been so crispy

The GOV.UK Design System is a toolkit and a set of guidelines for creating sites and services for Her Majesty's Government which are clear and consistent and accessible. Services from the Met Office to the Government Property Agency all have a similar user experience which makes life easier for developers and citizens alike.

The HTML and CSS classes in the toolkit are very easy to use. However the result is a bit "wordy". Take the markup for a single text field with a single validation error (reformatted for readability):

<div class="govuk-form-group govuk-form-group--error">

<label for="id_name" class="govuk-label govuk-label--m">
Your name

<div id="id_name_hint" class="govuk-hint">
Enter your name as it appears on your passport.

<span id="id_name_1_error" class="govuk-error-message">
<span class="govuk-visually-hidden">Error:</span>
You must enter your full name

<input type="text"
aria-describedby="id_name_hint id_name_1_error"
class="govuk-input govuk-input--error"


You do get a lot for your money though. You have a set of standard styles for everything. The Aria attributes are excellent too - everything in the Design System is designed for accessibility. That takes a lot of guesswork out of the process of designing forms and ensures everything is designed and presented to a high standard.

However, this is quite a bit different from the basic HTML rendered by Django:

<label for="id_name">
Your name:
<ul class="errorlist">
<li>You must enter your full name</li>
<input type="text" name="name" required id="id_name"><br>
<span class="helptext">
Enter your name as it appears on your passport.

Ignoring the tags, clearly the structure is different. That's a problem because it means that forms have to be laid out explicitly in a template:

{% for field in form.visible_fields %}

<div class="govuk-form-group">

<label for="{{ field.auto_id }}"
class="govuk-label govuk-label--m">
{{ field.label_tag }}

{{ field.help_text }}

{% for error in field.errors %}
<span id="id_name_{{ forloop.counter }}_error"
<span class="govuk-visually-hidden">Error:</span>
You must enter your full name
{% endfor %}

{{ field }}

{% endfor %}

However it gets worse. You can't just lay out the fields in form. The problem is the markup for accessibility and error reporting. All the help text and error messages cross-reference each other. Django, out of the box, is simply not designed to handle that.

There is a straightforward solution - write our own templates for every type of form widget. That way we can support the Design System markup with 100% accuracy. That's a lot of work. I mean, a LOT of work. That also means that work has to be repeated for each project. Unless... Unless there was an easy way to share the templates. Enter, django-crispy-forms

Django-crispy-forms was designed to give you a high level of control over how your forms is laid out. With the FormHelper and layout classes you can organise the fields exactly how you want them. However, for us, the secret ingredients are the template packs. Each pack is a complete set of templates for rendering all the fields in a form, from simple CharFields through to MultiValueFields that allow you to add your own custom widgets. django-crispy-forms has template packs for Bootstrap 3 & 4. There are also template packs for Foundation, Materialize and others.

The essential beauty of the template pack approach is that you don't have to recreate the wheel with an entirely new rendering system for Design System forms with all the attendant problems of support and getting it adopted. Instead, thanks to the excellent instructions, you write a basic set of templates, package them up so they can be easily installed and you are done. My lords, ladies and gentlemen I give you, crispy-forms-gds.

Now when building GOV.UK services using Django all the work of rendering fields, with cross referenced identifiers and the rather daunting Error summary is reduced to this:

{% load crispy_forms_tags crispy_forms_gds %}
{% error_summary form %}
{% crispy form %}

That's as complex as your template gets. No more writing HTML. The crispy-forms-gds templates handle all the markup. You get 100% compliance with the Design System style guide and accessibility baked right in with no extra work on your part. It's all fully tested so you don't have to.

crispy-forms-gds supports all the Components in the Design System. The one exception is the Summary list is which is not really intended for forms. The package also includes a production ready template that you can drop right into your project and start creating Design System compliant pages on day one.

The package is available on PyPI however if you checkout the source from Github then you also get a full demo site with sample forms showing how easy it is to add each of the Design System Components to a form.

This project is going to save us a lot of time. crispy-forms-gds is Open Source and freely available so we hope it will save you some time too.

You must accept cookies and allow javascript to view and post comments
Wildfish logo


We really are quite an approachable bunch! Simply give us a call or email. Alternatively if you’d prefer, drop into the office for a chat!