What's new Report an issue
GIG Cymru NHS Wales, Digital Health and Care Wales

Typography

Our fonts and typographic styles, and how to apply them.

Font

Roboto

Roboto is the primary typeface across every Single Record product. It is an open-source typeface, freely available from Google Fonts, and it is supported on web, Blazor, React and .NET MAUI. Using one typeface everywhere keeps clinical and administrative screens consistent for staff who move between products during a shift.

Roboto is set at four weights: Regular 400 for body text, Medium 500 for labels and smaller headings, Bold 700 for headings, and Light 300 where a large heading needs to feel less dense.

Fallback font

Default to Arial when Roboto is not available. Arial is present on every managed NHS Wales device, has closely matching metrics, and so avoids a visible layout shift while Roboto loads. Set the stack once, at the root, and let every component inherit it.

:root {
  font-family: Roboto, Arial, sans-serif;
}

Headings

Use heading tags such as <h1> and <h2> to mark up the headings on a page, then apply a heading class such as sr-type-heading-l to style them. Applying headings consistently gives every screen a clear content structure that both sighted users and screen reader users can navigate.

Use one <h1> per page and do not skip levels to reach a size you like. The heading level describes the structure of the page; the class controls how it looks. When a heading needs to be visually smaller, change the class, not the level.

Heading XL, 36px
Heading L, 28px
Heading M, 24px
Heading S, 20px
Heading XS, 16px
<h1 class="sr-type-heading-xl">Patient summary</h1>
<h2 class="sr-type-heading-l">Current medication</h2>
<h3 class="sr-type-heading-m">Repeat prescriptions</h3>
<h4 class="sr-type-heading-s">Issued in the last 6 months</h4>
<h5 class="sr-type-heading-xs">Dosage changes</h5>

Headings on record and dashboard screens

Single Record screens are denser than a public-facing service, so the top of the scale is used sparingly. Heading XL is reserved for the page title. Within a patient record, Heading M and Heading S carry most of the section structure, which keeps a long record scannable without the headings crowding out the content.

Labels and legends as headings

On a form where a single question fills the page, the label or legend is the page heading. Wrap it in the heading tag and apply the heading class to the label or legend itself, so the question is announced once rather than twice.

On the multi-question forms that are more common in Single Record, keep labels as labels. Use sr-type-label at 14px Medium, which pairs with the 14px data values used across tables and record views.

NHS number
485 777 3456
Date of birth
Use the format 06 Dec 1974
<label class="sr-type-label" for="nhs-number">NHS number</label>
<span class="sr-type-caption" id="nhs-number-hint">10 digits, spaced in groups of 3, 3 and 4</span>
<input class="sr-input" id="nhs-number" aria-describedby="nhs-number-hint">

<fieldset>
  <legend class="sr-type-heading-s">Contact preferences</legend>
</fieldset>

Paragraphs

Body text has two sizes and the choice between them is about content type, not about fitting more on screen.

Body S at 14px is a deliberate divergence from the 16px default that public-facing NHS and GOV.UK guidance uses. Single Record is a clinical, table-heavy system where a clinician needs to compare many values at once, and 14px keeps a usable number of rows in view. This still meets WCAG 2.2 AA: there is no minimum font size criterion, and resize to 200%, reflow and contrast are all met at this size.

Body M, 16px. Use for long-form reading and clinical notes, where the text is meant to be read in full rather than scanned.
Body S, 14px. The primary content size in tables and other data-dense views, and for supporting text and form values.
Caption, 12px. Hints, timestamps and metadata only.
<p class="sr-type-body-m">Long-form reading and clinical notes.</p>
<p class="sr-type-body-s">Primary content in tables and data-dense views.</p>
<p class="sr-type-caption">Last updated 06 Dec 2024 at 14:22</p>

Font size and weight overrides

There are no override utilities in this system, and that is deliberate. If a size or weight you need is not in the scale, that is a decision about the scale, not something to patch in a single component.

Two rules cover almost every case:

If you find yourself needing a value the scale does not have, request the change so it can be assessed once and applied everywhere.

<!-- Correct: a heading that needs to look smaller takes a smaller style,
     and the heading level still reflects the document structure. -->
<h2 class="sr-type-heading-s">Allergies and adverse reactions</h2>

<!-- Wrong: never hardcode type values on a component. -->
<h2 style="font-size: 21px; font-weight: 600">Allergies and adverse reactions</h2>

Links

Links are underlined and use the link colour token, which meets AA contrast against every surface in the system. Keep the underline. In a dense clinical interface, colour alone is not a reliable signal, and removing the underline fails WCAG 1.4.1.

Write link text that makes sense on its own, because screen reader users often navigate by pulling up a list of links. Use "current medication list" rather than "click here". Where a link opens in a new tab, say so in the link text.

<p class="sr-type-body-m">
  Review the <a href="/medication">current medication list</a> before prescribing.
</p>

<!-- Opening a new tab must be announced in the link text. -->
<a href="https://www.nhs.uk/" target="_blank" rel="noopener">
  NHS.UK guidance (opens in a new tab)
</a>

Lists

Lists inherit the body styles, so pick the body size that matches the surrounding content and apply it to the list. Use an unordered list where the order does not matter, such as a list of current medications, and an ordered list for steps that must happen in sequence.

Keep list items short. If an item runs past two lines, it is usually a paragraph or a sub-heading with content under it, not a list item.

  • Aspirin 75mg, once daily
  • Atorvastatin 20mg, once daily at night
  1. Confirm the patient identity
  2. Check for recorded allergies
<ul class="sr-type-body-m">
  <li>Aspirin 75mg, once daily</li>
  <li>Atorvastatin 20mg, once daily at night</li>
</ul>

<ol class="sr-type-body-m">
  <li>Confirm the patient identity</li>
  <li>Check for recorded allergies</li>
</ol>

Section break

Use a horizontal rule to separate distinct groups of content, for example between a patient banner and the record beneath it. The rule uses the default border token so it stays quiet against the page.

Use section breaks sparingly. In a record view, whitespace and headings usually do the job better, and too many rules make a screen look busier than it is.

Patient banner sits above the rule.


The record begins below it.

<hr>

<!-- Where the break is structural but should not be seen,
     use spacing instead of a visible rule. -->
<div style="margin-block: var(--space-6)"></div>

Text alignment

Align text left. Left-aligned text gives every line the same starting point, which is what makes a column of text or a table of values quick to scan, and it is easier to read for users with dyslexia. Do not justify text: justification creates uneven word spacing and rivers of white space that make reading harder.

The one exception is numeric table columns, where right-aligning the values lets users compare magnitudes down the column. Right-align the column heading to match the values beneath it. Never centre body text or table content.

Medication Dose (mg)
Atorvastatin20
Aspirin75
Levothyroxine125
<!-- Text columns: left aligned, which is the default. -->
<td class="sr-table__cell">Atorvastatin 20mg</td>

<!-- Numeric columns: right aligned, heading matches the values. -->
<th scope="col" class="sr-table__cell--numeric">Dose (mg)</th>
<td class="sr-table__cell sr-table__cell--numeric">20</td>

The type scale

Every style below is rendered live from the built tokens, so what you see is what ships.

ClassDesktopMobileWeightUse when
sr-type-heading-xl36/4428/36BoldPage title, one per page
sr-type-heading-l28/3624/32BoldMajor section
sr-type-heading-m24/3220/28BoldSub-section
sr-type-heading-s20/2818/24BoldCard or group heading
sr-type-heading-xs16/2416/24MediumSmallest heading
sr-type-body-m16/2416/24RegularLong-form reading, clinical notes
sr-type-body-s14/2014/20RegularPrimary content in tables and data-dense views
sr-type-label14/2014/20MediumForm labels, column headers
sr-type-caption12/1612/16RegularHints, timestamps, metadata
.sr-type-heading-xl The patient record is clear and legible
.sr-type-heading-l The patient record is clear and legible
.sr-type-heading-m The patient record is clear and legible
.sr-type-heading-s The patient record is clear and legible
.sr-type-heading-xs The patient record is clear and legible
.sr-type-body-m The patient record is clear and legible
.sr-type-body-s The patient record is clear and legible
.sr-type-label The patient record is clear and legible
.sr-type-caption The patient record is clear and legible

Accessibility requirements

RequirementWCAG SCHow Single Record meets itTest method
Text resizes to 200% without loss of content1.4.4All sizes are set in relative units, so the whole scale grows with the browser text size.Browser zoom to 200%
Content reflows to a single column1.4.10Line lengths are capped and layouts reflow at 320px with no horizontal scrolling.Resize to 320px
Body text meets contrast1.4.3Text colours are checked against every surface token they can sit on before release.Automated contrast
Headings describe the structure1.3.1 / 2.4.6Heading level follows the document outline; the class only controls the visual size.Heading map, screen reader
Links identifiable without colour1.4.1Links keep their underline in body content rather than relying on colour alone.Greyscale review
Text spacing can be overridden1.4.12Line heights sit on the 4px grid and are set in relative units, so user stylesheets apply cleanly.Text spacing bookmarklet