IO Marketing OS
Chrome Extension
Capture any webpage, analyze it through the IO Marketing OS framework, and build a live intelligence library — all from your browser toolbar.
Installation · Features · Workflows · Troubleshooting
The IO Chrome Extension sits in your browser toolbar and turns any webpage into an IO Marketing OS intelligence input. One click extracts the page's full structure — title, headings, word count, body text — and prepares it for analysis through the IO Platform's URL Intelligence Engine.
It does not require an account, does not send data to any server without your action, and stores your capture library locally in Chrome's storage. The analysis happens on demand through the IO Platform, where you control what gets sent and when.
Installation Guide
The extension installs directly in Chrome without requiring the Chrome Web Store — a process called "unpacked installation." It takes three minutes and four files.
Developer Mode · Load Unpacked · Four Files · Three Minutes
Because the IO Chrome Extension is not listed in the Chrome Web Store, it is installed as a developer extension. This is completely normal for internal tools, enterprise extensions, and extensions in active development. The process is supported natively by Chrome and takes no technical knowledge beyond following the steps below.
manifest.json, popup.html, popup.js, and background.js. All four are available in the IO Platform under the Chrome Extension tab, with copy buttons. You do not need an icon file; Chrome will use a default icon.io-extension. Place it somewhere permanent — your Desktop, Documents, or a dedicated dev folder. Do not move this folder after installation, or Chrome will lose the extension.mkdir ~/Documents/io-extension
io-extension folder with the exact filenames shown.manifest.jsonpopup.htmlpopup.jsbackground.js- Type
chrome://extensionsin the address bar and press Enter - Menu (⋮) → Extensions → Manage Extensions
- Keyboard: ⌘+Shift+E (Mac) or Ctrl+Shift+E (Windows)
io-extension folder — select the folder itself, not any file inside it. Click Open (or Select Folder on Windows). The IO Chrome Extension will appear in your extensions list.Verifying installation
After installation, you should see the IO Marketing OS extension card in your Extensions list. Navigate to any webpage, click the IO icon in the toolbar — the popup should appear with the current page's URL displayed. If it opens but shows an error, see the .
Updating the extension
When new versions of the extension files are released, copy the updated files into your io-extension folder (replacing the old ones), then go to chrome://extensions and click the refresh icon on the IO Marketing OS card. No need to re-install from scratch.
Permissions Explained
Chrome extensions require explicit permissions to function. The IO extension requests four — each with a specific, limited purpose. No data is sent to any external server without a deliberate user action.
activeTab · storage · scripting · contextMenus
| Permission | What it accesses | Why it's needed | What it cannot do |
|---|---|---|---|
| activeTab | The tab you are currently viewing, only when you click the extension icon | Reads the URL and title of the current tab to display in the popup | Cannot access any tab you haven't clicked the icon on; cannot run in background |
| storage | Chrome's local browser storage on your device | Saves your capture library (up to 100 pages) locally without a server or account | Stays on your machine; not synced to Google or any external service |
| scripting | Ability to run JavaScript on the active page when triggered | Extracts headings, body text, and word count from the current page when you click "Analyze" | Only runs when you explicitly click Analyze; cannot run automatically |
| contextMenus | The right-click menu in Chrome | Adds the "Analyze with IO Marketing OS" option to the right-click context menu | Only adds one menu item; cannot intercept other context menu actions |
The host_permissions field
The manifest includes "host_permissions": ["<all_urls>"]. This sounds broad — it means the extension can theoretically run on any URL — but in practice it is constrained by the other permissions. The scripting permission only executes when you click "Analyze This Page," and activeTab limits that to the tab you are currently looking at. The broad host permission is needed because the URLs of the sites you might want to analyze are not known in advance.
Reviewing what's stored
To see everything the extension has stored: open the Chrome DevTools console on any page, type chrome.storage.local.get(null, console.log), and press Enter. You will see the full contents of the local storage — your capture library and any pending analysis URLs. You can clear it all with chrome.storage.local.clear().
Your First Analysis
A complete walkthrough — from clicking the IO icon on a competitor's page to reading your first IO framework analysis in the Platform.
End-to-End Walkthrough · Extension → Platform → Insight
This walkthrough uses a real competitor's blog post as the example. The same flow works for any public webpage: a competitor's homepage, a piece of content you want to benchmark, a client's website, or an article you want to reverse-engineer.
The complete workflow
"The extension is not the analysis — it is the capture mechanism. The intelligence lives in the IO Platform. The extension makes capturing fast enough that it becomes a habit."
Analyze This Page
The primary feature — one click extracts the complete structural content of any webpage and prepares it for IO framework analysis. Here is exactly what it captures and how.
Content Extraction · What Gets Captured · How the Script Works
When you click "Analyze This Page," the extension injects a short content script into the active tab. The script strips non-content elements (scripts, styles, navigation, footers, headers, sidebars), then extracts the meaningful content. The extraction runs in milliseconds in your browser — nothing is sent to a server at this stage.
What gets extracted
The popup display after capture
After extraction, the popup shows a formatted summary in monospace text: the page title, word count, H1 headings, and the top five H2 headings. This gives you an immediate read on the page's structure before opening the IO Platform for full analysis. The URL is shown at the bottom — ready to copy.
What the capture does not do
The capture does not: access pages other than the one you are currently viewing; run without you clicking the button; log your browsing history; send any data to a server; capture paywalled or login-protected content (it reads only what the page renders publicly); or store images, CSS, or any non-text content.
Save to Library
Quick-save any page to your local capture library without running a full extraction. Use it while browsing to bookmark pages for later analysis — build your competitive intelligence archive one click at a time.
Quick Save · Capture Library · Local Storage · 100 Pages
The "Save to Library" button is the lightweight alternative to "Analyze This Page." Where Analyze runs a full content script extraction, Save to Library records only the URL, tab title, and a timestamp — making it near-instant and usable even on pages where scripting is restricted.
Use it when: you are actively browsing and find a page worth analyzing later; you want to batch-collect competitor pages for a competitive intelligence sprint; or you are on a page where full extraction doesn't work but you want to remember the URL.
What gets saved
| Field | Value | Source |
|---|---|---|
| url | The full page URL including query parameters | chrome.tabs API |
| title | The tab's title as shown in the browser | chrome.tabs API |
| savedAt | ISO 8601 timestamp of when Save was clicked | new Date().toISOString() |
| source | "quick-save" (from button) or "analyze" (from full extraction) | Extension logic |
Library limits and rotation
The library holds up to 100 entries. When the limit is reached, the oldest entry is removed automatically (first-in, first-out). The library persists across browser sessions — it survives closing and reopening Chrome. It is cleared if you uninstall the extension or manually clear extension storage.
Accessing your library
The popup displays your current library count in the footer area ("12 pages in library"). To view the full library contents, open Chrome DevTools (F12) on any page, go to the Console tab, and type:
chrome.storage.local.get(['io_captures'], (data) => { console.table(data.io_captures); });
This outputs your full capture library as a formatted table in the console — sortable by URL, title, timestamp, or source. To clear the library: chrome.storage.local.remove(['io_captures']).
The Capture Library
Your capture library is the persistent archive of every page you have saved or analyzed. It is stored locally in Chrome, survives browser restarts, and holds up to 100 entries — a rolling competitive intelligence queue.
Local Storage · 100-Entry Archive · Batch Analysis · Export
Think of the capture library as your IO analysis inbox. As you browse — reading competitors' blogs, checking backlink sources, reviewing industry publications — you save pages to the library. Later, in a dedicated analysis session, you open each saved URL in the IO Platform and run the analysis mode most relevant to that source.
Managing your library
| Operation | Command |
|---|---|
| View all entries | chrome.storage.local.get(['io_captures'], d => console.table(d.io_captures)) |
| Count entries | chrome.storage.local.get(['io_captures'], d => console.log(d.io_captures?.length)) |
| Export as JSON | chrome.storage.local.get(['io_captures'], d => console.log(JSON.stringify(d.io_captures, null, 2))) |
| Clear all entries | chrome.storage.local.remove(['io_captures']) |
| Remove one entry | chrome.storage.local.get(['io_captures'], d => { d.io_captures.splice(0,1); chrome.storage.local.set({io_captures: d.io_captures}) }) |
Using the library for competitive sprints
A competitive intelligence sprint using the IO extension: over one browsing session, right-click and save 15–20 competitor pages, blog posts, and landing pages. End of the session: 20 entries in the library. Next day: open each URL in the IO Platform URL Intelligence tool, run Competitive Analysis mode, copy each report. In 30–45 minutes you have 20 structured IO framework analyses of your competitive landscape.
The 100-entry limit
Chrome's storage.local has a 5MB default quota. The 100-entry limit is a precautionary cap set in the extension code — at typical page metadata sizes, 100 entries uses roughly 50–100KB, well within the quota. If you need more than 100 entries, export the current library to JSON (using the command above), clear it, and continue capturing.
The Full Analysis Workflow
Extension and Platform are two parts of one workflow. Neither is the complete system on its own. This is how they work together — from browser toolbar to actionable IO framework insight.
Extension → IO Platform → Analysis Mode → Insight → Action
The extension handles capture: extracting URLs and basic structure from any page, anywhere on the web, with one click. The IO Platform handles intelligence: running the captured URL through the full IO framework using Claude Sonnet 4, producing structured analysis in one of four modes. The two tools are designed to be used in sequence, not in isolation.
The complete workflow
| Stage | Tool | Action | Output |
|---|---|---|---|
| Capture | Chrome Extension | Click icon → "Analyze This Page" | Page URL + structural summary in popup |
| Queue | Chrome Extension | Right-click → "Save to Library" while browsing | URLs saved to local library for batch processing |
| Scrape | IO Platform · URL Intelligence | Paste URL → click Analyze | Full page content extraction via CORS proxy |
| Analyze | IO Platform · Claude API | Select analysis mode → run | Structured IO framework analysis (15–20 seconds) |
| Act | IO Platform · Content Builder | Use gaps identified → generate content | IO-formatted article HTML ready to paste |
| Build | io-complete.html | Paste generated HTML | New content live in the editorial site |
"The intelligence loop: capture with the extension, analyze in the platform, generate in the content builder, publish to the editorial site. Each stage feeds the next. The loop runs continuously."
Using analysis results to brief content production
The most productive use of a URL Intelligence analysis: take the "Content Gaps" section of the IO Full Analysis and paste it directly into the IO Platform Content Builder as the topic. The Content Builder receives the gap description and generates a complete IO-formatted article addressing that exact gap. From competitive intelligence to published content in under 30 minutes.
The URL Intelligence Engine
The IO Platform's URL Intelligence tool scrapes any URL and maps its content to the IO Marketing OS framework. It is the destination for URLs captured by the extension.
CORS Proxy Scraping · Four Analysis Modes · Claude Sonnet 4 · Copy Reports
The URL Intelligence Engine uses allorigins.win as a CORS proxy to fetch public webpage content from the browser — bypassing the Same-Origin Policy that prevents direct browser-to-URL fetching. This means it works on any public URL without requiring a server. The fetched HTML is cleaned (scripts, styles, and navigation stripped), parsed into structural components, and sent to Claude Sonnet 4 along with the selected analysis mode's system prompt.
What the engine analyzes
The engine sends the following to Claude for analysis: the full page URL, the document title and meta description, all H1/H2/H3 headings, and up to 4,500 words of body content. This gives the model enough context to run a substantive IO framework analysis without hitting token limits.
Limitations of the CORS proxy approach
The proxy approach works on most public pages but has edge cases: heavily JavaScript-rendered sites may return incomplete HTML (the proxy sees server-rendered HTML, not post-JavaScript content); pages behind paywalls or login walls return only the gating content; some sites actively block proxy requests and return a 403 or a bot-detection page. For pages that fail, use the extension's extraction (which runs in your actual browser, past any rendering) and manually note the URL for analysis of the extracted content.
The Four Analysis Modes
Each mode sends a different system prompt to Claude, framing the same page content through a different IO framework lens. Choose the mode that matches your intelligence objective before clicking Analyze.
Full Analysis · SEO+GEO · Competitive · Content Gap
| Mode | Best used when | Key output sections | Primary IO series |
|---|---|---|---|
| Full IO Analysis | You want a comprehensive first look at a page — overall fit, gaps, and recommendations | Fit score · Content gaps · Strengths · Top 5 improvements · Relevant IO articles | All 13 series |
| SEO + GEO Audit | You want to benchmark a page's search architecture and AI search readiness | Technical SEO rating · Topic cluster assessment · Entity SEO · GEO readiness · AEO coverage | Series 04 (seo1–seo9) |
| Competitive Analysis | You are analyzing a competitor's page and want strategic counter-positioning | Their positioning · Content strategy · Channel mix · Weaknesses · 3 counter-moves | Series 07, 09, 12 |
| Content Gap Finder | You want a specific list of missing content with IO article references for each gap | Critical gaps · Thin content · Missing formats · Journey stage gaps · Quick wins vs strategic gaps | Series 01, 04, 05 |
Running multiple modes on the same URL
There is no cost to running multiple modes on the same URL — each call is a separate API request, and the URL is scraped fresh each time (the IO Platform does not cache scrape results). For a comprehensive competitive analysis, run both "Competitive Analysis" (for strategic positioning insights) and "Content Gap Finder" (for tactical content opportunities) on the same competitor URL. Copy both reports and cross-reference them.
Reading the fit score
The Full IO Analysis mode returns a fit score from 0–100 with a one-sentence rationale. This is Claude's assessment of how well the page's content architecture aligns with IO Marketing OS standards — it is a directional benchmark, not a precise metric. Pages scoring above 70 have strong content foundations; pages below 40 have significant structural gaps. Use the gap list, not the score, to prioritize action.
Popup UI Walkthrough
Every element of the extension popup — what it shows, what it does, and what the status messages mean.
Popup Anatomy · Status States · Button Behaviors · Error Messages
The popup is a 340px-wide HTML interface that opens when you click the IO icon. It is built from a single popup.html file and controlled by popup.js. Understanding each element prevents confusion when the popup behaves unexpectedly.
The popup UI — annotated
| Element | What it shows | State when empty / loading |
|---|---|---|
| Header "Project IO" label | Static branding — always "Project IO" and "IO Marketing OS" | Always visible |
| URL bar (grey monospace) | The current tab's URL, truncated with ellipsis if long | Shows "Loading..." briefly on popup open |
| Analyze This Page (black button) | Primary action — triggers full content extraction | Disabled with opacity 0.4 while analysis is running |
| Save to Library (outlined button) | Quick save — records URL and title only | Always enabled |
| Status line (amber text) | Progress messages during analysis; confirmation after save | Empty between actions |
| Result block (monospace box) | The capture summary — title, word count, headings, URL | Hidden until first analysis |
| Library count (small grey footer) | "N pages saved in library" — updates after save or analyze | Hidden when library is empty |
Status message guide
Keyboard Shortcuts
Chrome extension keyboard shortcuts speed up capture during active research sessions. Most are Chrome defaults; one is IO-specific.
Keyboard Navigation · Custom Shortcuts · Chrome Defaults
| Action | Mac | Windows / Linux | Source |
|---|---|---|---|
| Open popup | ⌘ Shift E | Ctrl Shift E | Chrome default (last extension) |
| Open Extensions page | ⌘ Shift E (then click Manage) | Ctrl Shift E | Chrome default |
| Open Chrome DevTools | ⌘ Option I | F12 | Chrome default |
| Reload extension (after file update) | Go to chrome://extensions → click ↻ | Same | Manual — no shortcut |
| Analyze current page (popup open) | Tab to "Analyze" → Enter | Same | Standard HTML tab order |
| Save to library (popup open) | Tab → Tab → Enter | Same | Standard HTML tab order |
Setting a custom shortcut
Chrome allows custom keyboard shortcuts for extension actions. To set one: go to chrome://extensions/shortcuts, find "IO Marketing OS," and assign a shortcut for "Activate the extension." Recommended: ⌘+Shift+I (for "IO"). This opens the popup on any page with the shortcut.
// Add this to manifest.json (inside the root object) "commands": { "_execute_action": { "suggested_key": { "default": "Ctrl+Shift+I", "mac": "Command+Shift+I" }, "description": "Open IO Marketing OS" } }
After adding this to manifest.json, reload the extension at chrome://extensions. The shortcut will be active on the next popup open.
Common Errors & Fixes
The ten errors that appear most frequently — each with a diagnosis and a specific fix, not generic troubleshooting steps.
Error Diagnosis · Specific Fixes · Edge Cases
| Error message / Symptom | Cause | Fix |
|---|---|---|
| Extension icon is greyed out | You are on a chrome:// page, the Chrome Web Store, or a PDF | Navigate to a normal webpage. Chrome disables extensions on its own pages by design. |
| "Extension context invalidated" | The extension was reloaded (after a file update) but the popup was still open | Close the popup, reload the page you were on, then click the icon again. |
| "Frame was removed" | The page navigated away while the script was running | Wait for the target page to fully load before clicking Analyze. |
| Popup opens but URL shows blank | Race condition between tab query and popup render on very fast machines | Close and reopen the popup. The URL query runs on popup open; reopening triggers it again. |
| Analyze returns no headings | Page is client-rendered (React/Vue/Next.js) and headings aren't in the initial HTML | Scroll the page to trigger rendering, wait 2 seconds, click Analyze again. |
| Result box shows generic content | Site uses anti-scraping that injects fake H2s or replaces body content with CAPTCHA | The extension captures what Chrome renders. If Chrome shows you the real content, the extension captures it. If Chrome shows a CAPTCHA, the extension captures that instead. Solve the CAPTCHA and retry. |
| IO Platform scrape returns 403 | The target site blocks the allorigins.win proxy | Use the extension's popup summary (title, headings) as your analysis context instead. The extension runs in your browser and bypasses scraper blocks. |
| "Cannot access chrome:// URL" | Expected — Chrome's protected pages are inaccessible to extensions | Navigate to a normal http:// or https:// page to use the extension. |
| Context menu option missing | Background service worker crashed or did not start | Go to chrome://extensions → find IO Marketing OS → click the "Service Worker" link → if it shows an error, reload the extension. |
| Library count not updating | chrome.storage.local write failed (rare, usually quota-related) | Open DevTools console → run chrome.storage.local.getBytesInUse(null, console.log) to check quota. If near 5MB, export and clear the library. |
Checking the service worker health
If the context menu is missing or the extension behaves erratically, inspect the background service worker: go to chrome://extensions, find the IO Marketing OS card, and click the "Service Worker" link in the extension details. This opens a DevTools window for the service worker — the Console tab shows any errors. The most common: a syntax error in background.js from a copy-paste issue. Fix the file, reload the extension.
Site Compatibility
Not all websites work the same way. Here is how the extension handles the major website architectures it will encounter.
SSR vs CSR · Paywalls · SPAs · Enterprise Sites
LinkedIn and social platforms
The extension works on LinkedIn public pages — company pages, public posts, public profiles — because Chrome renders the full content when you are logged in. It captures the visible text, headings, and structure. For Twitter/X and Facebook, it captures whatever the page renders before any "Show more" expansions. The captures may be shorter than the full content but are sufficient for structural analysis.
Handling soft paywalls
Many publications (Medium, Substack paid) allow a limited number of free reads. If you have access to a page (the full content is visible in your browser), the extension captures it — because it reads what your browser has rendered, not what the server would show to an anonymous request. The IO Platform's proxy scraper operates as an anonymous request and will see only the gating screen; the extension sees the full article. Use the extension for soft-gated content.
FAQ
Fifteen questions that come up consistently — about data, privacy, capabilities, and practical use.
Data · Privacy · Capabilities · Practical Use
Does the extension send my browsing history anywhere?
No. The extension only runs when you explicitly click a button. It does not monitor your browsing, does not send data in the background, and does not communicate with any external server. Your capture library stays on your machine in Chrome's local storage.
Does it work in Incognito mode?
Not by default. Chrome prevents extensions from running in Incognito unless you explicitly allow it. To enable: go to chrome://extensions → IO Marketing OS → Details → toggle "Allow in Incognito." Data saved in Incognito does not persist after the Incognito window closes.
Can I use it on multiple computers?
Yes, but the capture library does not sync between computers — it is stored in local Chrome storage, not synced Chrome storage. You would need to install the extension on each computer and maintain separate libraries. The captured URLs are the portable part: they work anywhere.
Will Chrome warn me that this extension can "read and change data on all websites"?
Yes. This warning appears because of the host_permissions: <all_urls> field in the manifest — Chrome warns users about extensions that can run on any site. This is the same warning shown for most developer extensions. The actual data read is limited to what you trigger manually. The warning is accurate but broad — it describes the potential permission, not the actual behavior.
How do I know the extension is not doing something I cannot see?
The extension's code is fully visible to you — it is the four files you installed. background.js only registers the context menu item. popup.js only runs when the popup is open and you click a button. You can inspect both files at any time. You can also monitor extension network activity in Chrome DevTools (Network tab) — you will see no requests initiated by the extension itself.
Can it capture PDFs?
No. Chrome renders PDFs through its built-in PDF viewer, which is treated as a protected context that extensions cannot inject scripts into. To analyze a PDF's content, download it, extract the text manually, and paste key sections into the IO Platform's Content Builder directive field.
Does it work on the IO Platform itself (recursive capture)?
Yes. You can click "Analyze This Page" on the IO Platform and the extension captures its structure. This is not particularly useful for analysis, but it works. The IO editorial site (io-complete.html) can also be analyzed if hosted at a public URL.
Why is the capture library limited to 100 entries?
Chrome's storage.local has a 5MB default quota. The 100-entry cap is conservative — a precautionary limit. At typical page metadata sizes, 100 entries uses roughly 50–100KB. If you regularly need more, export the library as JSON and clear it to reset. A future version may increase the limit or add export/import functionality.
Can I share my capture library with a colleague?
Not directly — the library is in Chrome's local storage, which is not accessible from outside the browser. To share: export the library as a JSON array (see the Library page), send the JSON file, and have your colleague import it. There is no native import function in the current version; they would run chrome.storage.local.set({io_captures: [paste JSON array here]}) in their DevTools console.
Does capturing a page trigger any analytics or notifications on the target site?
The extension reads the DOM as Chrome has already rendered it — it does not make additional requests to the server. From the target site's perspective, only the normal page load event occurred. The extraction is invisible to the target site's analytics.
Can I analyze pages that require login?
If you are logged in to a site in Chrome, the extension can capture that page's content — because Chrome has rendered the full authenticated view. The extension reads what you see. What it cannot do is log you in to a site automatically or capture content that requires authentication you do not have.
Will it work on the next version of Chrome?
The extension is built to Manifest V3, which is Chrome's current and long-term supported extension format. Chrome deprecated Manifest V2 in 2024. MV3 extensions will continue to be supported for the foreseeable future.
How long does the Analyze action take?
The content extraction (in the extension) takes under 2 seconds on most pages. The IO Platform analysis (Claude API call) takes 15–25 seconds depending on API load and response length. Total time from click to IO framework analysis: under 30 seconds.
Can I modify the extension for my own use?
Yes — the files are yours. Common modifications: add additional content extraction fields to popup.js, modify the context menu label in background.js, change the popup dimensions in popup.html, or add your own IO Platform URL to the context menu action. Reload the extension after any file change.
Does it work on Brave, Edge, or other Chromium browsers?
Yes. Brave, Microsoft Edge, Opera, and most other Chromium-based browsers support Chrome extensions. Load it the same way — Developer Mode → Load Unpacked. The extension uses standard Chrome Extension APIs that are supported across all Chromium browsers.
File Reference
Complete documentation for each of the four extension files — what each does, what each contains, and how they interact.
manifest.json · popup.html · popup.js · background.js
| File | Purpose | Runs when | Communicates with |
|---|---|---|---|
| manifest.json | Extension configuration — declares permissions, file references, metadata, and version | At install time and on each Chrome restart | Chrome browser (configuration file, not executable) |
| popup.html | The UI markup — defines the visual structure of the popup window | Loaded when the popup opens (each click of the toolbar icon) | Loads popup.js |
| popup.js | All popup logic — tab URL display, content extraction, library save, status updates | Executes when popup.html loads; runs for the duration the popup is open | chrome.tabs, chrome.scripting, chrome.storage APIs |
| background.js | Service worker — registers context menu item on install; handles context menu clicks | On Chrome startup; wakes on context menu click events | chrome.contextMenus, chrome.storage, chrome.tabs APIs |
manifest.json — key fields explained
{
"manifest_version": 3, // MV3 — required by Chrome since 2024
"name": "IO Marketing OS", // Shown in toolbar tooltip and Extensions list
"version": "1.0.0", // SemVer — update when files change
"description": "...", // Shown in Extensions list
"permissions": [ // Explicit permission declarations
"activeTab", // Current tab access (click-triggered only)
"storage", // chrome.storage.local for library
"scripting", // Inject content script for extraction
"contextMenus" // Right-click menu item
],
"host_permissions": ["<all_urls>"], // Required for scripting on unknown URLs
"action": {
"default_popup": "popup.html", // The popup file
"default_title": "..." // Toolbar tooltip text
},
"background": {
"service_worker": "background.js" // MV3 uses service workers, not background pages
}
}
How the files interact
Chrome loads manifest.json at install time. It starts the background.js service worker. The service worker registers the context menu. When you click the IO icon, Chrome loads popup.html, which loads popup.js. When you click "Analyze This Page," popup.js calls chrome.scripting.executeScript to inject a content extraction function into the active tab. The extracted data is returned to popup.js, which displays the result in the popup UI and saves it to chrome.storage.local.
Changelog
Version history for the IO Chrome Extension — what changed, when, and why.
Version History · Breaking Changes · Deprecations
| Version | Date | Changes | Breaking? |
|---|---|---|---|
| 1.0.0 | March 2026 | Initial release. Four core features: Analyze This Page, Save to Library, Right-Click Context Menu, Capture Library. Manifest V3 architecture. | — |
Planned for v1.1
- Direct launch of IO Platform URL Intelligence from the popup (single-click flow)
- Selected text capture — right-click a text selection to add it to your library as a quoted insight
- Library export/import as JSON from within the popup
- Custom keyboard shortcut pre-configured in
manifest.json
Planned for v2.0
- Embedded analysis result in the popup — run IO framework analysis without leaving the page
- Site-wide crawl mode — save all links on a page to the library in one click
- Integration with the IO Platform content builder — one-click from analysis to article generation
Updating from a previous version
When a new version is released, copy the updated files into your io-extension folder (replacing the old ones). Go to chrome://extensions and click the refresh icon on the IO Marketing OS card. The extension reloads without losing your capture library — library data is in chrome.storage.local, which persists across extension reloads. If a version includes a breaking change to the storage format, it will be noted here with migration instructions.
"Software versions exist because understanding improves. Version 1.0 is not the final word — it is the most honest representation of current knowledge."
Brand Overview
The IO Platform design system is built on one foundational decision: editorial clarity over interface decoration. Every visual choice serves legibility, hierarchy, and trust — not novelty.
Design Philosophy · Core Principles · Identity System · Voice
Project IO began as an editorial system — a library of marketing intelligence organized like a publication. The design follows that origin. The aesthetic is closer to a quality journal than a SaaS dashboard: generous whitespace, a serif-and-sans pairing that gives content typographic authority, a restrained color palette that reserves accent color for genuine information, and a layout system that makes long-form content readable at length.
The visual language is deliberately un-trendy. No gradients. No glassmorphism. No decorative animations. The system should look as correct in 2030 as it does today.
The five design principles
What the IO brand is not
The IO brand is not a SaaS product brand. It does not use purple gradients, floating cards with drop shadows, or bold geometric illustration. It is not a startup brand — it does not chase trend cycles or use Figma-era sans-serif typefaces (Inter, Geist, DM Sans). It is not an agency brand — it does not use full-bleed black backgrounds with white-on-black type. It is a publication brand built for a long time horizon, designed to communicate depth and expertise through restraint and consistency.
"The best design systems are invisible. The interface disappears and the reader is left with the content. That is the goal."
The Color System
Thirteen CSS custom properties, one accent color, and a strict rule: color carries meaning. Never decoration.
CSS Variables · Palette · Usage Rules · Series Colors · Contrast
The core palette — CSS variables
The 13 series colors
Each series in the IO Marketing OS has an assigned color. These colors are used exclusively for that series' sidebar dot, article number label, article index dot, and tag background. They are never mixed — the color belongs to the series, not to a semantic meaning.
Color usage rules
| Use case | Color to use | Never use |
|---|---|---|
| Primary text | --ink (#1a1a1a) | Pure black (#000). Pure black reads as harsh; --ink is warm near-black. |
| Secondary / supporting text | --ink-muted (#5a5a5a) | Grey that is lighter than #5a5a5a — fails WCAG AA contrast on --page background. |
| Labels, overlines, meta text | --ink-faint (#999) | For body copy — too light for reading. Labels only. |
| Page background | --page (#fafaf8) | Pure white (#fff) as page bg — too stark. --page has a warm tint. |
| Surface (cards, sidebar) | --surface (#ffffff) | --page on cards — the distinction between page and surface creates visual depth. |
| Borders | --rule (1px) or --ink (structural/brand borders) | Color-family borders on non-series UI elements — borders carry structure, not decoration. |
| Accent / interactive / brand | --amber (#c47b1a) | Any other color as an interactive or accent color. Amber is the only accent. |
| Series identifier elements | The assigned series color | Using a series color for non-series UI elements — these colors are owned by their series. |
--page background exceeds WCAG AA (4.5:1 ratio). --ink on --page = 16.5:1. --ink-muted on --page = 7.1:1. --ink-faint on --page = 2.9:1 — use for large text and labels only (WCAG AA large text threshold: 3:1).Typography System
Three typefaces. A strict hierarchy. No custom fonts — the entire system runs on system-native typefaces available in every browser without a single font request.
Three Typefaces · Scale · Hierarchy · Usage Rules
The three typefaces
Operating System
font-family: 'Georgia', 'Times New Roman', seriffont-family: 'Helvetica Neue', Helvetica, Arial, sans-serifchrome://extensions
--ink: #1a1a1a;
font-family: 'Courier New', Courier, monospace| Element | Font | Size | Weight | Letter-spacing | Used for |
|---|---|---|---|---|---|
| Page title (.masthead-title) | Serif | clamp(2.2rem, 5vw, 3.8rem) | 400 | −0.02em | Masthead hero headlines |
| Article headline (.art-hl) | Serif | 2.1rem | 400 | −0.02em | Article page titles |
| Article deck (.art-deck) | Serif italic | 1.1rem | 400 | normal | Standfirst / article sub-title |
| Body copy (.body p) | Serif | 15px | 400 | normal | All long-form body text |
| Section heading (.body h3) | Sans | 13px | 700 | 0.08em | In-article section breaks, all-caps |
| Pull quote (.pull p) | Serif italic | 1.08rem | 400 | normal | Highlighted quotations |
| Sidebar item (.sb-item) | Sans | 11.5px | 400 / 600 active | normal | Navigation links |
| Section label (.sb-section) | Sans | 8.5px | 700 | 0.22em | Sidebar group headings, all-caps |
| Overline (.art-num, .series-label) | Sans | 10–11px | 700 | 0.15–0.22em | Series attribution, all-caps |
| Table header (.spec th) | Sans | 9.5px | 700 | 0.15em | Column headers, all-caps |
| Table body (.spec td) | Sans | 12.5px | 400 / 600 .sk | normal | Table data |
| Inline code (.body code) | Mono | 12.5px | 400 | normal | Code, file names, CSS vars in prose |
| Code block (pre) | Mono | 12.5px | 400 | normal | Full code examples |
Typography rules
- Never use bold (
font-weight: 700) in serif body text. Bold is reserved for the sans-serif labeling layer. - Never use italic in the sans-serif layer. Italic is reserved for serif body — specifically decks and pull quotes.
- Letter-spacing is only applied to the sans-serif layer. Serif text always uses normal letter-spacing.
- All-caps text is only used with the sans-serif, never with serif or mono.
- Do not add intermediate sizes. The scale is defined — use the defined steps, do not interpolate.
- Line-height: body text uses 1.82. Labels use 1.3. Navigation items use 1.3. Code uses 1.7.
Logo & Wordmark
The IO identity is a wordmark — "IO Marketing OS" set in Georgia at the brand's base size. There is no icon, no logomark, no symbol. The name is the logo.
Wordmark · Sidebar Brand · Clear Space · Misuse
The wordmark — as it appears in the sidebar
The three-line brand block
The sidebar brand block has three elements that always appear together in this exact order and hierarchy: (1) Overline — "Project IO" in sans-serif 9px, all-caps, spaced, faint — establishes the project namespace. (2) Wordmark — "IO Marketing OS" in Georgia at 1.25rem, near-black — the primary identity. (3) Subtitle — contextual descriptor (e.g. "13 Series · 94 Articles") in sans-serif 10px, faint — changes per page context.
Permitted wordmark variations
| Variant | Where used | Subtitle line |
|---|---|---|
| IO Marketing OS | Editorial site (io-complete.html) sidebar | 13 Series · 94 Articles |
| Chrome Extension User Guide | Documentation site sidebar | IO Marketing OS · v1.0 |
| IO Platform | IO Platform app sidebar | 13 Series · 94 Articles |
| Project IO | Topbar breadcrumb first segment | — (no subtitle in topbar) |
What never to do with the wordmark
- Never change the typeface from Georgia. The wordmark is inseparable from the typeface.
- Never set the wordmark in bold. Georgia regular is the only weight used.
- Never use an icon, symbol, or logomark alongside or in place of the wordmark.
- Never apply color to the wordmark. It is always
--inkon--surface. - Never use the wordmark without the overline ("Project IO"). The overline establishes the namespace — without it, "IO Marketing OS" is ambiguous.
- Never abbreviate to "IO" alone in UI contexts — "IO" is a namespace prefix, not the brand name.
The topbar breadcrumb identity
In the sticky topbar, the brand appears as a breadcrumb: Project IO › Current Section. This is the only other context where the brand appears as interface text. It uses the sans-serif family, not Georgia — because topbar text is UI, not identity.
Brand Voice & Tone
The IO editorial voice is expert, direct, and opinionated. It does not hedge. It does not use vague marketing language. It says what it means in the fewest words that still carry the full weight of the idea.
Voice Dimensions · Tone by Context · Dos and Don'ts · Writing Standards
The voice in four dimensions
| Off-voice | On-voice | Why |
|---|---|---|
| There are many approaches to consider | Use this approach. | The IO voice recommends. It does not list options without guidance. |
| Leveraging synergistic content strategies | Publishing content that earns backlinks | Replace marketing nominalizations with specific actions. |
| It's important to note that results may vary | Results vary by industry — B2B typically sees 3–6 months to first cluster ranking | Replace hedging with a specific, useful qualifier. |
| This powerful tool helps you optimize your workflow | This tool cuts briefing time from 40 minutes to 8 | Replace marketing adjectives with measurable outcomes. |
| We recommend considering utilizing a structured approach | Use a structured approach. | Remove modal hedging and nominalizations simultaneously. |
| Content marketing is a complex discipline | Most content marketing fails because the brief comes before the audience research | Replace a generic truth with a specific, arguable claim. |
Tone by context
The editorial voice does not change across contexts — the register adjusts. In long-form articles: full sentences, subordinate clauses, evidence-based argumentation, pull quotes that provoke. In UI labels: 2–4 words maximum, imperative verbs, no articles ("The" / "A") where they can be omitted. In error messages: state what happened, why, and what to do — in that order, in plain language, without blame. In section labels and overlines: all-caps, abbreviated, sparse — three words maximum.
The pull quote standard
Every article ends its major sections with a pull quote. The pull quote is always: (1) a complete, standalone sentence — it is not a teaser for the paragraph; (2) a claim, not a summary — it argues something, not just states it; (3) under 35 words; (4) set in the first person or present tense — "The brief is the most undervalued document in a marketing operation" not "Pull quotes should be statements."
"Write the pull quote first. If you cannot summarise the section's core argument in one opinionated sentence, the section does not have a clear argument yet."
Motion & Interaction
All motion in the IO system is functional. It communicates state change, confirms actions, or guides spatial orientation — never attracts attention or decorates.
Transitions · Easing · Hover States · Active States · Scroll
| Element | Property | Duration | Easing | Purpose |
|---|---|---|---|---|
| Sidebar (.sb-item) | background, color | 100ms | linear | Hover state feedback — fast, immediate |
| Sidebar collapse (.sb-sub) | max-height | 280ms | ease | Shows/hides sub-navigation naturally |
| Sidebar arrow (.arr) | transform (rotate) | 180ms | linear | Indicates open/closed state of section |
| Mobile sidebar (#sidebar) | transform (translateX) | 250ms | ease | Slide in/out on mobile |
| Index grid item (.ii) | background | 120ms | linear | Hover state on article grid cards |
| Article footer button (.afb) | background | 120ms | linear | Hover state on prev/next nav |
| Code copy button | background, color | 150ms | linear | Hover feedback on copy button |
| Page scroll (#main) | scrollTop | smooth | browser default | Smooth scroll on navigation (scrollTo) |
Hover state rules
- All interactive elements have a visible hover state. There are no "guess what's clickable" patterns.
- Hover background change:
#f5f4f0(slightly darker than--page). Used on sidebar items, index cards, arch cells. - Active / selected state:
#f0ede6(amber-tinted) with a right-hand 2px solid border in--ink. Used on sidebar items. - No scale transforms on hover. No float or elevation changes. No glow effects.
- Cursor:
pointeron all clickable elements without exception. The system never requires the user to guess.
What the system never does with motion
- No loading spinners — use status text instead (e.g. "Running analysis...")
- No entrance animations on panels — panels switch immediately
- No parallax scrolling — the reading surface is flat and stable
- No pulsing, bouncing, or attention-seeking animations of any kind
- No transition on color changes that are not state-related
"Motion that the user notices is motion that failed. The goal is state change confirmation, not choreography."
Component Library Overview
Every reusable pattern in the IO design system — documented with its HTML structure, CSS class names, usage rules, and live examples. The library covers the full surface area of the editorial site, documentation site, and platform UI.
13 Component Categories · HTML Patterns · CSS Classes · Usage Rules
The IO component library is not a collection of React components or a Storybook — it is a set of well-defined HTML patterns and CSS class conventions that compose consistently into pages. Every component in the system is built from the same underlying CSS custom properties defined in :root. Changing a CSS variable updates the entire system simultaneously.
The library is organized by layer: navigation first, then page structure, then content blocks, then inline elements, then feedback patterns. Each section documents the exact HTML required, which CSS classes are available, and the usage rules that make the pattern work correctly.
Masthead & Hero
The masthead is the full-width page header that appears at the top of index and overview panels. It contains the series label, page title, subtitle, a rule with tag line, and an optional stats bar.
.masthead · .series-label · .masthead-title · .masthead-rule · .masthead-stats
<div class="masthead"> <!-- Overline: series / section context --> <div class="series-label">Project IO · Series 04 of 13</div> <!-- The page title — Georgia, large, centered --> <h1 class="masthead-title">The SEO & GEO<br>Architecture</h1> <!-- Subtitle: 14px sans, --ink-muted, max-width 620px --> <p class="masthead-sub">One sentence describing what the section covers.</p> <!-- Rule with tag line --> <div class="masthead-rule"> <hr><span>Tag One · Tag Two · Tag Three</span><hr> </div> <!-- Stats bar — omit if not applicable --> <div class="masthead-stats"> <div class="mstat"> <span class="mstat-num">9</span> <span class="mstat-label">Articles</span> </div> </div> </div>
Usage rules
- The masthead only appears on index/overview panels. Individual article panels use the
.art-num+.art-hl+.art-deckpattern instead. - The
<br>in the title is intentional — masthead titles are often two lines for visual balance. Do not force all titles to one line. - The
.masthead-subsubtitle has a max-width of 620px and is centered. It should be a single sentence — no longer. - The
.masthead-statsbar is optional. Use it only when you have 3–7 meaningful quantitative stats for the section. Do not pad with weak stats. - The masthead has a
border-bottom: 2px solid var(--ink)— the same 2px weight used in the sidebar brand block. This creates visual consistency between the two structural anchors.
Article Structure
Every article panel in the IO system follows the same structural template: panel wrapper, content wrapper, article number, headline, deck, divider line, body content, and footer navigation.
.panel · .aw · .art-num · .art-hl · .art-deck · .art-div
<!-- The panel div — id must match go() calls --> <div class="panel" id="panel-{id}"> <!-- Content wrapper: max-width 800px, centered, padded --> <div class="aw"> <!-- Overline: series context, colored by series --> <div class="art-num" style="color:{series-hex}"> Series 04 · Article III of IX </div> <!-- Article headline: Georgia, 2.1rem --> <h1 class="art-hl">Technical SEO Infrastructure</h1> <!-- Deck: italic Georgia, muted, standfirst --> <p class="art-deck">One compelling standfirst sentence.</p> <!-- Divider with topic tags --> <div class="art-div"> <hr> <span>Topic · Topic · Topic</span> <hr> </div> <!-- Body content --> <div class="body"> <p>Body text...</p> <h3>Section Heading</h3> <p>More body text...</p> </div> <!-- Pull quote --> <div class="pull"><p>"Pull quote here."</p></div> <!-- Article footer navigation --> <div class="af"> <button class="afb" onclick="go('prev-id',null,'S','Prev')">← Prev</button> <div class="af-issue">Article III of IX · Series 04</div> <button class="afb" onclick="go('next-id',null,'S','Next')">Next →</button> </div> </div><!-- /.aw --> </div><!-- /.panel -->
The .art-div divider
The .art-div is a horizontal rule with a centered text label. The two <hr> elements grow to fill the available width using flex:1. The <span> contains 2–5 topic tags separated by · (·). These tags describe the article's key concepts — they appear only once, in the divider, and nowhere else in the article.
Panels vs. the .aw wrapper
The .panel div is the visibility container — it is display:none by default and display:block when the .active class is added by the go() function. The .aw div is the content wrapper — it sets max-width:800px, margin:0 auto, and the consistent padding. Every article panel has both layers. Index and overview panels may use the masthead outside the .aw wrapper (before it), then the .aw for the body content below.
Body & Typography
All text elements inside the .body wrapper — paragraphs, section headings, lists, inline code, links — and the pull quote component that lives between body and footer.
.body p · .body h3 · .body ul/ol · .body code · .pull
| Element | Rendered as | Key styles | Usage rule |
|---|---|---|---|
| .body p | Georgia 15px, 1.82 line-height, --ink | margin-bottom:1.2rem | Default body text. Never use div for prose. |
| .body h3 | Helvetica 13px, 700, all-caps, 0.08em spacing | margin:2rem 0 0.7rem | Section headings only. No h2 or h4 in body content. |
| .body h4 | Georgia italic 1.05rem | margin:1.5rem 0 0.5rem | Sub-section headings when h3 would be too heavy. |
| .body ul li | Georgia 15px, 1.7 line-height, disc marker | margin-left:1.4rem | Unordered lists. Use for non-sequential collections. |
| .body ol li | Georgia 15px, 1.7 line-height, decimal marker | margin-left:1.4rem | Ordered lists. Use only for sequential steps. |
| .body a | --amber color, border-bottom: 1px solid --amber | text-decoration:none; hover: opacity:.75 | Inline links in body prose. Use sparingly. |
| .body code | Courier New 12.5px, #f0ede6 background | padding:1px 5px; border-radius:3px | Inline code, file names, CSS vars in prose. |
| .body strong | font-weight:700 (sans context: sans bold; serif context: serif bold) | — | Use rarely — emphasis loses power when overused. |
The pull quote
The pull quote (.pull) lives outside the .body wrapper, between a content section and the next section or the footer. It is always the last element before the .af footer. The pull quote has a 3px amber left border and an amber-tinted background. The text inside is Georgia italic.
<div class="pull"> <p>"Pull quote text — one sentence, a claim, under 35 words."</p> </div>
.body div. The .body class applies Georgia and line-height through descendant selectors — nesting creates specificity conflicts.Data Tables
The spec table is the primary data display pattern. It handles reference tables, comparison matrices, configuration guides, and specification sheets — always inside the system card container.
.sc · .spec · .sk · .sv · .sc-label · .sc-title
<!-- The .sc container wraps every spec table --> <div class="sc"> <!-- Small uppercase label above the title --> <div class="sc-label">Reference</div> <!-- Georgia title for the table --> <div class="sc-title">Table Title Here</div> <table class="spec"> <thead> <tr> <th>Column A</th> <th>Column B</th> <th>Column C</th> </tr> </thead> <tbody> <tr> <!-- .sk: key cell — bold, --ink, nowrap --> <td class="sk">Row key</td> <!-- .sv: value cells — --ink-muted --> <td class="sv">Value one</td> <td class="sv">Value two</td> </tr> </tbody> </table> </div>
Table design rules
- Always wrap spec tables in a
.scsystem card. A bare table without the.scwrapper lacks the visual container that gives it separation from body text. - The first column always gets
.sk(key). It is bold,--ink, andwhite-space:nowrap. All other columns get.sv(value). - Column headers (
th) are automatically styled: 9.5px sans-serif, 700, all-caps, 0.15em letter-spacing,--ink-muted, with a1.5px solid var(--ink)bottom border. - Rows have a hover state:
background:#f5f4f0on all cells in the hovered row. - Tables should have 3–7 columns maximum. Beyond 7 columns, readability degrades. Split into two tables or simplify.
- Row count: 4–12 rows is the sweet spot. Below 4: use a list instead. Above 12: consider splitting into sections.
Cards & Grid Layouts
Four grid and card patterns cover all multi-column layout needs: the system card for contained content blocks, the channel columns grid for feature comparison, the index grid for article listings, and the architecture grid for section overviews.
.ch-cols · .ig · .arch-grid · .sc · CSS Grid · auto-fit
| Pattern | Classes | grid-template-columns | Use for |
|---|---|---|---|
| Channel Columns | .ch-cols → .ch-col → .ch-head + .ch-item | repeat(auto-fit, minmax(140px, 1fr)) | Feature comparison, property lists, multi-column attribute groups. 3–5 columns typical. |
| Index Grid | .ig → .ii → .ii-num + .ii-name + .ii-sub | repeat(auto-fill, minmax(210px, 1fr)) | Article listings, overview index cards. Each card is clickable and navigates to its panel. |
| Architecture Grid | .arch-grid → .arch-cell → .ac-over + .ac-title + .ac-desc | repeat(3, 1fr) — or inline override | Series overview cards on the home panel. Separated by 1px rules with the grid gap as the rule. |
| System Card | .sc → .sc-label + .sc-title + (table or content) | N/A — block layout | Wraps spec tables, config examples, and any contained reference block. |
<div class="ch-cols"> <div class="ch-col"> <!-- .ch-head uses inline style for color: background + color --> <div class="ch-head" style="background:#faeeda;color:#412402">Column Title</div> <div class="ch-item">Item one</div> <div class="ch-item">Item two</div> <div class="ch-item">Item three</div> </div> <!-- repeat for each column --> </div> <!-- Index grid (.ig) --> <div class="ig"> <div class="ii" onclick="go('panel-id',null,'Section','Title')"> <div class="ii-num"> <span class="iddot" style="background:{series-color}"></span>Article I </div> <div class="ii-name">Article Title</div> <div class="ii-sub">One sentence description.</div> </div> </div>
The .ig gap trick
The index grid uses gap:1px; background:var(--rule) on the container, with background:var(--surface) on each .ii card. This creates 1px grid lines between cards using the gap color — no borders needed on individual cards. The result is a seamless grid that maintains the dividing lines without doubling border thickness at the joins.
Notices & Callouts
Four notice types for contextual information that sits outside the regular flow: tips, warnings, informational notes, and cautions. Each has a distinct left border color, background tint, and icon.
.notice · .notice.tip · .notice.warn · .notice.info · .notice.caution
<!-- Replace class "tip" with: info | warn | caution --> <div class="notice tip"> <!-- Icon: ✓ (tip) · ℹ (info) · ⚠ (warn) · ! (caution) --> <span class="notice-icon">✓</span> <div class="notice-body"> <!-- Optional strong lead-in --> <strong>Lead-in label:</strong> Notice body text here. </div> </div>
| Type | When to use | When not to use |
|---|---|---|
| tip | Best practices, "pro tip" guidance, recommended patterns, positive reinforcement | Do not use for neutral information — that's info. Do not use to compliment the reader. |
| info | Definitions, background context, cross-references, "before you start" prerequisites | Do not use for warnings or best practices — specificity matters. |
| warn | Common mistakes, edge cases that break things, "note that" qualifications, gotchas | Do not use for normal caveats in body prose — reserve warn for genuinely important qualifications. |
| caution | Irreversible actions (deleting data, clearing library), security risks, breaking changes | Do not overuse — caution loses impact if it appears more than once per article. |
Code Blocks
The dark-background code block with filename header, syntax highlighting classes, and an interactive copy button. Used for multi-line code examples, configuration files, and shell commands.
.codeblock · .codeblock-head · .codeblock-copy · .kw .st .cm .fn .nm
<div class="codeblock"> <div class="codeblock-head"> <!-- Left: filename or description --> <span>manifest.json</span> <!-- Right: copy button — calls copyCode(this) --> <button class="codeblock-copy" onclick="copyCode(this)">Copy</button> </div> <pre> <!-- Syntax highlighting classes: --> <!-- .kw = keyword / HTML tag → amber #c47b1a --> <!-- .st = string / value → green #7ab87a --> <!-- .cm = comment → grey #666, italic --> <!-- .fn = function / attr → blue #88aadd --> <!-- .nm = number / special → purple #dda0dd --> <span class="kw"><div></span>example<span class="kw"></div></span> </pre> </div>
The copyCode() function
The global copyCode(btn, text) function handles copy button behavior. It accepts the button element and an optional text string. If text is provided, it copies that string. If not, it reads the inner text of the nearest pre element (the code block's content). After copying, it temporarily changes the button label to "Copied!" for 1.8 seconds, then resets. All copy buttons in the system use this same function — it is defined once in the page-level script block.
When to use a code block vs inline code
Inline code (.body code) is for short strings in prose: file names, CSS class names, CSS variables, short commands. The dark code block is for: anything multi-line, anything the user needs to copy in full, configuration files, shell commands, and full HTML patterns. The rule: if it needs a copy button, it needs a code block.
Tags & Badges
The tag system provides colored labels for classification, status indication, and topic marking. Sixteen color classes cover the full palette — each with a matching background tint, text color, and border.
.tag · .c-amber · .c-green · .c-blue · .c-coral · all 16 classes
<!-- Tags container --> <div class="tags"> <span class="tag c-amber">Tag label</span> <span class="tag c-green">Another tag</span> <span class="tag c-blue">Third tag</span> </div>
| Class | Background | Text | Border | Use for |
|---|---|---|---|---|
| .c-amber | #faeeda | #412402 | #ba7517 | S01 · Primary accent · Featured items |
| .c-green | #eaf3de | #173404 | #3b6d11 | S02 · Success · Active · Enabled |
| .c-coral | #faece7 | #4a1b0c | #d85a30 | S03 · Warning · Changed · Modified |
| .c-pink | #fbeaf0 | #4b1528 | #993556 | S04 variant · Highlighted |
| .c-blue | #e6f1fb | #042c53 | #185fa5 | S10 · Informational · Link-adjacent |
| .c-purple | #eeedfe | #26215c | #534ab7 | S08 · Component library · UI elements |
| .c-red | #fcebeb | #501313 | #a32d2d | Error · Destructive · Danger · S09 |
| .c-teal | #e1f5ee | #04342c | #0f6e56 | S02 variant · Selected · Active state |
| .c-gray | #f1efe8 | #2c2c2a | #5f5e5a | Neutral · Archived · Disabled |
| .c-orange | #fef0e6 | #4a1800 | #c94f35 | S12 · Pending · In-progress |
| .c-cyan | #e1f7fc | #002a35 | #0088aa | S06 · Audience · Community |
| .c-gold | #fdf6e0 | #3d2600 | #b8961a | S07 · Brand · Premium |
| .c-violet | #f5eafe | #330052 | #7a2fa5 | S08 variant · Creative |
| .c-indigo | #e8ecfe | #060d33 | #1a3fa5 | S13 · Data · Technical |
| .c-rose | #fce8ef | #450020 | #b53060 | S05 · Creative Production |
Step Flows
The step flow pattern documents sequential processes. It uses amber numerals for orientation, step titles in the sans-serif labeling system, and step descriptions in body prose — with optional inline code blocks beneath each step.
.steps · .step · .step-num · .step-title · .step-desc · .step-code
<div class="steps"> <div class="step"> <!-- Amber numeral: 22px Georgia, right-aligned, 32px wide --> <div class="step-num">1</div> <div class="step-body"> <!-- Title: 13px sans, 700, all-caps --> <div class="step-title">Step Title Here</div> <!-- Description: Georgia 14.5px prose --> <div class="step-desc">Description of what to do in this step.</div> <!-- Optional: inline code example --> <code class="step-code">$ command --flag value</code> </div> </div> <!-- Repeat .step for each step. Last step has no border-bottom. --> </div>
Step flow design rules
- Steps are separated by a 1px
--ruleborder-bottom. The last step has no border — CSS's:last-childselector handles this automatically in the.steprule. - Step numbers are always numerals (1, 2, 3...) — never letters, Roman numerals, or bullets. The amber color from
--ambergives them visual weight without competing with body text. - Step titles are short (3–6 words), all-caps, sans-serif — they function as commands, not descriptions.
- The
.step-codeelement is an inline<code>with a block display style: grey background, monospace, padded. Use it for single commands. For multi-line code, use a full.codeblockafter the.step-body. - Maximum 8–10 steps per flow. Beyond that, split into phases or use a different pattern.
The UI Mockup Frame
A browser-window frame for embedding interface demonstrations in documentation. It provides a chrome bar with traffic light dots and a title, creating the visual context of "this is a UI screenshot" without requiring actual images.
.ui-mock · .ui-mock-bar · .ui-dot · .ui-mock-title · .ui-mock-body
dashboard widget, or any interface element.
<div class="ui-mock"> <!-- Browser chrome bar --> <div class="ui-mock-bar"> <!-- Traffic light dots: always these three colors --> <div class="ui-dot" style="background:#ff5f57"></div> <div class="ui-dot" style="background:#ffbd2e"></div> <div class="ui-dot" style="background:#28c940"></div> <!-- Window / tab title --> <span class="ui-mock-title">Window title</span> </div> <!-- Content area — no padding applied; add inline --> <div class="ui-mock-body"> <!-- Your UI content here --> </div> </div>
Usage rules
- The traffic light colors are always the exact macOS values —
#ff5f57,#ffbd2e,#28c940. Do not substitute other reds/yellows/greens. - The
.ui-mock-bodyhas no default padding — add it inline on the body element to match the UI being demonstrated. - The
.ui-mockcontainer has a1.5px solid var(--ink)border and a subtlebox-shadow. Do not add additional borders or backgrounds to the container. - Use this pattern only when showing a UI as an example or annotation. Do not use it as a decorative container for non-UI content.
- For extension popup mockups specifically: set the
.ui-mock-bodycontent width to 340px to match the actual popup dimensions.