Skip to content

Frontend Themes

Capell themes connect installed admin theme records to public rendering. The admin owns selection and customization; the frontend receives a resolved runtime with a theme definition, preset, brand tokens, and safe assets.

Layouts are the editorial bridge between page records and public templates. Theme records then decide the presentation runtime used when those layouts render.

The optional Layout Builder package owns its editor UI and screenshot evidence.

Theme Library admin workflow Theme Library admin workflow
  1. Admin installs a package, creates an available theme definition, or creates a custom theme record.
  2. The package registers ThemeDefinitionData with ThemeRegistry.
  3. The installed themes.key points at the registered definition key.
  4. Sites either use the global default theme or an explicit sites.theme_id.
  5. Frontend context resolves the current site, page, layout, and theme before Blade renders.
  6. The RenderHookLocation::HeadClose hook resolves the active preset through ResolveThemeRuntimeAction and adds the theme-token stylesheet when available.
  7. The frontend response pipeline builds hydrated public render data and produces public HTML through the configured response renderer.

The public page does not need to know that Theme Library, Customize, or Preview exist.

Theme Studio settings still provide the fallback active preset. Installed theme records can override that through meta.editor.preset.active, which is set from the admin Customize slide-over or initialized from the first preset when an available theme definition is created.

Resolution order:

  1. preview token preset from ThemePreviewContext
  2. installed theme meta.editor.preset.active
  3. ThemeRuntimeSettings::activePreset()
  4. first preset in the theme definition when the configured preset is missing

If a preview token names a missing preset, runtime throws ThemePresetNotFoundException. Normal public rendering falls back to the first registered preset so public pages continue loading.

Presets should use this vocabulary for shared brand values:

TokenPurpose
primaryColormain action and link colour
accentColorhighlights, badges, and small visual emphasis
neutralColorborders, dividers, and quiet surfaces
surfaceColordefault page surface colour
foregroundColordefault text colour
headingFontheading font family key
bodyFontbody font family key
radiusdefault public component radius: none, sm, md, lg, or xl
headingScaleheading scale: compact, balanced, or expressive
cardDensitycomponent density: compact, comfortable, or spacious
overlayTreatmentmedia overlay treatment: none, subtle, or strong
spacing, alignment, cardStyle, navigationStyle, layoutPresentation, motionIntensity, mediaTreatmentrenderer-facing presentation choices

Theme packages may include package-specific keys. Public theme code must tolerate unknown keys and continue with safe defaults.

Primary and accent backgrounds emit --theme-primary-contrast and --theme-accent-contrast for readable black or white labels. These values derive from the colours after sanitisation and runtime contrast repair. Use the paired token for a filled control; a fixed dark label can become unreadable when the runtime replaces a bright accent with a dark fallback. Editor previews derive the same pairs after extension token mappings.

Theme definitions declare frontend assets in ThemeDefinitionData::assets. Package providers should register CSS/JS sources in PHP registration code instead of requiring every app to copy paths by hand.

Installed theme records can override package assets through clean editor state:

  • meta.editor.assets.paths
  • meta.editor.assets.buildPath

Legacy meta.assets and blueprint asset meta are only compatibility fallbacks when no editor asset state exists.

Token CSS is generated through ThemeTokenStore. The frontend provider adds the token stylesheet at RenderHookLocation::HeadClose when a registered theme and token path are available.

Theme changes touch public cache in two ways:

  • applying a global default saves the selected Theme, which lets ThemeObserver invalidate affected site surrogate keys
  • applying to selected sites saves each Site, which lets SiteObserver flush site/theme relation caches and invalidate those site keys

Do not bulk-update sites.theme_id from new code paths unless you also reproduce the observer cache behavior.

Theme views and frontend contributions must not print admin/editor details into public output. Avoid:

  • package names that only an admin should see
  • model IDs, field paths, selectors, permissions, or signed editor URLs
  • authoring markers or editor scripts
  • lazy model queries from public Blade views

Use route-backed frontend tests when changing theme runtime. Assert expected public content and assert the absence of authoring surface.