# When Does Hallmark Switch to Custom Theme Mode? A Deep Dive into the Source Code

> Discover when Hallmark switches to custom theme mode. Explore the source code and triggers including user clicks, keyboard shortcuts, and initial page load in Nutlope/hallmark.

- Repository: [Hassan El Mghari/hallmark](https://github.com/Nutlope/hallmark)
- Tags: deep-dive
- Published: 2026-08-15

---

**Hallmark switches to custom theme mode whenever the `applyTheme(theme)` function is invoked in [`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js), which occurs through three distinct triggers: user clicks on the theme picker, keyboard shortcuts (T, Shift+T, or R), or initial page load with URL parameters or saved preferences.**

The open-source Hallmark project by Nutlope implements a dynamic theming system that rebuilds the page’s visual archetypes instantly. Understanding exactly when and how this transition occurs requires examining the JavaScript event handlers and state management logic that drive the user interface.

## How Hallmark Triggers the Custom Theme Mode Switch

The application monitors three specific interaction points to determine when to swap themes. Each pathway ultimately calls the core `applyTheme()` function, which updates `root.dataset.theme` and initiates the visual transition.

### User Interaction Through the Theme Picker

When visitors click a theme button in the sticky banner interface, the event listener attached to the dot navigation triggers the switch. The code iterates over each button and calls `applyTheme()` with the selected theme identifier:

```javascript
// Theme picker button click (sticky banner)
dots.forEach(btn => {
  btn.addEventListener('click', () => {
    applyTheme(btn.dataset.themeBtn);   // triggers theme switch
    closeThemeDropdown();
  });
});

```

This handler resides in [`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js) alongside the `applyTheme` implementation (lines 77-90). After the click, the function updates the dataset attribute, swaps the page archetypes, and persists the choice to `localStorage` using `STORAGE_KEY`.

### Keyboard Shortcuts

Power users can switch themes without clicking by using dedicated keyboard commands. The global `keydown` listener in [`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js) (lines 91-108) captures **T** to cycle forward, **Shift + T** to cycle backward, and **R** to select a random theme:

```javascript
// Keyboard shortcuts – T cycles forward/back, R picks random
document.addEventListener('keydown', e => {
  if (e.key === 't' || e.key === 'T') {
    const order = Object.keys(THEMES);
    const i = order.indexOf(root.dataset.theme);
    const dir = e.shiftKey ? -1 : 1;
    applyTheme(order[(i + dir + order.length) % order.length]); // switch
  } else if (e.key === 'r' || e.key === 'R') {
    applyTheme(pickRandomTheme()); // random theme
  }
});

```

The handler computes the next theme index and invokes `applyTheme(next)`, ensuring the same cascade executes as with click interactions.

### Initial Load via URL or Saved Preference

On page load, the script checks for a `theme` query parameter (`?theme=…`) or retrieves the persisted value from `localStorage` (see **initial theme detection** in [`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js), lines 93-101). If a valid theme exists in either location, the application applies it immediately before the first paint:

```javascript
// Initial load – URL param or stored value
const queried = new URL(window.location.href).searchParams.get('theme');
const stored  = localStorage.getItem(STORAGE_KEY);
const initial = THEMES[queried] ? queried
               : THEMES[stored] ? stored
               : (root.dataset.theme || 'hum');

root.dataset.theme = initial;
swapArchetypes(initial);
setPressed(initial);

```

This ensures returning visitors see their last selected theme, while shared links can specify a theme directly.

## The Theme Application Cascade

When `applyTheme` is invoked, Hallmark executes a precise sequence of operations to complete the switch. This cascade ensures the DOM, CSS, and UI state remain synchronized.

1. **`applyTheme` sets `root.dataset.theme = theme`** — The assignment triggers CSS custom property updates and view transitions.
2. **`swapArchetypes` rebuilds components** — Located in [`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js) (lines 98-124), this function reconstructs the hero and footer sections using the **ARCHETYPES** mapping specific to the new theme.
3. **`setPressed` updates UI state** — This utility (lines 55-71) adjusts the pressed state of dot buttons, updates the banner label, and refreshes genre and ordinal displays.
4. **Persistence to `localStorage`** — The choice is saved via `localStorage.setItem(STORAGE_KEY, theme)` inside `applyTheme`, ensuring the selection survives page reloads.

## Key Source Files and Functions

| File | Purpose |
|------|---------|
| [`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js) | Core UI logic containing the **THEMES** object, **ARCHETYPES** mapping, `applyTheme()`, keyboard shortcuts, and initial load handling. |
| [`site/index.html`](https://github.com/Nutlope/hallmark/blob/main/site/index.html) | Markup for the theme picker dots, banner, and placeholder elements populated during theme switches. |
| `applyTheme(theme)` | Central function (lines 77-90) that updates `root.dataset.theme` and orchestrates the switch. |
| `swapArchetypes(theme)` | Rebuilds page structure (lines 98-124) based on the selected theme’s archetype configuration. |
| `setPressed(theme)` | Synchronizes UI controls (lines 55-71) to reflect the active theme state. |

## Summary

- Hallmark enters custom theme mode immediately when `applyTheme(theme)` executes.
- Three triggers invoke this function: theme picker clicks, keyboard shortcuts (T/Shift+T/R), and initial load with URL parameters or `localStorage` values.
- The switch updates `root.dataset.theme`, rebuilds page archetypes via `swapArchetypes()`, syncs UI state via `setPressed()`, and persists the choice to `localStorage`.
- All theme logic resides in [`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js), with the **THEMES** and **ARCHETYPES** objects defining available options and their structural mappings.

## Frequently Asked Questions

### How does Hallmark remember my theme selection between visits?

Hallmark persists your choice using `localStorage.setItem(STORAGE_KEY, theme)` inside the `applyTheme` function. When you return, the initial load detection script retrieves this value and applies it before the page renders, defaulting to the last selected theme or falling back to the URL parameter if present.

### Can I link directly to a specific Hallmark theme?

Yes. Append `?theme=` followed by a valid theme key (such as `hum`, `specimen`, or `garden`) to the URL. The initial load detection logic in [`site/js/main.js`](https://github.com/Nutlope/hallmark/blob/main/site/js/main.js) checks `URLSearchParams` for this parameter and applies it immediately if the theme exists in the **THEMES** registry.

### What happens if I press an invalid keyboard key?

Only **T**, **Shift + T**, and **R** trigger theme changes. The global `keydown` listener specifically checks for these keys; all other keystrokes pass through without invoking `applyTheme()`, leaving the current custom theme mode unchanged.

### Does Hallmark support view transitions when switching themes?

Yes. The update to `root.dataset.theme` occurs within the browser’s view transition lifecycle where supported, and the `swapArchetypes` function rebuilds the DOM structure instantly. The CSS architecture uses the dataset attribute for styling, ensuring the visual switch appears seamless.