---
title: "Headings and text"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Headings and text}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
library(shinyGovstyle)
```

## 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](layout-options.html) vignette.

---

## `heading_text()`

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

```{r, eval = FALSE}
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>`:

```{r, eval = FALSE}
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](https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships).

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

```{r, eval = FALSE}
# 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](https://design-system.service.gov.uk/styles/headings/).

### 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:

```{r, eval = FALSE}
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.

```{r, eval = FALSE}
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 |

```{r, eval = FALSE}
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](https://design-system.service.gov.uk/styles/lists/).

---

## `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.

```{r, eval = FALSE}
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](https://design-system.service.gov.uk/components/inset-text/).

---

## `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.

```{r, eval = FALSE}
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](https://design-system.service.gov.uk/components/warning-text/).

---

## `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.

```{r, eval = FALSE}
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.

```{r, eval = FALSE}
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](https://design-system.service.gov.uk/components/notification-banner/).

---

## `external_link()`

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

```{r, eval = FALSE}
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](https://owasp.org/www-community/attacks/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)](https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context), which requires link text to describe the destination without needing the surrounding context to make sense of it.

```{r, eval = FALSE}
# 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:

```{r, eval = FALSE}
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](https://design-system.service.gov.uk/styles/links/).

---

## `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.

```{r, eval = FALSE}
# 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](https://design-system.service.gov.uk/styles/typeface/).
