Skip to content

Theme anatomy & the two manifests

The complete theme directory layout plus theme.json and salieno.json field by field, and which files Core and the marketplace actually require.

9 min readUpdated Aug 13, 2026
On this page

Theme anatomy & the two manifests#

A Salieno theme is a self-contained directory of Blade views, precompiled assets, and JSON manifests. Because Core ships no default theme and privileges none, the fallback view finder only steps in when a view is missing — so anything your theme doesn't ship becomes a neutral Core holding page. That makes the layout of the directory, and the two manifests that describe it, the contract you're building against. This article is the map: what goes where, which files are required, and what every manifest field means.

If you haven't yet, read the overview for how resolution and distribution work, then scaffold a starting point per the quickstart. This article assumes you have a theme folder in front of you.

Where a theme lives#

Every theme is a single directory named after the theme:

code
resources/views/themes/{name}/

The directory name is the theme's identity everywhere in Core — it's what activeThemeName() returns, what session('template') ?? gs('active_theme') stores, what themeViewName('home') prefixes to themes.{name}.home, and the DB key under which admin page-builder content is stored. Keep it lowercase, hyphen-free, and stable: renaming the folder orphans stored customisation.

The directory tree#

Here is the shape of a complete theme, taken from the first-party salieno reference theme. Not every folder is mandatory (see Required vs optional below), but this is the full surface a storefront can present:

code
resources/views/themes/{name}/
├── theme.json                 # runtime manifest Core reads (REQUIRED)
├── salieno.json               # marketplace packaging manifest (REQUIRED to publish)
├── sections.json              # section order + page-builder definitions (REQUIRED)
│
├── layouts/
│   ├── app.blade.php          # the page shell — REQUIRED, and the manifest "entry"
│   └── frontend.blade.php     # optional inner content layout
│
├── home.blade.php             # top-level page views (home, pricing, contact, …)
├── pricing.blade.php
├── contact.blade.php
├── page.blade.php             # generic CMS page
├── maintenance.blade.php      # rendered via activeTheme().'maintenance'
├── cookie.blade.php  policy.blade.php  unsubscribe.blade.php  affiliate.blade.php
│
├── sections/                  # one blade per section slug in sections.json
│   ├── hero.blade.php
│   ├── pricing.blade.php
│   ├── faq.blade.php
│   └── partials/              # shared bits used by several sections
│
├── partials/                  # header, footer, render-sections, breadcrumbs, …
│   ├── header.blade.php
│   ├── footer.blade.php
│   ├── render-sections.blade.php
│   └── illustrations/
│
├── components/theme/          # optional overrides of the x-theme.* library
│   ├── card.blade.php
│   ├── btn-primary.blade.php
│   ├── pricing/  compare/  ui/
│
├── auth/                      # Livewire auth page bodies
│   ├── login.blade.php  register.blade.php
│   ├── forgot-password.blade.php  reset-password.blade.php
│   ├── verify-email.blade.php  two-factor-verify.blade.php
│   └── partials/
│
├── cart/
│   ├── index.blade.php  checkout.blade.php  confirmation.blade.php
├── domains/
│   ├── index.blade.php  search.blade.php  configure.blade.php  transfer.blade.php  pricing.blade.php
├── products/
│   ├── category.blade.php  order.blade.php
├── store/
│   └── compare-page.blade.php
├── kb/                        # knowledge base: index, category, article, search, partials/
├── blog/                      # blog: index, details, partials/
│
├── errors/
│   ├── 403.blade.php  404.blade.php  419.blade.php
│   ├── 429.blade.php  500.blade.php  503.blade.php
│
└── assets/                    # PRECOMPILED, copied to public/ on install
    ├── css/theme.css
    └── js/theme.js

A few facts worth internalising now:

  • Views map to pages by name. home.blade.php backs the home route, the cart/ folder backs the cart routes, errors/404.blade.php backs a 404, and so on. Pages and how they resolve walks every one.
  • `sections/` and `sections.json` move together. The array in sections.json names slugs; each slug needs a matching sections/{slug}.blade.php. A slug with no blade is silently skipped, not fatal. See Building sections.
  • `assets/` ships already-built CSS/JS. Uploaded themes run precompiled — no @vite, no @tailwind/@apply at runtime. Reference files with themeAsset('css/theme.css'). Details and the copy-time security allowlist are in CSS, JavaScript & images.
  • Seed data for sections is not part of the theme. When admin content is empty, section blades fall back to hardcoded defaults or to Core's own resources/data/*.json seeds — those live in Core, not in your theme folder. Your job is to render sensibly when a field is empty.

theme.json — the runtime manifest#

theme.json is what Core reads at runtime to describe, gate, and colour the theme. It lives at the theme root. Here is the reference theme's file, trimmed to one representative colour set:

json
{
    "name": "Salieno Theme",
    "version": "1.2.0",
    "author": "Salieno Team",
    "description": "A premium, fast-loading hosting & domains theme — clean typography, accessible components, mobile-first layout, and light & dark modes.",
    "preview": "preview.jpg",
    "requires_salieno": "1.0.0",
    "supports": {
        "sections": true,
        "dark_mode": true,
        "rtl": false
    },
    "settings": {
        "show_mode_switcher": true
    },
    "colors": {
        "primary": "#ef4444",
        "secondary": "#18181d",
        "neutral_dark": "#08080a",
        "neutral_light": "#ffffff",
        "footer_bg": "#111114",
        "footer_text": "#fafafa"
    }
}

Field by field:

FieldTypeWhat it does
namestringHuman-readable display name shown in Admin → Frontend → Themes. Required — validation fails without it. This is the label, not the folder name.
versionstring (semver)The installed version. Required. Must equal salieno.json's version at publish time; the marketplace enforces strictly-increasing versions on update.
authorstringAttribution shown in the gallery. Optional.
descriptionstringOne-line summary in the gallery card. Optional, strongly recommended.
previewstringFilename of the preview image at the theme root (e.g. preview.jpg), shown as the gallery thumbnail. Optional.
requires_salienostring (min version)The minimum Core version this theme needs. Core refuses to install a theme whose requires_salieno is newer than the running Core, with a clear "update Core first" message. Absent means "no minimum" and is allowed. This is the one field that distinguishes a deliberately minimal theme from one built for a Core you don't have.
supports.sectionsboolDeclares the theme renders admin-editable sections.
supports.dark_modeboolDeclares the theme ships a light + dark palette. Pair with settings.show_mode_switcher. See Colours, dark mode & the mode switcher.
supports.rtlboolDeclares right-to-left support.
settingsobjectFree-form theme options Core surfaces/reads. The reference uses show_mode_switcher to toggle the light/dark control.
colorsobjectNamed colour tokens for the theme. Values are hex or rgba(). They document the palette and back the admin colour preview; you still author the real CSS in assets/css. Keys like primary, secondary, neutral_*, footer_*, button*, and svg_* are conventions from the reference theme, not a fixed schema — use the ones your CSS consumes.

Core reads these via the themeMeta(key, default) helper (backed by theme.json); the full helper set is catalogued in the helper reference. Validation is minimal by design: a missing theme.json, invalid JSON, or an empty name/version blocks installation; everything else is optional metadata.

salieno.json — the marketplace packaging manifest#

salieno.json is the packaging descriptor the marketplace reads when you submit a version. It is not consulted at page-render time — theme.json is the runtime manifest — but it is what identifies the archive as a theme and states its compatibility. Here is the reference file in full:

json
{
    "kind": "theme",
    "theme_target": "storefront",
    "name": "Salieno",
    "version": "1.2.0",
    "requires_core": ">=1.0.0 <2.0.0",
    "entry": "layouts/app.blade.php",
    "description": "The default Salieno Core storefront theme — a fast, accessible hosting and domains front end with light and dark modes, built entirely from sections.",
    "author": "Salieno",
    "license": "proprietary",
    "homepage": "https://salieno.com",
    "support_url": "https://salieno.com/support",
    "requires": {
        "php": ">=8.2"
    }
}

Field by field:

FieldTypeWhat it does
kindstringMust be "theme". This is how the marketplace tells a theme package from an extension.
theme_targetstringWhere the theme applies. Storefront themes use "storefront".
namestringPackage name shown in the marketplace listing.
versionstring (semver)The version of this package. Must match theme.json's version, and each new marketplace version must be strictly greater than the last published one.
requires_corestring (semver range)The Core versions this theme supports, e.g. ">=1.0.0 <2.0.0". The marketplace and installer use this to keep a theme off a Core it can't run on — the sibling to theme.json's requires_salieno.
entrystringThe theme's shell view, relative to the theme root — layouts/app.blade.php.
requires.phpstring (range)Minimum PHP the theme's compiled assets/views assume, e.g. ">=8.2".
description, author, license, homepage, support_urlstringListing metadata for the marketplace page. Optional but expected on a public submission.

At submission the marketplace scans the archive (locating salieno.json at the zip root or one level down), a human reviews it, and Core signs it server-side with Ed25519 before publishing. Installs verify that signature and atomically swap the theme directory — file swaps only, so admin page-builder content, which is keyed by theme name in the database, is never touched by an update. The mechanics and the versioning rules are in Publishing & updating on the marketplace.

Required vs optional#

There are two overlapping notions of "required": what Core needs on disk to treat a folder as a working theme, and what the marketplace needs to accept a package.

Required for a theme to install and validate (ThemeService):

  • layouts/app.blade.php — the page shell / manifest entry.
  • sections.json — must be present and valid JSON.
  • theme.json — present, valid JSON, with non-empty name and version.

Required to publish additionally needs salieno.json with a matching version, a kind of theme, and a requires_core range that admits real Core versions.

Everything else is optional but load-bearing. Because Core has no default theme to borrow from, any public view you omit renders as a bare Core holding page, not a styled fallback. A theme meant for real use should therefore ship the whole surface: the section blades named in sections.json, the auth pages, cart/domains/products/kb/blog pages, all six errors/ codes, and the assets they reference. The testing checklist is the completeness audit for exactly this.

Two folders are genuinely optional in behaviour, not just in validation:

  • components/theme/ — you only ship an override when you want to replace a Core base x-theme.* component; otherwise Core's version renders. See Components: using and overriding x-theme.*.
  • layouts/frontend.blade.php — an inner content layout is a convenience, not a contract; only app.blade.php is required.

Next#

You now know the map and the manifests. Next, build the shell everything renders into: Layouts and the page shell.

themesanatomymanifesttheme.jsonstructure
Was this article helpful?
Still stuck?Contact support
Theme anatomy & the two manifests · Salieno Docs