Sass and BEM — Complete Guide
In this tutorial, you will learn about Sass and BEM. We cover key concepts, practical examples, and best practices to help you master this topic.
Sass nesting with BEM methodology creates structured component styles using the & parent selector for block, element, and modifier naming patterns.
What You'll Learn
- BEM naming convention (Block Element Modifier)
- Nesting BEM with the & selector
- The $block variable pattern for nested modifiers
- BEM mixins for reusable patterns
- BEM in component-based architecture
- Common BEM-Sass patterns
Why It Matters
- BEM prevents naming conflicts
- Sass makes BEM more readable
- Reduces repetition of block names
- Creates predictable, scoped styles
Real-World Use
- A design system uses BEM-Sass for all components
- A large team avoids CSS conflicts with BEM namespacing
- A component library exports scoped BEM classes
- A Migration from global CSS to BEM-Sass
flowchart LR A[BEM] --> B[Block .card] A --> C[Element .card__title] A --> D[Modifier .card--featured] B --> E[Nesting with &] C --> E D --> E E --> F[Self-contained component]
BEM with Sass
Code Example: Basic BEM Nesting
// Without Sass (CSS)
.card { background: #fff; }
.card__title { font-size: 1.25rem; }
.card__text { color: #666; }
.card__image { width: 100%; }
.card--featured { border-color: #0066CC; }
.card--featured .card__title { color: #0066CC; }
// With Sass (SCSS) - cleaner nesting
.card {
background: #fff;
border: 1px solid #eee;
border-radius: 8px;
overflow: hidden;
// Elements
&__image {
width: 100%;
height: auto;
display: block;
}
&__body {
padding: 1.5rem;
}
&__title {
font-size: 1.25rem;
margin: 0 0 0.5rem;
color: #333;
}
&__text {
color: #666;
line-height: 1.6;
margin: 0;
}
&__footer {
padding: 1rem 1.5rem;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
}
// Modifiers
&--featured {
border-color: #0066CC;
box-shadow: 0 4px 12px rgba(0, 102, 204, 0.15);
// Element styling within modifier
#{$this}__title {
color: #0066CC;
}
}
&--compact {
#{$this}__body {
padding: 1rem;
}
#{$this}__title {
font-size: 1rem;
}
}
&--dark {
background: #333;
color: #fff;
#{$this}__title { color: #fff; }
#{$this}__text { color: #ccc; }
}
}
Expected output: BEM classes are created with &__element and &--modifier syntax. The #{$this} variable enables nested modifier styles to target child elements without repeating the block name.
Code Example: The $block Variable Pattern
// Store the parent selector for nested modifier usage
.card {
$block: &; // Stores '.card'
background: #fff;
border-radius: 8px;
&__title {
font-size: 1.25rem;
}
&__text {
color: #666;
}
// Modifier: uses $block to reference elements
&--featured {
border-color: #0066CC;
#{$block}__title {
color: #0066CC;
}
#{$block}__text {
color: #444;
}
}
// Modifier with context
&--compact {
#{$block}__body {
padding: 0.5rem;
}
#{$block}__title {
font-size: 1rem;
}
}
}
// Multiple blocks sharing patterns
.alert {
$block: &;
padding: 1rem;
border-radius: 4px;
border: 1px solid transparent;
&__title {
font-weight: bold;
margin-bottom: 0.25rem;
}
&__text {
font-size: 0.875rem;
}
// Variants
&--success {
background: #d4edda;
border-color: #c3e6cb;
#{$block}__title { color: #155724; }
}
&--error {
background: #f8d7da;
border-color: #f5c6cb;
#{$block}__title { color: #721c24; }
}
}
Expected output: The $block variable stores the parent selector for use inside nested contexts. This allows modifiers to style child elements without hard-coding the block name.
Code Example: BEM Mixins
// BEM element mixin
@mixin element($name) {
&__#{$name} {
@content;
}
}
// BEM modifier mixin
@mixin modifier($name) {
&--#{$name} {
@content;
}
}
// BEM block mixin with context
@mixin bem-block($name) {
.#{$name} {
$block: &;
@content;
// Store block for child usage
$block: & !global;
}
}
// Usage
.card {
$block: &;
background: #fff;
border-radius: 8px;
@include element('title') {
font-size: 1.25rem;
margin: 0;
}
@include element('body') {
padding: 1.5rem;
}
@include element('text') {
color: #666;
}
@include modifier('featured') {
border-color: #0066CC;
#{$block}__title {
color: #0066CC;
}
}
@include modifier('compact') {
#{$block}__body {
padding: 0.5rem;
}
}
}
// Alternative: BEM mixin with element context
@mixin bem($element: null, $modifier: null) {
$selector: &;
@if $element {
$selector: "#{$selector}__#{$element}";
}
@if $modifier {
@if $element {
$selector: "#{$selector}--#{$modifier}";
} @else {
$selector: "#{$selector}--#{$modifier}";
}
}
@at-root #{$selector} {
@content;
}
}
// Less verbose usage (but less readable)
// @include bem('title') { font-size: 1.25rem; }
Expected output: Mixins can encapsulate BEM syntax. The @include element('title') pattern is more readable than &__title. Choose patterns that your team finds clearest.
Code Example: BEM with Component Architecture
// components/_card.scss
.card {
$block: &;
background: #fff;
border: 1px solid #eee;
border-radius: 8px;
overflow: hidden;
// Elements
&__image {
width: 100%;
height: 200px;
object-fit: cover;
@media (min-width: 768px) {
height: 300px;
}
}
&__body {
padding: 1.5rem;
}
&__title {
font-size: 1.25rem;
margin: 0 0 0.5rem;
font-weight: 600;
}
&__text {
color: #666;
line-height: 1.6;
}
&__meta {
font-size: 0.875rem;
color: #999;
margin-top: 0.5rem;
}
&__actions {
padding: 1rem 1.5rem;
border-top: 1px solid #eee;
display: flex;
gap: 0.5rem;
}
// Modifiers
&--horizontal {
display: flex;
#{$block}__image {
width: 200px;
height: auto;
flex-shrink: 0;
}
#{$block}__body {
flex: 1;
}
@media (max-width: 600px) {
flex-direction: column;
#{$block}__image {
width: 100%;
height: 200px;
}
}
}
&--featured {
border-color: #0066CC;
box-shadow: 0 4px 12px rgba(0, 102, 204, 0.15);
#{$block}__title {
color: #0066CC;
}
}
// States
&.is-highlighted {
background: #f0f7ff;
}
}
Expected output: The component file is self-contained with all BEM variants. The $block variable enables element targeting from modifiers. Media queries are nested inside the block.
Common Mistakes
- Over-nesting BEM — BEM with Sass can lead to &__element__nested which invalidates BEM principles. BEM only has one level of elements.
- Not using $block for nested context — Without $block, modifier styles that target elements become repetitive or use hard-coded selectors.
- Confusing &__element and & --modifier — &__element (no space) appends __element. &--modifier appends --modifier. & .class (with space) creates a descendant selector.
- BEM inside BEM — A BEM block inside another BEM block should be a separate block, not a nested element.
- Using element inside modifier — In BEM, modifiers are not nested under elements. Use &:hover or --modifier at the block level.
- Not using interpolation for variable-based selectors — #{$block}__title requires interpolation. $block__title would try to access a variable.
- Inconsistent naming — Choose a naming convention (camelCase, kebab-case) and stick with it. BEM conventionally uses kebab-case.
Practice Questions
- What does &__title compile to in BEM nesting? .block__title (the & represents the block class).
- Why use the $block variable pattern? To reference child elements from within modifiers without repeating the block name.
- Can BEM elements have their own elements? No. BEM only allows one level: block__element. Nested elements break the methodology.
- How do you style an element differently within a modifier? Use #{$block}__element inside the modifier block.
FAQ
Mini Project
Build 5 BEM components using Sass: card (with horizontal and featured modifiers), button (primary, secondary, large, small, icon), alert (success, error, warning, info), form-group (with label, input, error, hint elements), and nav (with dropdown modifier). Each component must: use the $block variable pattern, have at least 2 elements, have at least 2 modifiers, use BEM-compatible nesting (no nested BEM blocks), and include responsive styles with media queries. Create a sample page that uses all 5 components together, demonstrating that BEM naming prevents style conflicts.
What's Next
Continue with Lesson 17: Sass for Responsive Design to learn responsive patterns with Sass.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro