Vanilla Breeze

Glossary terms in prose

How to link a vocabulary concept inline so the popover enhancement and the glossary page both pick it up. First mention in , subsequent mentions plain.

Why this exists

The site has a single glossary generated from site/src/_data/vocabulary.json. When you mention one of those concepts in article prose, link to it. Two things light up:

  • The glossary page is the canonical anchor (every concept has id="term-{@id}"), so the link is always valid even without JS.
  • A small popover script (/js/definition-popover.js, generated at build) fetches /definitions.json on first interaction and shows the definition in a native popover anchored to the term. Modifier-clicks fall through to the glossary page.

The pattern

Three forms, depending on whether the mention is the first defining use, a subsequent reference, or an abbreviation.

First (defining) use — <dfn>

Wrap the link in <dfn>. The data-concept attribute carries the SKOS @id from vocabulary.json. The href deep-links to the term's anchor on the glossary page so the link is meaningful with no JS.

Subsequent uses — plain anchor

Plain anchor — no <dfn>. <dfn> marks the defining instance; using it on every mention would be wrong semantically and visually noisy.

Abbreviated mentions — <abbr>

When you reference a concept by an abbreviation that lives in its skos:altLabel array, wrap the link in <abbr> with title set to the prefLabel. The title tooltip gives sighted hover-readers the expansion; the popover still triggers because the script queries a[data-concept] regardless of wrapper.

Use <dfn> exactly once per concept per page (the defining mention, regardless of label form). Subsequent mentions are either plain anchors (full label) or <abbr>-wrapped (altLabel/abbreviation).

Rules of thumb

  • Link the concept, not the word. Only link when you genuinely mean the vocabulary entry — not every casual use of the word "review" or "trust".
  • One <dfn> per concept per page. Subsequent mentions on the same page should be plain anchors.
  • Don't link inside a heading. Headings are navigation; the popover would fight with the heading-link affordance. Link from the body prose right after.
  • Don't link inside <code>, <pre>, or another <a>. Code samples carry their own typography contract; nested anchors are invalid HTML.
  • Don't link more than one or two terms per paragraph. The dotted-underline cue is helpful in moderation; turn into noise quickly.
  • Add the concept to the page's frontmatter. If the page is about the concept, list it in concepts: [...]. The meta-tag contract already emits <meta name="concept"> + <link rel="tag"> + <link rel="glossary"> automatically.

How to find the right @id

Open site/src/_data/vocabulary.json and search for the concept's pref-label. The @id is the slug-form (e.g. "meta-tag-contract", "data-provenance", "page-info"). The href is always /glossary/#term-{@id} — that's what the build emits and what the popover script expects.

If the concept isn't in the vocabulary yet but should be, add it to vocabulary.json first (see the SKOS shape used by sibling entries) and re-run npm run build. The glossary page, /definitions.json, and head metadata regenerate from that single source.

What the popover does (and doesn't)

  • Feature-detects the Popover API; on browsers without it, every link is a normal navigation to the glossary anchor. Same accessibility either way.
  • Caches /definitions.json in sessionStorage under vb:definitions. One fetch per browsing session.
  • Respects Cmd / Ctrl / Shift / Alt / middle-click — those open the glossary page like any other link.
  • Native popover handles Escape and light-dismiss. We don't reimplement focus management.

Auto-linking at build time

Cook ships an auto-link-glossary plugin that scans every docs prose page (/docs/concepts/, /docs/elements/, /docs/attributes/, /docs/patterns/, /docs/integrations/, /docs/recipes/, /docs/snippets/) and wraps vocabulary terms automatically. The manual pattern above still works — author-written mentions always win and suppress auto-linking of that concept anywhere in the page.

What it does

  • First occurrence of a concept's prefLabel wraps with <dfn><a data-concept="...">
  • First occurrence of an opted-in altLabel wraps with <abbr title="prefLabel"><a data-concept="...">
  • Subsequent occurrences get a bare <a data-concept="...">
  • Max one auto-link per <p> / <li> / <td> / <dd> to keep prose readable

What it never touches

  • Text inside <a>, <code>, <pre>, <dfn>, <abbr>, <script>, <style>, <template>, <noscript>
  • Any heading (<h1><h6>)
  • Concepts the author already manually mentioned anywhere in the page

Opt-outs (three layers, decreasing scope)

MechanismScopeWhen to use
autolink: false in page frontmatter Whole page Reference pages, the glossary itself, anywhere prose isn't the focus
data-no-glossary attribute on any element That element and all descendants A specific section where auto-linking would distract
Manual <a data-concept="..."> anywhere on the page That one concept on this page You want explicit control over where the concept links from

Per-concept opt-in via vocabulary.json

By default, prefLabel auto-links and altLabel does not. That keeps short common-word altLabels like “review”, “lens”, or “index” from generating noise. To opt a concept's altLabels in, add a vb:autoLink object:

Defaults: prefLabel: true (auto-link the canonical name), altLabel: false (opt-in only). Both fields are optional.

See also