Building a Grid System with SASS
Building a Grid System with SASS
Grid systems are the foundation of responsive web design. While frameworks like Bootstrap provide ready-made grids, building your own custom grid system with SASS gives you complete control over layout behavior, naming conventions, and performance. In this comprehensive lesson, we'll explore how to build professional-grade grid systems using SASS's powerful features.
Why Build a Custom Grid System?
Before diving into implementation, let's understand why you might want to build your own grid system:
- Full Control: Define exactly the number of columns, gutters, and breakpoints you need
- Performance: Generate only the classes you actually use, reducing CSS file size
- Learning: Understanding grid systems deeply makes you a better front-end developer
- Customization: Create naming conventions that match your team's preferences
- Framework Independence: No dependency on third-party frameworks
- Project-Specific: Tailor the grid to your exact project requirements
Grid System Fundamentals
Every grid system consists of three core components:
1. Container
The container centers and constrains the maximum width of your layout:
Basic Container
// Grid Variables
$container-max-width: 1200px;
$container-padding: 15px;
// Container Class
.container {
width: 100%;
max-width: $container-max-width;
margin-left: auto;
margin-right: auto;
padding-left: $container-padding;
padding-right: $container-padding;
}
// Fluid Container (full-width)
.container-fluid {
width: 100%;
padding-left: $container-padding;
padding-right: $container-padding;
}
2. Row
Rows contain columns and use negative margins to offset container padding:
Row with Flexbox
$gutter: 30px;
.row {
display: flex;
flex-wrap: wrap;
margin-left: -($gutter / 2);
margin-right: -($gutter / 2);
}
// Remove gutters variant
.row-no-gutters {
margin-left: 0;
margin-right: 0;
> [class*="col-"] {
padding-left: 0;
padding-right: 0;
}
}
3. Columns
Columns are the building blocks that define content width:
Base Column Styles
// All columns share these base styles
[class*="col-"] {
position: relative;
width: 100%;
padding-left: ($gutter / 2);
padding-right: ($gutter / 2);
}
Generating Column Classes with @for
Instead of writing 12 separate column classes manually, we use SASS's @for loop to generate them automatically:
Basic 12-Column Grid
$columns: 12;
// Generate .col-1 through .col-12
@for $i from 1 through $columns {
.col-#{$i} {
flex: 0 0 percentage($i / $columns);
max-width: percentage($i / $columns);
}
}
percentage() function is a SASS built-in that converts decimals to percentages. For example, percentage(1/12) outputs 8.33333%.
This generates CSS like:
Generated CSS Output
.col-1 {
flex: 0 0 8.33333%;
max-width: 8.33333%;
}
.col-2 {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
/* ... continues through col-12 ... */
.col-12 {
flex: 0 0 100%;
max-width: 100%;
}
Responsive Grid with Breakpoint Mixins
Real-world grids need to respond to different screen sizes. Let's build a responsive grid system:
Breakpoints Configuration
// Define your breakpoints
$breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px
);
// Mixin to generate media queries
@mixin respond-above($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
$breakpoint-value: map-get($breakpoints, $breakpoint);
@media (min-width: $breakpoint-value) {
@content;
}
} @else {
@warn "Invalid breakpoint: #{$breakpoint}.";
}
}
Generating Responsive Columns
// Generate columns for each breakpoint
@each $breakpoint, $value in $breakpoints {
@if $breakpoint == xs {
// Mobile-first: xs classes have no prefix
@for $i from 1 through $columns {
.col-#{$i} {
flex: 0 0 percentage($i / $columns);
max-width: percentage($i / $columns);
}
}
} @else {
// Larger breakpoints get prefixed classes
@include respond-above($breakpoint) {
@for $i from 1 through $columns {
.col-#{$breakpoint}-#{$i} {
flex: 0 0 percentage($i / $columns);
max-width: percentage($i / $columns);
}
}
}
}
}
This generates classes like .col-6, .col-md-4, .col-lg-3, etc.
Offset Classes
Offset classes push columns to the right by adding left margin:
Generating Offset Classes
// Generate offset classes for each breakpoint
@each $breakpoint, $value in $breakpoints {
@if $breakpoint == xs {
@for $i from 0 through ($columns - 1) {
.offset-#{$i} {
margin-left: if($i > 0, percentage($i / $columns), 0);
}
}
} @else {
@include respond-above($breakpoint) {
@for $i from 0 through ($columns - 1) {
.offset-#{$breakpoint}-#{$i} {
margin-left: if($i > 0, percentage($i / $columns), 0);
}
}
}
}
}
// Example usage:
// <div class="col-6 offset-3">Centered content</div>
Order Classes (Push and Pull)
Order classes allow you to rearrange columns visually without changing HTML order:
Flexbox Order Classes
// Generate order classes
@each $breakpoint, $value in $breakpoints {
@if $breakpoint == xs {
@for $i from 1 through $columns {
.order-#{$i} {
order: $i;
}
}
.order-first {
order: -1;
}
.order-last {
order: $columns + 1;
}
} @else {
@include respond-above($breakpoint) {
@for $i from 1 through $columns {
.order-#{$breakpoint}-#{$i} {
order: $i;
}
}
.order-#{$breakpoint}-first {
order: -1;
}
.order-#{$breakpoint}-last {
order: $columns + 1;
}
}
}
}
Nested Grids
Grids can be nested within columns. The key is that each new row resets the column context:
Nested Grid Example
<div class="container">
<div class="row">
<div class="col-8">
<!-- Nested grid inside a column -->
<div class="row">
<div class="col-6">Nested column (50% of parent)</div>
<div class="col-6">Nested column (50% of parent)</div>
</div>
</div>
<div class="col-4">Sidebar</div>
</div>
</div>
Flexbox-Based Grid System (Complete)
Let's put it all together into a complete flexbox grid system:
Complete Grid System SCSS
// grid.scss - Complete Flexbox Grid System
// Configuration
$columns: 12;
$gutter: 30px;
$container-max-width: 1200px;
$container-padding: 15px;
$breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px
);
// Mixin for media queries
@mixin respond-above($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
$breakpoint-value: map-get($breakpoints, $breakpoint);
@media (min-width: $breakpoint-value) {
@content;
}
} @else {
@warn "Invalid breakpoint: #{$breakpoint}.";
}
}
// Container
.container {
width: 100%;
max-width: $container-max-width;
margin-left: auto;
margin-right: auto;
padding-left: $container-padding;
padding-right: $container-padding;
}
.container-fluid {
width: 100%;
padding-left: $container-padding;
padding-right: $container-padding;
}
// Row
.row {
display: flex;
flex-wrap: wrap;
margin-left: -($gutter / 2);
margin-right: -($gutter / 2);
}
.row-no-gutters {
margin-left: 0;
margin-right: 0;
> [class*="col-"] {
padding-left: 0;
padding-right: 0;
}
}
// Base column styles
[class*="col-"] {
position: relative;
width: 100%;
padding-left: ($gutter / 2);
padding-right: ($gutter / 2);
}
// Generate columns for each breakpoint
@each $breakpoint, $value in $breakpoints {
@if $breakpoint == xs {
@for $i from 1 through $columns {
.col-#{$i} {
flex: 0 0 percentage($i / $columns);
max-width: percentage($i / $columns);
}
}
.col {
flex-grow: 1;
flex-basis: 0;
max-width: 100%;
}
.col-auto {
flex: 0 0 auto;
width: auto;
max-width: 100%;
}
} @else {
@include respond-above($breakpoint) {
@for $i from 1 through $columns {
.col-#{$breakpoint}-#{$i} {
flex: 0 0 percentage($i / $columns);
max-width: percentage($i / $columns);
}
}
.col-#{$breakpoint} {
flex-grow: 1;
flex-basis: 0;
max-width: 100%;
}
.col-#{$breakpoint}-auto {
flex: 0 0 auto;
width: auto;
max-width: 100%;
}
}
}
}
// Generate offset classes
@each $breakpoint, $value in $breakpoints {
@if $breakpoint == xs {
@for $i from 0 through ($columns - 1) {
.offset-#{$i} {
margin-left: if($i > 0, percentage($i / $columns), 0);
}
}
} @else {
@include respond-above($breakpoint) {
@for $i from 0 through ($columns - 1) {
.offset-#{$breakpoint}-#{$i} {
margin-left: if($i > 0, percentage($i / $columns), 0);
}
}
}
}
}
// Alignment utilities
.row-align-start {
align-items: flex-start;
}
.row-align-center {
align-items: center;
}
.row-align-end {
align-items: flex-end;
}
.row-justify-start {
justify-content: flex-start;
}
.row-justify-center {
justify-content: center;
}
.row-justify-end {
justify-content: flex-end;
}
.row-justify-between {
justify-content: space-between;
}
.row-justify-around {
justify-content: space-around;
}
CSS Grid-Based Layout with SASS
While Flexbox is great for one-dimensional layouts, CSS Grid excels at two-dimensional layouts. Let's build a CSS Grid system with SASS:
CSS Grid System
// css-grid.scss
$grid-columns: 12;
$grid-gap: 30px;
// Container using CSS Grid
.grid-container {
display: grid;
grid-template-columns: repeat($grid-columns, 1fr);
gap: $grid-gap;
}
// Generate grid column span classes
@for $i from 1 through $grid-columns {
.grid-col-#{$i} {
grid-column: span $i;
}
}
// Generate grid row span classes
@for $i from 1 through 6 {
.grid-row-#{$i} {
grid-row: span $i;
}
}
// Start position classes
@for $i from 1 through $grid-columns {
.grid-col-start-#{$i} {
grid-column-start: $i;
}
}
// Responsive CSS Grid
@each $breakpoint, $value in $breakpoints {
@if $breakpoint != xs {
@include respond-above($breakpoint) {
@for $i from 1 through $grid-columns {
.grid-col-#{$breakpoint}-#{$i} {
grid-column: span $i;
}
}
}
}
}
// Grid alignment utilities
.grid-place-center {
place-items: center;
}
.grid-place-start {
place-items: start;
}
.grid-place-end {
place-items: end;
}
// Self alignment
.grid-self-center {
place-self: center;
}
.grid-self-start {
place-self: start;
}
.grid-self-end {
place-self: end;
}
Comparing Custom Grids to Bootstrap's Grid
Bootstrap Grid
- Pros: Battle-tested, comprehensive documentation, large community
- Pros: Includes many utility classes beyond just the grid
- Cons: Larger file size (includes features you might not use)
- Cons: Fixed naming conventions (can't easily customize)
- Cons: Requires understanding Bootstrap's ecosystem
Custom SASS Grid
- Pros: Generate only what you need (smaller file size)
- Pros: Complete control over naming and behavior
- Pros: No external dependencies
- Cons: You're responsible for testing and maintenance
- Cons: No built-in documentation (you create your own)
Performance Optimization
Here are strategies to optimize your custom grid system:
Conditional Generation
// Only generate the breakpoints you need
$enabled-breakpoints: (sm, md, lg); // Skip xs and xl
@each $breakpoint, $value in $breakpoints {
@if index($enabled-breakpoints, $breakpoint) {
@include respond-above($breakpoint) {
// Generate classes...
}
}
}
// Only generate specific column counts
$column-counts: (1, 2, 3, 4, 6, 12); // Skip 5, 7, 8, 9, 10, 11
@each $count in $column-counts {
.col-#{$count} {
flex: 0 0 percentage($count / $columns);
max-width: percentage($count / $columns);
}
}
Exercise 1: Build a Basic Grid System
Create a simple 12-column grid system with the following requirements:
- Container with max-width of 1140px
- Gutter of 20px
- Generate column classes from .col-1 to .col-12
- Include a .row class with flexbox
- Test with a three-column layout
Exercise 2: Add Responsive Breakpoints
Extend your grid system with responsive features:
- Add breakpoints for sm (576px), md (768px), and lg (992px)
- Generate responsive column classes (.col-sm-6, .col-md-4, etc.)
- Create a layout that is full-width on mobile, 2 columns on tablet, and 4 columns on desktop
- Test at different screen sizes
Exercise 3: Add Offset and Order Classes
Enhance your grid system with advanced features:
- Generate offset classes for all breakpoints
- Add order classes (order-1 through order-12)
- Create a layout where the sidebar appears first on mobile but last on desktop (using order classes)
- Use offset to center a 6-column content area
Summary
In this lesson, you've learned how to build professional grid systems with SASS. You now understand:
- The core components of grid systems (containers, rows, columns)
- How to use @for loops to generate column classes automatically
- Building responsive grids with breakpoint mixins
- Creating offset and order classes for advanced layouts
- Implementing both Flexbox and CSS Grid-based systems
- Optimizing grid systems for performance
- When to use custom grids vs. framework grids
A well-designed grid system is the backbone of any professional website. With SASS, you can create grid systems that are exactly tailored to your project's needs while maintaining clean, maintainable code.