BBC's guide to development
  • General

    • About
    • Tools
    • Git(hub)
    • Showpad
    • Hosting
    • Maintenance
    • Security
    • Go live checklist
  • Front-end development

    • Bundlers
    • CSS/SCSS
    • Javascript
    • Vue
    • PHP
    • Mails
    • Dev Faq
  • Functions
  • Mixins
  • General

    • OOP Structure
  • Component Classes

    • Accordion
    • App
    • Component
    • HighwayApp
    • Popup
    • PNG Sequencer
    • Tab
  • Manager Classes

    • BountListenerMgr
    • Cache
    • Configuration
    • InViewStateMgr
    • Instance Manager
    • Event dispatcher
  • Factories

    • SwiperFactory
  • PDF

    • AssetLoader
    • BasePdfDoc
    • TemplatePdfDoc
    • CustomPdfDoc
  • Utility functions

    • canvas
    • Connection Status
    • css
    • dev
    • placeholder
    • dom
    • fetch
    • json
    • object
    • scroll
    • scrollbar
    • spreadsheets
    • string
    • url
  • General

    • ComponentMgr
    • ThreeJsViewer
  • Components

    • ComponentMgr
    • GltfModel
    • Snappable
    • Socket
    • ThreeJsViewer
    • ThreeJsViewerCamera
  • Loaders

    • ConfigurationSerializer
    • GltfBlockParser
  • Utils

    • CanvasInputAdapter
    • CollisionManager
    • SocketGridExpander
    • blender
    • headless
  • General

    • Troubleshooting
    • Legacy
  • Components

    • AssetBar
    • ConfigGenerator
    • ShowpadApp
  • Managers

    • Assets
    • AppsDb
    • Config
  • Utils

    • Connection Status
    • general
    • showpad-interactive
    • showpad-upload
  • Components

    • Accordion
    • BackButton
    • Breadcrumb
    • ByltButton
    • Hamburger
    • Icon
    • Logo
    • Loader
    • Modal
    • Popup
    • Prompt
    • ProgressBar
    • TextLoader
  • Composables

    • useDebugMode
    • useConnectionStatus
  • Utils

    • dom
    • props
  • General

    • General
    • Tracking
  • Components

    • Accordion
    • ActionButton
    • AssetItem
    • AssetList
    • BackButton
    • ConfigGenButton
    • Logo
    • Media
    • Modal
    • Popup
    • Prompt
    • SPButton
    • SPRouterView
    • SPTrackedRouterLink
    • TextLoader
    • View
  • Composables

    • useConnectionStatus
  • Stores

    • useAppsDbStore
    • useBreadcrumbStore
    • useShowpadAPIStore
    • useShowpadSDKStore
    • useSpConfigStore
    • useSpStore
    • useSpTrackingStore
  • The New Kit

    • General
    • Installation & Usage
    • ACF Blocks
    • PHPCS
    • Functions
    • Vite
    • WP Config
    • Staging Deployment
  • Best Practices

    • Page Structure
    • Fonts/Typography
  • Todo
GitHub
  • General

    • About
    • Tools
    • Git(hub)
    • Showpad
    • Hosting
    • Maintenance
    • Security
    • Go live checklist
  • Front-end development

    • Bundlers
    • CSS/SCSS
    • Javascript
    • Vue
    • PHP
    • Mails
    • Dev Faq
  • Functions
  • Mixins
  • General

    • OOP Structure
  • Component Classes

    • Accordion
    • App
    • Component
    • HighwayApp
    • Popup
    • PNG Sequencer
    • Tab
  • Manager Classes

    • BountListenerMgr
    • Cache
    • Configuration
    • InViewStateMgr
    • Instance Manager
    • Event dispatcher
  • Factories

    • SwiperFactory
  • PDF

    • AssetLoader
    • BasePdfDoc
    • TemplatePdfDoc
    • CustomPdfDoc
  • Utility functions

    • canvas
    • Connection Status
    • css
    • dev
    • placeholder
    • dom
    • fetch
    • json
    • object
    • scroll
    • scrollbar
    • spreadsheets
    • string
    • url
  • General

    • ComponentMgr
    • ThreeJsViewer
  • Components

    • ComponentMgr
    • GltfModel
    • Snappable
    • Socket
    • ThreeJsViewer
    • ThreeJsViewerCamera
  • Loaders

    • ConfigurationSerializer
    • GltfBlockParser
  • Utils

    • CanvasInputAdapter
    • CollisionManager
    • SocketGridExpander
    • blender
    • headless
  • General

    • Troubleshooting
    • Legacy
  • Components

    • AssetBar
    • ConfigGenerator
    • ShowpadApp
  • Managers

    • Assets
    • AppsDb
    • Config
  • Utils

    • Connection Status
    • general
    • showpad-interactive
    • showpad-upload
  • Components

    • Accordion
    • BackButton
    • Breadcrumb
    • ByltButton
    • Hamburger
    • Icon
    • Logo
    • Loader
    • Modal
    • Popup
    • Prompt
    • ProgressBar
    • TextLoader
  • Composables

    • useDebugMode
    • useConnectionStatus
  • Utils

    • dom
    • props
  • General

    • General
    • Tracking
  • Components

    • Accordion
    • ActionButton
    • AssetItem
    • AssetList
    • BackButton
    • ConfigGenButton
    • Logo
    • Media
    • Modal
    • Popup
    • Prompt
    • SPButton
    • SPRouterView
    • SPTrackedRouterLink
    • TextLoader
    • View
  • Composables

    • useConnectionStatus
  • Stores

    • useAppsDbStore
    • useBreadcrumbStore
    • useShowpadAPIStore
    • useShowpadSDKStore
    • useSpConfigStore
    • useSpStore
    • useSpTrackingStore
  • The New Kit

    • General
    • Installation & Usage
    • ACF Blocks
    • PHPCS
    • Functions
    • Vite
    • WP Config
    • Staging Deployment
  • Best Practices

    • Page Structure
    • Fonts/Typography
  • Todo
GitHub
  • (S)CSS - BEM

(S)CSS - BEM

Everyone knows the common meme about css. You change the color of the title and something else non-related starts bugging on an entirely different section of the website. There are many solutions to this issue on the web like BEM, SMACSS, Atomic CSS, … that all have their pro's and con's. We chose BEM as a base that uses a clear and simple structure with the least ambiguity possible and started building our guidelines from there.

Units

Body font size will always be set to the browser's default value (16px by default. Prioritize the use of relative units ((em, rem, %, fr, ...) over absolute units unless (px, vw, vh, ...) it is absolutely reasonable to avoid certain issues like rounding errors (example: border-width, border-radius, ...). Every other value will mainly use relative units like.

BEM

Stands for Block__Element--Modifier. It's a way of writing css classes that informs the reader about both the which element it is and what its relation is to the parent.

BEM tries to keep every css selector declaration at root level. In ideal cases we don't apply any nesting whatsoever. Usually developers try to reflect the html structure in the nesting of the classes but this will never generate the clarity everyone hopes during development or maintenance / debugging. CSS's specificity should be kept to an absolute minimum.

All css is written in kebab-case. In case a name of a block, element or modifier consists of multiple words, we use a single dash (-) for space.

At BBC we use BEM as a base to build our own guidelines. These are written to please many large-scale projects across the world, across organizations and across teams. Here at BBC we can add some rules to further fine-tune BEM to our needs.

/* Block (main component) */
.block {

}

/* Element (child component of main component */
.block__element {

}

/* Modifier on block (modifies block / provides a state) */
.block--modifier {

}

/* Modifier on element (modifies element / provides a state) */
.block__element--modifier {

}

Block

The design is cut in very specific separate components called Blocks. We style these in such a way they share the least amount of styling possible with other Blocks in the project. It helps them become very transposable throughout the website/app as their styling is largely cut off from the parent and their children.

Blocks may be nested inside each other in the HTML, but that should never be mirrored in the css. Since they are agnostic of their environment, they know very well how to look and behave on their own so there is no need. If they need to look different inside certain environments, we use modifiers. But more on that later.

Blocks can be any separate component like: header, article-teaser, accordion, poppup …

/* Block (main component) */
.block {

}

Element

Elements are parts of blocks that help create the structure for the visual design we try to apply.

The name of the element is prefixed by the name of the block with two underscores(__). This is very important to avoid any naming conflicts. The title of a video paragraph may be totally different in styling than the title of an article teaser.

Elements are nested inside a block only. Elements are never nested inside a block.

Element can be any block-related component like: header__logo, article-tease__title, accordion__content, …

/* Element (child component of main component */
.block__element {

}

Modifier

Modifiers are there to change small things to a Block or Element. These changes can be necessary to create different types or to set a state to a Block or Element.

Types can be: .button--big, .button--rounded, .button--bordered

States can be: .button--active, .button--visible, .button--hidden

These styles use a Block or Element's base styling and overrides some of their properties. They replace the main need for selector nesting. A button inside the header might've been selected with .header.button, this changes to .button--header

Nesting when using modifiers is allowed but should be limited to 1 level. This allows us to influence the styling of many elements inside a block by using only a modifier at block level.

/* Modifier on block (modifies block / provides a state) */
.block--modifier {

}

/* Modifier on element (modifies element / provides a state) */
.block__element--modifier {

}

/* Modifier on block influences element */
.block--modifier .block__icon {

}

BBC Modifier

To improve on this we made a small variation of the modifier rule here at BBC.

As modifier class names tend to create very repetitive code that could be shared between components since they may contain exactly the same styling, we decided to allow a modifier to exist as its own class. Still prefixed with 2 dashes (--) to signify it is expected to be used purely as a modifier to another css class, reusable styling may be kept separate at root level (mainly colors, fonts, …) although should be limited. We want to avoid creating an new boottstrap.

It creates an extra level of nesting when used, but we found out it didn't really interfere with the overall purpose of BEM.

SCSS / Sass

SCSS makes our CSS more programmable. It adds extra functionalities that are similar to programming languages like variables, loops, conditionals, …

Variable names

Variable names are written in kebab-case to follow the css convention. Some variables in the config that are used throughout the codebase should be prefixed with the type of variable they are. These variables include fonts ($f-), colors ($c-), breakpoints ($bp-) and grid ($grid-)

Color variables should be named according to a color table Name That Color (vscode plugin) and be grouped by color type $c-blue-ribbon

Nesting

Nesting is powerfull in SCSS with the ampersant placeholder (&) but should still be reduced to an absolute minimum.

All pseudo selector and elements should always be nested with the scss placeholder. In case of pseudo selectors (:hover, :active, …) only then it is allowed to add an extra level nesting of other classes.

Nesting is allowed when parent elements with modifiers have influence on their child elements. Only apply this kind of nesting if it seems logical.

/* Modifier on block influences element */
.block--modifier {
    .block__element {
        &:hover {
            &::after {

            }

            .block__element {

            }
        }
    }
}

Always write selectors in a full, never use concatinations for selectors names.

Although it feels like the reduced keystrokes are a benefit, it is strongly not allowed to use concatinations for selectors names due to its exponential complexity which renders the BEM methodology useless.

Legacy projects may show examples of this, but it is strongly discouraged to use it in new projects.

.card {
    &__header {
        &:hover {
            // Now you want to style the title inside the header on hover
            &__title {
                // But wait, you also want a modifier on the title
                &--highlighted {
                    // And maybe a pseudo-element too?
                    &::after {
                        // And what if you need to style another element?
                        &__icon {
                            // This becomes unreadable very quickly!
                        }
                    }
                }
            }
            
            // Or maybe you want to style a sibling element?
            &__icon {
                &:hover {
                    // Now we're 4 levels deep just to style an icon on hover
                }
            }
        }
    }
}

// Compiles as: 
.card__header:hover .card__header__title--highlighted::after .card__header__title--highlighted::after__icon {
    /* This is wrong! The selector is malformed */
}

Mixins

Mixins are a powerfull way to automate css in our codebase. We strongly encourage to use this feature but we also want do strongly encourage to keep them as simple as possible. Mixins in our code base should be as clear as possible and contain the least amount of logic as possible. Their main purpose in our codebase is to shorten the repeated combination of css properties. Such cases can easily be automated like when setting flex properties, positioning properties, ...

Example of our Position and Flex mixins: Before:

.element {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 100;

    display: flex;
    justify-items: space-between;
    align-items: baseline;

    // ... other properties
}

Becomes this:

.element {
    @include position(absolute, 0 0 null 0, 100);
    @include flex-row(space-between, baseline);

    // ... other properties
}

PostCss

When our SCSS files are compiled, run it through PostCSS for extra functionalities. Plugins like autoprefixer and easing-gradients are added into the mix.

Filename

Every Block component gets their own file with the same name: .accordion → accordion.scss.

This comes in very handy during debugging as the bug on the block called .blog in the inspector will definitely be found in the file of the same name blog.scss. Many editors have a command palette at disposal which makes file-switching also extremely quick compared to the ordinary point and click.

It can be handy to separate a version of a block to a separate file based on a modifier. In that case it is advised to create a folder with the name of the block to group them.

  • accordion/accordion.scss
  • accordion/accordion--faq.scss
Edit this page
Last Updated: 4/27/26, 12:56 PM
Contributors: Nicolas Jaenen