How to Create a Custom Theme in Hallmark vs Using the Catalog

To create a custom theme in Hallmark, you manually extend the three core data structures—THEMES, ARCHETYPES, and COPY—in site/js/main.js, whereas using the catalog simply means selecting from pre-populated entries in those same structures via the UI picker.

Hallmark’s theming architecture is flexible enough to support both curated catalog themes and完全 bespoke designs. Whether you’re extending the open-source Nutlope/hallmark repository for a brand-specific landing page or experimenting with new component layouts, understanding the difference between catalog consumption and custom theme creation is essential.

Understanding Hallmark's Theming Architecture

Hallmark does not rely on traditional CSS theme switching. Instead, it uses a JavaScript-driven configuration system that swaps entire component archetypes and content based on three centralized data structures defined in site/js/main.js.

The Core Data Structures

The theming system revolves around three immutable-like maps that define every available theme:

  • THEMES (lines 42-63): Maps theme keys (e.g., marquee, split) to human-readable display names.
  • ARCHETYPES (lines 70-91): Maps theme keys to specific component templates, such as hero: "marquee" or footer: "colophon".
  • COPY (lines 35-56): Maps theme keys to text fixtures (eyebrow, lede, quotes) that populate the templates via interpolation.

How the applyTheme() Function Works

When a user selects a theme, the applyTheme(theme) function (lines 53-66) orchestrates the transition:

function applyTheme(theme) {
  if (!THEMES[theme]) return;
  root.dataset.theme = theme;
  swapArchetypes(theme);
  setPressed(theme);
  localStorage.setItem(STORAGE_KEY, theme);
}

This function validates the theme key, updates the DOM’s data-theme attribute, swaps the component archetypes via swapArchetypes(theme), and persists the choice to localStorage under the key hallmark-theme.

How to Create a Custom Theme in Hallmark

Creating a custom theme requires extending the three data structures with your own key-value pairs. Unlike CSS variable theming, you are configuring which HTML templates get injected and what copy populates them.

Step 1: Register the Theme in THEMES

Add your theme key and display name to the THEMES object in site/js/main.js:

const THEMES = {
  // ... existing entries ...
  mytheme: "My Custom Theme",
};

This key (mytheme) will be used across all three data structures to identify your configuration.

Step 2: Define Archetypes in ARCHETYPES

Map your theme key to specific component templates in the ARCHETYPES object:

const ARCHETYPES = {
  // ... existing entries ...
  mytheme: { 
    hero: "marquee", 
    footer: "colophon",
    navbar: "default" 
  },
};

You can reuse existing archetype IDs (like marquee or split) or reference new templates you create (see Step 4).

Step 3: Add Copy Content in COPY

Provide text content for interpolation by extending the COPY object:

const COPY = {
  // ... existing entries ...
  mytheme: {
    eyebrow: "Custom Collection",
    title: "Build Your Own Theme",
    lede: "A bespoke description for my custom Hallmark theme.",
    ctaLabel: "Get Started",
    quote: "Design is intelligence made visible."
  },
};

Missing keys will fall back to default specimen copy during interpolation.

Step 4: (Optional) Create Custom HTML Templates

If the existing templates in site/index.html don’t meet your needs, define a new <template> block:

<!-- In site/index.html -->
<template id="hero-myhero">
  <section class="hero hero--myhero">
    <span class="eyebrow">{{eyebrow}}</span>
    <h1>{{title}}</h1>
    <p class="lede">{{lede}}</p>
    <button class="cta">{{ctaLabel}}</button>
  </section>
</template>

Then reference this new ID in your ARCHETYPES mapping:

mytheme: { 
  hero: "myhero",  // matches template id "hero-myhero"
  footer: "colophon" 
}

Step 5: Activate and Test

Reload the page. Your custom theme will appear in the theme picker (the dot menu UI) alongside catalog entries. When selected, applyTheme() will automatically persist your choice to localStorage and render your custom configuration.

How to Use the Built-In Catalog

Using the catalog requires zero code modification. The UI provides theme picker dots (data-theme-btn attributes) that call applyTheme() with predefined keys like marquee, editorial, or splash. The shuffle button (.banner__shuffle) randomly selects from existing THEMES entries.

All catalog themes are pre-registered in the three data structures with complete archetype mappings and copy fixtures. To switch themes, simply click the UI controls or use the keyboard shortcuts T (toggle) and R (random).

Key Differences: Custom Theme vs Catalog

Aspect Built-In Catalog Custom Theme
Setup Zero configuration; select from UI Edit site/js/main.js to extend data structures
Templates Uses existing <template id="hero-..."> blocks in site/index.html Can reuse existing or create new templates
Copy Pre-defined in COPY object Must define custom copy object (optional but recommended)
Persistence Automatic via localStorage (hallmark-theme key) Automatic via same mechanism
Styling Defined in site/css/components.css (e.g., .hero--marquee) Must add corresponding CSS classes for new archetypes

Summary

  • Hallmark’s theming system is configuration-driven through three core objects in site/js/main.js: THEMES, ARCHETYPES, and COPY.
  • Custom themes require manually extending these objects with new keys, optionally creating new HTML templates in site/index.html, and ensuring corresponding CSS exists in site/css/components.css.
  • Catalog themes are pre-populated entries that work immediately via the UI picker without code changes.
  • The applyTheme() function handles all theme switching, archetype swapping via swapArchetypes(), and localStorage persistence automatically.

Frequently Asked Questions

Can I create a custom theme without editing the core source files?

No, Hallmark currently requires editing site/js/main.js to extend the THEMES, ARCHETYPES, and COPY objects. There is no external JSON configuration loader; the theme registry is hardcoded in the JavaScript source to ensure zero dependencies and prevent CORS issues when running locally.

What happens if I don't provide copy for my custom theme?

If your custom theme key exists in ARCHETYPES but has no corresponding entry in COPY, Hallmark falls back to the specimen copy (default placeholder text). The interpolation engine in swapArchetypes() only replaces keys that exist in your theme’s copy object, leaving other template placeholders empty or using the template’s default innerHTML.

How do I debug a custom theme that isn't rendering?

First, verify your theme key exists in all three data structures—THEMES, ARCHETYPES, and optionally COPY. Check the browser console for early returns in applyTheme() (which occurs if !THEMES[theme]). Next, inspect the DOM to confirm the data-theme attribute on the root element updated. Finally, verify that your template ID matches the archetype reference exactly (e.g., hero-mytheme for ARCHETYPES.mytheme.hero = "mytheme").

Where are the visual styles defined for each theme?

Visual styles are defined in .hero--[archetype] classes within site/css/components.css, not in the JavaScript theme configuration. For example, the marquee hero archetype uses .hero--marquee for its layout and typography. When creating a new archetype, you must add corresponding CSS rules to ensure the component renders correctly.

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 →