Skip to content

SCSS Architecture

Inhalt dieser Seite:

Das Guppy Theme nutzt eine durchdachte SCSS-Architektur, die auf dem Bootstrap-Framework aufbaut und um eigene Komponenten und Funktionalitäten erweitert wird.

Verzeichnisstruktur

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.scss

Abstract Layer

Functions

px-to-rem.scss

Konvertiert Pixel-Werte zu rem-Einheiten:

scss
@function px-to-rem($px, $base-font-size: 16px) {
    @return ($px / $base-font-size) * 1rem;
}

// Verwendung
.example {
    font-size: px-to-rem(18px); // 1.125rem
    margin: px-to-rem(24px);    // 1.5rem
}

strip-unit.scss

Entfernt Einheiten von Werten:

scss
@function strip-unit($value) {
    @return ($value / ($value * 0 + 1));
}

// Verwendung
$width: strip-unit(100px); // 100

boolean.scss

Hilfsfunktionen für Boolean-Operationen:

scss
@function is-true($value) {
    @return $value == true;
}

Mixins

RLH (Responsive Line Heights)

Erweitert das Bootstrap RFS-System um responsive Zeilenhöhen:

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);
}

Verwendung:

scss
h1 {
    @include line-height(1.2); // Responsive line-height
}

.title {
    @include rlh(1.5, line-height); // Explizite Property
}

Focus-Style

Einheitliche Focus-Styles für Barrierefreiheit:

scss
@mixin focus-style {
    outline-offset: $focus-outline-offset;
    outline: $focus-outline-color solid $focus-outline-width;
    box-shadow: $focus-outline-box-shadow;
}

Verwendung:

scss
button:focus-visible {
    @include focus-style;
}

Button-Adaptive-Hover

Intelligente Hover-Effekte für Buttons:

scss
@mixin button-adaptive-hover($base-color) {
    &:hover {
        @media (hover: hover) {
            background-color: shade-color($base-color, 15%);
        }
    }
}

Variables

Bootstrap Variables (bootstrap.scss)

Umfassende Bootstrap-Konfiguration mit Theme-Integration:

scss
// Farben
$gray-100: $sw-color-gray-100 !default;
$gray-200: $sw-color-gray-200 !default;
// ... weitere Grautöne

// 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;
// ... weitere Schriftgrößen

// 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-spezifische Variablen:

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;
// ... weitere Zeilenhöhen

// 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;

// Delivery Information
$guppy-delivery-shipping-free-color: #023B5A;
$guppy-delivery-shipping-free-background: #CEEDFE;
$guppy-delivery-available-color: #154634;
$guppy-delivery-available-background: #D3F2E7;
$guppy-delivery-restock-color: #5D3B00;
$guppy-delivery-restock-background: #FFEFD4;
$guppy-delivery-soldout-color: #303030;
$guppy-delivery-soldout-background: #EEEEEE;

Base Layer

Accessibility (accessibility.scss)

Grundlegende Barrierefreiheits-Styles:

scss
button, a {
    &:focus-visible {
        @include focus-style;
    }
    
    &:focus:not(:focus-within) {
        box-shadow: none;
        outline: 0;
    }
}

Typography (typography.scss)

Erweiterte Typografie-Definitionen:

scss
// Responsive Headlines
h1 {
    @include line-height($h1-line-height);
}

h2 {
    @include line-height($h2-line-height);
}

// Display-Klassen
.display-1 {
    @include line-height($display-1-line-height);
}

Container (container.scss)

Responsive Container-Konfiguration:

scss
.container {
    @include media-breakpoint-down(sm) {
        padding-left: $guppy-container-default-padding-x-mobile;
        padding-right: $guppy-container-default-padding-x-mobile;
    }
    
    @include media-breakpoint-up(sm) {
        padding-left: $guppy-container-default-padding-x-sm;
        padding-right: $guppy-container-default-padding-x-sm;
    }
}

Components Layer

Product Box (product-box.scss)

Erweiterte Produktbox-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;
            }
        }
    }
}

Splide Slider (splide-slider.scss)

Splide-Integration:

scss
.splide {
    // Accessibility
    .splide__arrow {
        @include focus-style;
        
        &:focus-visible {
            @include focus-style;
        }
    }
    
    // Responsive
    @include media-breakpoint-down(md) {
        .splide__arrows {
            display: none;
        }
    }
}

Barrierefreie 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;
    }
}

Layout Layer

Header (header.scss)

Responsive Header-Varianten:

scss
.header-main {
    .logo {
        height: $guppy-header-max-height;
        
        img {
            max-height: 100%;
            width: auto;
        }
    }
    
    // Header-Varianten
    &.header-variant-compact {
        .header-top {
            display: none;
        }
    }
    
    &.header-variant-extended {
        .header-top {
            background-color: $sw-color-gray-100;
        }
    }
}

USP Banner (usp-banner.scss)

Responsive USP-Banner:

scss
.usp-banner {
    @if $guppy-usp-active {
        background-color: $guppy-usp-background-color;
        color: $guppy-usp-text-color;
        
        .usp-item {
            // Responsive Anzahl
            @include media-breakpoint-up(lg) {
                flex: 0 0 25%; // 4 Items
            }
            
            @include media-breakpoint-between(md, lg) {
                flex: 0 0 50%; // 2 Items
            }
            
            @include media-breakpoint-down(md) {
                flex: 0 0 100%; // 1 Item
            }
        }
    }
}

Shopware Skin Integration

Base Integration (skin/shopware/base.scss)

Shopware-spezifische Overrides:

scss
// Shopware-Komponenten überschreiben
.cms-element-product-listing {
    .product-box {
        @extend .guppy-product-box;
    }
}

.cms-element-image-slider {
    .image-slider {
        @extend .guppy-image-slider;
    }
}

Component Overrides

Spezifische Shopware-Komponenten:

scss
// cms-element.scss
.cms-element {
    &.cms-element-guppy-slider {
        .splide {
            @extend .guppy-splide;
        }
    }
}

// product-box.scss
.product-box {
    @if $guppy-productcard-config == 'guppy-default' {
        @extend .guppy-product-box-default;
    }
}

Build-System

Compilation Order

Das Theme nutzt eine spezifische Compilation-Reihenfolge:

json
{
  "style": [
    "app/storefront/src/scss/overrides.scss",
    "@StorefrontBootstrap",
    "@Plugins",
    "app/storefront/src/scss/skin/shopware/base.scss",
    "app/storefront/src/scss/base.scss"
  ]
}

Overrides (overrides.scss)

Frühe Variablen-Definitionen:

scss
// Frühe Bootstrap-Overrides
@import "abstract/variables/bootstrap";
@import "abstract/variables/custom";

Main Entry (base.scss)

Haupt-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";
// ... weitere Komponenten

// Layout
@import "layout/header";
@import "layout/footer";
@import "layout/navigation";
// ... weitere Layout-Komponenten

// Elements
@import "elements/skip-link";
@import "elements/splide-slider";

// Pages
@import "page/account/account";
@import "page/checkout/checkout";
@import "page/product-detail/product-detail";
// ... weitere Seiten

Customization

Child Theme Overrides

In einem Child-Theme kannst du spezifische Styles überschreiben:

scss
// overrides.scss
// Guppy-Variablen überschreiben
$guppy-usp-background-color: #f8f9fa !default;
$product-image-height: 300px !default;

// Bootstrap-Variablen überschreiben
$primary: #007bff !default;
$secondary: #6c757d !default;
scss
// base.scss
// Zusätzliche Styles
.my-custom-component {
    background-color: $primary;
    
    .title {
        @include line-height(1.3);
    }
}

Best Practices

Variablen-Nutzung

scss
// Gut: Theme-Variablen verwenden
.custom-component {
    color: $sw-text-color;
    background-color: $sw-color-gray-100;
}

// Schlecht: Hardcodierte Werte
.custom-component {
    color: #333;
    background-color: #f9f9f9;
}

Responsive Design

scss
// Gut: Bootstrap-Breakpoints verwenden
.custom-component {
    @include media-breakpoint-up(md) {
        display: flex;
    }
}

// Schlecht: Hardcodierte Breakpoints
.custom-component {
    @media (min-width: 768px) {
        display: flex;
    }
}

Accessibility

scss
// Gut: Focus-Styles verwenden
.custom-button {
    &:focus-visible {
        @include focus-style;
    }
}

// Schlecht: Eigene Focus-Styles
.custom-button {
    &:focus {
        outline: 2px solid blue;
    }
}

Debugging

SCSS-Debugging

scss
// Debug-Variablen
@debug "Container width: #{$guppy-container-width-default}";
@debug "Primary color: #{$sw-color-brand-primary}";

// Conditional Debugging
@if $debug-mode {
    .debug-info {
        position: fixed;
        top: 0;
        right: 0;
        background: red;
        color: white;
        padding: 1rem;
        z-index: 9999;
    }
}

Performance-Optimierung

scss
// Lazy Loading für große Komponenten
@if $enable-product-slider {
    @import "components/product-slider";
}

// Conditional Imports
@if $guppy-usp-active {
    @import "layout/usp-banner";
}

Responsive Breakpoints

Bootstrap Grid Breakpoints

Guppy verwendet die Standard Bootstrap 5 Breakpoints:

BreakpointKlassen-InfixDimensionContainer max-width
Extra small-< 576px100%
Smallsm≥ 576px540px
Mediummd≥ 768px720px
Largelg≥ 992px960px
Extra largexl≥ 1200px1140px
XXLxxl≥ 1400px1320px

Breakpoint-Verwendung

scss
// Mobile-First: Styles ab bestimmter Breite
@include media-breakpoint-up(md) {
    // Styles für ≥ 768px
}

// Desktop-First: Styles bis zu bestimmter Breite
@include media-breakpoint-down(lg) {
    // Styles für < 992px
}

// Bereich zwischen zwei Breakpoints
@include media-breakpoint-between(md, xl) {
    // Styles für 768px bis 1199px
}

// Nur für einen bestimmten Breakpoint
@include media-breakpoint-only(lg) {
    // Styles nur für 992px bis 1199px
}

Guppy Container-Paddings pro Breakpoint

scss
// Mobile (< 576px)
$guppy-container-default-padding-x-mobile: 10px;

// Small (≥ 576px)
$guppy-container-default-padding-x-sm: 24px;

// Medium (≥ 768px)
$guppy-container-default-padding-x-md: 48px;

// Large (≥ 992px)
$guppy-container-default-padding-x-lg: 48px;

// Extra Large (≥ 1200px)
$guppy-container-default-padding-x-xl: 48px;

// XXL (≥ 1400px)
$guppy-container-default-padding-x-xxl: 48px;

Alle verfügbaren Mixins

Layout-Mixins

scss
// Container-Breite
@mixin make-container($padding-x: $container-padding-x) {
    width: 100%;
    padding-right: $padding-x;
    padding-left: $padding-x;
    margin-right: auto;
    margin-left: auto;
}

// Flex-Zentrierung
@mixin flex-center {
    display: flex;
    align-items: center;
    justify-content: center;
}

Typografie-Mixins

scss
// Responsive Line Height
@mixin line-height($value) {
    @include rlh($value);
}

// Text-Ellipsis (einzeilig)
@mixin text-ellipsis {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

// Text-Clamp (mehrzeilig)
@mixin text-clamp($lines: 2) {
    display: -webkit-box;
    -webkit-line-clamp: $lines;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

Accessibility-Mixins

scss
// Focus-Style (einheitlich)
@mixin focus-style {
    outline-offset: $focus-outline-offset;
    outline: $focus-outline-color solid $focus-outline-width;
    box-shadow: $focus-outline-box-shadow;
}

// Visuell versteckt (für Screen-Reader)
@mixin visually-hidden {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
}

Button-Mixins

scss
// Adaptiver Hover-Effekt
@mixin button-adaptive-hover($base-color) {
    &:hover {
        @media (hover: hover) {
            background-color: shade-color($base-color, 15%);
        }
    }
}

// Button-Reset
@mixin button-reset {
    padding: 0;
    border: none;
    background: none;
    cursor: pointer;
    font: inherit;
    color: inherit;
}

Alle verfügbaren Functions

Einheiten-Konvertierung

scss
// Pixel zu REM
@function px-to-rem($px, $base-font-size: 16px) {
    @return ($px / $base-font-size) * 1rem;
}

// Verwendung
.element {
    font-size: px-to-rem(18px);  // 1.125rem
    padding: px-to-rem(24px);     // 1.5rem
}

// Einheit entfernen
@function strip-unit($value) {
    @return ($value / ($value * 0 + 1));
}

// Verwendung
$value: strip-unit(100px);  // 100

Farb-Functions

scss
// Farbe aufhellen (Bootstrap)
shade-color($color, $weight)   // Dunkler machen
tint-color($color, $weight)    // Heller machen
shift-color($color, $weight)   // Automatisch (basierend auf Helligkeit)

// Verwendung
.element {
    background-color: shade-color($primary, 20%);  // 20% dunkler
    border-color: tint-color($primary, 40%);       // 40% heller
}

Theme-Variablen Übersicht

Farb-Variablen

VariableStandardBeschreibung
$sw-color-brand-primary#215AFFPrimärfarbe
$sw-color-brand-secondary#0B1845Sekundärfarbe
$sw-background-color#FFFFFFHintergrund
$sw-border-color#CFD1D7Rahmenfarbe
$sw-text-color#1B1F29Textfarbe
$sw-headline-color#1B1F29Überschriften

Graustufen

VariableStandardVerwendung
$sw-color-gray-100#F9F9F9Hintergründe, Footer
$sw-color-gray-200#EEEEEEDeaktivierte Elemente
$sw-color-gray-300#BCC1C7Rahmen, Trenner
$sw-color-gray-400#CED4DAInput-Rahmen
$sw-color-gray-500#ADB5BDPlatzhalter
$sw-color-gray-600#798490Sekundärtext
$sw-color-gray-700#495057Text
$sw-color-gray-800#4A545BÜberschriften
$sw-color-gray-900#212529Haupttext

Typografie-Variablen

VariableStandardBeschreibung
$sw-font-size-base1remBasis-Schriftgröße
$sw-h1-font-size32pxH1-Größe
$sw-h2-font-size24pxH2-Größe
$sw-h3-font-size20pxH3-Größe
$sw-h4-font-size18pxH4-Größe
$sw-h5-font-size16pxH5-Größe
$sw-h6-font-size15pxH6-Größe
$sw-h1-line-height44pxH1-Zeilenhöhe
$sw-h2-line-height36pxH2-Zeilenhöhe
$sw-h3-line-height32pxH3-Zeilenhöhe

Accessibility-Variablen

VariableStandardBeschreibung
$focus-outline-width2pxFocus-Rahmenbreite
$focus-outline-color$sw-color-brand-primaryFocus-Rahmenfarbe
$focus-outline-offset2pxAbstand zum Element
$focus-outline-box-shadow0 0 0 2px #fffZusätzlicher Schatten
$icon-accessibility-touch-size44pxMin. Touch-Target

Praktische Beispiele

Custom Component erstellen

scss
// _my-component.scss
.my-component {
    // Basis-Styles mit Theme-Variablen
    background-color: $sw-color-gray-100;
    border: 1px solid $sw-border-color;
    border-radius: $border-radius;
    padding: $spacer;

    // Responsive Typography
    .title {
        font-size: $sw-h3-font-size;
        @include line-height($h3-line-height);
        color: $sw-headline-color;
    }

    // Responsive Layout
    @include media-breakpoint-up(md) {
        display: flex;
        gap: $spacer * 2;
    }

    // Accessibility
    &:focus-visible {
        @include focus-style;
    }

    // Interaktive Elemente
    .action-button {
        @include button-reset;
        @include button-adaptive-hover($sw-color-brand-primary);
    }
}

Produktkarte anpassen

scss
// overrides.scss (in Child-Theme)
$product-image-aspect-ratio: #{4/3};  // 4:3 statt 10:9
$product-image-height: 280px;
$guppy-productcard-image-object-fit: cover;

// base.scss (in Child-Theme)
.product-box {
    // Schatten hinzufügen
    box-shadow: $box-shadow-sm;
    transition: box-shadow 0.3s ease;

    &:hover {
        box-shadow: $box-shadow;
    }

    // Produktname-Styling
    .product-name {
        font-weight: $font-weight-semibold;
        @include text-clamp(2);
    }

    // Preis hervorheben
    .product-price {
        color: $sw-color-brand-primary;
        font-size: $font-size-lg;
    }
}