Welcome to Vanilla Breeze
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
Treat the native HTML rel attribute as VB's first-class vocabulary for link intent. Authors get safer outbound links by default, semantic CSS hooks with no classes, and a JS-discoverable index of links by intent.
The native rel attribute on <a> is standardised, ASCII-tokenised, machine-readable, and works directly with attribute selectors like a[rel~="external"]. Most sites under-use it — they reach for ad-hoc classes (.external-link, .tag-chip) or data attributes for things HTML already names. VB treats rel as the native vocabulary of link intent so authors don't have to invent one.
Doing so unlocks three concrete wins: safer outbound links by default, semantic CSS styling with zero classes, and a JS-discoverable index of links by intent that other VB features can build on.
These are the high-value tokens VB styles or upgrades. All are defined by the HTML Standard and MDN.
| Token | Meaning | VB treatment |
|---|---|---|
noopener | New window can't access window.opener | Auto-added by JS to any <a target="_blank"> without an explicit opener policy |
noreferrer | Sends no Referer header; implies noopener | Auto-added when <html data-link-privacy="strict"> |
opener | Explicitly preserve window.opener | Honored; suppresses the noopener upgrade |
external | Destination is on another site | Trailing ↗ arrow via CSS (no class needed) |
nofollow | Don't endorse the destination for ranking | No styling — pure metadata |
| Token | Meaning | VB treatment |
|---|---|---|
help | Context-sensitive help | Dotted-underline styling |
license | License under which the page is offered | Muted (smaller, slightly transparent) — footer-friendly |
privacy-policy | Link to the site's privacy policy | Muted |
terms-of-service | Link to the site's terms | Muted |
search | Link to a search resource for this document | No default styling — available as a CSS hook |
author | Link to info about the author | Available for author-card components to discover |
me | Linked resource represents the current author | Available for identity / profile blocks to discover |
| Token | Meaning | VB treatment |
|---|---|---|
prev | Previous document in a series | Leading ← arrow |
next | Next document in a series | Trailing → arrow |
bookmark | Permalink to nearest <article> ancestor | No default styling — CSS hook only |
| Token | Meaning | VB treatment |
|---|---|---|
tag | A tag (topic / facet) that applies to the page | Pill-chip styling |
alternate | Alternate representation (PDF, translation, etc.) | Pair with hreflang / type for language or media-type alternates; no default styling |
VB's link authoring discipline is just three rules. They let class, data-*, and rel each carry the kind of information they're best at without overlap.
rel is for link meaningIf a link has a meaningful relationship to the current document (it's external, it's the next chapter, it's the license, it's a tag), express that with rel. CSS and JS can then upgrade it without an extra class.
class is for visual variantsIf the only thing you want to change is appearance — muted, plain, button-styled — that's a class concern. VB ships a[data-variant] hooks for the common ones; pick those first.
data-* is for app configAnything app-specific (tracking IDs, analytics labels, behavior toggles) goes in data-*. Never overload rel as a styling hook — see the anti-patterns at the bottom.
All of these work with zero JavaScript. Just add the rel token in your HTML and the styling applies via src/utils/links.css.
<a href="https://example.com" rel="external">External site</a><!-- Renders with a trailing ↗ arrow. --><!-- target="_blank" is independent — add it if you want a new window. -->
<p> Topics: <a href="/tags/html" rel="tag">HTML</a> <a href="/tags/css" rel="tag">CSS</a> <a href="/tags/semantics" rel="tag">Semantics</a></p><!-- Each renders as a pill chip. -->
<nav aria-label="Lesson sequence"> <a href="/lesson-4" rel="prev">Previous lesson</a> <a href="/lesson-6" rel="next">Next lesson</a></nav><!-- Prev gets a leading ←, next gets a trailing →. -->
<footer> <a href="/license" rel="license">License</a> <a href="/privacy" rel="privacy-policy">Privacy</a> <a href="/terms" rel="terms-of-service">Terms</a> <a href="https://github.com/profpowell/vanilla-breeze" rel="external">Source</a></footer><!-- Legal links render muted; external picks up the ↗. -->
The init module at src/utils/links-init.js auto-runs on any page with a <a target="_blank">, and exports two helpers for direct use.
Every <a href target="_blank"> without an explicit opener policy (opener, noopener, or noreferrer) picks up rel="noopener" after the module runs. Idempotent — running twice is the same as running once.
<a href="https://example.com" target="_blank">External</a>
<a href="https://example.com" target="_blank" rel="noopener">External</a>
Other utilities can ask for "all the help links" or "all the tag links" on a page via collectLinksByRel:
import { collectLinksByRel } from '/src/utils/links-init.js'; const byRel = collectLinksByRel(document);const helpLinks = byRel.get('help') ?? [];const tagLinks = byRel.get('tag') ?? [];const legalLinks = [ ...(byRel.get('license') ?? []), ...(byRel.get('privacy-policy') ?? []), ...(byRel.get('terms-of-service') ?? []),];
Sites that want stronger privacy on outbound links can opt in by setting data-link-privacy="strict" on the root <html> element. Upgraded links then also receive rel="noreferrer", which suppresses the Referer header on the outbound request.
<html lang="en" data-link-privacy="strict"> <!-- ... --> <a href="https://example.com" target="_blank">External</a> <!-- After init: rel="noopener noreferrer" --></html>
This is opt-in because some site flows (referral analytics, affiliate tracking, server-side request filtering) legitimately depend on the Referer header. Don't enable it globally without understanding the consequences.
Don't use rel as a styling hook for things that aren't link relationships. The whole point of the three rules is that each attribute carries the kind of information it's best at.
<!-- rel is not for visual variants --><a href="/checkout" rel="primary-cta big-button">Buy now</a>
<!-- class is for visual variants --><a href="/checkout" class="button primary">Buy now</a>
<!-- rel is not for analytics or behavior config --><a href="/pricing" rel="track-pricing-click">Pricing</a>
<!-- data-* is for app config --><a href="/pricing" data-track="pricing-click">Pricing</a>
<!-- target="_blank" without an opener policy is a security smell --><a href="https://example.com" target="_blank">External</a>
<!-- VB's JS adds noopener automatically; you can also do it by hand --><a href="https://example.com" target="_blank" rel="external noopener">External</a>