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.
| Directory | Holds | Docker | On upgrade |
|---|---|---|---|
resources/themes/ | The shipped default theme | Baked into the image | Replaced every time |
themes/ | Your own themes | Bind-mounted from the host | Never 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
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:
{
"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:
| Attribute | Applies to | Effect |
|---|---|---|
data-theme-key="setting" | string and html | Replaces the element's contents |
data-theme-wrapper="setting" | string and html | Wrapper hidden when the text is emptied |
data-theme-toggle="setting" | bool | Shows or hides the element |
data-theme-variant="setting" | select | Swaps classes, read from a data-variant-{value} attribute per option |
data-theme-file="setting" | file | Applies 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
- Follow the steps for a remote SSH VS Code instance: https://code.visualstudio.com/docs/remote/ssh.
- Once connected, open your theme at
{your-store-instance}/themes/{theme-name}.{your-store-instance}will likely be/var/www/storeor~/store, depending on where you put the app when you set up hosting.
- Edit your theme files.
- Rebuild the assets to see the change, using the commands in the next section.
Using PhpStorm or other JetBrains IDEs
- Open your remote project at
{your-store-instance}/themes/{theme-name}over SSH: https://www.jetbrains.com/help/phpstorm/remote-development-starting-page.html. - Edit your theme files.
- 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 builddocker compose up -d --buildThe 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.