SCSS Architecture
Contents of this page:
The Guppy Theme uses a well-thought-out SCSS architecture that builds on the Bootstrap framework and is extended with custom components and functionalities.
Directory Structure
src/scss/
├── abstract/
│ ├── functions/
│ │ ├── boolean.scss
│ │ ├── px-to-rem.scss
│ │ └── strip-unit.scss
│ ├── mixins/
│ │ ├── button-adaptive-hover.scss
│ │ ├── focus-style.scss
│ │ └── rlh.scss
│ └── variables/
│ ├── bootstrap.scss
│ └── custom.scss
├── base/
│ ├── accessibility.scss
│ ├── base.scss
│ ├── container.scss
│ └── typography.scss
├── components/
│ ├── alert.scss
│ ├── badge.scss
│ ├── base-slider.scss
│ ├── breadcrumb.scss
│ ├── button.scss
│ ├── card.scss
│ ├── category-navigation.scss
│ ├── cms-block.scss
│ ├── cms-element.scss
│ ├── cms-section.scss
│ ├── delivery-information.scss
│ ├── filter-panel.scss
│ ├── forms.scss
│ ├── gallery-slider.scss
│ ├── guppy-theme-card-style.scss
│ ├── icon.scss
│ ├── image-slider.scss
│ ├── line-item.scss
│ ├── login.scss
│ ├── nav.scss
│ ├── offcanvas.scss
│ ├── pagination.scss
│ ├── payment-method.scss
│ ├── product-badges.scss
│ ├── product-box.scss
│ ├── product-slider.scss
│ ├── product-wishlist.scss
│ ├── quantity-selector.scss
│ └── shipping-method.scss
├── elements/
│ ├── skip-link.scss
│ └── splide-slider.scss
├── layout/
│ ├── account-menu.scss
│ ├── container.scss
│ ├── footer-minimal.scss
│ ├── footer.scss
│ ├── header-minimal.scss
│ ├── header.scss
│ ├── main-navigation.scss
│ ├── navbar.scss
│ ├── navigation-flyout.scss
│ ├── offcanvas-cart.scss
│ ├── top-bar.scss
│ └── usp-banner.scss
├── page/
│ ├── account/
│ │ ├── account.scss
│ │ ├── aside.scss
│ │ └── register.scss
│ ├── checkout/
│ │ ├── aside.scss
│ │ ├── cart.scss
│ │ ├── checkout.scss
│ │ ├── confirm.scss
│ │ └── finish.scss
│ ├── content/
│ │ └── breadcrumb.scss
│ └── product-detail/
│ ├── configurator.scss
│ ├── product-detail.scss
│ └── tabs.scss
├── skin/
│ └── shopware/
│ ├── abstract/
│ │ ├── variables.scss
│ │ └── variables/
│ │ ├── bootstrap.scss
│ │ ├── custom.scss
│ │ └── theme.scss
│ ├── base.scss
│ ├── base/
│ │ ├── base.scss
│ │ └── inter-fontface.scss
│ └── component/
│ ├── cms-block.scss
│ ├── cms-element.scss
│ ├── custom-select.scss
│ ├── modal.scss
│ ├── pagination.scss
│ ├── product-box.scss
│ ├── quickview-modal.scss
│ └── tab-menu.scss
├── base.scss
└── overrides.scssAbstract Layer
Functions
px-to-rem.scss
Converts pixel values to rem units:
scss
@function px-to-rem($px, $base-font-size: 16px) {
@return ($px / $base-font-size) * 1rem;
}
// Usage
.example {
font-size: px-to-rem(18px); // 1.125rem
margin: px-to-rem(24px); // 1.5rem
}strip-unit.scss
Removes units from values:
scss
@function strip-unit($value) {
@return ($value / ($value * 0 + 1));
}
// Usage
$width: strip-unit(100px); // 100boolean.scss
Helper functions for boolean operations:
scss
@function is-true($value) {
@return $value == true;
}Mixins
RLH (Responsive Line Heights)
Extends the Bootstrap RFS system with responsive line heights:
scss
@mixin rlh($values, $property: line-height) {
@if $values != null {
$val: rfs-value($values);
$fluidVal: rfs-fluid-value($values);
@if $val == $fluidVal {
#{$property}: $val;
} @else {
@include _rfs-rule {
#{$property}: if($rfs-mode == max-media-query, $val, $fluidVal);
min-width: if($rfs-safari-iframe-resize-bug-fix, (0 * 1vw), null);
}
@include _rfs-media-query-rule {
#{$property}: if($rfs-mode == max-media-query, $fluidVal, $val);
}
}
}
}
// Shorthand
@mixin line-height($value) {
@include rlh($value);
}Usage:
scss
h1 {
@include line-height(1.2); // Responsive line-height
}
.title {
@include rlh(1.5, line-height); // Explicit property
}Focus Style
Unified focus styles for accessibility:
scss
@mixin focus-style {
outline-offset: $focus-outline-offset;
outline: $focus-outline-color solid $focus-outline-width;
box-shadow: $focus-outline-box-shadow;
}Usage:
scss
button:focus-visible {
@include focus-style;
}Button Adaptive Hover
Intelligent hover effects for buttons:
scss
@mixin button-adaptive-hover($base-color) {
&:hover {
@media (hover: hover) {
background-color: shade-color($base-color, 15%);
}
}
}Variables
Bootstrap Variables (bootstrap.scss)
Comprehensive Bootstrap configuration with theme integration:
scss
// Colors
$gray-100: $sw-color-gray-100 !default;
$gray-200: $sw-color-gray-200 !default;
// ... more gray tones
// Typography
$font-size-base: $sw-font-size-base !default;
$h1-font-size: $sw-h1-font-size !default;
$h2-font-size: $sw-h2-font-size !default;
// ... more font sizes
// Container
$container-max-widths: (xs: $guppy-container-width-default) !default;
$grid-gutter-width: 1.5rem !default;
// Buttons & Inputs
$btn-font-size: $sw-input-btn-font-size !default;
$btn-border-radius: $sw-input-btn-border-radius !default;
$btn-font-weight: $sw-input-btn-font-weight !default;Custom Variables (custom.scss)
Guppy-specific variables:
scss
// Icons
$icon-base-size: 22px !default;
$icon-base-color: currentColor !default;
$icon-accessibility-touch-size: 44px !default;
// Typography
$h1-line-height: $sw-h1-line-height !default;
$h2-line-height: $sw-h2-line-height !default;
// ... more line heights
// Product Images
$product-image-height: 240px !default;
$product-image-height-lg: fit-content !default;
$product-image-aspect-ratio: #{10/9} !default;
$product-image-background: $sw-color-product-image !default;
// Accessibility
$focus-outline-width: 2px !default;
$focus-outline-color: $sw-color-brand-primary !default;
$focus-outline-offset: 2px !default;
$focus-outline-box-shadow: 0 0 0 2px #fff;Component System
Product Box (product-box.scss)
Extended product box styles:
scss
.product-box {
.product-image {
aspect-ratio: $product-image-aspect-ratio;
background-color: $product-image-background;
img {
object-fit: if($guppy-productcard-image-object-fit == 'cover', cover, contain);
}
}
.product-badges {
@if $guppy-badges-show-outline {
.badge {
border: 1px solid currentColor;
}
}
}
}Skip Link (skip-link.scss)
Accessible navigation:
scss
.skip-link {
position: absolute;
left: -10000px;
top: auto;
width: 1px;
height: 1px;
overflow: hidden;
&:focus {
left: 0;
z-index: 1010;
width: auto;
height: auto;
padding: 0.5rem 1rem;
background-color: $sw-color-brand-primary;
color: white;
text-decoration: none;
font-weight: bold;
}
}Build System
Compilation Order
The theme uses a specific compilation order:
json
{
"style": [
"app/storefront/src/scss/overrides.scss",
"@StorefrontBootstrap",
"@Plugins",
"app/storefront/src/scss/skin/shopware/base.scss",
"app/storefront/src/scss/base.scss"
]
}Main Entry (base.scss)
Main stylesheet:
scss
// Functions & Mixins
@import "abstract/functions/px-to-rem";
@import "abstract/functions/strip-unit";
@import "abstract/functions/boolean";
@import "abstract/mixins/rlh";
@import "abstract/mixins/focus-style";
@import "abstract/mixins/button-adaptive-hover";
// Base Styles
@import "base/accessibility";
@import "base/base";
@import "base/typography";
@import "base/container";
// Components
@import "components/button";
@import "components/product-box";
@import "components/splide-slider";
// ... more components
// Layout
@import "layout/header";
@import "layout/footer";
@import "layout/navigation";
// ... more layout components
// Elements
@import "elements/skip-link";
@import "elements/splide-slider";
// Pages
@import "page/account/account";
@import "page/checkout/checkout";
@import "page/product-detail/product-detail";
// ... more pagesCustomization
Child Theme Overrides
In a child theme, you can override specific styles:
scss
// overrides.scss
// Override Guppy variables
$guppy-usp-background-color: #f8f9fa !default;
$product-image-height: 300px !default;
// Override Bootstrap variables
$primary: #007bff !default;
$secondary: #6c757d !default;scss
// base.scss
// Additional styles
.my-custom-component {
background-color: $primary;
.title {
@include line-height(1.3);
}
}Best Practices
Variable Usage
scss
// Good: Use theme variables
.custom-component {
color: $sw-text-color;
background-color: $sw-color-gray-100;
}
// Bad: Hardcoded values
.custom-component {
color: #333;
background-color: #f9f9f9;
}Responsive Design
scss
// Good: Use Bootstrap breakpoints
.custom-component {
@include media-breakpoint-up(md) {
display: flex;
}
}
// Bad: Hardcoded breakpoints
.custom-component {
@media (min-width: 768px) {
display: flex;
}
}Accessibility
scss
// Good: Use focus styles
.custom-button {
&:focus-visible {
@include focus-style;
}
}
// Bad: Custom focus styles
.custom-button {
&:focus {
outline: 2px solid blue;
}
}