# Headings and text

## Overview

This vignette covers all functions in shinyGovstyle that produce styled text content: headings, body text, lists, callout components, links, and typography. For page structure and layout, see the Layout options vignette.

## `heading_text()`

Creates a semantic HTML heading element with a GOV.UK heading CSS class.

    heading_text("Summary", size = "l", level = 2)

### Arguments

Argument | Default | Description
--- | --- | ---
`text_input` | — | The heading text to display
`size` | `"xl"` | Visual size: `"xl"`, `"l"`, `"m"`, or `"s"`
`level` | `1` | HTML heading level: integer 1–6
`id` | auto | Element ID — auto-generated from `text_input` if omitted

### Visual size and semantic level are independent

`size` controls the CSS class applied (`govuk-heading-xl`, `govuk-heading-l`, etc.), which determines how the heading looks. `level` controls the HTML element used (`<h1>`, `<h2>`, etc.), which determines its position in the document outline read by screen readers and assistive technologies.

These are independent because it is occasionally necessary to use a smaller visual heading at a higher semantic level, or vice versa. For example, a page where the first visible heading needs to be modest in size but is still structurally the `<h1>`:

    heading_text("User guide", size = "m", level = 1)

Always set both arguments deliberately. Do not leave `level` at its default of `1` for every heading on a page.

### Recommended size–level pairings

Standard pages (most dashboards and data tools):

Heading role | `size` | `level`
--- | --- | ---
Page title | `"l"` | `1`
Section heading | `"m"` | `2`
Sub-section heading | `"s"` | `3`

Long-form content (user guides, methodology, accessibility statements):

Heading role | `size` | `level`
--- | --- | ---
Page title | `"xl"` | `1`
Section heading | `"l"` | `2`
Sub-section heading | `"m"` | `3`
Sub-sub-section heading | `"s"` | `4`

### Accessibility

Do not skip heading levels. Moving from an `<h1>` straight to an `<h3>` breaks the document outline and makes it harder for screen reader users to navigate, as they commonly jump between headings to scan a page. This is a requirement under WCAG 2.2 success criterion 1.3.1: Info and Relationships.

Write headings in sentence case. Only capitalise the first word and proper nouns.

    # Correct — sentence case
    heading_text("Summary of findings", size = "l", level = 1)

    # Incorrect — title case
    heading_text("Summary Of Findings", size = "l", level = 1)

For more information, read the GOV.UK headings guidance.

### Heading IDs

If `id` is not supplied it is auto-generated by lowercasing `text_input` and replacing non-alphanumeric characters with underscores. Supply an explicit `id` when you need a stable anchor to link to, or when two headings would otherwise generate the same ID:

    heading_text("Methodology", size = "l", level = 2, id = "methodology")

## `gov_text()`

A wrapper that produces a `<p class="govuk-body">` paragraph element. Use it when you want to add body text with correct GOV.UK styling without writing raw tag calls.

    gov_box(
      size = "two-thirds",
      gov_text("This is a paragraph of body text.")
    )

## `gov_list()`

Creates a GOV.UK-styled list. The `style` argument controls both the HTML element used and the visual presentation:

`style` | HTML element | Appearance
--- | --- | ---
`"none"` (default) | `<ul>` | Plain, no markers
`"bullet"` | `<ul>` | Bulleted list
`"number"` | `<ol>` | Numbered list

    gov_list(c("First item", "Second item", "Third item"), style = "bullet")

    gov_list(c("First step", "Second step", "Third step"), style = "number")

Use `style = "number"` when order matters — steps in a process, ranked results, or sequential instructions. Use `style = "bullet"` for unordered items. The plain style (`"none"`) is useful when you want list semantics for screen readers without a visual marker.

For more information, read the GOV.UK lists guidance.

## `insert_text()`

Displays a GOV.UK inset text box — a bordered callout for supplementary information that is related to, but not the main focus of, the surrounding content.

    insert_text(
      inputId = "processing-note",
      text = "It can take up to 8 weeks to process your application."
    )

Use inset text for information the user needs to know but that is not the primary action or decision on the page — for example, a processing time, an exception to a rule, or a clarification. Do not use it for warnings about consequences; use `warning_text()` instead.

For more information, read the GOV.UK inset text guidance.

## `warning_text()`

Displays a GOV.UK warning text component: a bold statement with a prominent “!” icon, used to warn users about something with serious consequences.

    warning_text(
      inputId = "fine-warning",
      text = "You can be fined up to £5,000 if you do not register."
    )

The “!” icon carries `aria-hidden = "true"` and the word “Warning” is prepended as visually hidden text, so screen readers announce “Warning: [your text]” without reading out the icon character. Given this, you should avoid starting the text with ‘Warning’, else you’ll submit users to ‘Warning Warning’.

Use `warning_text()` when the consequence of missing the information is serious. For less critical supplementary information, use `insert_text()` instead.

For more information, read the GOV.UK warning text guidance.

## `noti_banner()`

Displays a GOV.UK notification banner for information that is not directly related to the current page content — such as a service-wide problem, an upcoming deadline, or the outcome of a previous action.

### Two types

Standard (default, blue): for neutral information such as service problems or upcoming events.

    noti_banner(
      inputId = "service-notice",
      title_txt = "Important",
      body_txt = paste0(
        "This service will be unavailable on Saturday ",
        "14 June from 8am to 6pm."
      )
    )

Success (green): to confirm that a previous action completed successfully. Uses `role="alert"` so screen readers announce it automatically on page load.

    noti_banner(
      inputId = "submission-confirm",
      title_txt = "Success",
      body_txt = "Your report has been submitted.",
      type = "success"
    )

### When to use each component

Situation | Component
--- | ---
Information not related to the current page task | `noti_banner()`
Supplementary information related to the page | `insert_text()`
Serious consequences if the user misses information | `warning_text()`
Form validation errors | `error_summary()` / error messages

Use notification banners sparingly — users often overlook them when they appear frequently. Show only one at a time, and never alongside an error summary.

For more information, read the GOV.UK notification banner guidance.

## `external_link()`

A wrapper for HTML anchor elements that produces safe, accessible external links with consistent behaviour.

    external_link("https://www.example.gov.uk/guidance", "Guidance for applicants")

### What the function does automatically

- Adds `target="_blank"` to open the link in a new tab
- Adds `rel="noopener noreferrer"` to prevent reverse tabnabbing
- Appends “(opens in new tab)” to the visible link text by default
- Adds a visually hidden “(opens in new tab)” span for screen readers when `add_warning = FALSE`

### Descriptive link text

The function validates `link_text` and will error if you supply a raw URL as the link text, vague text such as “click here” or “here”, or text ending with a full stop. It will also warn if the text is fewer than 7 characters.

This enforces WCAG 2.2 success criterion 2.4.4: Link Purpose (In Context), which requires link text to describe the destination without needing the surrounding context to make sense of it.

    # Correct — descriptive
    external_link("https://www.example.gov.uk/apply", "Apply for a licence")

    # Will error — vague text
    external_link("https://www.example.gov.uk/apply", "click here")

    # Will error — raw URL as text
    external_link(
      "https://www.example.gov.uk/apply",
      "https://www.example.gov.uk/apply"
    )

### Grouped links

When displaying several external links together, repeating “(opens in new tab)” on each is visually repetitive. Set `add_warning = FALSE` and add a single explanatory sentence above the group instead:

    gov_text("The following links open in a new tab.")
    shiny::tags$ul(
      shiny::tags$li(
        external_link(
          "https://www.example.gov.uk/a",
          "Guidance document A",
          add_warning = FALSE
        )
      ),
      shiny::tags$li(
        external_link(
          "https://www.example.gov.uk/b",
          "Guidance document B",
          add_warning = FALSE
        )
      )
    )

For more information, read the GOV.UK links guidance.

## `font()`

GDS Transport is a restricted typeface and must only be used on GOV.UK domains. If your app is not hosted on a GOV.UK domain, do not call `font()`. You do not need to do anything and your app will default to Arial instead, which is the correct behaviour.

If you are on a GOV.UK domain and therefore want to use the `font()` function, putting it within your UI will load the GDS Transport typeface for use in your app. By default the GOV.UK Frontend CSS specifies `font-family: GDS Transport, arial, sans-serif` — if the font files are not loaded the browser falls back to Arial automatically.

    # Only include this if your app is on a GOV.UK domain
    font()

For more information on when GDS Transport is permitted, read the GOV.UK typeface guidance.