How Hallmark's Frontend Architecture Works: A Zero-Dependency SPA Built on Template Swapping

Hallmark uses a pure vanilla‑JavaScript single‑page application that rebuilds the visible page on the fly by swapping archetype templates and injecting theme‑specific copy—no build step, no framework, and no external dependencies.

This architecture powers 20 distinct visual themes in the Hallmark project, a code analysis tool from Nutlope. The entire frontend lives in site/ and relies on native browser APIs: Web Components aren't used, yet the system achieves component‑like modularity through a slot‑based template system. Understanding this design reveals how far modern vanilla JS can take you without React, Vue, or Svelte.


The Core Abstraction: Three Maps Drive Everything

Hallmark's frontend architecture centers on three plain JavaScript objects that together define what users see. These reside in site/js/main.js and are consulted on every theme change.

Map Purpose Lines in main.js
THEMES Theme key → human‑readable label for the picker UI 42‑63
ARCHETYPES Theme → {hero, footer} component tuple (structural "bones") 70‑91
COPY Theme → text fixtures (eyebrow, title, lede, CTA, proof points) 98‑200

The archetype map is the distinctive design choice. Rather than applying CSS variables to recolor a fixed layout, switching themes in Hallmark rebuilds the DOM structure by selecting different hero and footer templates. This guarantees visual variety—two themes can share no markup whatsoever.


Slot System and Template Swapping

Templates live as standard <template> elements in site/index.html, organized by archetype. The JavaScript manipulates them through two DOM regions marked with data-slot attributes.

Slot Placement in HTML

<!-- site/index.html excerpt -->
<main>
  <div data-slot="hero"></div>
  <!-- ...page content... -->
  <div data-slot="footer"></div>
</main>

<template id="hero-marquee">...</template>
<template id="hero-quote-led">...</template>
<template id="footer-colophon">...</template>
<template id="footer-minimal">...</template>

The swapArchetypes() Function

This is the engine of Hallmark's frontend architecture. Located at lines 74‑96 in site/js/main.js, it:

  1. Looks up the archetype tuple from ARCHETYPES[theme]
  2. Finds the corresponding <template> elements by ID
  3. Clones their content
  4. Interpolates copy from COPY[theme] into marked elements
  5. Replaces the slot contents with the new fragment
// Conceptual flow from site/js/main.js lines 74-96
function swapArchetypes(theme) {
  const { hero, footer } = ARCHETYPES[theme];
  const copy = COPY[theme];
  
  // Hero slot
  const heroTemplate = document.getElementById(`hero-${hero}`);
  const heroClone = heroTemplate.content.cloneNode(true);
  interpolateCopy(heroClone, copy);  // fills [data-copy] elements
  document.querySelector('[data-slot="hero"]').replaceChildren(heroClone);
  
  // Footer slot (mirrors hero pattern)
  // ...
  
  // One-shot fade-in animation
  document.documentElement.style.viewTransitionName = 'theme-swap';
}

The copy interpolation matches elements with data-copy attributes (e.g., <span data-copy="eyebrow">) against keys in the theme's copy fixture.


Theme Application and State Management

The applyTheme() function (lines 53‑66) orchestrates the complete user‑visible transition:

// site/js/main.js lines 53-66
async function applyTheme(theme) {
  // 1. Persist choice
  localStorage.setItem('hallmark-theme', theme);
  document.documentElement.dataset.theme = theme;
  
  // 2. Update picker UI
  setPressed(theme);
  
  // 3. Visual transition (View Transition API where supported)
  if (document.startViewTransition) {
    await document.startViewTransition(() => swapArchetypes(theme));
  } else {
    swapArchetypes(theme);
  }
}

Progressive enhancement is built in: browsers with document.startViewTransition get a smooth morphing animation; others fall back to instant replacement without polyfills.


Critical Rendering Path: Beating First Paint

Hallmark's architecture includes a blocking script in <head> that eliminates flash-of-unstyled-content for themes. From site/index.html lines 65‑71:

<script>
  // Runs before first paint
  const params = new URLSearchParams(location.search);
  const stored = localStorage.getItem('hallmark-theme');
  const initial = params.get('theme') || stored || 'default';
  document.documentElement.dataset.theme = initial;
</script>

This guarantees the correct CSS theme selectors apply immediately. A second optimization waits for fonts before revealing content:

// site/js/main.js lines 73-88
document.fonts.ready.then(() => {
  document.documentElement.dataset.fontsReady = 'true';
});

CSS Architecture: Tokens to Sections

The stylesheet organization mirrors the component boundaries. All files live in site/css/:

File Responsibility
tokens.css Design tokens: OKLCH color palettes, typographic scales, motion curves, spacing units
base.css CSS reset, global layout foundations, data-theme attribute selectors
components.css Reusable UI primitives: buttons, tooltips, copy-buttons, theme picker dots
sections.css Hero and footer layouts specific to each archetype

Theme-specific styling uses attribute selectors generated from the data-theme value set during the critical rendering path:

/* site/css/base.css excerpt */
[data-theme="cobalt"] {
  --surface-1: oklch(20% 0.05 250);
  --text-1: oklch(95% 0.01 250);
  --accent: oklch(70% 0.15 250);
}

Interaction Layer: Zero-Dependency Utilities

The remaining ~200 lines of site/js/main.js implement ergonomic features without libraries:

  • attachCopyButtons() (lines 101‑110): Clipboard API with fallback for [data-copy-source] elements
  • Keyboard shortcuts: T cycles themes, R picks random
  • Hover preview: Tooltip on picker dots showing theme name
  • Easter egg: Hidden activation pattern for a surprise theme

These attach during DOMContentLoaded and use event delegation where possible.


Extending Hallmark: Adding a New Theme

The frontend architecture is intentionally flat and data-driven. Adding a theme requires no HTML changes if reusing existing archetypes, or new <template> blocks if creating novel layouts.

// site/js/main.js — extend three maps

const THEMES = {
  // ...existing themes...
  aurora: "Aurora",
};

const ARCHETYPES = {
  // ...existing themes...
  aurora: { hero: "gradient-text", footer: "colophon" },
};

const COPY = {
  // ...existing themes...
  aurora: {
    eyebrow: "Gradient typography",
    title: "HALLOWS' EVE",
    lede: "A theme that lives in the space between colors.",
    ctaLabel: "Explore themes",
    proofLabel: "From the Hallmark community",
  },
};

Then add to site/index.html:

<template id="hero-gradient-text">
  <h1 style="background: linear-gradient(...)">...</h1>
</template>

Reload—the picker automatically includes "Aurora" and handles all state transitions.


Summary

  • Hallmark's frontend architecture is a template‑swapping SPA with zero dependencies, using native <template> elements and the View Transition API.

  • Three maps (THEMES, ARCHETYPES, COPY) in site/js/main.js completely define available themes and their content.

  • Structural variety comes from archetype selection—each theme picks hero and footer templates, not just color schemes.

  • Performance is optimized through a blocking head script for theme selection and document.fonts.ready to prevent FOUT.

  • Extensibility is data-driven: new themes require only map entries and optional template definitions, with no build process.


Frequently Asked Questions

What frontend framework does Hallmark use?

Hallmark uses no frontend framework. It is built with vanilla JavaScript, HTML templates, and CSS. All interactivity—including theme switching, copy-to-clipboard, and keyboard shortcuts—is implemented with native browser APIs in site/js/main.js. The only modern API feature used optionally is the View Transition API for smooth theme changes.

How does Hallmark switch themes without reloading the page?

The applyTheme() function in site/js/main.js persists the choice to localStorage, updates the data-theme attribute on <html>, and calls swapArchetypes() to clone new template content and replace the data-slot regions. This happens entirely client-side; no server request occurs. Browsers supporting document.startViewTransition animate the swap natively.

Where are the theme templates defined in Hallmark?

Templates are inline <template> elements in site/index.html, located around line 885 in the "HERO ARCHETYPES" section. Each has an id following the pattern hero-{archetype} or footer-{archetype}. The JavaScript references these by ID when swapArchetypes() clones them for the active theme.

Can I add custom themes to Hallmark without modifying the core code?

Yes, but you must edit the source files. Extend THEMES, ARCHETYPES, and COPY in site/js/main.js, then add corresponding <template> blocks in site/index.html if using new archetypes. Because Hallmark has no build step, saving these files and reloading the browser immediately surfaces the new theme in the picker UI.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →