Skip to main content

🏗️ Sass Architecture and Organization

Writing Sass is easy; keeping thousands of lines of it maintainable is the real skill. This lesson covers the folder patterns the industry actually uses, the modern @use/@forward module system that replaced @import, the naming conventions that keep class names predictable, and how component frameworks reshape all of it.

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Compare the 7-1, ITCSS, SMACSS, and Atomic Design architectures and choose one for a project
  • Structure a stylesheet with partials, an entry file, and a sensible import order
  • Use the modern @use and @forward module system instead of the deprecated @import
  • Apply BEM and understand alternatives (SUIT, OOCSS, utility-first)
  • Adapt Sass organization to component-based frameworks and CSS Modules

Estimated Time: 35–45 minutes  •  Difficulty: Intermediate

Hands-on: Draft a 7-1 folder tree and its @use-based entry file for a small app.

In This Lesson

Why Architecture Matters

As a project grows, unstructured styles rot into a tangle of specificity fights, duplicated rules, and files nobody dares touch. A deliberate architecture prevents that. It buys you maintainability, scalability, modularity, easier team collaboration, and leaner output.

💡 Analogy — city planning. A well-planned city has residential zones, commercial districts, and industrial areas with clear boundaries and roads connecting them. A well-organized Sass project has distinct sections — base styles, layout, components — with clear rules for how they interact. Without planning, both grow into chaotic messes that are painful to navigate.

📖 What "architecture" means here

Two decisions, really: how you split files into folders (the pattern), and how those files reference each other (the module system). Get both right and a newcomer can find any style in seconds.

The Four Common Patterns

1. The 7-1 Pattern

The most popular pattern: 7 folders, 1 main file. Everything lives in a category folder as a partial, and one entry file pulls them together.

graph TD A["main.scss (entry)"] --> B[abstracts/] A --> C[base/] A --> D[components/] A --> E[layout/] A --> F[pages/] A --> G[themes/] A --> H[vendors/]
sass/
├── abstracts/   # variables, functions, mixins, placeholders (no output)
├── base/        # reset, typography, base element styles
├── components/  # buttons, cards, forms, modals, navigation
├── layout/      # header, footer, grid, sidebar
├── pages/       # page-specific styles
├── themes/      # default, dark, admin
├── vendors/     # third-party (Bootstrap, etc.)
└── main.scss    # imports everything, in order

Best for: large projects with many pages and components, and teams that need an obvious place for every style.

2. ITCSS (Inverted Triangle CSS)

ITCSS orders styles by specificity and reach — from far-reaching generic rules down to narrow, specific ones — so specificity only ever climbs. Picture an inverted triangle, widest at the top:

graph TD A["Settings — variables"] --> B["Tools — mixins, functions"] B --> C["Generic — reset, normalize"] C --> D["Elements — bare HTML tags"] D --> E["Objects — layout patterns"] E --> F["Components — UI pieces"] F --> G["Utilities — helper overrides"]

Best for: codebases where controlling specificity is the priority, or legacy CSS you must integrate with.

3. SMACSS

SMACSS sorts every rule into five buckets: Base, Layout, Module, State, Theme. Lighter than 7-1, with an explicit place for interactive state classes (like .is-active).

Best for: medium projects with a clear split between layout and reusable modules.

4. Atomic Design

Atomic Design composes UIs from five levels: atoms → molecules → organisms → templates → pages. Small pieces combine into bigger ones.

graph LR A[Atoms] --> B[Molecules] B --> C[Organisms] C --> D[Templates] D --> E[Pages]

Best for: design systems and pattern libraries built around heavy component reuse.

Choosing an Architecture

There's no single winner — match the pattern to the project. And you can mix them: a 7-1 folder tree whose files follow ITCSS's increasing-specificity order is common.

Your situationReach for
Small project, few componentsSimplified 7-1 or SMACSS
Large project, many pages7-1 Pattern
Design system / pattern libraryAtomic Design
Legacy code with specificity warsITCSS
Component framework (React, Vue)Component-centric (below)

Modern Modules: @use & @forward

For years, Sass files were stitched together with @import. It's now deprecated and being removed, because it dumped everything into one global namespace — variable and mixin name collisions were constant, and it re-emitted files that were imported more than once.

Modern Dart Sass replaces it with two rules:

  • @use — loads another file as a namespaced module. Its members are accessed via a namespace, and each module is loaded only once.
  • @forward — re-exports a file's members so a folder can present a single tidy entry point.
// abstracts/_colors.scss
$primary: #0066cc;
$spacing-md: 1rem;

// abstracts/_index.scss  — the folder's public face
@forward "colors";
@forward "typography";
@forward "spacing";

// components/_button.scss  — consume it under a namespace
@use "../abstracts" as a;

.button {
  background-color: a.$primary;
  padding: a.$spacing-md;
  color: white;
}

✅ Why @use is safer than @import

  • Namespacing (a.$primary) makes the origin of every value obvious and prevents collisions.
  • Each module loads once, no matter how many files use it — no duplicated output.
  • Members prefixed with - or _ stay private to their module.

⚠️ Migrate away from @import

New code should use @use/@forward. For existing projects, the official sass-migrator tool automates most of the conversion. Global color/math functions like darken() and $a / $b are deprecated alongside @import — use color.adjust() and math.div().

Naming Conventions (BEM & friends)

A consistent naming convention makes class names predictable — you can guess a selector without opening the file. BEM (Block, Element, Modifier) is the most widespread.

.card {                 // Block — a standalone component
  background: white;
  border-radius: 4px;

  &__title {            // Element — a part of the block
    font-size: 18px;
    font-weight: bold;
  }

  &__content { padding: 15px; }

  &--featured {         // Modifier — a variation of the block
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
  }
}

Sass's & parent selector makes BEM pleasant to write: &__title inside .card compiles to .card__title. The markup reads clearly too: <div class="card card--featured">.

ConventionIdeaExample
BEMBlock / Element / Modifier.card__title--large
SUITPascalCase components, is- states.Button--primary.is-disabled
OOCSSSeparate structure from skin.module .module-dark
Utility-firstSingle-purpose classes in markupclass="p-4 rounded-md"

Many teams blend them — BEM for components, a handful of utility classes for spacing and alignment. Consistency matters more than which one you pick.

Component-Based Architecture

Frameworks like React, Vue, and Angular shift the picture: styles live next to the component they style, not in a distant global folder.

src/
├── components/
│   ├── Button/
│   │   ├── Button.jsx        # component logic
│   │   ├── Button.module.scss # scoped styles
│   │   └── index.js
│   └── Card/
│       ├── Card.jsx
│       ├── Card.module.scss
│       └── index.js
└── styles/
    ├── abstracts/            # shared variables & mixins
    ├── base/                 # reset, typography
    └── main.scss             # GLOBAL styles only

With CSS Modules, class names are hashed at build time so they can't collide between components — you get local scope without long BEM names:

/* Button.module.scss */
.button {
  padding: 10px 15px;
  border-radius: 4px;

  &.primary   { background-color: #0066cc; color: white; }
  &.secondary { background-color: transparent; border: 1px solid #0066cc; color: #0066cc; }
}
import styles from "./Button.module.scss";

function Button({ variant = "primary", children }) {
  return <button className={`${styles.button} ${styles[variant]}`}>{children}</button>;
}

Even here you keep a small global layer — variables, resets, and utilities — that component files import via @use. Local scope handles the component; the global layer keeps design tokens in one place.

File-Organization Best Practices

✅ Do

  • Prefix partials with an underscore (_variables.scss) so they aren't compiled to their own file.
  • Use an _index.scss per folder that @forwards its members — one clean import per folder.
  • Keep files small and single-purpose (_form-validation.scss, not a 500-line _forms.scss).
  • Order dependencies generic → specific: abstracts, vendors, base, layout, components, pages, themes.
  • Document the architecture in a short README so the team shares one mental model.

⚠️ Don't

  • Let one file grow to cover everything — it becomes the file everyone fears editing.
  • Mix unrelated concerns in a folder; each category should map to one clear responsibility.
  • Reach for @import in new code — it's deprecated.

A folder's index file is the tidy public face of that folder:

// abstracts/_index.scss
@forward "variables";
@forward "functions";
@forward "mixins";
@forward "placeholders";

// main.scss — one line instead of four
@use "abstracts" as *;

Hands-on Exercise

🏋️ Draft a 7-1 Structure for a Small App

Objective: Plan the folders and the modern entry file for a simple blog with a home page, an article page, a button and a card component, and light/dark themes.

Instructions:

  1. Sketch a 7-1 folder tree, listing at least one partial in each folder you actually need (you can omit folders the app doesn't use, like vendors/).
  2. Write main.scss using @use, loading things in dependency order (abstracts first).
  3. Decide a naming convention for the components and note it in a one-line comment.
💡 Hint

Abstracts produce no CSS, so they load first. Themes and page styles override components, so they load last. Give each folder an _index.scss that forwards its partials, then @use the folder.

✅ Sample solution
sass/
├── abstracts/  (_variables, _mixins, _index)
├── base/       (_reset, _typography, _index)
├── components/ (_button, _card, _index)
├── layout/     (_header, _footer, _index)
├── pages/      (_home, _article, _index)
├── themes/     (_light, _dark, _index)
└── main.scss
// main.scss — BEM naming for components
@use "abstracts" as *;   // 1. tools & tokens (no output)
@use "base";             // 2. reset & typography
@use "layout";           // 3. structural layout
@use "components";       // 4. UI components
@use "pages";            // 5. page-specific styles
@use "themes";           // 6. theme overrides last

Abstracts load first because everything depends on them; themes load last so they can override. Each folder is reached through its _index.scss.

🎯 Quick Quiz

Question 1: In the 7-1 pattern, what is the "1"?

Question 2: Why is @use preferred over the deprecated @import?

Question 3: In BEM, what does .card__title--large represent?

Summary & Quiz

🎉 Key Takeaways

  • A deliberate architecture buys maintainability, scalability, and easier collaboration.
  • Know the four patterns — 7-1, ITCSS, SMACSS, Atomic — and pick by project size and goals.
  • Use the modern @use/@forward module system; @import is deprecated.
  • BEM keeps class names predictable and pairs beautifully with Sass's & nesting.
  • Component frameworks co-locate styles and scope them (CSS Modules), keeping only tokens and resets global.
  • Keep files small and single-purpose, and load them generic → specific.

📚 Further Reading

🚀 What's Next?

That wraps up advanced Sass. Next you'll apply this foundation to prebuilt CSS frameworks, starting with CSS Framework Types and Comparison — where understanding architecture pays off as you learn to customize and extend tools like Bootstrap and Tailwind.

🎉 Well done!

You can now structure a stylesheet that stays sane from ten lines to ten thousand.