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.scssAbstract Layer
Functions
px-to-rem.scss
Konvertiert Pixel-Werte zu rem-Einheiten:
@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:
@function strip-unit($value) {
@return ($value / ($value * 0 + 1));
}
// Verwendung
$width: strip-unit(100px); // 100boolean.scss
Hilfsfunktionen für Boolean-Operationen:
@function is-true($value) {
@return $value == true;
}Mixins
RLH (Responsive Line Heights)
Erweitert das Bootstrap RFS-System um responsive Zeilenhöhen:
@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:
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:
@mixin focus-style {
outline-offset: $focus-outline-offset;
outline: $focus-outline-color solid $focus-outline-width;
box-shadow: $focus-outline-box-shadow;
}Verwendung:
button:focus-visible {
@include focus-style;
}Button-Adaptive-Hover
Intelligente Hover-Effekte für Buttons:
@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:
// 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:
// 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:
button, a {
&:focus-visible {
@include focus-style;
}
&:focus:not(:focus-within) {
box-shadow: none;
outline: 0;
}
}Typography (typography.scss)
Erweiterte Typografie-Definitionen:
// 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:
.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:
.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:
.splide {
// Accessibility
.splide__arrow {
@include focus-style;
&:focus-visible {
@include focus-style;
}
}
// Responsive
@include media-breakpoint-down(md) {
.splide__arrows {
display: none;
}
}
}Skip Link (skip-link.scss)
Barrierefreie Navigation:
.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:
.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:
.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:
// 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:
// 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:
{
"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:
// Frühe Bootstrap-Overrides
@import "abstract/variables/bootstrap";
@import "abstract/variables/custom";Main Entry (base.scss)
Haupt-Stylesheet:
// 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 SeitenCustomization
Child Theme Overrides
In einem Child-Theme kannst du spezifische Styles überschreiben:
// 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;// base.scss
// Zusätzliche Styles
.my-custom-component {
background-color: $primary;
.title {
@include line-height(1.3);
}
}Best Practices
Variablen-Nutzung
// 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
// 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
// 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
// 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
// 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:
| Breakpoint | Klassen-Infix | Dimension | Container max-width |
|---|---|---|---|
| Extra small | - | < 576px | 100% |
| Small | sm | ≥ 576px | 540px |
| Medium | md | ≥ 768px | 720px |
| Large | lg | ≥ 992px | 960px |
| Extra large | xl | ≥ 1200px | 1140px |
| XXL | xxl | ≥ 1400px | 1320px |
Breakpoint-Verwendung
// 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
// 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
// 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
// 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
// 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
// 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
// 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); // 100Farb-Functions
// 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
| Variable | Standard | Beschreibung |
|---|---|---|
$sw-color-brand-primary | #215AFF | Primärfarbe |
$sw-color-brand-secondary | #0B1845 | Sekundärfarbe |
$sw-background-color | #FFFFFF | Hintergrund |
$sw-border-color | #CFD1D7 | Rahmenfarbe |
$sw-text-color | #1B1F29 | Textfarbe |
$sw-headline-color | #1B1F29 | Überschriften |
Graustufen
| Variable | Standard | Verwendung |
|---|---|---|
$sw-color-gray-100 | #F9F9F9 | Hintergründe, Footer |
$sw-color-gray-200 | #EEEEEE | Deaktivierte Elemente |
$sw-color-gray-300 | #BCC1C7 | Rahmen, Trenner |
$sw-color-gray-400 | #CED4DA | Input-Rahmen |
$sw-color-gray-500 | #ADB5BD | Platzhalter |
$sw-color-gray-600 | #798490 | Sekundärtext |
$sw-color-gray-700 | #495057 | Text |
$sw-color-gray-800 | #4A545B | Überschriften |
$sw-color-gray-900 | #212529 | Haupttext |
Typografie-Variablen
| Variable | Standard | Beschreibung |
|---|---|---|
$sw-font-size-base | 1rem | Basis-Schriftgröße |
$sw-h1-font-size | 32px | H1-Größe |
$sw-h2-font-size | 24px | H2-Größe |
$sw-h3-font-size | 20px | H3-Größe |
$sw-h4-font-size | 18px | H4-Größe |
$sw-h5-font-size | 16px | H5-Größe |
$sw-h6-font-size | 15px | H6-Größe |
$sw-h1-line-height | 44px | H1-Zeilenhöhe |
$sw-h2-line-height | 36px | H2-Zeilenhöhe |
$sw-h3-line-height | 32px | H3-Zeilenhöhe |
Accessibility-Variablen
| Variable | Standard | Beschreibung |
|---|---|---|
$focus-outline-width | 2px | Focus-Rahmenbreite |
$focus-outline-color | $sw-color-brand-primary | Focus-Rahmenfarbe |
$focus-outline-offset | 2px | Abstand zum Element |
$focus-outline-box-shadow | 0 0 0 2px #fff | Zusätzlicher Schatten |
$icon-accessibility-touch-size | 44px | Min. Touch-Target |
Praktische Beispiele
Custom Component erstellen
// _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
// 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;
}
}