ScandiPWA
Create Magento AppCreate ScandiPWA AppUser ManualGitHub
  • Why Scandi
  • πŸš€Quick-start Guide
  • πŸ—ΊοΈRoadmap
  • Introduction to the Stack
    • CMA, CSA, and ScandiPWA
    • Challenges
  • Setting up Scandi
    • Storefront Mode Setup
      • Proxying requests to server
    • Magento Mode Setup
    • Existing Magento 2 setup
    • Magento Commerce Cloud setup
    • Updating to new releases
      • Storefront mode upgrade
      • Magento mode upgrade
      • CMA upgrade
      • CSA upgrade
      • Custom ScandiPWA composer dependency update
      • Local ScandiPWA Composer Package Setup
    • Docker Setup [deprecated]
      • Legacy Docker setup
      • Migrating to CMA & CSA
  • Developing with Scandi
    • Override Mechanism
      • Overriding JavaScript
        • Overriding classes
        • Overriding non-classes
      • Overriding Styles
      • Overriding the HTML / PHP
      • Parent Themes
    • Extensions
      • Creating an extension
      • Installing an extension
      • Migrating from 3.x to 4.x
      • Publishing an extension
      • Extension Terminology
    • Working With Magento
      • Magento troubleshooting
      • Working with Magento modules
      • Working with GraphQL
      • GraphQL Security
      • Working with "granular cache"
    • Developer Tools
      • Debugging in VSCode
      • ScandiPWA CLI
      • Configuring ESLint
      • CSA Commands
    • Deploying Your App
      • Build & Deploy Android app
      • Build & Deploy iOS app
  • Structure
    • Directory Structure
    • Building Blocks
      • Components
        • Styling Components
      • Routes
      • Redux Stores
      • GraphQL Queries
      • Global Styles
      • The Util Directory
      • Type Checking
    • Application assets
    • Code Style
      • JavaScript Code Style
      • SCSS Code Style
  • Tutorials
    • Customizing Your Theme
      • Styling
        • Customizing the Global Styles
        • Adding a New Font
        • Overriding a Components Styles
        • Extending a Component's Styles
      • Customizing JavaScript
        • Customizing the Footer Copyright
        • Adding a New Page
        • Adding a Section in My Account
        • Adding a Tab on the Product Page
        • Creating a New Redux Store
    • Payment Method Integration
      • Setting Up for Development
      • Redirecting to the Payment Provider
      • Handling the Customer's Return
    • Creating a Custom Widget
      • Scandi CMS System Overview
      • Creating a Magento Widget
      • Implementing the Rendering
    • Video Tutorials
      • #1 Setting up and talking theory
      • #2 Templating in React
      • #3 Overriding a file
      • #4 Styling the application
      • #5 Patterns of ScandiPWA
    • Dark Mode Extension
    • Deploying Native Apps
    • Product 3D Model Extension
      • Part 1: Magento 3D Model Uploads
      • Part 2: GraphQL API
      • Part 3: Scandi Frontend
    • Social Share, Full Extension Development
      • STEP-1 and 2 Creating Magento 2 Module
      • STEP-3 Backend Configurations Settings
      • STEP-4 Simple GraphQl and Resolver
      • STEP-5 Creating Extension, Base Redux Store
      • STEP-6 Extension plugins
      • STEP-7 GraphQL types, Helpers
      • STEP-8 Query Field and FieldList
      • STEP-9 render Plugins and MSTP Plugin, Component creation
      • STEP-10 SocialShare Component Development
      • STEP-11 SocialShare for CategoryPage
      • TASK-1 Changing LinkedIn to Twitter
      • STEP-12 Comments for Admin Users
      • STEP-13 Final, bugfixes
    • Accessing Magento 2 Controllers
      • STEP-1 Creating Magento 2 Module
      • STEP-2 - Create Magento 2 Frontend Route and Basic Controller
      • STEP-3 Accessing Magento 2 Controller, Bypassing ScandiPWA frontend
      • STEP-4 Creating ScandiPWA Extension with additional dependencies
      • STEP-5 Creating Plugin and Axios request
  • About
    • Support
    • Release notes
    • Technical Information
    • Data Analytics
    • Contributing
      • Installation from Fork
      • Repository structure
      • Code contribution process
      • Submitting an Issue
      • Publishing ScandiPWA
Powered by GitBook
On this page
  • The BEM Methodology
  • BEM in JavaScript
  • Styling Components in SCSS
  • Selecting BEM
  • Breakpoints
  • CSS Variables
  • Mixes

Was this helpful?

  1. Structure
  2. Building Blocks
  3. Components

Styling Components

ScandiPWA uses the BEM methodology and SCSS

PreviousComponentsNextRoutes

Last updated 4 years ago

Was this helpful?

To style components, ScandiPWA uses the methodology. BEM is based on assigning meaningful and unique class names to each HTML element we want to style while ensuring that we never need to use any CSS nesting in selectors.

We strive to follow this methodology in the core ScandiPWA theme, and we strongly encourage you to use it when overriding this theme as well.

Benefits of folowing our BEM guidelines:

  • The codebase is consistent

  • Styles are maintainable and can be re-used by composition with mixes

  • Styles can be overriden easily in child themes

The BEM Methodology

This section should serve as a quick introduction to how BEM is used in ScandiPWA. You can read the official . ScandiPWA uses the .

All primary BEM classes are composed of these 2 parts:

  • <Block> - in UpperCamelCase, the name of the component this element belongs to

  • -<Element> (can be left out) - in UpperCamelCase, a meaningful name of this element (e.g. Container, Button, Divider, Image... whatever identifies the purpose of this element)

Blocks indicate which component owns this element and preserves uniqueness accross all components. By keeping it the same as the component name, you don't need to worry about name-clashes with other components.

The "main" HTML element of each block may be left without a BEM Element. All other elements should have one to indicate their function.

In addition to its primary <Block[-Element]> class, a block or element may have any number of modifiers of the form:

  • <Block[-Element]>_<booleanModifier> - for boolean modifiers such as isActive, isVisible, isBold, etc. The presence of this modifier indicates that it is "true", and it's absence indicates that it is "false".

  • <Block[-Element]>_<modifierName>_<someValue> - for boolean modifiers with values such as color_red, type_primary, size_thumbnail

BEM modifiers can be used to indicate state, available actions, or to distinguish between similar instances of the same element.

BEM in JavaScript

component/ProductCard/ProductCard.component.js (excerpt)
    render() {
        const {
            children,
            mix,
            isLoading
        } = this.props;

        return (
            <li
              block="ProductCard"
              mix={ mix }
            >
                <Loader isLoading={ isLoading } />
                { this.renderCardWrapper((
                    <>
                        <figure block="ProductCard" elem="Figure">
                            { this.renderPicture() }
                        </figure>
                        <div block="ProductCard" elem="Content">
                            { this.renderReviews() }
                            { this.renderProductPrice() }
                            { this.renderVisualConfigurableOptions() }
                            { this.renderTierPrice() }
                            { this.renderMainDetails() }
                            { this.renderAdditionalProductDetails() }
                        </div>
                    </>
                )) }
                <div block="ProductCard" elem="AdditionalContent">
                    { children }
                </div>
            </li>
        );
    }

Note that the block is always the same as the name of the component. This ensures consistency and prevents name clashes.

To add modifiers, pass an object with modifiers to the mods prop. Boolean modifiers will be automatically detected and treated as such.

    renderMainDetails() {
        const { product: { name } } = this.props;

        return (
            <p
              block="ProductCard"
              elem="Name"
              mods={ { isLoaded: !!name } }
            >
                <TextPlaceholder content={ name } length="medium" />
            </p>
        );
    }

Styling Components in SCSS

Selecting BEM

component/ProductCard/ProductCard.style.scss (excerpt, annotated)
.ProductCard {
    // style the block
    padding-left: 0;
    min-width: 0;

    &::before {
        content: none;
    }

    // & will get replaced with the parent selector, .ProductCard.
    // so this selects .ProductCard-Content (the Content element
    // of the ProductCart block
    &-Content {
        padding: 1rem;
        display: flex;
        flex-wrap: wrap;
        padding-top: 23px;
    }

    &-Brand {
        font-weight: 300;
        opacity: .5;
    }

    &-Figure {
        flex-grow: 1;
    }
    
    &-Name {
        width: 100%;
        font-size: .9rem;

        // this selector will compile to .ProductCard-Name_isLoaded
        &_isLoaded {
            text-overflow: ellipsis;
        }
    }
}

Breakpoints

// ...
    &-Brand {
        font-weight: 300;
        opacity: .5;

        // will only affect mobile devices
        @include mobile {
            line-height: 1;
            font-size: 12px;
        }
    }

CSS Variables

CSS variables are useful when:

  • You want to reuse the same value multiple times

  • You want to be able to override a value based on the context

  • You want to make it more clear what a value represents by naming it

CSS variables are always defined in :root. That way, re-defining them anywhere else is an easy way to override them. Example:

component/CartItem/CartItem.style.scss (simplified & annotated)
// we define variables in :root
:root {
    --cart-item-background: #fff;
    --cart-item-actions-color: #000;
}

.CartItem {
    &:hover  {
        // we can re-define them to override values
        --cart-item-actions-color: #222
    }
    
    &-Wrapper {
        background: var(--cart-item-background);
    }

    &-Delete {
        height: 35px;
        color: var(--cart-item-actions-color);
    }
}

ScandiPWA uses an auto-prefixer. When compiling, vendor-specific versions of rules are added to make sure they work on most browsers.

Mixes

Sometimes, you may want to allow other components to add additional style rules to a component. For example, the Image component needs to define some styles, but can't predict ahead of time the exact styling features that will be needed for Images in parent components.

The solution is to allow other components to add their own styles to the Image component. The BEM methodology allows this by "mixing" 2 BEM classes together. For example, in the CategoryDetails component, in addition to the regular Image block, the CategoryDetails-Picture class will be added. Since the element will now have both of these classes, the parent component can additionally style the element with new rules.

component/CategoryDetails/CategoryDetails.component.js
    renderCategoryImagePlaceholder() {
        return (
            <Image
              mix={ { block: 'CategoryDetails', elem: 'Picture' } }
              objectFit="cover"
              ratio="custom"
              isPlaceholder
            />
        );
    }

Note: to allow a component's styles to be mixed, you need to pass the mix prop to an element in the Component – this won't happen automatically. For example, consider how the Image component passes on the mix prop:

component/Image/Image.component.js (simplified excerpt)
    render() {
        const {
            mix
        } = this.props;


        return (
            <div
              block="Image"
              mix={ mix }
            >
                { this.renderImage() }
            </div>
        );
    }

Formatting BEM classes manually with className would get repetitive, so ScandiPWA uses to be able to use block and elem to specify the class. As an example, look at the render method of ProductCard:

We can take advantage of to reduce the repetitiveness of selecting BEM classes:

ScandiPWA defines certain breakpoints that enable you to write viewport width-specific styles. . To select a specific device, simply use the @include directive:

Block-Element-Modifier (BEM)
BEM Guide
React variation
rebem-jsx
SCSS amperstand operator
These can be found in the global style directory