Light Services

Theming Light Store

A guide on building your own theme for Light Store.

Overview

Light Store renders its storefront through a theme. Themes are plain Blade views styled with Tailwind CSS, with a little TypeScript for the interactive parts.

Where themes live

Themes live in two separate directories, and the difference between them matters as soon as you upgrade.

DirectoryHoldsDockerOn upgrade
resources/themes/The shipped default themeBaked into the imageReplaced every time
themes/Your own themesBind-mounted from the hostNever touched

Never edit the default theme in place

resources/themes/ comes out of the release image and is overwritten by every upgrade, so any change you make there is lost the next time you update. Copy it into themes/ first, using the command below, and edit the copy.

Your own themes sit in a top-level themes/ directory, which docker-compose.yml mounts as ./themes:/var/www/themes. Because it is a bind mount, it lives on your host machine and upgrades leave it alone. resources/ is no longer mounted at all, which is what lets it stay baked into the image.

If a theme in themes/ has the same name as a shipped one, the user copy wins.

Creating a theme

Bootstrap a new theme with the convenience command. It copies the shipped default theme into themes/{theme-name}, giving you a complete, working starting point rather than an empty folder.

php artisan make:theme {theme-name}
docker compose exec app php artisan make:theme {theme-name}

Theme names may only contain lowercase letters, digits, dashes and underscores, and have to start with a letter or a digit. The name default is reserved.

Selecting your theme

In the admin panel, go to Settings, then the General tab, and open the Theme section. Pick your theme under Active Theme and save the settings. The dropdown lists shipped and user themes together.

What is in a theme

theme.json
welcome.blade.php
cart.blade.php

views/ holds every Blade template the storefront renders. css/theme.css is the Tailwind entry point, and it imports addon-safelist.css, a generated file that keeps utilities used only by addon markup from being stripped out of the build. Leave that import in place, and do not edit the safelist by hand.

Theme settings

A theme can expose its own settings, so the store owner can recolor it or change its wording without touching any code. They are declared in theme.json:

themes/my-theme/theme.json
{
	"name": "My Theme",
	"description": "A theme for my store",
	"sections": {
		"Dark Mode Palette": { "columns": 3 },
		"Hero": {}
	},
	"settings": {
		"primary_color": {
			"type": "color",
			"label": "Primary Color",
			"default": "#ad4de1",
			"section": "Dark Mode Palette"
		},
		"headline": {
			"type": "html",
			"label": "Hero Headline",
			"default": "Welcome to the store",
			"section": "Hero"
		},
		"headline_size": {
			"type": "select",
			"label": "Headline Size",
			"default": "lg",
			"options": { "sm": "Small", "lg": "Large" },
			"section": "Hero"
		}
	}
}

Each setting takes a type, a label, a default and the section it is grouped under. The available types are color, string, html, bool, number, file and select. A select also takes an options map of stored value to label. Sections are declared separately and can set a columns count to control their layout.

Read the values back in your Blade templates with the r_theme() helper, which works like Laravel's own config():

<h1 class="text-2xl">{!! r_theme('headline') !!}</h1>

Color settings are also injected as CSS custom properties on <body>, for example --color-primary, so your Tailwind classes can reference them directly.

Saved values live in the theme_settings table, keyed by theme, and theme.json supplies the default whenever a setting has never been saved.

The theme manager

Settings are edited in the theme manager, a dedicated page at /admin/theme-manager. You can reach it from the Theme Manager button in the header of the Settings page. It shows every setting your theme.json declares, grouped into the sections you named, next to a live preview that updates as you type. Nothing is written until you save.

The preview renders your welcome page in an iframe and applies the unsaved values to it, so an element only follows along if you mark it up for the preview. Color settings need nothing, as they are applied as CSS custom properties. The rest are matched by data attributes:

AttributeApplies toEffect
data-theme-key="setting"string and htmlReplaces the element's contents
data-theme-wrapper="setting"string and htmlWrapper hidden when the text is emptied
data-theme-toggle="setting"boolShows or hides the element
data-theme-variant="setting"selectSwaps classes, read from a data-variant-{value} attribute per option
data-theme-file="setting"fileApplies the image as the element's background

An element driven by data-theme-toggle has to stay in the markup when the setting is off, hidden with an inline display:none. Leave it out with an @if instead and the preview has nothing to reveal when the setting is switched back on.

<h1 data-theme-toggle="show_site_name" data-theme-variant="site_name_size" data-variant-sm="text-base"
    data-variant-lg="text-lg" @style(['display:none' => !r_theme('show_site_name')])
    class="{{ r_theme('site_name_size') === 'sm' ? 'text-base' : 'text-lg' }}">
    {{ $generalSettings->site_name }}
</h1>

Editing locally

If you run the store locally for development, edit your theme directly at themes/{theme-name}. Running composer run dev starts the Vite dev server alongside the app, which gives you hot-module reloading so changes appear as soon as you save.

Editing on a server

If your store is hosted remotely, connect an editor over SSH.

Because themes/ is a bind mount, the files are on the host machine rather than inside the container. You can edit them straight from the host, and the running container sees the change.

Using VS Code

  1. Follow the steps for a remote SSH VS Code instance: https://code.visualstudio.com/docs/remote/ssh.
  2. Once connected, open your theme at {your-store-instance}/themes/{theme-name}.
    • {your-store-instance} will likely be /var/www/store or ~/store, depending on where you put the app when you set up hosting.
  3. Edit your theme files.
  4. Rebuild the assets to see the change, using the commands in the next section.

Using PhpStorm or other JetBrains IDEs

  1. Open your remote project at {your-store-instance}/themes/{theme-name} over SSH: https://www.jetbrains.com/help/phpstorm/remote-development-starting-page.html.
  2. Edit your theme files.
  3. Rebuild the assets to see the change.

Rebuilding assets

Blade changes show up on the next page load, but any change to a theme's CSS or TypeScript needs the assets rebuilt.

pnpm run build
docker compose up -d --build

The app container ships no Node.js. Assets are built in a separate stage while the image is built, so rebuilding the image is what picks up theme changes. Your themes/ directory is part of the build context, so your own themes are compiled alongside the shipped one.

Upgrading

Upgrades replace resources/themes/ wholesale and leave themes/ untouched, so your own theme survives. It does not automatically gain whatever changed in the shipped theme, though. After a release that reworks the storefront, it is worth diffing your theme against the new default and porting across anything you want, particularly new views and any additions to theme.json.

On this page