Website Antipatterns

Common antipatterns that break consistency, maintainability, and user experience.

What Is an Antipattern?

An antipattern is a common approach that seems helpful but causes more problems than it solves. These documents catalog the antipatterns to avoid and the right way to do things.

1. Copy-Pasting Navigation and Footers

Antipattern: Every page contains a full copy of the navbar (40+ lines) and footer (85+ lines) as inline HTML. When you need to update the nav, you must edit 60+ files.

Why it fails:

  • Changes require editing every page individually — error-prone and time-consuming
  • Inconsistencies creep in: different pages link to different destinations, use different badge spacing, etc.
  • A single typo in one page's footer goes unnoticed
  • The site grows stale because updates are too tedious to apply everywhere

Right way: Extract shared components (navbar, footer, scripts) into fragment files and load them via JavaScript. Define the nav and footer in ONE place.

<!-- Page only contains placeholders -->
<div id="nav-placeholder"></div>
... page content ...
<div id="footer-placeholder"></div>
<script src="js/include.js"></script>

<!-- includes/nav.html defines the nav once -->

2. Hand-Calculating Relative Paths

Antipattern: Each page manually computes paths like `../../../css/styles.css` or `../../products/sam.html` based on its directory depth. When pages move or a new directory level is added, all paths break.

Why it fails:

  • A page at depth 3 uses `../../../` but gets moved to depth 2 — now all paths 404
  • Developers copy a page from one directory to another and forget to adjust paths
  • Inconsistencies: some pages use `../docs/index.html`, others use `../../index.html` for the same destination
  • External links like GitHub URLs are easy to get right; relative paths are error-prone

Right way: Calculate paths at runtime based on the page's URL depth. A single JS function handles all depth levels.

// js/include.js calculates the correct root prefix
function calculatePaths() {
    var parts = window.location.pathname.split('/');
    var dirParts = parts.slice(1, -1); // remove filename and leading empty
    var root = '../'.repeat(dirParts.length);
    return { root: root, docs: root + 'docs/' };
}

3. Inline <style> Blocks in HTML

Antipattern: Pages define their typography, layout, and component styles inside <style> tags in the <head>. Each page has its own copy.

Why it fails:

  • Styles diverge between pages — same element looks different on different pages
  • When you want to change a heading size or color, you must edit 60+ files
  • Bloat: the same CSS is shipped on every page, just duplicated
  • Impossible to maintain a consistent design system

Right way: All shared styles live in css/styles.css. Pages only include the external stylesheet.

4. Redundant UI Elements

Antipattern: Adding elements that duplicate information already present. For example, a "product switcher" dropdown on the docs hub page when the page already has product cards below it. Or a "Better Together" section when the hero already explains the ecosystem.

Why it fails:

  • Cognitive overload: users see the same information twice
  • Maintenance burden: when one copy is updated, the other is forgotten
  • Visual noise: the page feels cluttered and unfocused

Right way: One element per piece of information. If the docs hub has product cards, don't also add a product switcher dropdown.

5. Having Two Sources of Truth

Antipattern: Writing documentation that duplicates or contradicts the source code. For example, listing 10 tools when the source has 11, or showing a model name that's been deprecated.

Why it fails:

  • Documentation drifts from reality — users follow docs that don't match the software
  • Trust erodes: when docs are wrong, users stop believing them
  • Maintaining two copies means updating both when changes happen

Right way: Read source code before writing documentation. Verify every fact against ../SAM/Sources/, ../CLIO/lib/CLIO/, or ../ALICE/src/. If a fact can't be verified, label it [NEEDS VERIFICATION].

6. Specific Model Names in Docs

Antipattern: Listing specific LLM model names like "GPT-4o" or "Claude 3.5 Sonnet" in documentation examples.

Why it fails:

  • Model names change frequently — "GPT-4o" becomes "o1" or "GPT-5"
  • Docs become stale within months, misleading users
  • Different providers offer different models at different times

Right way: Use provider-level descriptions. Say "Set model (run /api models to list available)" instead of "Set model (gpt-4o, claude-3-5-sonnet, etc.)".

7. ES Module Scripts in Simple HTML

Antipattern: Using <script type="module"> with import statements for libraries in simple static HTML pages.

Why it fails:

  • ES module imports require CORS headers that python3 -m http.server doesn't set
  • Breaks in production if the CDN is unreachable
  • Simpler script tags work everywhere without configuration

Right way: Use standard <script src="..."> tags for CDN libraries like Mermaid.js.

Prevention

To avoid these antipatterns in future sessions:

  1. Always read the .doc-template.html first — it defines the intended page structure
  2. Check if a fragment already exists in includes/ before copy-pasting markup
  3. Run the link validator (python3 scripts/validate-links.py) before committing
  4. Verify against source code — read ../CLIO/lib/CLIO/, ../SAM/Sources/, ../ALICE/src/
  5. Test in the browser — don't just check HTTP 200, actually inspect the rendered page