Sass 7-1 Architecture: File Organization for CSS Methodologies
The 7-1 Pattern is the most widely adopted Sass file organization system — 7 folders, 1 main file that imports them all. Created by Hugo Giraudel, it maps directly to ITCSS layers and is the standard implementation vehicle for BEM, SMACSS, and ITCSS in real-world projects.
1. What is the 7-1 Pattern?
7 folders, each representing a category of CSS:
├── abstracts/ ← Settings + Tools (no CSS output)
├── vendors/ ← Third-party CSS
├── base/ ← Generic + Elements (reset + bare HTML)
├── layout/ ← Layout/ SMACSS Layout (page structure)
├── components/ ← Components (UI elements)
├── pages/ ← Page-specific overrides
└── themes/ ← Theme/ SMACSS Theme layer
1 main file:
└── main.scss ← @use's all folders in correct order
2. @use vs @import — The Modern Sass Way
Sass deprecated @import in 2021. Always use @use and @forward:
// ❌ Deprecated @import (still works but will be removed)
@import 'abstracts/variables';
@import 'abstracts/mixins';
@import 'base/reset';
// ✅ Modern @use — explicit namespacing
@use 'abstracts/variables' as v;
@use 'abstracts/mixins' as m;
@use 'base/reset';
// Access variables via namespace
.c-btn { background: v.$color-primary; }
@use vs @forward
// @use: import into THIS file — use variables/mixins here
// abstracts/_mixins.scss
@use 'variables' as v;
@mixin focus-ring { outline: 2px solid v.$color-primary; }
// @forward: re-export so other files can access
// abstracts/_index.scss — the "barrel" file
@forward 'variables';
@forward 'mixins';
@forward 'functions';
// In component files — one @use pulls everything:
@use '../abstracts' as a;
.c-btn { background: a.$color-primary; @include a.focus-ring; }
3. Complete 7-1 File Structure
scss/
│
├── abstracts/ ← No CSS output — pure Sass tools
│ ├── _variables.scss ← Sass variables (complement to CSS custom props)
│ ├── _functions.scss ← Sass functions (px-to-rem, contrast, etc.)
│ ├── _mixins.scss ← Sass mixins (respond-to, truncate, etc.)
│ ├── _placeholders.scss ← Sass silent classes (%visually-hidden)
│ └── _index.scss ← @forward all of the above
│
├── vendors/ ← Third-party CSS (don't edit)
│ ├── _normalize.scss ← Normalize.css
│ └── _prism.scss ← Code highlighting
│
├── base/ ← CSS foundation
│ ├── _reset.scss ← Modern CSS reset
│ ├── _typography.scss ← h1-h6, p, a, lists, code defaults
│ ├── _forms.scss ← input, select, textarea base styles
│ └── _animations.scss ← @keyframes definitions
│
├── layout/ ← Page skeleton structure
│ ├── _container.scss ← .l-container, .l-narrow, .l-wide
│ ├── _grid.scss ← .l-grid-*, .o-grid-*
│ ├── _header.scss ← .l-header
│ ├── _footer.scss ← .l-footer
│ ├── _sidebar.scss ← .l-sidebar, .l-content
│ └── _section.scss ← .l-section spacing
│
├── components/ ← UI components (BEM-named)
│ ├── _btn.scss ← .c-btn, .c-btn--primary, etc.
│ ├── _card.scss ← .c-card, .c-card__*, .c-card--*
│ ├── _nav.scss ← .c-nav, .c-nav__*
│ ├── _hero.scss ← .c-hero, .c-hero__*
│ ├── _modal.scss ← .c-modal, .c-modal__*
│ ├── _form.scss ← .c-form, .c-form__*
│ ├── _badge.scss ← .c-badge, .c-badge--*
│ ├── _alert.scss ← .c-alert, .c-alert--*
│ ├── _dropdown.scss ← .c-dropdown, .c-dropdown__*
│ ├── _tabs.scss ← .c-tabs, .c-tab__*
│ ├── _accordion.scss ← .c-accordion, .c-accordion__*
│ ├── _testimonial.scss ← .c-testimonial, .c-testimonial__*
│ ├── _pricing.scss ← .c-pricing-card, .c-pricing-card__*
│ └── _footer-widget.scss ← .c-footer-widget, .c-footer-widget__*
│
├── pages/ ← Page-specific CSS (use sparingly)
│ ├── _home.scss ← .page-home specific overrides
│ ├── _about.scss ← .page-about
│ ├── _contact.scss ← .page-contact
│ └── _woocommerce.scss ← WooCommerce overrides
│
├── themes/ ← Visual skin layers
│ ├── _dark.scss ← [data-theme="dark"] overrides
│ ├── _light.scss ← [data-theme="light"]
│ └── _admin.scss ← WordPress admin overrides
│
└── main.scss ← Entry point — @use all in order
4. The Main Entry File (main.scss)
The order matters — mirrors ITCSS specificity pyramid:
// main.scss
// ==========
// 7-1 Sass Architecture Entry Point
// Order: Low specificity → High specificity
// ==========
// ── ABSTRACTS ──────────────────────────
// No CSS output — must be first so all files can @use abstracts
@use 'abstracts/index' as *; // Namespace-less: use variables/mixins directly
// ── VENDORS ────────────────────────────
// Third-party — lowest priority (ITCSS: Generic/Reset level)
@use 'vendors/normalize';
// ── BASE ───────────────────────────────
// Element-level defaults (ITCSS: Generic + Elements)
@use 'base/reset';
@use 'base/typography';
@use 'base/forms';
@use 'base/animations';
// ── LAYOUT ─────────────────────────────
// Page structure (ITCSS: Objects — structural level)
@use 'layout/container';
@use 'layout/grid';
@use 'layout/header';
@use 'layout/footer';
@use 'layout/sidebar';
@use 'layout/section';
// ── COMPONENTS ─────────────────────────
// UI components — most of your code lives here (ITCSS: Components)
@use 'components/btn';
@use 'components/card';
@use 'components/nav';
@use 'components/hero';
@use 'components/modal';
@use 'components/form';
@use 'components/badge';
@use 'components/alert';
@use 'components/dropdown';
@use 'components/tabs';
@use 'components/accordion';
@use 'components/testimonial';
@use 'components/pricing';
@use 'components/footer-widget';
// ── PAGES ──────────────────────────────
// Page-specific overrides (sparingly)
@use 'pages/home';
@use 'pages/woocommerce';
// ── THEMES ─────────────────────────────
// Theme overrides — last so they win (ITCSS: Theme)
@use 'themes/dark';
@use 'themes/light';
5. The abstracts/ Folder in Detail
_variables.scss — Sass Variables
// abstracts/_variables.scss
// Sass variables for values that NEED Sass computation
// CSS custom properties handle runtime tokens — Sass vars handle build-time config
$breakpoints: (
'sm': 480px,
'md': 768px,
'lg': 1024px,
'xl': 1280px,
'2xl': 1536px,
);
$z-index: (
'base': 1,
'raised': 10,
'dropdown': 100,
'sticky': 200,
'overlay': 300,
'modal': 400,
'toast': 500,
'max': 9999,
);
// Reference to CSS custom properties (bridging Sass ↔ CSS tokens)
$color-primary: var(--color-primary);
$font-body: var(--font-body);
_functions.scss — Sass Functions
// abstracts/_functions.scss
// Convert px to rem
@function rem($px, $base: 16) {
@return math.div($px, $base) * 1rem;
}
// Get breakpoint value
@function bp($name) {
@if map-has-key($breakpoints, $name) {
@return map-get($breakpoints, $name);
}
@error 'Breakpoint `#{$name}` not found in $breakpoints';
}
// Get z-index value
@function z($layer) {
@if map-has-key($z-index, $layer) {
@return map-get($z-index, $layer);
}
@error 'Z-index layer `#{$layer}` not found';
}
// Strip units
@function strip-unit($number) {
@return math.div($number, ($number * 0 + 1));
}
_mixins.scss — Sass Mixins
// abstracts/_mixins.scss
@use 'variables' as v;
@use 'sass:math';
// ── Responsive breakpoint mixin ──
@mixin respond-to($bp) {
@media (min-width: #{bp($bp)}) { @content; }
}
// ── Mobile-first responsive with max-width ──
@mixin respond-until($bp) {
@media (max-width: calc(#{bp($bp)} - 1px)) { @content; }
}
// ── Focus ring ──
@mixin focus-ring($color: var(--color-primary), $offset: 3px) {
&:focus-visible {
outline: 2px solid #{$color};
outline-offset: $offset;
}
}
// ── Truncate to N lines ──
@mixin line-clamp($lines: 2) {
@if $lines == 1 {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} @else {
display: -webkit-box;
-webkit-line-clamp: $lines;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
// ── Fluid typeface ──
@mixin fluid-type($min-size, $max-size, $min-screen: 320px, $max-screen: 1440px) {
font-size: clamp(
#{$min-size},
calc(#{$min-size} + #{strip-unit($max-size - $min-size)} * ((100vw - #{$min-screen}) / #{strip-unit($max-screen - $min-screen)})),
#{$max-size}
);
}
// ── Visually hidden (accessible) ──
@mixin visually-hidden {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
}
// ── Aspect ratio box (legacy browsers) ──
@mixin aspect-ratio($width: 16, $height: 9) {
position: relative;
&::before {
content: '';
display: block;
padding-bottom: math.percentage(math.div($height, $width));
}
> * {
position: absolute;
inset: 0;
width: 100%; height: 100%;
}
}
// ── Glass effect ──
@mixin glass($opacity: 0.08, $blur: 20px) {
background: rgba(255, 255, 255, #{$opacity});
backdrop-filter: blur(#{$blur});
-webkit-backdrop-filter: blur(#{$blur});
border: 1px solid rgba(255, 255, 255, #{$opacity * 2});
}
_placeholders.scss — Silent Classes
// abstracts/_placeholders.scss
// @extend these in components — generates no CSS until extended
%clearfix {
&::after { content: ''; display: table; clear: both; }
}
%visually-hidden {
position: absolute; width: 1px; height: 1px;
padding: 0; margin: -1px; overflow: hidden;
clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
%reset-list { list-style: none; padding: 0; margin: 0; }
%reset-button {
appearance: none; background: none; border: none;
cursor: pointer; padding: 0; font: inherit; color: inherit;
}
%transition-base { transition: all 0.2s ease; }
abstracts/_index.scss — The Barrel File
// abstracts/_index.scss
// Forward everything so consuming files use ONE @use statement
@forward 'variables';
@forward 'functions';
@forward 'mixins';
@forward 'placeholders';
6. A Component File in Full Detail (_btn.scss)
// components/_btn.scss
// ==========
// Component: Button
// Block: .c-btn
// Elements: .c-btn__icon, .c-btn__label
// Modifiers: .c-btn--primary, --outline, --ghost, --danger, --sm, --lg, --loading
// States: .is-loading, :disabled
// ==========
@use '../abstracts' as a;
.c-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 0.7rem 1.5rem;
border: 2px solid transparent;
border-radius: var(--radius-full);
font-family: var(--font-body);
font-size: var(--text-base);
font-weight: 600;
line-height: 1;
cursor: pointer;
text-decoration: none;
white-space: nowrap;
transition: all var(--duration-base, 0.2s) ease;
background: transparent;
color: var(--color-text);
@include a.focus-ring;
&:disabled,
&[aria-disabled="true"] { opacity: 0.5; pointer-events: none; cursor: not-allowed; }
// ── Elements ──
&__icon { font-size: 1em; flex-shrink: 0; }
// ── Modifiers: color variants ──
&--primary { background: var(--color-primary); color: white; }
&--primary:hover { background: var(--color-primary-dark); }
&--outline { border-color: var(--color-primary); color: var(--color-primary); }
&--outline:hover { background: var(--color-primary); color: white; }
&--ghost { color: var(--color-text-muted); }
&--ghost:hover { background: rgba(255,255,255,0.06); color: var(--color-text); }
&--danger { background: var(--color-danger); color: white; }
&--danger:hover { background: #c0392b; }
// ── Modifiers: sizes ──
&--sm { padding: 0.4rem 1rem; font-size: var(--text-sm); }
&--lg { padding: 1rem 2rem; font-size: 1.125rem; }
&--xl { padding: 1.25rem 2.5rem; font-size: 1.25rem; }
&--block { width: 100%; }
// ── State: loading ──
&.is-loading {
position: relative;
color: transparent;
pointer-events: none;
&::after {
content: '';
position: absolute;
width: 1rem; height: 1rem;
border: 2px solid rgba(255,255,255,0.3);
border-top-color: white;
border-radius: 50%;
animation: btn-spin 0.7s linear infinite;
}
}
}
@keyframes btn-spin { to { transform: rotate(360deg); } }
7. WordPress Child Theme Setup with 7-1
my-child-theme/
├── functions.php
├── style.css ← Theme header + legacy CSS
├── scss/
│ ├── abstracts/
│ ├── base/
│ ├── layout/
│ ├── components/
│ ├── pages/
│ │ └── _woocommerce.scss
│ ├── themes/
│ │ └── _customizer.scss ← WordPress Customizer CSS variable overrides
│ └── main.scss
└── assets/
└── css/
└── main.css ← Compiled output
// functions.php
function theme_enqueue_styles() {
// Enqueue parent theme first
wp_enqueue_style('parent-style', get_template_directory_uri() . '/style.css');
// Enqueue compiled Sass output after parent
wp_enqueue_style(
'child-style',
get_stylesheet_directory_uri() . '/assets/css/main.css',
['parent-style'],
filemtime(get_stylesheet_directory() . '/assets/css/main.css')
);
}
add_action('wp_enqueue_scripts', 'theme_enqueue_styles');
Compile command:
# Install Sass (Node.js Sass — recommended)
npm install -D sass
# Compile once
npx sass scss/main.scss assets/css/main.css --style=compressed
# Watch for changes during development
npx sass --watch scss/main.scss:assets/css/main.css
# In package.json scripts
{
"scripts": {
"css:build": "sass scss/main.scss assets/css/main.css --style=compressed",
"css:watch": "sass --watch scss/main.scss:assets/css/main.css",
"css:dev": "sass --watch scss/main.scss:assets/css/main.css --source-map"
}
}
8. ITCSS + 7-1 Layer Mapping
7-1 Folder ITCSS Layer SMACSS Category
──────────────────────────────────────────────────────────
abstracts/ 1. Settings (config only)
abstracts/ 2. Tools (config only)
vendors/ 3. Generic (third-party reset)
base/ 3. Generic Base
base/ 4. Elements Base
layout/ 5. Objects Layout
components/ 6. Components Module
pages/ 6. Components Module (page-specific)
themes/ 7. Utilities OR Theme Theme
utilities/ 7. Utilities (sometimes added)
──────────────────────────────────────────────────────────
When you combine 7-1 Sass with ITCSS layers and BEM naming, you get the full professional stack:
// components/_card.scss
// ITCSS: Layer 6 — Component
// Naming: BEM
// Prefix: c- (ITCSS prefix)
@use '../abstracts' as a;
.c-card { // BEM Block with ITCSS c- prefix
background: var(--color-surface); // CSS token from :root (ITCSS Settings)
border-radius: var(--radius-lg);
@include a.respond-to('md') { // Sass mixin from abstracts (ITCSS Tools)
display: grid;
grid-template-columns: 200px 1fr;
}
// BEM Elements
&__image { width: 100%; aspect-ratio: 16/9; object-fit: cover; }
&__body { padding: var(--space-6); }
&__title { font-size: var(--text-xl); @include a.line-clamp(2); }
// BEM Modifiers
&--featured { border-color: var(--color-primary); }
&--glass { @include a.glass(0.06); }
// SMACSS states (toggled by JS)
&.is-loading { opacity: 0.5; pointer-events: none; }
}
9. Quick Setup Checklist
□ Run: npm init -y && npm install -D sass
□ Create scss/ folder structure (7 folders)
□ Create abstracts/_index.scss (@forward vars, functions, mixins)
□ Create main.scss (@use all in order)
□ Add npm scripts: css:watch and css:build
□ Set CSS custom properties in :root (settings layer)
□ Write reset in base/_reset.scss
□ Write element defaults in base/_typography.scss
□ Write layout objects in layout/_container.scss
□ Write first component: components/_btn.scss
□ Enqueue compiled CSS in functions.php
□ Test: npm run css:watch, make a change, verify browser updates
□ Add .gitignore: node_modules/ — commit scss/ and assets/css/