Sass Project — Build a Complete Themed Stylesheet
In this tutorial, you will learn about Sass Project. We cover key concepts, practical examples, and best practices to help you master this topic.
This Sass project guides you through building a complete themed component library using variables, mixins, functions, loops, and the 7-1 architecture pattern.
What You'll Learn
You will build a production-ready Sass stylesheet with design tokens, responsive utilities, themed components, and a modular 7-1 structure that can be reused across projects.
Why It Matters
A complete project consolidates all Sass skills. DodaTech's component library follows this same pattern for consistent theming across all products.
Real-World Use
The stylesheet you build mirrors the pattern used in Doda Browser, DodaZIP, and Durga Antivirus Pro for their UI component libraries.
flowchart LR
A[7-1 Pattern] --> B[Sass Project]
B --> C[Setup Structure]
B --> D[Design Tokens]
B --> E[Components]
B --> F[Themes]
B --> G[Build]
style B fill:#c69,stroke:#c69,color:#fff
style G fill:#22c55e,stroke:#16a34a,color:#fff
Project Structure
sass-project/
scss/
main.scss
abstracts/
_variables.scss
_mixins.scss
_functions.scss
base/
_reset.scss
_typography.scss
components/
_buttons.scss
_cards.scss
_forms.scss
_badges.scss
_alerts.scss
layout/
_grid.scss
_header.scss
_footer.scss
themes/
_light.scss
_dark.scss
dist/
index.html
package.json
Step 1: Design Tokens
// scss/abstracts/_variables.scss
// Colors
$brand-primary: #7c3aed !default;
$brand-secondary: #6d28d9 !default;
$success: #10b981 !default;
$danger: #ef4444 !default;
$warning: #f59e0b !default;
$info: #3b82f6 !default;
// Neutral palette
$white: #ffffff;
$gray-50: #f8fafc;
$gray-100: #f1f5f9;
$gray-200: #e2e8f0;
$gray-300: #cbd5e1;
$gray-400: #94a3b8;
$gray-500: #64748b;
$gray-600: #475569;
$gray-700: #334155;
$gray-800: #1e293b;
$gray-900: #0f172a;
// Typography
$font-family-sans: "Inter", system-ui, sans-serif;
$font-family-mono: "JetBrains Mono", monospace;
$font-size-sm: 0.875rem;
$font-size-base: 1rem;
$font-size-lg: 1.125rem;
$font-size-xl: 1.25rem;
$font-size-2xl: 1.5rem;
$font-size-3xl: 1.875rem;
// Spacing
$spacing-unit: 0.25rem;
$spacing: (
0: 0,
1: $spacing-unit * 1,
2: $spacing-unit * 2,
3: $spacing-unit * 4,
4: $spacing-unit * 6,
5: $spacing-unit * 8,
6: $spacing-unit * 12,
8: $spacing-unit * 16,
10: $spacing-unit * 20,
);
// Breakpoints
$breakpoints: (
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
);
// Borders
$border-radius-sm: 0.25rem;
$border-radius: 0.5rem;
$border-radius-lg: 0.75rem;
$border-radius-xl: 1rem;
$border-radius-full: 9999px;
// Shadows
$shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
$shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
$shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.06);
$shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05);
Step 2: Mixins and Functions
// scss/abstracts/_mixins.scss
@use "sass:map";
@mixin respond-to($bp) {
@if map.has-key($breakpoints, $bp) {
@media (min-width: map.get($breakpoints, $bp)) {
@content;
}
} @else {
@error "Unknown breakpoint: #{$bp}";
}
}
@mixin flex-center {
display: flex;
justify-content: center;
align-items: center;
}
@mixin text-truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
@mixin visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
// scss/abstracts/_functions.scss
@use "sass:math";
@use "sass:map";
@function spacing($key) {
@if map.has-key($spacing, $key) {
@return map.get($spacing, $key);
}
@return $spacing-unit * $key;
}
@function rem($px) {
@return math.div($px, 16px) * 1rem;
}
Step 3: Component Buttons
// scss/components/_buttons.scss
.button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: spacing(2) spacing(4);
font-size: $font-size-base;
font-family: $font-family-sans;
font-weight: 500;
line-height: 1.5;
border: 2px solid transparent;
border-radius: $border-radius;
cursor: pointer;
transition: all 0.2s ease;
&--primary {
background: $brand-primary;
color: $white;
border-color: $brand-primary;
&:hover {
background: $brand-secondary;
border-color: $brand-secondary;
}
}
&--outline {
background: transparent;
color: $brand-primary;
border-color: $brand-primary;
&:hover {
background: $brand-primary;
color: $white;
}
}
&--danger {
background: $danger;
color: $white;
border-color: $danger;
}
&--sm {
padding: spacing(1) spacing(2);
font-size: $font-size-sm;
}
&--lg {
padding: spacing(3) spacing(6);
font-size: $font-size-lg;
}
&--block {
display: flex;
width: 100%;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
Step 4: Component Cards
// scss/components/_cards.scss
.card {
background: $white;
border: 1px solid $gray-200;
border-radius: $border-radius-lg;
overflow: hidden;
transition: box-shadow 0.2s ease;
&:hover {
box-shadow: $shadow-md;
}
&__image {
width: 100%;
height: auto;
display: block;
}
&__body {
padding: spacing(4);
}
&__title {
font-size: $font-size-xl;
font-weight: 600;
margin-bottom: spacing(2);
color: $gray-900;
}
&__text {
color: $gray-600;
line-height: 1.6;
}
&__footer {
padding: spacing(3) spacing(4);
border-top: 1px solid $gray-200;
background: $gray-50;
}
}
Step 5: Main Entry Point
// scss/main.scss
// 1. Abstracts
@import "abstracts/variables";
@import "abstracts/mixins";
@import "abstracts/functions";
// 2. Base
@import "base/reset";
@import "base/typography";
// 3. Layout
@import "layout/grid";
@import "layout/header";
@import "layout/footer";
// 4. Components
@import "components/buttons";
@import "components/cards";
@import "components/forms";
@import "components/badges";
@import "components/alerts";
// 5. Themes (load last to allow overrides)
@import "themes/light";
@import "themes/dark";
Step 6: Build Script
{
"scripts": {
"build": "sass scss/main.scss dist/styles.css --style=expanded",
"build:min": "sass scss/main.scss dist/styles.min.css --style=compressed",
"watch": "sass --watch scss/main.scss dist/styles.css --style=expanded"
}
}
Common Mistakes
1. Skipping the Reset
Without a CSS reset, browser defaults (margins, paddings) cause inconsistent spacing. Always include base/reset styles.
2. Not Using Map Functions for Configuration
Hard-coded values in components defeat the purpose of a design token system. Always reference variables and map values.
3. Theme Overrides Not Loading Last
Themes must import after components to override styles correctly.
4. Forgetting Vendor Prefixes
Sass does not add vendor prefixes automatically. Use Autoprefixer in your build pipeline.
5. Not Compiling Before Deploy
Always compile Sass to CSS during the build step. Do not serve SCSS files directly.
Practice Questions
What is the correct import order in main.scss? Abstracts, vendors, base, layout, components, pages, themes.
Why should themes import last? Themes override component colors. They must load after components to take effect.
How do you access a spacing value from the $spacing map?
spacing(4)ormap-get($spacing, 4).What is the @include respond-to(md) mixin used for? It generates a media query at the medium breakpoint (768px).
How do you generate a compressed/minified CSS file? Use
--style=compressedflag:sass input.scss output.min.css --style=compressed.
Challenge
Extend the component library with an alerts component that supports 4 variants (success, danger, warning, info). Use the theme variables so each variant changes color based on the active theme.
FAQ
Mini Project
Complete the component library by adding a forms component (inputs, selects, textareas, checkboxes), a badges component (with color variants), and a responsive navigation header. Verify the build produces clean CSS.
What's Next
Congratulations on completing Sass! Continue with Tailwind CSS for utility-first styling. Or explore Bootstrap for a component-based framework.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro