Skip to main content

Documentation

No results found.
Features

Downloadable Designs (Design Files)

A design is the identity-free look of a site — its brand colors, fonts, and design tokens — packaged as a small portable .design.json file. Any WebProCMS install can export its current design, download the design of any theme variant, and i...

A design is the identity-free look of a site — its brand colors, fonts, and design tokens — packaged as a small portable .design.json file. Any WebProCMS install can export its current design, download the design of any theme variant, and import a design file to restyle itself in one step. Pages, header, footer, navigation, content, and business identity (logo, favicon, white-label) are never touched.


Where it lives

Dashboard → Design → Theme, in the Variants section:

  • Export current design — downloads the live design as {site-name}.design.json.
  • Import design — upload a .design.json, preview its name/colors/fonts, then apply.
  • Every variant card shows color swatches (primary / secondary / tone), the heading + body font names, and a download icon that exports that variant's design as {variant-name}.design.json.

Applying an imported design uses the same pipeline as switching a theme variant: settings are written, the cached inline brand CSS is busted, and the Tailwind color allowlist regenerates (triggering a CSS rebuild only when the allowlist actually changed). The change is instant and content-safe.

What travels in a design

Exactly the setting keys in BrandingStyleService::DESIGN_KEYS:

Group Keys
Colors branding.colors, branding.tone_scale, branding.color_group_toggles, branding.color_display_names
Typography branding.typography (fonts + heading/body element defaults), branding.body_font, branding.heading_font, branding.heading_accent_font
Design tokens branding.radius, branding.shadow, branding.section_spacing (+ _banner, _hero), branding.section_side_padding, branding.section_item_gap, branding.container_width
Page surface branding.page_bg_light/dark, branding.page_text_light/dark
Appearance branding.dark_mode (light / dark / system)

Deliberately excluded (identity, never travels): branding.logo_url, branding.dark_logo_url, branding.favicon_url, branding.white_label, branding.admin_logo_mode, and per-admin editor preferences (branding.smart_picker_prefs). This exclusion is what makes a design safe to drop onto any client site.

The file format

{
    "schema_version": 1,
    "name": "Ocean Slate",
    "exported_from": "webprocms",
    "branding": {
        "branding.colors": { "primary": "oklch(0.55 0.15 220)", "secondary": "#0d9488", "tone": "oklch(0.4 0.01 260)" },
        "branding.body_font": "inter",
        "branding.heading_font": "lora",
        "branding.radius": "large"
    }
}

Unset keys are simply omitted — on import they fall through to the receiving install's current values. The DB stays the source of truth; the file is an export/import envelope, not a storage backend.

Security model — the whitelist is the boundary

Two independent layers keep a design file from writing anything outside the design:

  1. Import validation (BrandDesign::validate()) rejects loudly, with a user-readable message, on: an unknown schema_version, any key outside DESIGN_KEYS (including identity keys), unknown font slugs (checked against BrandingStyleService::FONTS), malformed colors, and out-of-range token values. Color function notation is restricted to a safe character set so a crafted value can never smuggle CSS (;, {, }) into the emitted inline style block. Nothing is written until the admin confirms the preview.
  2. Apply whitelist (ThemeInstaller::applyDesignBranding()) silently ignores any key not in DESIGN_KEYS. Every branding write — variant switches, theme-switch design restores, and design imports — flows through this one method.

Relationship to theme variants

A design maps 1:1 to a theme variant's branding block. Variant snapshots (themes:capture, "Save current design as variant", and the theme switcher's A→B→A design restore) capture all DESIGN_KEYS — colors, fonts, and tokens — via ThemeCapturer::captureVariantData(). Variants additionally carry header/footer + shared-row role slugs; a design file carries branding only, which is why it is safe cross-site while a variant is theme-scoped.

Planned (not yet built)

  • Remote design gallery — a browse/download feed served by the webprocms.com mothership over the existing CMS_LICENSE_KEY channel, alongside the update feed.
  • Saved named designs — persisting imported designs as named variants in a gitignored custom tree (resources/themes-custom/). MVP import applies directly to the live settings instead.