# Banner

A strip the width of the page, above everything, for one announcement: a lamp, one line, an optional key that goes somewhere and a dismiss key. It is a raised plate, not a recess, so it reads as part of the frame and not as a message in the page. It is a labelled region, there on load, and says its thing once. When it goes it closes like a drawer, so the page glides up by its height instead of jumping.

Kit part · Free · https://tactileui.dev/parts/banner

- Element: `<section class="tui-banner" aria-label="Announcement">`
- Needs: `kit.css` and `kit.mjs` ([how to install the kit](https://tactileui.dev/docs/installation#the-kit))
- Covers: Banner, Announcement bar
- Categories: Alerts, Notifications
- Finishes: Aluminium, Graphite, Oak, Walnut, Porcelain, Black, Daylight, Night, Paper, Phosphor, E-paper, AMOLED
- Volumes: Tactile, Quiet

## Variants

### Info · white lamp, with a key

```html
<div style="width:min(100%,560px)"><section class="tui-banner" aria-label="Announcement"><div class="tui-banner__row"><div class="tui-banner__line"><span class="tui-lamp" data-lamp="white" data-on aria-hidden="true"></span><p class="tui-banner__text"><b>Lode 4.2 is out.</b> Exports now run in the background.</p></div><a class="tui-key tui-key--flat tui-key--sm tui-banner__link" href="#release-notes">Read the notes<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h9"/><path d="M8.6 4.6 12 8l-3.4 3.4"/></svg></a><button class="tui-key tui-key--flat tui-key--icon tui-key--sm tui-banner__dismiss" type="button" aria-label="Dismiss announcement" data-tip="Dismiss" data-banner-dismiss><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7"/></svg></button></div></section></div>
```

### Done · green lamp

```html
<div style="width:min(100%,560px)"><section class="tui-banner" aria-label="Announcement"><div class="tui-banner__row"><div class="tui-banner__line"><span class="tui-lamp" data-on aria-hidden="true"></span><p class="tui-banner__text"><b>Your plan changed.</b> The new price starts on 1 November.</p></div><button class="tui-key tui-key--flat tui-key--icon tui-key--sm tui-banner__dismiss" type="button" aria-label="Dismiss announcement" data-tip="Dismiss" data-banner-dismiss><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7"/></svg></button></div></section></div>
```

### Check · amber lamp

```html
<div style="width:min(100%,560px)"><section class="tui-banner" aria-label="Announcement"><div class="tui-banner__row"><div class="tui-banner__line"><span class="tui-lamp" data-lamp="amber" data-on aria-hidden="true"></span><p class="tui-banner__text"><b>Maintenance tonight from 22:00 to 22:30.</b> Exports will wait.</p></div><a class="tui-key tui-key--flat tui-key--sm tui-banner__link" href="#release-notes">What this means<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h9"/><path d="M8.6 4.6 12 8l-3.4 3.4"/></svg></a><button class="tui-key tui-key--flat tui-key--icon tui-key--sm tui-banner__dismiss" type="button" aria-label="Dismiss announcement" data-tip="Dismiss" data-banner-dismiss><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7"/></svg></button></div></section></div>
```

### Fault · red lamp, role="alert"

```html
<div style="width:min(100%,560px)"><section class="tui-banner" aria-label="Announcement" role="alert"><div class="tui-banner__row"><div class="tui-banner__line"><span class="tui-lamp" data-lamp="red" data-on aria-hidden="true"></span><p class="tui-banner__text"><b>Sign-in is slow.</b> We are on it; your work is safe.</p></div><a class="tui-key tui-key--flat tui-key--sm tui-banner__link" href="#release-notes">Status page<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h9"/><path d="M8.6 4.6 12 8l-3.4 3.4"/></svg></a><button class="tui-key tui-key--flat tui-key--icon tui-key--sm tui-banner__dismiss" type="button" aria-label="Dismiss announcement" data-tip="Dismiss" data-banner-dismiss><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7"/></svg></button></div></section></div>
```

### No dismiss key · must stay

```html
<div style="width:min(100%,560px)"><section class="tui-banner" aria-label="Announcement"><div class="tui-banner__row"><div class="tui-banner__line"><span class="tui-lamp" data-lamp="amber" data-on aria-hidden="true"></span><p class="tui-banner__text"><b>Your card expires this month.</b> Update it to keep your plan.</p></div></div></section></div>
```

### A long line wraps, the key drops under it

```html
<div style="width:min(100%,340px)"><section class="tui-banner" aria-label="Announcement"><div class="tui-banner__row"><div class="tui-banner__line"><span class="tui-lamp" data-lamp="white" data-on aria-hidden="true"></span><p class="tui-banner__text"><b>Northwind is moving to a new address.</b> From 3 November all post goes to the Harbour Road office, and the Quay Street desk closes.</p></div><a class="tui-key tui-key--flat tui-key--sm tui-banner__link" href="#release-notes">See the map<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h9"/><path d="M8.6 4.6 12 8l-3.4 3.4"/></svg></a><button class="tui-key tui-key--flat tui-key--icon tui-key--sm tui-banner__dismiss" type="button" aria-label="Dismiss announcement" data-tip="Dismiss" data-banner-dismiss><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7"/></svg></button></div></section></div>
```

### Pinned · .tui-banner--sticky

```html
<div style="width:min(100%,360px)"><section class="tui-banner tui-banner--sticky" aria-label="Announcement"><div class="tui-banner__row"><div class="tui-banner__line"><span class="tui-lamp" data-lamp="white" data-on aria-hidden="true"></span><p class="tui-banner__text"><b>Lode 4.2 is out.</b></p></div><a class="tui-key tui-key--flat tui-key--sm tui-banner__link" href="#release-notes">Notes<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h9"/><path d="M8.6 4.6 12 8l-3.4 3.4"/></svg></a><button class="tui-key tui-key--flat tui-key--icon tui-key--sm tui-banner__dismiss" type="button" aria-label="Dismiss announcement" data-tip="Dismiss" data-banner-dismiss><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7"/></svg></button></div></section></div>
```

## Every state

- Info
- Done
- Check
- Fault

Hover, pressed and focus are forced with `.is-hover`, `.is-down` and `.is-focus`; checked and disabled are real.

## API

| Name | Kind | Values | What it does |
| - | - | - | - |
| section.tui-banner[aria-label] | element | name it | The strip: a raised plate the width of its container, a labelled region. Name it “Announcement” or what it announces. |
| .tui-banner__row | element | one | The row inside, centred, with room each side for the dismiss key. It is the grid row that closes. |
| .tui-banner__line > .tui-lamp + .tui-banner__text | element | the lamp and the line | The lamp is hidden from a screen reader and set by data-lamp: white for news, green for done, amber to check, red for a fault. The line is one sentence, the first clause in . |
| a.tui-banner__link | element | optional, a flat key | Where it goes. Say what is there (“Read the notes”), not “Click here”. It drops under the line when the strip is narrow. |
| .tui-banner__dismiss · data-banner-dismiss | class · attribute | on a flat icon key | The cross. Needs aria-label and data-tip. Leave it out for a banner that must stay. |
| --sticky | modifier | tui-banner--sticky | Holds the strip at the top of the window while the page scrolls under it. |
| role | attribute | "alert" only when urgent | A banner is a labelled region and is read as part of the page. Add role="alert" only for a fault that appears while someone works. |
| dismissBanner(banner) | function | needs kit.mjs | Lets it go from a script: the row closes, it is hidden, the event fires and focus moves on. |
| tui-banner-dismiss | event | bubbles | Fired on the banner once it is hidden. The page decides whether to remember it. |
| Enter · Space | key | on the cross | Dismisses; focus moves to the next focusable thing after the banner. |

## Markup

```html
<!-- A labelled region at the very top of the page. The lamp only agrees with the words. -->
<section class="tui-banner" aria-label="Announcement">
  <div class="tui-banner__row">
    <div class="tui-banner__line">
      <span class="tui-lamp" data-lamp="white" data-on aria-hidden="true"></span>
      <p class="tui-banner__text"><b>Lode 4.2 is out.</b> Exports now run in the background.</p>
    </div>
    <a class="tui-key tui-key--flat tui-key--sm tui-banner__link" href="/releases/4-2">Read the notes …→…</a>
    <button class="tui-key tui-key--flat tui-key--icon tui-key--sm tui-banner__dismiss" type="button"
            aria-label="Dismiss announcement" data-tip="Dismiss" data-banner-dismiss>…×…</button>
  </div>
</section>

<!-- Only for something urgent that appears while someone works: add role="alert". -->
```

```js
import '/kit/kit.mjs';   // wires every [data-banner-dismiss], now and later

document.addEventListener('tui-banner-dismiss', (e) => remember(e.target.id));   // the page decides what to remember
// From a script:
import { dismissBanner } from '/kit/kit.mjs';
dismissBanner(document.querySelector('#release-banner'));
```

The demo on the page:

```html
<div style="display:grid;gap:12px;justify-items:center;width:min(100%,640px)">
  <div class="tui-panel" style="padding:0;overflow:hidden;width:min(100%,640px);text-align:left"><section class="tui-banner" aria-label="Announcement" data-bn><div class="tui-banner__row"><div class="tui-banner__line"><span class="tui-lamp" data-lamp="white" data-on aria-hidden="true"></span><p class="tui-banner__text"><b>Lode 4.2 is out.</b> Exports now run in the background.</p></div><a class="tui-key tui-key--flat tui-key--sm tui-banner__link" href="#release-notes">Read the notes<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8h9"/><path d="M8.6 4.6 12 8l-3.4 3.4"/></svg></a><button class="tui-key tui-key--flat tui-key--icon tui-key--sm tui-banner__dismiss" type="button" aria-label="Dismiss announcement" data-tip="Dismiss" data-banner-dismiss><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7"/></svg></button></div></section><div style="padding:20px 24px;display:grid;gap:10px"><span class="tui-label">Lode · Projects</span><div style="display:grid;gap:6px"><span>Harbour survey</span><span class="tui-muted" style="font-size:var(--tui-t-small)">Updated yesterday</span></div><div class="tui-separator tui-separator--flush" role="none"></div><div style="display:grid;gap:6px"><span>Quay Street plans</span><span class="tui-muted" style="font-size:var(--tui-t-small)">Updated 3 days ago</span></div></div></div>
  <div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;justify-content:center"><button class="tui-key tui-key--sm" type="button" data-bn-show>Show the banner</button><output class="tui-muted" style="font-size:var(--tui-t-small)" data-bn-out aria-live="polite"></output></div>
</div>
```

## Usage notes

Press the cross, or Tab to it and press Enter or Space: the strip closes to nothing and the page below rises with it, and focus moves on to the next thing. Press Show the banner to bring it back. Narrow the window and the key drops under the words.

## Accessibility

- It is a `<section>` with an `aria-label`, so it is a named region a screen reader user can jump to and skip, and it is read in its place in the page. It is not `role="alert"`: a banner that is there when the page loads does not interrupt anyone.
- The lamp is hidden from a screen reader. The line says what the announcement is, so nothing depends on the lamp’s colour, and the words are in `--tui-ink-2` with the first clause in full ink, above 4.5:1 on the plate.
- The cross is a real button with an `aria-label`: Tab reaches it after the key, and Enter or Space lets the banner go. After it goes, focus moves to the next focusable thing on the page, never to the top of the document or to nothing.
- The page does not jump: the row closes with an ease, so what is under it rises smoothly, and if you have scrolled past the banner it goes at once and your place is given back. Under reduced motion it goes at once.
- On a phone the cross and the key are 44px tall, and the line wraps with the key under it so nothing scrolls sideways. Focus is a 2px ring.

## How it’s built

The raised plate the kit gives a panel, with the same bead-blast, cut across the page with square corners and a hairline under it. The row is one grid row of `1fr` that eases to `0fr` when the banner leaves, so the strip is never given a height of its own, and its contents are hidden as it closes so nothing can be pressed on the way out. The dismiss is about twenty lines in `banner.mjs`, delegated from the document so banners added later work too, and it borrows the Alert’s way of finding where focus goes next.

## Rules

One announcement across the top of the page. A lamp agrees with the words, the key goes where the words say, and the dismiss key lets it go without moving anything you are reading.

The full rules are in `banner.rules.md` in the brain, which ships with the Kit.

## Related parts

- [Alert](https://tactileui.dev/parts/alert): A message let into the surface, with an annunciator: a small lit legend (Note, Done, Check, Fault) beside the words that say what happened.
- [Toast](https://tactileui.dev/parts/toast): A confirmation after an action: a small plate with a lamp and one factual sentence, read out politely, gone in 3.6 seconds.
- [Icon key](https://tactileui.dev/parts/icon-key): A key with an icon and no words, square or round: transport, power, the small tools. Its aria-label names it; a tip shows the name.
