Skip to content

Sass Project — Build a Complete Themed Stylesheet

DodaTech Updated 2026-06-28 5 min read

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

  1. What is the correct import order in main.scss? Abstracts, vendors, base, layout, components, pages, themes.

  2. Why should themes import last? Themes override component colors. They must load after components to take effect.

  3. How do you access a spacing value from the $spacing map? spacing(4) or map-get($spacing, 4).

  4. What is the @include respond-to(md) mixin used for? It generates a media query at the medium breakpoint (768px).

  5. How do you generate a compressed/minified CSS file? Use --style=compressed flag: 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

Can I use this project as a template for real projects?

Yes. Copy the structure and modify the design tokens for your brand. The architecture scales to any project size.

Should I use @import or @use?

For new projects, use @use and @forward. This project uses @import for simplicity, but modern Sass recommends @use.

How do I add more components?

Create a new partial in components/, write the styles, and add an @import in main.scss.

Can I use CSS custom properties with this project?

Yes. Use Sass variables for design tokens and assign them to CSS custom properties for runtime theming.

How do I integrate this with a build tool like Vite?

Install sass as a dev dependency. Import main.scss in your JavaScript entry point.

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