Skip to content

Manual Child Theme Development

This page covers the manual workflow for developers maintaining a child theme as a regular plugin in a repository, without the browser-based Theme Builder.

When manual vs. Theme Builder?

NeedRecommendation
Brand theme with variable / asset adjustmentsTheme Builder
Custom Twig overrides, custom JS, your own pluginsManual
Complex layouts, custom CMS elementsManual
Version control via gitManual (or builder + snapshot in repo)
Fast iteration without local setupTheme Builder

Hybrid path

Use the Theme Builder as a starter, extract the ZIP, drop the contents into your repo, then continue manually. Check the snapshot (.guppy-builder.json) into the repo as well.

Theme skeleton with guppy:theme:create

The fastest path to a child theme you can iterate on manually:

bash
bin/console guppy:theme:create

Interactive process. Inputs:

  • Namespace (e.g. Dmf, Acme)
  • Theme name (e.g. CustomerShop)
  • Theme title for the admin
  • Brand colors (primary, secondary, border, background)
  • Asset filenames (plugin icon, preview, logo, share icon, favicon)

Generated structure:

text
custom/static-plugins/DmfCustomerShopTheme/
├── composer.json
└── src/
    ├── DmfCustomerShopTheme.php
    └── Resources/
        ├── app/storefront/
        │   ├── src/scss/
        │   │   ├── base.scss
        │   │   └── overrides.scss
        │   └── dist/
        │       ├── assets/
        │       └── storefront/js/
        ├── config/plugin.png
        ├── theme.json
        └── views/storefront/

Auto-generated:

  • complete composer.json with the dmf/sw6-guppy-theme dependency
  • preconfigured theme.json with the correct inheritance
  • PHP plugin class with the correct namespace
  • default assets copied

Inheritance in theme.json is hard-wired:

json
{
  "configInheritance": [
    "@Storefront",
    "@DmfGuppyTheme"
  ]
}

Prepare assets first

Have plugin icon, preview image, logo, share icon, and favicon ready before running the command, the generator copies them into Resources/.

Manual creation (without the generator)

If you don't want to use the generator:

text
custom/plugins/AcmeChildTheme/
├── composer.json
└── src/
    ├── AcmeChildTheme.php
    └── Resources/
        ├── theme.json
        ├── config/plugin.png
        └── app/storefront/src/scss/
            ├── overrides.scss
            └── base.scss

composer.json:

json
{
  "name": "acme/child-theme",
  "description": "Acme Child Theme",
  "type": "shopware-platform-plugin",
  "license": "proprietary",
  "require": {
    "shopware/core": "~6.7.0",
    "dmf/sw6-guppy-theme": "^2.0"
  },
  "autoload": {
    "psr-4": {
      "AcmeChildTheme\\": "src/"
    }
  },
  "extra": {
    "shopware-plugin-class": "AcmeChildTheme\\AcmeChildTheme",
    "label": {
      "de-DE": "Acme Child Theme",
      "en-GB": "Acme Child Theme"
    }
  }
}

theme.json:

json
{
  "name": "AcmeChildTheme",
  "author": "Acme",
  "views": ["@Storefront", "@Plugins", "@AcmeChildTheme"],
  "style": [
    "app/storefront/src/scss/overrides.scss",
    "@StorefrontBootstrap",
    "@Plugins",
    "app/storefront/src/scss/base.scss"
  ],
  "script": [
    "@Storefront",
    "@Plugins"
  ],
  "asset": ["@Storefront", "@Plugins", "app/storefront/dist/assets"],
  "configInheritance": ["@Storefront", "@DmfGuppyTheme"],
  "previewMedia": "app/storefront/dist/assets/theme-preview.jpg"
}

Install and activate the plugin

bash
bin/console plugin:refresh
bin/console plugin:install --activate AcmeChildTheme
bin/console theme:change                           # pick the sales channel
bin/console theme:compile
bin/console cache:clear

Adjust theme variables

In overrides.scss (loads before Bootstrap):

scss
// Override values from abstract/variables/bootstrap.scss
$body-bg: #f00 !default;
$body-color: #000 !default;

// Guppy-specific variables
$icon-base-size: 16px !default;
$product-image-height: 320px !default;

// Bootstrap variables
$primary: #1A223E !default;
$secondary: #F49927 !default;

In base.scss (loads after everything):

scss
// Selector styles
.custom-component {
    background-color: $primary;
    color: $body-color;
}

Order matters

Variable overrides belong in overrides.scss, in base.scss they no longer apply because Bootstrap is already compiled.

Full variable and mixin reference: Variables & Tokens.

Twig overrides

Templates mirror the original path and use sw_extends:

twig
{# src/Resources/views/storefront/layout/header/header.html.twig #}
{% sw_extends '@Storefront/storefront/layout/header/header.html.twig' %}

{% block layout_header_logo %}
    <div class="header-logo-acme">
        {{ parent() }}
    </div>
{% endblock %}

Detail: Twig Overrides.

Iteration

After each change:

bash
bin/console theme:compile
bin/console cache:clear

With the storefront watcher running, Ctrl+S is enough, the watcher rebuilds automatically:

bash
bin/watch-storefront.sh

Install plugins

DmfGuppyTheme ships a command that pulls recommended plugins via Composer:

bash
bin/console guppy:install:plugins

More under Recommended Plugins.