kommandhub / base-theme-sw
Modern, premium base theme for Shopware 6.7: neutral monochrome UI with colour reserved for meaning, design tokens, dark mode, accessible navigation and refined checkout.
Package info
github.com/KommandHub-Org/base-theme-sw
Language:SCSS
Type:shopware-platform-plugin
pkg:composer/kommandhub/base-theme-sw
Requires
- php: >=8.2
- shopware/core: ~6.7.0
- shopware/storefront: ~6.7.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.94
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
- rregeer/phpunit-coverage-check: ^0.3.1
- shopware/dev-tools: ^1.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-16 23:30:38 UTC
README
Base Theme for Shopware 6
A modern, premium base theme for Shopware 6.7 in a contemporary product-UI style (think Linear / Vercel / Stripe) — a reusable foundation for storefront projects, not a single-store skin. The interface is neutral monochrome: hierarchy and interaction come from tone, typography, spacing and hairline borders. Hue is reserved for meaning — status (success / warning / danger / info) and rating stars — so the few coloured things carry information.
Developed by Kommandhub. Light and dark mode, desktop and phone, WCAG AA text contrast — covered by a Playwright suite.
Contents
- Requirements
- Installation
- Design system
- Theme configuration
- Branding assets
- Fonts
- Kommandhub plugin skins (optional)
- Shopping Experiences (CMS)
- End-to-end tests
- Development
- Coverage
- Support
- Contributing
- Security
- License
Requirements
- Shopware 6.7 (
shopware/coreandshopware/storefront~6.7.0) - PHP 8.2 or newer
Installation
Composer (recommended)
The theme is published on Packagist:
composer require kommandhub/base-theme-sw
bin/console plugin:refresh
bin/console plugin:install --activate KmhBaseTheme
bin/console theme:change --all KmhBaseTheme # or pick it per sales channel
bin/console cache:clear
Zip upload
Download the release zip from
GitHub Releases and
upload it under Extensions → My extensions → Upload extension, or extract it
to custom/plugins/KmhBaseTheme and run the plugin: commands above.
Assign the theme
In the Administration open Sales Channels → (your storefront) → Theme and
choose KmhBaseTheme, or use theme:change as above. After changing theme
settings or updating the theme, recompile with bin/console theme:compile.
Update
composer update kommandhub/base-theme-sw bin/console plugin:update KmhBaseTheme bin/console theme:compile bin/console cache:clear
See CHANGELOG.md for what changed in each release.
Design system
All visual decisions flow from one token layer, so brands customise through
theme config / SCSS variables rather than editing components. Bootstrap stays
the foundation; its variables and --bs-* custom properties are re-pointed at
the tokens instead of being fought with per-selector overrides.
app/storefront/src/scss/
overrides.scss ← loads BEFORE @Storefront: Bootstrap & Shopware
!default variables (type scale, control sizes,
radii, surfaces) → tokens
base.scss ← loads AFTER @Storefront: token CSS vars + layers
abstract/
_palette.scss ← neutral scale (mono-0…mono-1000), semantic aliases,
status hues (+ derived -text / -bg / -border)
_config.scss ← theme.json config → radius scale, widths, spacing
_tokens-css.scss ← ALL tokens as :root custom properties + dark mode
_mixins.scss ← focus-ring, elevation, motion-safe helpers
base/ ← reboot/typography & links, focus, buttons, forms,
core-colour bridge (dark mode)
component/ ← cards/lists/tables, overlays (dropdown, modal,
tooltip), alerts & badges, listing, product card
layout/ ← header & nav, off-canvas, footer, chrome, container
page/ ← PDP, checkout, account
Tokens (_tokens-css.scss)
| Group | Tokens |
|---|---|
| Colour | --color-text-*, --color-bg-*, --color-surface, --color-border(-subtle/-strong/-input), --color-action*, --color-disabled-*, --color-focus-ring |
| Status | --color-{success,warning,danger,info} + -text (AA), -bg (tint), -border |
| Type | --font-size-2xs … 3xl (11 → 34px fluid), --font-weight-*, --line-height-*, --tracking-* |
| Space | --space-1 … 16 (4px grid) |
| Controls | --control-height-sm/-/-lg (32 / 40 / 46px) |
| Radius | --radius-sm (chips, menu rows) · --radius (controls, 8px) · --radius-lg (cards, 12px) · --radius-xl (dialogs, 16px) · --radius-full |
| Elevation | --shadow-xs/sm/md/lg, --focus-halo |
| Motion | --ease-standard, --transition-fast/base |
Dark mode is the same names re-valued under [data-theme="dark"].
Principles
- Typography — Inter (self-hosted by core). Semibold headings with tight tracking; 16px/1.6 body; 14px medium for labels, nav and controls; muted text stays ≥ 4.5:1.
- Links — no default underline. Chrome links (nav, footer, cards,
breadcrumbs) read by position. Links inside running text keep a hairline
underline because they share the text colour (WCAG 1.4.1).
.btn-linklooks like a link. - Navigation — muted at rest → ink on hover with a tinted pill; the active section (and the one whose flyout is open) gets a 2px ink indicator on the bar's hairline. Account, category and drawer navs use the same inset rounded-row idiom.
- Buttons — primary (ink), secondary (surface + hairline), outline, ghost
(
.btn-light), link, destructive (.btn-danger,.btn-outline-danger); disabled via tokens (not opacity); loading keeps the button's look and shows core's spinner incurrentColor. - Forms — 40px, 16px text (no iOS zoom), borders kept at 3:1 (WCAG 1.4.11), ink border + soft halo on focus, status-red invalid state + message.
- Breadcrumb → page header → content — 13px muted trail with
/separators (current page in ink, plain text,aria-current; on product pages the product is the current crumb), 12px to a semibold title, 8px to an optional description, 32px to content. Phones collapse the trail to… / parent / current. Reuse the.page-headercomponent (component/_page-header.scssdocuments the markup: title, description, meta badges, actions, tabs, toolbar); core headers are mapped onto the same mixins. - Surfaces — hairline border + xs shadow for cards; lg shadow for anything floating (dropdowns, modals, search suggest, flyout).
- Focus — one solid 2px ring,
:focus-visibleonly, never suppressed. - Reduced motion — global
prefers-reduced-motionsafety net. - Ratings —
--color-rating(amber, theme config Rating stars).
Theme configuration
Beyond the standard Shopware colour/logo/typography fields (neutral defaults; Status colours drive the status system), two extra blocks expose base-theme controls:
- Layout — container width (fixed/fluid), content max-width, section spacing, sticky header, top bar, breadcrumb visibility, navigation style, footer columns.
- Components — corner radius, primary button style, card style, product image ratio, product-grid columns per breakpoint, dark-mode toggle.
Dark mode
Dark mode toggle (on by default) adds a moon button next to the account and
cart icons, at every breakpoint. It's implemented inline (a no-flash <head>
script + a small wiring script, both gated on the setting) — no webpack build
required. The visitor's choice is stored in localStorage and the whole palette
flips via [data-theme="dark"] on <html>. Turn the setting off to ship zero
extra markup/script.
Core Shopware bakes some colours in at compile time; base/_core-colors.scss
re-points those elements at the tokens so they follow the flip too. When a new
core element shows up dark-on-dark, add it there.
Config fields are consumed in SCSS ($kmh-*); the sticky-header, top-bar,
breadcrumb and dark-mode switches also gate markup in Twig via
theme_config('kmh-...'). The Commerce block adds the rating-star colour.
Branding assets
Master artwork lives in src/Resources/config/ (plugin.png = icon only,
kommandhub.png = logo + wordmark). Derived, web-optimised copies are generated
into src/Resources/app/storefront/src/assets/:
| Asset | Source | Used for | Size |
|---|---|---|---|
config/plugin.png |
— | Plugin-manager icon (composer.json → extra.plugin-icon) |
256² |
.../logo/favicon.png |
plugin.png | Favicon (sw-logo-favicon) |
96² |
.../logo/kommandhub-logo.png |
kommandhub.png | Storefront logo — desktop/tablet/mobile + share (one shared file) | 600×144 |
.../preview.png |
kommandhub.png | Theme preview card (theme.json → previewMedia) |
1530×1000 |
The logo is monochrome (pure black), so dark mode inverts it via CSS
([data-theme="dark"] .header-logo-main-img) rather than shipping a second file.
Merchants override any of these through the theme settings.
Fonts
Performance/privacy first: no Google Fonts CDN. Uses the Inter variable font
that Shopware core already self-hosts (@Storefront assets), falling back to the
system stack. To use another font, add an @font-face in base.scss and point
sw-font-family-base / sw-font-family-headline at it.
Kommandhub plugin skins (optional)
The theme styles the storefront UI of the Kommandhub plugins so they match the
design system — and depends on none of them. Every rule in scss/plugins/
targets the plugin's own markup, so with a plugin absent nothing matches; no
Twig of the theme references a plugin.
| Plugin | What the theme adjusts |
|---|---|
| KmhClickAndPickSW | pickup labels and selects at checkout; the pickup card's inline styles (gradient, fixed colours, Segoe UI) replaced with tokens — light + dark |
| KmhPaystackSW / KmhFlutterwaveSW | bank details as label/value pairs; with both active, the two account-overview cards sit side by side |
| KmhSmsSW | dial-code + number field keeps its rounded outer corners |
| KmhAfricaCommerceCoreSW | none needed — its address fields use core form markup |
Generic fix that also serves plugins: any <select class="form-control"> gets
the same chevron as .form-select. tests/e2e/plugins.spec.ts checks each skin
and skips itself when the plugin isn't on the shop.
Shopping Experiences (CMS)
Every core CMS block is styled on the design system (component/_cms.scss):
text typography and .lead, rounded images, sliders and galleries, circular
bubbles, hairline image-text cards, equal-height product boxes (also in
product sliders), a scrim + light text on Text on image, and a themed consent
card for YouTube/Vimeo. Core's baked white frames and backdrops follow dark
mode.
What an editor sets in the Administration keeps working: block and section margins, background colours and media, sizing mode and alignment are never overridden. On top of that, three opt-in looks go into a block's CSS class field (Layout → block → Advanced):
| Class | Look |
|---|---|
kmh-cms-surface |
tinted panel with hairline border and padding |
kmh-cms-cards |
each column of the block becomes a card |
kmh-cms-center |
centred text in every column |
Rich-text elements can use <p class="kmh-eyebrow"> (small label above a
heading), <p class="lead"> and btn btn-primary / btn btn-outline-secondary
links.
tests/e2e/fixtures/seed-cms-home.mjs builds a homepage that uses every block
that works outside a product or listing page (32 blocks, including a sidebar
section) and assigns it to a sales channel's root category. It is idempotent
(fixed layout id) and leaves the previous homepage layout in place — assign it
back in the Administration to roll back. Run it with make e2e-cms on the
theme stack, or against any shop with an integration's credentials:
SW_URL=http://localhost SW_CLIENT_ID=… SW_CLIENT_SECRET=… SW_SALES_CHANNEL=Storefront \ node tests/e2e/fixtures/seed-cms-home.mjs
Images come from product covers; on a shop without product media it uploads neutral SVG placeholders.
End-to-end tests
A Playwright suite in tests/e2e/ drives a live storefront that has the theme
assigned — desktop (1440×900) and phone (Pixel 7) — and checks behaviour the
theme is responsible for:
| Spec | Covers |
|---|---|
header.spec.ts |
header icons share one centre/size, no sideways scroll, one-row phone header, full-bleed 32px top bar, active nav indicator, dark-mode toggle (switch, persist, revert); breadcrumb semantics (current page is text + aria-current, product crumb on PDP, single-item trail hidden, phone collapse, keyboard focus ring); page titles share one style |
listing.spec.ts |
horizontal filter row (even 40px row, 24px under title), boolean filter ring + active chip, phone filter sheet stacks full width; sidebar active category, card rows align, pagination |
wishlist.spec.ts |
red heart when saved, header counter fully visible, wishlist page header + no empty toolbar |
checkout.spec.ts |
cart drawer + success tint, cart page title/counter, register-form validation colour; logged-in customer places an order → finish page → order history with status chip; account pages share the page-header pattern |
theme.spec.ts |
WCAG AA text contrast (4.5:1 / 3:1 large) on guest and customer pages and the open account menu, light and dark |
address.spec.ts |
address manager (6.7): rounded address cards, neutral default chip, search row on phones, new-address page spacing; checkout address modal title, segmented shipping/billing switch and contrast, light and dark |
sliders.spec.ts |
every visible slider arrow is a round 40px button with a centred icon, inside the slider's clip (home, PDP gallery, cross-selling); cross-selling cards not clipped; room for the card hover lift; currency menu opens above the header |
product-tabs.spec.ts |
PDP description (measure, unstriped properties) and reviews (score, aligned distribution bars, date beside stars, reply panel, CTA toggles the form) + contrast, light and dark |
cms.spec.ts |
Shopping Experiences homepage: no sideways scroll, circular bubbles, highlight-row elevation, equal product cards, text-on-image scrim, themed video consent, opt-in classes |
plugins.spec.ts |
optional plugin skins (pickup controls + contrast, bank details layout, dial-code field); each skips when its plugin is absent |
Run it against the plugin's own stack (recommended — see Development):
make up # once: build + start the stack, composer-install theme + demo data make e2e-setup # activate, seed demo catalogue + CMS homepage, map domains to :8100, assign theme make e2e-install # once, on the host: npm deps + Chromium make e2e # run the suite; `make e2e-report` opens the HTML report
…or against any storefront that has the theme assigned:
E2E_BASE_URL=https://staging.example.com npx playwright test
Notes:
- Page paths default to the KmhDemoDataSW catalogue; override with
E2E_SIDEBAR_CATEGORY,E2E_TOOLBAR_CATEGORY,E2E_SEARCH. The product the suite opens is the first one listed in the sidebar category, unlessE2E_PRODUCTandE2E_PRODUCT_NAMEare both set (seetests/e2e/support.ts). - Each run registers one throwaway customer (
kmh-e2e-…@example.com) and places one real order (cash on delivery) — run it against dev/staging, not production. Its session and credentials stay in the git-ignoredtests/e2e/.auth/. - Cookie consent is pre-set, so the banner never covers controls.
make e2e-cmscreates a new admin integration (kmh-e2e-cms) on each run; delete old ones under Settings → Integrations if they pile up.- The contrast probe skips text over CMS background images (no single
background colour to measure);
cms.spec.tschecks the scrim instead. - Order- and wishlist-changing tests run on the desktop project only (both projects share the run's customer); skipped tests are these plus device-specific checks.
Development
The theme carries its own dockware stack (Shopware 6.7.8 + shopware-cli),
published on http://localhost:8100 so it can run beside other shops.
| Command | What it does |
|---|---|
make up / make down |
start (and composer-install the theme + KmhDemoDataSW) / tear down |
make check |
php-cs-fixer dry-run + PHPStan + PHPUnit |
make test · make analyse · make cs-fix |
the individual steps |
make e2e-setup · make e2e |
prepare the storefront and run Playwright |
make validate-plugin |
shopware-cli store-compliance check |
make shell |
shell in the container (bin/console theme:compile after SCSS edits) |
CI (.github/workflows/php.yml) runs the PHP checks with coverage, then the
Playwright suite in the same stack.
Releasing
- Bump
versionincomposer.jsonand add the entry toCHANGELOG.mdandCHANGELOG_de-DE.md. make check,make e2eandmake validate-pluginmust pass.make zipwrites the store-ready archive tobuild/.- Tag the commit (
git tag 1.2.0) and push the tag; Packagist picks up the new version from the tag, and the zip goes on the GitHub release.
Coverage
The storefront is styled end-to-end. All work is token-driven SCSS; the Twig overrides are small and block-preserving (sticky-header wrapper, header actions for the theme toggle, dark logo, top-bar gate, breadcrumb (setting gate, non-link current crumb, product crumb) and the PDP breadcrumb include).
| Area | Files |
|---|---|
| Design system, base layer | abstract/*, base/* |
| Header, nav, search, actions | layout/_header.scss, views/…/header.html.twig, header/header.html.twig, header/logo.html.twig, top-bar.html.twig |
| Listing + product cards | component/_product-card.scss |
| Product detail (PDP) | page/_product-detail.scss, page/_product-tabs.scss, views/…/component/review/review-widget.html.twig |
| Shopping Experiences (CMS blocks) | component/_cms.scss |
| Sliders: arrows, dots, product-slider track and clipping | component/_slider.scss |
| Breadcrumb + page headers | component/_page-header.scss, views/…/breadcrumb.html.twig, views/…/page/content/product-detail.html.twig |
| Filters, pagination, category nav | component/_listing.scss |
| Off-canvas nav + cart, line items | layout/_offcanvas.scss, component/_line-item.scss |
| Checkout (cart/confirm/finish/register) | page/_checkout.scss |
| Alerts, badges, order status | component/_alert.scss |
| Dropdowns, modals, tooltips, popovers | component/_modal.scss |
| Footer + global switches | layout/_footer.scss, layout/_container.scss, base/_buttons.scss |
| Cards, list groups, tables, account, address manager + modal | component/_card.scss, page/_account.scss |
| Dark-mode bridge for core's baked colours | base/_core-colors.scss |
State signalling — where it lives
- Alerts & badges — status tint + border + AA text shade, plus the per-type icon core renders (colour is never the only cue).
- Order status — open/in progress = info, completed = success, cancelled/failed = danger chips.
- Selection (payment/shipping method, address, swatch) — ink ring + the control's own check/radio.
- Navigation & lists — tinted row + ink + semibold for the current item; 2px indicator on the main nav.
- Pagination — current page in solid ink.
CMS text/HTML blocks and CMS forms inherit the base typography and form styles — no dedicated overrides needed.
Support
- Bugs and feature requests: GitHub issues
- Questions: GitHub Discussions
- Commercial support and custom development: kommandhub.com/support
Contributing
Contributions are welcome — see CONTRIBUTING.md and the Code of Conduct.
Security
Please report vulnerabilities privately as described in SECURITY.md, never in a public issue.
License
The source code is licensed under the Apache License 2.0; keep the NOTICE file in redistributions. The Kommandhub name and logo are not covered by the code licence — forks must be rebranded, see TRADEMARKS.md. "Shopware" is a registered trademark of shopware AG.