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

> Learn how to create a custom Hallmark theme by extending core data structures or simply select pre-made options using the catalog UI. Choose the best method for your project.

- Repository: [Hassan El Mghari/hallmark](https://github.com/Nutlope/hallmark)
- Tags: how-to-guide
- Published: 2026-07-24

---

**To create a custom theme in Hallmark, you manually extend the three core data structures—`THEMES`, `ARCHETYPES`, and `COPY`—in [`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/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`](https://github.com/Nutlope/hallmark/blob/main/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:

```javascript
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`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js):

```javascript
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:

```javascript
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:

```javascript
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`](https://github.com/Nutlope/hallmark/blob/main/site/index.html) don’t meet your needs, define a new `<template>` block:

```html
<!-- 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:

```javascript
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`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js) to extend data structures |
| **Templates** | Uses existing `<template id="hero-...">` blocks in [`site/index.html`](https://github.com/Nutlope/hallmark/blob/main/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`](https://github.com/Nutlope/hallmark/blob/main/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`](https://github.com/Nutlope/hallmark/blob/main/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`](https://github.com/Nutlope/hallmark/blob/main/site/index.html), and ensuring corresponding CSS exists in [`site/css/components.css`](https://github.com/Nutlope/hallmark/blob/main/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`](https://github.com/Nutlope/hallmark/blob/main/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`](https://github.com/Nutlope/hallmark/blob/main/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.