Appearance
Theme Settings (Customize)
Introduction
A theme offers its owner a few settings that need no code: the accent colour, the logo, a line in the footer, a sidebar on the left or right. The owner changes them on Appearance → Customize, looks at the site with the change before anyone else sees it, and publishes. It is WordPress's Customizer, kept to what a theme declares.
Settings are part of the core (kitloom/site), not a module: a theme reads them even when nobody has changed anything, so they are always there.
For the site's owner
Appearance → Customize (Filament and Nova) shows the active theme's settings on the left, grouped the way the theme groups them, and the site in a frame on the right. The active theme's card on Appearance → Themes has a Customize button too.
- A colour changes in the frame at once, as you pick it.
- Anything else — a text, a picture, a switch — redraws the page in the frame.
- Nobody else sees the changes yet. They are your draft for an hour, shown only to you, also in another tab. The page you see is never cached and never indexed by search engines.
- Publish saves them for every visitor. Discard changes throws the draft away.
A picture comes from the media library. A site moved from WordPress keeps its logo: if the theme has no logo of its own yet, it shows the one WordPress had (custom_logo).
Who may: users with Edit theme options (edit_theme_options, as in WordPress) — administrators by default. Switching themes stays a separate permission.
A child theme has its parent's settings, with values of its own. The first time it is activated, it starts from the parent's values.
For theme authors
Declare the settings
A theme declares its settings in its service provider:
php
use Kitloom\Site\Themes\Settings\ThemeSetting;
protected function settings(): iterable
{
return [
ThemeSetting::color('accent', '#c2410c')->label(__('Accent'))->section(__('Colors'))->css('--accent'),
ThemeSetting::font('heading_font', 'serif', [
'serif' => ['label' => 'Serif', 'stack' => 'Georgia, serif'],
'sans' => ['label' => __('Sans-serif'), 'stack' => 'system-ui, sans-serif'],
])->label(__('Heading font'))->section(__('Typography'))->css('--heading-font'),
ThemeSetting::image('custom_logo')->label(__('Logo'))->section(__('Header')),
ThemeSetting::toggle('show_tagline', true)->label(__('Show the tagline'))->section(__('Header')),
ThemeSetting::text('footer_text')->label(__('Footer text'))->section(__('Footer'))
->description(__('Empty: the year and the site\'s name.')),
];
}settings() runs only when something needs it, so labels can go through __() there.
| Type | Value | Notes |
|---|---|---|
color | #rrggbb or #rgb | |
image | An attachment ID | Name the logo custom_logo, as WordPress does, so a moved site keeps its logo |
text | Up to 255 characters | |
textarea | Up to 5,000 characters | |
toggle | true / false | |
choice | One of its options | choice('sidebar', 'right', ['left' => __('Left'), 'right' => __('Right')]) |
number | Within its bounds | number('font_size', 17, 14, 22, 'px') |
font | One of the theme's fonts | Each a CSS font stack; nothing is loaded from elsewhere |
Read them
CSS. A setting with css('--accent') is printed into every page's head, in @siteHead, as :root{--accent:#c2410c}. Every layout puts @siteHead after the theme's own styles, so the theme writes:
css
.button { background: var(--accent, #c2410c); }Only checked values get there: a colour is a hex code, a font one of the theme's stacks, a choice one of its plain-word values. A text setting cannot become CSS at all.
Blade:
blade
@use('Kitloom\Site\Facades\Theme')
@if ($logo = Theme::image('custom_logo'))
<img src="{{ $logo->url }}" alt="{{ $logo->alt }}">
@endif
<footer>{{ Theme::setting('footer_text') ?: '© '.date('Y') }}</footer>Vue and React: list the theme part among the theme's parts (Kitloom\Site\Page\Parts\ThemeSettings). The template gets theme, with images as images (theme.custom_logo.url). A headless front end asks for it with ?include=theme.
Where they are kept
In the theme_mods_<theme> option (theme_mods_Journal), a map by setting name, as JSON — the way WordPress keeps a theme's mods. What nobody has changed is the declared default. A value the declaration no longer accepts (you renamed a font) falls back to the default instead of breaking the page.
Every write goes through the settings writer with the user who made it, so the activity log shows who changed the colours. The console and imports get the same checks.
For AI agents
describe_site lists the active theme's settings — type, options, bounds, current value — under theme_settings. theme_customize changes them as the agent's user, with the same checks as the screen:
json
{ "values": { "accent": "#1d4ed8", "footer_text": "Printed in the hills" } }The change is published at once: agents do not work with drafts. null brings a setting back to its default.