Appearance
Live Preview
Editing any setting in the editor patches the preview iframe in place. This page explains the two live-update modes, what the iframe does per change, and where values are stored.
Live-update modes
The SchemaCompiler routes every field through one of two Livewire live modes. You don't configure this — it's chosen automatically per setting type.
- Instant
live()— the round-trip fires on every change. Used for discrete, one-click inputs:Checkbox,Radio,EnumButtons,Select,ColorScheme,ColorSchemeGroup,Font,Icon,Spacing,Gradient. - Lazy
live(onBlur: true)— the round-trip is deferred until the field loses focus. Used for typed, dragged, or uploaded inputs where a half-finished value shouldn't trigger a re-render mid-edit:Text,Textarea,Number,Range,Color,Image,Link,RichText.
Raw Filament components passed into a schema join the live-update loop automatically, sorted the same way: components that commit on a discrete event — Toggle, Checkbox, Select, Radio, ToggleButtons, ColorPicker, FileUpload — get a bare ->live(), because onBlur on a widget that never blurs would leave the iframe stale until reload. Everything you type into gets live(onBlur: true). If you set ->live(...) yourself, your choice is kept.
The lazy mode is load-bearing for RichText: Filament v4's TipTap editor throws if Livewire morphs the surrounding DOM mid-keystroke, so its round-trip is always deferred to blur.
What the iframe does per change
| Change | What the iframe does |
|---|---|
| Any global theme setting — active color scheme, authored color tokens, font slug, range / checkbox / etc. | Requests a server templateRefresh (single GET), then morphs <body>, re-syncs <head> (the <style id="fc-tokens"> text is replaced, stale Bunny <link> tags pruned and fresh ones added), and copies lang / dir / data-fc-color-scheme from the refreshed document onto <html>. No flash, no FOUC for cached fonts. |
| Section settings (e.g. a Hero heading) | Either patched in place via the LivePatcher (any element with data-fc-live="…" gets a surgical update) or via a sectionRefresh — a single GET with a sectionId query parameter, served by SectionRefreshController, that re-renders and swaps just that section — depending on the field shape. |
Section authors opt elements into the surgical LivePatcher path with the liveUpdate() helper, which emits the data-fc-live attributes. From a built-in section view:
blade
<h1 {!! $section->liveUpdate('heading') !!}>{{ $heading }}</h1>
<img {!! $section->liveUpdate('bg_image')->attr('src') !!} src="{{ $url }}">liveUpdate() defaults to text patching. The other modes are ->text() (explicit), ->html(), ->outerHtml(), ->attr('src'), ->style('color'), and ->toggleClass('is-active'). Repeater blocks use $block->liveUpdate('title') the same way.
Fonts in the live loop
The renderer auto-injects a Bunny Fonts stylesheet for every Font setting whose value resolves to a remote family, where {remote} is the catalog entry's Bunny id:
html
<link rel="stylesheet"
href="https://fonts.bunny.net/css?family={remote}:400,500,600,700&display=swap">System stacks (system-sans, system-serif, system-mono) have no remote id and emit no <link> because they resolve entirely to platform fonts.
The font list itself comes from FontCatalog, which fetches the full roster live from fonts.bunny.net/list and caches it for 24 hours. On network failure it degrades to a small built-in fallback list (Inter, Roboto, Merriweather, JetBrains Mono plus the system stacks), so the editor keeps working offline. See Font Picker for details.
Persistence
Where values live once you save:
- Active color scheme slug — on
Site.settings_json['color_scheme']. It is a global setting: every template, region, and host-rendered shell page on the site renders on it, so picking a scheme while editing one page restyles the whole site. Per-page variation is a section concern — set a section's ownschemesetting instead. - User-authored color schemes (Add scheme + 22 tokens) — on
Site.settings_json['schemes'], kept separate from the 14 package-shipped built-ins. - Every other theme setting value — on
Site.settings_json, keyed by setting id. Writes are diff-only: a value matching its schema default isn't stamped, so unset values inherit the schema's default and package upgrades to those defaults flow through automatically.
Programmatic read/write (migrations, seeds, API endpoints):
php
use FilamentCraft\Support\SiteThemeSettings;
use FilamentCraft\Support\SiteColorSchemes;
// Read merged values (overrides + schema defaults).
$values = SiteThemeSettings::values($site, $site->theme->templateSettings());
// Replace the site's entire override map (values equal to their schema
// default, null, or '' are dropped). To keep existing overrides, merge
// SiteThemeSettings::values($site, $schema) into what you pass.
SiteThemeSettings::sync($site, $schema, ['button_radius' => 12]);
// Color-scheme overrides (per-site authored map).
$schemes = SiteColorSchemes::forSite($site); // 14 built-ins + overrides
SiteColorSchemes::add($site, cloneFrom: 'cupcake'); // returns "scheme-N"
SiteColorSchemes::remove($site, 'scheme-13');
SiteColorSchemes::activeFor($site); // the site's global scheme slug
SiteColorSchemes::setActive($site, 'noir'); // restyles every page on the siteRelated
- The Editor — the editing UI as a whole.
- Setting Types — the per-type live-mode summary.
- Color Schemes — the scheme system the first row above relies on.
