# Theme Authoring

A Divine theme is a normal WordPress theme. You author it in real theme files, and Divine adds a workflow around those files rather than a different place to put them. Everything you write ships in the exported theme, and the same files render in preview, in `dv show`, and on a live site after deploy. This page covers where theme work belongs, the runtime helpers Divine gives you to use directly in templates, and the loop you run while building.

## Where Theme Work Belongs

Theme work goes in ordinary theme files. There is no separate Divine source format and no hidden lane; what you edit is what the theme is.

| Location | Holds |
| --- | --- |
| `pages/` | Divine pages rendered by canvas, preview, and `dv show`. |
| `blocks/` | Blockstudio blocks the theme registers. |
| `patterns/` | Block patterns. |
| Template files | Theme templates and template parts. |
| `style.css` | The theme header and stylesheet. |
| `functions.php` | Theme bootstrap and PHP. |
| `divine.json` | The theme-level source of truth for runtime performance behavior. |
| Assets | Theme frontend assets, scripts, and styles. |

Keep the bootstrap simple and prefer theme-prefixed function names so the theme stays self-contained and free of collisions. A theme that namespaces its functions and leans on its own files is a theme that exports cleanly and runs without surprises on a destination site.

Worktree pages under `pages/` are theme files, not WordPress posts. Divine renders them from the files for canvas and preview, and they do not appear in the WordPress pages list until you deploy them into the app theme. The native block editor edits main WordPress pages only; it does not edit worktree pages.

## Tailwind Helpers In Templates

Divine's runtime provides two Tailwind class composition helpers. Use them directly in templates. Do not add theme-local wrappers around them, and do not re-roll your own class merge or variant builder; the theme already has these, and they ship with the exported runtime.

| Helper | Purpose |
| --- | --- |
| `dv_tw_merge( ...$classes )` | Merge Tailwind class strings and resolve conflicts so the last utility wins. |
| `dv_tw_variants( array $config )` | Build a CVA-style variant composer that returns classes for a set of props. |

### Merging Classes

`dv_tw_merge()` joins any number of class arguments into one string and resolves Tailwind conflicts, so a later utility overrides an earlier one in the same group. This is what you reach for when you compose a base class list with conditional overrides and need the result to be conflict-free rather than a pile of competing utilities.

```php
<div class="<?php echo esc_attr( dv_tw_merge( 'rounded-md px-4 py-2 bg-slate-200', $is_active ? 'bg-blue-600 text-white' : '' ) ); ?>">
	<?php echo esc_html( $label ); ?>
</div>
```

Because the merge resolves conflicts, the active state's `bg-blue-600` replaces the base `bg-slate-200` cleanly instead of both landing on the element. The helper accepts strings, arrays, and nested arrays, and it ignores empty, null, and false values, so conditional expressions are safe to pass straight in.

### Building Variants

`dv_tw_variants()` takes a config of a `base` class list and named `variants`, with optional `defaultVariants` and `compoundVariants`, and returns a callable. You call that callable with props to get the composed class string. This is the pattern for a component with size and tone options, where each prop selects a slice of classes.

```php
<?php
$button = dv_tw_variants(
	array(
		'base'     => 'inline-flex items-center rounded-md font-medium',
		'variants' => array(
			'tone' => array(
				'primary' => 'bg-blue-600 text-white',
				'muted'   => 'bg-slate-200 text-slate-900',
			),
			'size' => array(
				'sm' => 'px-3 py-1 text-sm',
				'md' => 'px-4 py-2 text-base',
			),
		),
		'defaultVariants' => array(
			'tone' => 'primary',
			'size' => 'md',
		),
	)
);
?>
<button class="<?php echo esc_attr( $button( array( 'tone' => 'muted', 'size' => 'sm' ) ) ); ?>">
	<?php echo esc_html( $label ); ?>
</button>
```

A prop that you omit falls back to its default variant, and you can pass a `class` or `className` prop to append extra classes onto the composed result. Both helpers degrade safely: when the bundled Tailwind engine is unavailable they fall back to joining classes, so templates render either way.

## Media Helpers In Templates

Use `divine_media_image()` for images that should participate in Divine's lazy-loading and stable sizing runtime. The helper reads `<theme-root>/assets/media.json` for intrinsic dimensions, so theme asset images should be regenerated after files under `assets/` change.

```php
echo divine_media_image(
	array(
		'src' => 'assets/images/hero.png',
		'alt' => 'Hero',
	)
);
```

Regenerate the manifest from WordPress with the runtime builder:

```php
( new \Divine\Runtime\Media\MediaMetadataBuilder() )->write( get_stylesheet_directory(), true );
```

Pass `false` as the second argument when the manifest should include theme assets only and skip WordPress media-library attachments.

## The Authoring Loop

Theme building in Divine is a tight loop: make a change, check it, see it, review it, and ship it. Each step has a command or surface, and you repeat the loop until the theme is ready.

1. Create or select a worktree to work in.
2. Edit the normal theme files.
3. Run `dv check .` from the theme to validate it. This is fast and does not bootstrap WordPress; it returns 0 for no findings, 1 for findings, and 2 for usage or internal errors.
4. Run `dv show page`, `dv show block`, or `dv show compare` to get a capturable render URL when you need visual proof, and let your agent or browser tool open it.
5. Preview the worktree in canvas or the signed browser preview to see it against real site data.
6. Review the diff of what changed.
7. Deploy the accepted files back into the app theme.
8. Run `dv export .` to build the standalone, client-ready theme zip when the theme is ready to ship.

The first six steps are the inner loop you run constantly while building; deploy and export are the outer steps you reach once the work is accepted. The check and show steps are designed to run on every change, which is why check stays fast and show stays a URL rather than a screenshot.

## Checking And Showing

`dv check .` is the validation gate. It runs in a fast mode that does not bootstrap WordPress, open network connections, or shell out, so it is cheap enough to run after every edit. A `--full` mode is opt-in for CI and wp-env, and may bootstrap WordPress in-process to run the render dry-run lane.

`dv show` is the visual gate. It prints a capturable URL for a page, block, or comparison, and never captures the image itself. See [dv show](dv-show) for the subcommands, flags, and how it resolves a local WordPress, the right stylesheet, and the right subsite.

Together they keep the loop honest: check proves the theme is structurally valid without a heavy bootstrap, and show gives an agent a precise, signed URL to capture so visual changes are verified rather than assumed.

## Deploying And Exporting

Deploy promotes accepted worktree files into the app theme. On multisite it targets the selected site's active theme only, and it copies files, not database state. See [Deploy](deploy) for how promotion works.

Export packages the app or worktree as a standalone theme zip with an embedded `divine-runtime/` copy, so the finished theme runs on a destination site with no Divine plugin installed. The Tailwind helpers and other runtime behavior keep working because the runtime ships inside the zip. See [Theme Export](theme-export) for what the export includes and excludes.
