SASS/SCSS

Real-World Project: Building a Complete Website Stylesheet

30 min Lesson 29 of 30

Project Overview: Portfolio Website Stylesheet

In this comprehensive, hands-on lesson, we'll build a complete, production-ready stylesheet for a portfolio/landing page from scratch using SASS. This project will consolidate everything you've learned throughout this course, applying best practices, proper architecture, responsive design, and modern CSS techniques. By the end, you'll have a fully functional, maintainable stylesheet that you can use as a template for future projects.

Note: This is an intensive lesson that simulates a real-world development workflow. Take your time with each section and make sure you understand the decisions being made.

Project Requirements

Our portfolio website will include:

  • Responsive navigation header with logo and menu
  • Hero section with call-to-action
  • About section with image and text
  • Skills/services cards section
  • Portfolio/projects gallery grid
  • Contact form section
  • Footer with social links
  • Dark mode toggle support
  • Responsive design (mobile-first)
  • Smooth animations and transitions

Step 1: Setting Up the 7-1 Folder Structure

We'll use the industry-standard 7-1 architecture pattern for organizing our SASS files.

Project Structure

portfolio/
├── scss/
│   ├── abstracts/
│   │   ├── _variables.scss
│   │   ├── _mixins.scss
│   │   └── _functions.scss
│   ├── base/
│   │   ├── _reset.scss
│   │   ├── _typography.scss
│   │   └── _animations.scss
│   ├── components/
│   │   ├── _buttons.scss
│   │   ├── _card.scss
│   │   ├── _form.scss
│   │   └── _social-links.scss
│   ├── layout/
│   │   ├── _header.scss
│   │   ├── _footer.scss
│   │   ├── _navigation.scss
│   │   ├── _grid.scss
│   │   └── _section.scss
│   ├── pages/
│   │   └── _home.scss
│   ├── themes/
│   │   └── _dark-mode.scss
│   ├── vendors/
│   │   └── (third-party styles if needed)
│   └── main.scss
├── dist/
│   └── css/
│       └── style.css
└── package.json

Step 2: Creating the Variables File

Our variables file will define all design tokens: colors, typography, spacing, breakpoints, and transitions.

abstracts/_variables.scss

// ========================================
// Color System
// ========================================

// Primary brand colors
$color-primary: #6366f1;
$color-primary-light: #818cf8;
$color-primary-dark: #4f46e5;

// Neutral colors
$color-white: #ffffff;
$color-black: #000000;
$color-gray-100: #f3f4f6;
$color-gray-200: #e5e7eb;
$color-gray-300: #d1d5db;
$color-gray-400: #9ca3af;
$color-gray-500: #6b7280;
$color-gray-600: #4b5563;
$color-gray-700: #374151;
$color-gray-800: #1f2937;
$color-gray-900: #111827;

// Semantic colors
$color-success: #10b981;
$color-warning: #f59e0b;
$color-error: #ef4444;
$color-info: #3b82f6;

// Background colors
$bg-light: $color-white;
$bg-light-alt: $color-gray-100;
$bg-dark: $color-gray-900;
$bg-dark-alt: $color-gray-800;

// Text colors
$text-light: $color-gray-900;
$text-light-secondary: $color-gray-600;
$text-dark: $color-gray-100;
$text-dark-secondary: $color-gray-400;

// ========================================
// Typography
// ========================================

// Font families
$font-primary: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
$font-heading: 'Poppins', $font-primary;
$font-mono: 'Fira Code', 'Courier New', monospace;

// Font sizes
$font-size-xs: 0.75rem;      // 12px
$font-size-sm: 0.875rem;     // 14px
$font-size-base: 1rem;       // 16px
$font-size-lg: 1.125rem;     // 18px
$font-size-xl: 1.25rem;      // 20px
$font-size-2xl: 1.5rem;      // 24px
$font-size-3xl: 1.875rem;    // 30px
$font-size-4xl: 2.25rem;     // 36px
$font-size-5xl: 3rem;        // 48px
$font-size-6xl: 3.75rem;     // 60px

// Font weights
$font-weight-normal: 400;
$font-weight-medium: 500;
$font-weight-semibold: 600;
$font-weight-bold: 700;

// Line heights
$line-height-tight: 1.25;
$line-height-normal: 1.5;
$line-height-relaxed: 1.75;

// ========================================
// Spacing Scale
// ========================================

$spacing-0: 0;
$spacing-1: 0.25rem;   // 4px
$spacing-2: 0.5rem;    // 8px
$spacing-3: 0.75rem;   // 12px
$spacing-4: 1rem;      // 16px
$spacing-5: 1.25rem;   // 20px
$spacing-6: 1.5rem;    // 24px
$spacing-8: 2rem;      // 32px
$spacing-10: 2.5rem;   // 40px
$spacing-12: 3rem;     // 48px
$spacing-16: 4rem;     // 64px
$spacing-20: 5rem;     // 80px
$spacing-24: 6rem;     // 96px

// ========================================
// Breakpoints
// ========================================

$breakpoint-sm: 640px;
$breakpoint-md: 768px;
$breakpoint-lg: 1024px;
$breakpoint-xl: 1280px;
$breakpoint-2xl: 1536px;

// Breakpoints map for loop usage
$breakpoints: (
  'sm': $breakpoint-sm,
  'md': $breakpoint-md,
  'lg': $breakpoint-lg,
  'xl': $breakpoint-xl,
  '2xl': $breakpoint-2xl
);

// ========================================
// Layout
// ========================================

$container-max-width: 1280px;
$container-padding: $spacing-6;

$header-height: 70px;
$header-height-mobile: 60px;

// ========================================
// Border & Radius
// ========================================

$border-width: 1px;
$border-color-light: $color-gray-200;
$border-color-dark: $color-gray-700;

$border-radius-sm: 0.25rem;   // 4px
$border-radius-base: 0.5rem;  // 8px
$border-radius-lg: 0.75rem;   // 12px
$border-radius-xl: 1rem;      // 16px
$border-radius-full: 9999px;

// ========================================
// Shadows
// ========================================

$shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
$shadow-base: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
$shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
$shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
$shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);

// ========================================
// Transitions
// ========================================

$transition-fast: 150ms ease-in-out;
$transition-base: 250ms ease-in-out;
$transition-slow: 350ms ease-in-out;

// ========================================
// Z-index Scale
// ========================================

$z-index-dropdown: 1000;
$z-index-sticky: 1020;
$z-index-fixed: 1030;
$z-index-modal-backdrop: 1040;
$z-index-modal: 1050;
$z-index-popover: 1060;
$z-index-tooltip: 1070;

Step 3: Building Reusable Mixins

Create mixins for responsive design, flexbox layouts, and common patterns.

abstracts/_mixins.scss

@use 'variables' as *;

// ========================================
// Responsive Breakpoints
// ========================================

@mixin respond-to($breakpoint) {
  @if map-has-key($breakpoints, $breakpoint) {
    @media (min-width: map-get($breakpoints, $breakpoint)) {
      @content;
    }
  } @else {
    @warn "Invalid breakpoint: #{$breakpoint}";
  }
}

// Custom breakpoint
@mixin custom-breakpoint($min-width) {
  @media (min-width: $min-width) {
    @content;
  }
}

// ========================================
// Flexbox Utilities
// ========================================

@mixin flex-center {
  display: flex;
  justify-content: center;
  align-items: center;
}

@mixin flex-between {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

@mixin flex-column {
  display: flex;
  flex-direction: column;
}

// ========================================
// Grid Utilities
// ========================================

@mixin grid($columns: 3, $gap: $spacing-6) {
  display: grid;
  grid-template-columns: repeat($columns, 1fr);
  gap: $gap;
}

@mixin grid-responsive($mobile: 1, $tablet: 2, $desktop: 3, $gap: $spacing-6) {
  display: grid;
  grid-template-columns: repeat($mobile, 1fr);
  gap: $gap;

  @include respond-to('md') {
    grid-template-columns: repeat($tablet, 1fr);
  }

  @include respond-to('lg') {
    grid-template-columns: repeat($desktop, 1fr);
  }
}

// ========================================
// Positioning
// ========================================

@mixin absolute-center {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

@mixin absolute-cover {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

// ========================================
// Text Utilities
// ========================================

@mixin text-truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@mixin line-clamp($lines: 2) {
  display: -webkit-box;
  -webkit-line-clamp: $lines;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

// ========================================
// Visual Effects
// ========================================

@mixin card-shadow {
  box-shadow: $shadow-base;
  transition: box-shadow $transition-base;

  &:hover {
    box-shadow: $shadow-lg;
  }
}

@mixin focus-ring($color: $color-primary) {
  outline: 2px solid transparent;
  outline-offset: 2px;

  &:focus-visible {
    outline-color: $color;
  }
}

Step 4: Base Styles

Set up the foundation with reset, typography, and global styles.

base/_reset.scss

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

body {
  min-height: 100vh;
  line-height: 1.5;
}

img,
picture,
video,
canvas,
svg {
  display: block;
  max-width: 100%;
}

input,
button,
textarea,
select {
  font: inherit;
}

p,
h1,
h2,
h3,
h4,
h5,
h6 {
  overflow-wrap: break-word;
}

ul,
ol {
  list-style: none;
}

a {
  text-decoration: none;
  color: inherit;
}

base/_typography.scss

@use '../abstracts' as *;

body {
  font-family: $font-primary;
  font-size: $font-size-base;
  font-weight: $font-weight-normal;
  line-height: $line-height-normal;
  color: $text-light;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: $font-heading;
  font-weight: $font-weight-bold;
  line-height: $line-height-tight;
  margin-bottom: $spacing-4;
}

h1 {
  font-size: $font-size-4xl;

  @include respond-to('md') {
    font-size: $font-size-5xl;
  }

  @include respond-to('lg') {
    font-size: $font-size-6xl;
  }
}

h2 {
  font-size: $font-size-3xl;

  @include respond-to('md') {
    font-size: $font-size-4xl;
  }
}

h3 {
  font-size: $font-size-2xl;

  @include respond-to('md') {
    font-size: $font-size-3xl;
  }
}

h4 {
  font-size: $font-size-xl;
}

h5 {
  font-size: $font-size-lg;
}

h6 {
  font-size: $font-size-base;
}

p {
  margin-bottom: $spacing-4;

  &:last-child {
    margin-bottom: 0;
  }
}

.lead {
  font-size: $font-size-lg;
  color: $text-light-secondary;

  @include respond-to('md') {
    font-size: $font-size-xl;
  }
}

Step 5: Building Components

Create reusable component styles for buttons, cards, and forms.

components/_buttons.scss

@use '../abstracts' as *;

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: $spacing-2;
  padding: $spacing-3 $spacing-6;
  font-weight: $font-weight-medium;
  border-radius: $border-radius-base;
  border: $border-width solid transparent;
  cursor: pointer;
  transition: all $transition-base;
  @include focus-ring;

  &:disabled {
    opacity: 0.6;
    cursor: not-allowed;
  }
}

.btn-primary {
  background-color: $color-primary;
  color: $color-white;

  &:hover:not(:disabled) {
    background-color: $color-primary-dark;
    transform: translateY(-2px);
    box-shadow: $shadow-md;
  }

  &:active:not(:disabled) {
    transform: translateY(0);
  }
}

.btn-secondary {
  background-color: transparent;
  color: $color-primary;
  border-color: $color-primary;

  &:hover:not(:disabled) {
    background-color: $color-primary;
    color: $color-white;
  }
}

.btn-ghost {
  background-color: transparent;
  color: $text-light;

  &:hover:not(:disabled) {
    background-color: $color-gray-100;
  }
}

.btn-lg {
  padding: $spacing-4 $spacing-8;
  font-size: $font-size-lg;
}

.btn-sm {
  padding: $spacing-2 $spacing-4;
  font-size: $font-size-sm;
}

components/_card.scss

@use '../abstracts' as *;

.card {
  background-color: $bg-light;
  border-radius: $border-radius-lg;
  padding: $spacing-6;
  @include card-shadow;

  &__image {
    width: 100%;
    height: 200px;
    object-fit: cover;
    border-radius: $border-radius-base;
    margin-bottom: $spacing-4;
  }

  &__title {
    font-size: $font-size-xl;
    margin-bottom: $spacing-3;
  }

  &__text {
    color: $text-light-secondary;
    margin-bottom: $spacing-4;
  }

  &__footer {
    @include flex-between;
    padding-top: $spacing-4;
    border-top: $border-width solid $border-color-light;
  }
}

Step 6: Layout Components

layout/_header.scss

@use '../abstracts' as *;

.header {
  position: sticky;
  top: 0;
  z-index: $z-index-sticky;
  background-color: $bg-light;
  border-bottom: $border-width solid $border-color-light;
  backdrop-filter: blur(10px);
  background-color: rgba(255, 255, 255, 0.9);

  &__container {
    max-width: $container-max-width;
    margin: 0 auto;
    padding: 0 $container-padding;
    @include flex-between;
    height: $header-height-mobile;

    @include respond-to('md') {
      height: $header-height;
    }
  }

  &__logo {
    font-size: $font-size-xl;
    font-weight: $font-weight-bold;
    color: $color-primary;
  }

  &__nav {
    display: none;

    @include respond-to('md') {
      display: flex;
      gap: $spacing-8;
    }
  }

  &__nav-link {
    font-weight: $font-weight-medium;
    transition: color $transition-base;

    &:hover {
      color: $color-primary;
    }
  }
}

Step 7: Responsive Design Implementation

Apply mobile-first responsive design throughout the project.

pages/_home.scss

@use '../abstracts' as *;

.hero {
  padding: $spacing-16 $container-padding;
  text-align: center;

  @include respond-to('lg') {
    padding: $spacing-24 $container-padding;
  }

  &__title {
    margin-bottom: $spacing-6;
    background: linear-gradient(135deg, $color-primary 0%, $color-primary-light 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
  }

  &__buttons {
    display: flex;
    flex-direction: column;
    gap: $spacing-4;
    max-width: 400px;
    margin: 0 auto;

    @include respond-to('sm') {
      flex-direction: row;
      justify-content: center;
    }
  }
}

.skills {
  padding: $spacing-20 $container-padding;
  background-color: $bg-light-alt;

  &__grid {
    max-width: $container-max-width;
    margin: 0 auto;
    @include grid-responsive($mobile: 1, $tablet: 2, $desktop: 3);
  }
}

Step 8: Dark Mode Support

themes/_dark-mode.scss

@use '../abstracts' as *;

[data-theme="dark"] {
  color-scheme: dark;

  body {
    background-color: $bg-dark;
    color: $text-dark;
  }

  .header {
    background-color: rgba(17, 24, 39, 0.9);
    border-bottom-color: $border-color-dark;
  }

  .card {
    background-color: $bg-dark-alt;
    color: $text-dark;
  }

  .btn-ghost {
    color: $text-dark;

    &:hover:not(:disabled) {
      background-color: $color-gray-800;
    }
  }

  .skills {
    background-color: $bg-dark-alt;
  }
}

Step 9: Final Compilation and Optimization

main.scss - Main Entry File

// Abstracts
@use 'abstracts/variables';
@use 'abstracts/mixins';
@use 'abstracts/functions';

// Base
@use 'base/reset';
@use 'base/typography';
@use 'base/animations';

// Layout
@use 'layout/header';
@use 'layout/footer';
@use 'layout/navigation';
@use 'layout/grid';
@use 'layout/section';

// Components
@use 'components/buttons';
@use 'components/card';
@use 'components/form';
@use 'components/social-links';

// Pages
@use 'pages/home';

// Themes
@use 'themes/dark-mode';

package.json - Build Scripts

{
  "scripts": {
    "dev": "sass --watch scss/main.scss:dist/css/style.css --source-map",
    "build": "sass scss/main.scss:dist/css/style.css --style=compressed --no-source-map",
    "build:dev": "sass scss/main.scss:dist/css/style.css --style=expanded --source-map"
  }
}

Exercise 1: Complete the Project

Build the complete portfolio stylesheet:

  1. Set up the 7-1 folder structure
  2. Create all the files shown in this lesson
  3. Add a footer component with social links
  4. Create a contact form component
  5. Implement smooth scroll animations
  6. Test dark mode toggle
  7. Verify responsive design on all breakpoints
  8. Build for production and check the output file size

Exercise 2: Extend the Project

Add these additional features:

  1. Create a blog card component for a blog section
  2. Add a testimonials carousel section
  3. Implement a hamburger menu for mobile navigation
  4. Add hover effects and transitions to all interactive elements
  5. Create utility classes for spacing, colors, and typography

Exercise 3: Optimize and Audit

Optimize your stylesheet:

  1. Run the build command and check the CSS file size
  2. Use a CSS analyzer to identify unused styles
  3. Remove any redundant code or unused variables
  4. Set up PostCSS with autoprefixer and cssnano
  5. Test the final build in multiple browsers
  6. Document your color system and component API