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.
Defines the document title shown in browser tabs, bookmarks, history, search results, and screen reader announcements.
The <title> element defines the document's name. It is the only required element inside <head> and serves as the primary identifier for the page across every browser surface: tabs, bookmarks, history, task switchers, and search engine results. Screen readers announce it first when a user lands on a page.
Beyond static identification, document.title is a live, writable property. This makes the browser tab a zero-UI status channel: prepend an emoji for state, a count for notifications, or cycle through animation frames for a loading indicator — all without touching the visible page.
<title>Page Title</title>
VB uses the Page | Site convention. The page-specific part comes first so users scanning many tabs see the distinguishing content before it gets truncated.
<title>Page Title | Site Name</title>
| Pattern | Example | When to Use |
|---|---|---|
Page | Site |
Pricing | Acme Corp | Default for most pages. VB's head partial generates this automatically. |
Site |
Acme Corp | Homepage only, where the page name would be redundant. |
(n) Page | Site |
(3) Inbox | My App | Notification count. The number draws the eye in a row of tabs. |
Emoji Page | Site |
✉️ New message | My App | Status indicator. Works without JavaScript if the state is known at render time. |
Keep titles under 60 characters. Search engines truncate around that point, and shorter titles are easier for screen reader users to parse.
The simplest form of tab status: prepend an emoji to the title in your HTML. No JavaScript required. This works for any state known at render time — connection status, build results, form state.
<!-- Connection status --><title>🟢 Connected | Dashboard</title><title>🔴 Offline | Dashboard</title> <!-- Activity status --><title>⏳ Uploading... | Files</title><title>✅ Upload complete | Files</title> <!-- Notification types --><title>📬 (3) new | Inbox</title><title>⚠️ Action required | Settings</title>
Choose emoji that are visually distinct at small sizes. Colored circles, warning signs, and mail icons work well. Avoid emoji that look similar across platforms or require fine detail to distinguish.
document.title is writable and updates the browser tab immediately. Use it for SPA route changes and notification badges.
<script>// Save the original title onceconst originalTitle = document.title; // Update on route change (SPA)function setPageTitle(pageName) { document.title = pageName + ' | My App';} // Add a notification countfunction setNotificationCount(count) { document.title = count > 0 ? `(${count}) ${originalTitle}` : originalTitle;}</script>
<title>(3) Inbox | My App</title>
Cycle through an emoji sequence with setInterval to create a loading spinner directly in the browser tab. This is a progressive enhancement — the tab title remains readable text with a rotating prefix.
<script>// Emoji sequences that animate well in browser tabsconst spinners = { moon: ['🌑','🌒','🌓','🌔','🌕','🌖','🌗','🌘'], clock: ['🕛','🕐','🕑','🕒','🕓','🕔','🕕','🕖','🕗','🕘','🕙','🕚'], earth: ['🌍','🌎','🌏'], braille: ['\u280B','\u2819','\u2839','\u2838','\u283C','\u2834','\u2826','\u2827','\u2807','\u280F'], dots: ['\u22C5','\u2219','\u25CF','\u2219'],}; let timer = null;const originalTitle = document.title; function startSpinner(type = 'moon') { const frames = spinners[type]; let i = 0; timer = setInterval(() => { document.title = frames[i++ % frames.length] + ' ' + originalTitle; }, 150);} function stopSpinner() { clearInterval(timer); timer = null; document.title = originalTitle;} // Usage: show spinner during a fetchstartSpinner('clock');fetch('/api/data') .then(res => res.json()) .then(data => { stopSpinner(); // ... use data }) .catch(() => { stopSpinner(); document.title = '\u26A0\uFE0F ' + originalTitle; });</script>
These sequences animate smoothly in browser tabs. Choose based on the mood you want:
Braille dot spinners are monochrome and compact — they work best when you want a subtle, terminal-style indicator. Moon and clock emoji are more playful and visible at a glance.</p> <h3>Performance</h3> <p>Pause the spinner when the tab is hidden. The animation is invisible anyway, and clearing the interval avoids unnecessary work:</p> <code-block language="javascript" label="Visibility-aware spinner" data-escape><script>// Pause spinner when tab is hidden (saves CPU)document.addEventListener('visibilitychange', () => { if (document.hidden) { stopSpinner(); } else if (isLoading) { startSpinner(); }});</script>
document.title updates are not throttled by browsers (unlike history.pushState, which Chrome and Safari limit to ~100 calls per 30 seconds). An interval of 100–200ms produces smooth animation without measurable overhead.
The Badging API is the platform-native way to show a numeric badge on an installed PWA's icon in the dock or taskbar. It complements title-based indicators but does not replace them — the Badging API only works for installed web apps, while title updates work in every browser tab.
<script>// Modern Badging API (installed PWAs only)// Sets a numeric badge on the app icon in the dock/taskbar // Show unread countnavigator.setAppBadge(5); // Show flag-only badge (no number)navigator.setAppBadge(); // Clear badgenavigator.clearAppBadge(); // Progressive enhancement: badge API + title fallbackfunction setBadge(count) { // Update the tab title (works everywhere) const base = document.title.replace(/^\(\d+\)\s*/, ''); document.title = count > 0 ? `(${count}) ${base}` : base; // Also set the app badge if available (installed PWAs) if ('setAppBadge' in navigator) { count > 0 ? navigator.setAppBadge(count) : navigator.clearAppBadge(); }}</script>
| Approach | Where It Shows | Requires |
|---|---|---|
document.title |
Browser tab, task switcher, history | Nothing (works everywhere) |
navigator.setAppBadge() |
App icon in dock/taskbar/home screen | PWA install, HTTPS, browser support |
Use both together for the best coverage. The title fallback ensures uninstalled users still see the notification count.
The <title> element has no element-specific attributes. It supports global attributes, though in practice none are commonly used on it.
Its content model is text only — no child elements, no HTML tags. The text between the opening and closing tags becomes the document's title.
Page | Site format puts the distinguishing content first. A screen reader user scanning multiple tabs hears the unique part immediately rather than hearing the site name repeated for every tab.🕐 is announced as "one o'clock," 🌑 as "new moon." This is meaningful but can be verbose — avoid stacking multiple emoji in the title.aria-live regions for important status messages rather than relying on title changes alone.<h1> heading. Mismatches between the tab title and the visible heading confuse both users and search engines.<title> and <meta name="description"> work as a pair — the title is the headline, the description is the summary. Make sure they complement each other.| Surface | What Shows |
|---|---|
| Tab bar | Title text, truncated to fit. Emoji render in color on macOS/iOS, monochrome on some Windows versions. |
| Bookmarks | Title at the time of bookmarking. Does not update with document.title changes. |
| History | Title at the time the history entry was created. SPA route changes should update document.title before calling history.pushState(). |
| Task switcher | OS-level alt-tab and task views show the current document.title. |
| Search results | Server-rendered title from the HTML source. JavaScript changes are not indexed. |
| Social previews | og:title takes precedence. Falls back to <title> if no OG tag is present. |
<head> — Parent element; VB's head partial generates the title from frontmatter<meta> — description meta pairs with the title for search results<link> — Canonical URL and favicon, which appear alongside the title in tabs<html> — The lang attribute on the root element affects how screen readers pronounce the title