# How to Create a Custom Visual Preset or Theme in Archify: A Complete Guide

> Learn to create custom visual presets and themes in Archify. Extend the JSON schema, add CSS rules, and update the UI with this complete guide.

- Repository: [tt-a1i/archify](https://github.com/tt-a1i/archify)
- Tags: how-to-guide
- Published: 2026-08-06

---

**To create a custom visual preset in Archify, extend the JSON schema with a new enum value, add scoped CSS rules under the `[data-preset]` attribute selector, and expose the option in the preset picker UI.**

Archify's **visual preset** system lets you rebrand diagrams without touching geometry or logic. The preset flows from the `meta.visual_preset` field in your diagram JSON through to CSS variables that style the entire rendered output. Because the renderer keeps layout and appearance strictly separated, you can invent entirely new aesthetics without risking diagram integrity.

This guide walks through the three-step process based on the actual Archify source code in `tt-a1i/archify`.

## Step 1: Extend the Schema to Accept Your Custom Preset

Before any renderer will load your theme, the JSON validator must recognize its name. In [`archify/schemas/architecture.schema.json`](https://github.com/tt-a1i/archify/blob/main/archify/schemas/architecture.schema.json), line 20 defines the allowed enum values for `visual_preset` [[source]](archify/schemas/architecture.schema.json#L20-L21).

Modify the schema to include your custom preset:

```json
{
  "visual_preset": {
    "enum": [
      "classic",
      "signal-flow",
      "blueprint",
      "editorial",
      "my-preset"
    ]
  }
}

```

Without this change, diagrams using your custom preset will fail validation at load time.

## Step 2: Define CSS Rules Scoped to Your Preset

Archify applies presets **client-side only**, using the `data-preset` attribute on the root `<html>` element. All styling hangs off this selector, as demonstrated in [`experiments/visual-evolution/prototype.html`](https://github.com/tt-a1i/archify/blob/main/experiments/visual-evolution/prototype.html) lines 129-170 [[source]](experiments/visual-evolution/prototype.html#L129-L170).

Create a CSS block that targets `html[data-preset="your-name"]`:

```css
/* Dark mode support via data-theme attribute */
html[data-preset="my-preset"][data-theme="dark"] {
  --bg-body: #1a1a2e;
  --fg-primary: #e0e0ff;
  --accent: #ff6f61;
}

/* Light mode fallback */
html[data-preset="my-preset"][data-theme="light"] {
  --bg-body: #fafafa;
  --fg-primary: #1a1a2e;
  --accent: #ff6f61;
}

/* Component-level styling */
html[data-preset="my-preset"] .header {
  padding-right: 2rem;
}

html[data-preset="my-preset"] .card {
  background: var(--bg-body);
  color: var(--fg-primary);
  border: 1px solid var(--accent);
}

/* Decorative overlay */
html[data-preset="my-preset"] .diagram-container::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    circle at 20% 20%,
    var(--accent) 0%,
    transparent 70%
  );
  pointer-events: none;
}

```

The renderer never recomputes layout based on these styles—geometry remains stable regardless of which preset is active [[source]](archify/renderers/architecture/render-architecture.mjs).

## Step 3: Expose the Preset in the UI Picker

The preset picker is a `menu`-pattern button that appears in every rendered page. The test suite in `archify/test/preset-tryon.test.mjs` validates that all four built-in presets appear as options and that selecting one synchronizes both `<html>` and `<svg>` elements [[source]](archify/test/preset-tryon.test.mjs#L40-L61).

To add your preset to the picker, extend the runtime preset list and generate the corresponding menu item. The shared UI utilities in `archify/renderers/shared/utils.mjs` (or equivalent) handle this:

```javascript
// Extend the supported preset values
const PRESET_VALUES = [
  'classic',
  'signal-flow',
  'blueprint',
  'editorial',
  'my-preset'
];

// Assign to global Archify config
Archify.preset = PRESET_VALUES;

function buildPresetMenu() {
  const menu = document.getElementById('preset-menu');
  
  PRESET_VALUES.forEach(value => {
    const item = document.createElement('button');
    item.dataset.presetValue = value;
    item.setAttribute('role', 'menuitemradio');
    item.textContent = value
      .split('-')
      .map(w => w.charAt(0).toUpperCase() + w.slice(1))
      .join(' ');
    
    item.addEventListener('click', () => applyPreset(value));
    menu.appendChild(item);
  });
}

function applyPreset(presetName) {
  const html = document.documentElement;
  const svg = document.querySelector('svg.archify-root');
  
  // Both elements must carry the attribute for full styling coverage
  html.setAttribute('data-preset', presetName);
  svg?.setAttribute('data-preset', presetName);
  
  // Update ARIA states
  menu.querySelectorAll('[role="menuitemradio"]')
       .forEach(btn => btn.setAttribute('aria-checked', 
         btn.dataset.presetValue === presetName));
}

```

This synchronization logic—setting `data-preset` on both `<html>` and `<svg>`—mirrors the test assertions at lines 58-61 of the preset try-on suite [[source]](archify/test/preset-tryon.test.mjs#L58-L61).

## Using Your Custom Preset in a Diagram

Once the three steps above are complete, reference your preset in any diagram JSON:

```json
{
  "schema_version": 1,
  "diagram_type": "architecture",
  "meta": {
    "title": "Production API Gateway",
    "visual_preset": "my-preset",
    "animation": "none"
  },
  "components": [
    {
      "id": "gateway",
      "type": "service",
      "label": "API Gateway"
    }
  ],
  "connections": []
}

```

The renderer will validate against your extended schema, apply the `data-preset` attribute, and your CSS will take effect immediately.

## Preset Architecture and Performance Guarantees

Understanding how presets operate helps you build efficiently:

- **Geometry preservation**: The architecture renderer in `render-architecture.mjs` never inspects `visual_preset` when computing node positions or edge routes. Your preset cannot accidentally corrupt layout.
- **Zero server-side processing**: The preset string passes straight from JSON to HTML attribute without transformation.
- **CSS-only activation**: All visual effects—colors, spacing, decorative elements—flow through standard CSS custom properties and selectors.

## Summary

- **Extend the schema** at [`archify/schemas/architecture.schema.json`](https://github.com/tt-a1i/archify/blob/main/archify/schemas/architecture.schema.json) to whitelist your preset name.
- **Scope all styles** under `html[data-preset="your-name"]` following the pattern in [`experiments/visual-evolution/prototype.html`](https://github.com/tt-a1i/archify/blob/main/experiments/visual-evolution/prototype.html).
- **Register in the UI** by adding to `PRESET_VALUES` and ensuring the picker synchronizes `data-preset` on both `<html>` and `<svg>` elements.
- **Reference in diagrams** via `meta.visual_preset` without any other changes to structure or semantics.

## Frequently Asked Questions

### What happens if I use a preset not in the schema?

Diagrams with unrecognized `visual_preset` values fail JSON validation at load time. The renderer never reaches the CSS stage—extend the schema first.

### Can a preset affect layout or node positioning?

No. The renderer separates geometry computation from styling. Presets only control CSS custom properties and decorative elements; they cannot modify edge routing, node sizes, or relative positioning.

### How do I support both light and dark modes in my custom preset?

Use the `data-theme` attribute alongside `data-preset`. Define parallel blocks: `html[data-preset="my-preset"][data-theme="light"]` and `[data-theme="dark"]`. Archify toggles `data-theme` independently of preset selection.

### Where should I store custom preset CSS?

Add it to the same file that hosts built-in preset styles, or load it as a separate stylesheet after the core Archify CSS. The [`experiments/visual-evolution/prototype.html`](https://github.com/tt-a1i/archify/blob/main/experiments/visual-evolution/prototype.html) file demonstrates inline `<style>` blocks for prototyping; production deployments typically externalize these rules.