mindrally

scss-best-practices

3
0
# Install this skill:
npx skills add Mindrally/skills --skill "scss-best-practices"

Install specific skill from multi-skill repository

# Description

SCSS/Sassy CSS best practices and coding guidelines for maintainable, scalable stylesheets

# SKILL.md


name: scss-best-practices
description: SCSS/Sassy CSS best practices and coding guidelines for maintainable, scalable stylesheets


SCSS Best Practices

You are an expert in SCSS (Sassy CSS), CSS architecture, and maintainable stylesheet development.

Key Principles

  • Write modular, reusable SCSS that scales with project complexity
  • Follow the DRY (Don't Repeat Yourself) principle using variables, mixins, and functions
  • Maintain clear separation between structure, skin, and state styles
  • Prioritize readability and maintainability over clever abstractions

File Organization

Architecture Pattern (7-1 Pattern)

scss/
β”œβ”€β”€ abstracts/
β”‚   β”œβ”€β”€ _variables.scss    # Global variables
β”‚   β”œβ”€β”€ _functions.scss    # SCSS functions
β”‚   β”œβ”€β”€ _mixins.scss       # Reusable mixins
β”‚   └── _placeholders.scss # Extendable placeholders
β”œβ”€β”€ base/
β”‚   β”œβ”€β”€ _reset.scss        # CSS reset/normalize
β”‚   β”œβ”€β”€ _typography.scss   # Typography rules
β”‚   └── _base.scss         # Base element styles
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ _buttons.scss      # Button components
β”‚   β”œβ”€β”€ _cards.scss        # Card components
β”‚   └── _forms.scss        # Form components
β”œβ”€β”€ layout/
β”‚   β”œβ”€β”€ _header.scss       # Header layout
β”‚   β”œβ”€β”€ _footer.scss       # Footer layout
β”‚   β”œβ”€β”€ _grid.scss         # Grid system
β”‚   └── _navigation.scss   # Navigation layout
β”œβ”€β”€ pages/
β”‚   β”œβ”€β”€ _home.scss         # Home page specific
β”‚   └── _contact.scss      # Contact page specific
β”œβ”€β”€ themes/
β”‚   └── _default.scss      # Default theme
β”œβ”€β”€ vendors/
β”‚   └── _bootstrap.scss    # Third-party overrides
└── main.scss              # Main manifest file

Import Order

// main.scss
@use 'abstracts/variables';
@use 'abstracts/functions';
@use 'abstracts/mixins';
@use 'abstracts/placeholders';

@use 'vendors/normalize';

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

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

@use 'components/buttons';
@use 'components/cards';
@use 'components/forms';

@use 'pages/home';

@use 'themes/default';

Variables

Naming Convention

// Use semantic, descriptive names
// Format: $category-property-variant

// Colors
$color-primary: #3498db;
$color-primary-light: lighten($color-primary, 15%);
$color-primary-dark: darken($color-primary, 15%);
$color-secondary: #2ecc71;
$color-text: #333333;
$color-text-muted: #666666;
$color-background: #ffffff;
$color-border: #e0e0e0;
$color-error: #e74c3c;
$color-success: #27ae60;
$color-warning: #f39c12;

// Typography
$font-family-base: 'Helvetica Neue', Arial, sans-serif;
$font-family-heading: 'Georgia', serif;
$font-size-base: 1rem;
$font-size-small: 0.875rem;
$font-size-large: 1.25rem;
$font-weight-normal: 400;
$font-weight-bold: 700;
$line-height-base: 1.5;

// Spacing (use consistent scale)
$spacing-unit: 8px;
$spacing-xs: $spacing-unit * 0.5;  // 4px
$spacing-sm: $spacing-unit;        // 8px
$spacing-md: $spacing-unit * 2;    // 16px
$spacing-lg: $spacing-unit * 3;    // 24px
$spacing-xl: $spacing-unit * 4;    // 32px
$spacing-xxl: $spacing-unit * 6;   // 48px

// Breakpoints
$breakpoint-sm: 576px;
$breakpoint-md: 768px;
$breakpoint-lg: 992px;
$breakpoint-xl: 1200px;
$breakpoint-xxl: 1400px;

// 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;

// Transitions
$transition-base: 0.3s ease;
$transition-fast: 0.15s ease;
$transition-slow: 0.5s ease;

// Border radius
$border-radius-sm: 2px;
$border-radius-md: 4px;
$border-radius-lg: 8px;
$border-radius-pill: 50px;
$border-radius-circle: 50%;

// Shadows
$shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
$shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
$shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
$shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);
// Use maps for grouped values
$colors: (
  'primary': #3498db,
  'secondary': #2ecc71,
  'danger': #e74c3c,
  'warning': #f39c12,
  'info': #17a2b8,
  'success': #27ae60
);

$breakpoints: (
  'sm': 576px,
  'md': 768px,
  'lg': 992px,
  'xl': 1200px,
  'xxl': 1400px
);

// Access with map-get
.element {
  color: map-get($colors, 'primary');
}

Mixins

Responsive Breakpoints

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

// Usage
.element {
  width: 100%;

  @include respond-to('md') {
    width: 50%;
  }

  @include respond-to('lg') {
    width: 33.333%;
  }
}

Flexbox Utilities

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

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

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

Typography

@mixin font-size($size, $line-height: null) {
  font-size: $size;
  @if $line-height {
    line-height: $line-height;
  }
}

@mixin truncate($lines: 1) {
  @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;
  }
}

Accessibility

@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;
}

@mixin focus-visible {
  &:focus-visible {
    outline: 2px solid $color-primary;
    outline-offset: 2px;
  }
}

BEM Naming Convention

Structure

// Block: Standalone component
// Element: Part of block (block__element)
// Modifier: Variant (block--modifier or block__element--modifier)

.card {
  // Block styles
  background: $color-background;
  border-radius: $border-radius-md;
  box-shadow: $shadow-md;

  // Element
  &__header {
    padding: $spacing-md;
    border-bottom: 1px solid $color-border;
  }

  &__title {
    margin: 0;
    font-size: $font-size-large;
    font-weight: $font-weight-bold;
  }

  &__body {
    padding: $spacing-md;
  }

  &__footer {
    padding: $spacing-md;
    border-top: 1px solid $color-border;
  }

  // Modifier
  &--featured {
    border: 2px solid $color-primary;
  }

  &--compact {
    .card__header,
    .card__body,
    .card__footer {
      padding: $spacing-sm;
    }
  }
}

Nesting Rules

Maximum Nesting Depth

// BAD: Too deep nesting
.nav {
  .nav-list {
    .nav-item {
      .nav-link {
        .nav-icon {
          // 5 levels deep - avoid this
        }
      }
    }
  }
}

// GOOD: Keep nesting to 3 levels maximum
.nav {
  // Level 1
}

.nav__list {
  // Level 1
}

.nav__item {
  // Level 1
}

.nav__link {
  color: $color-text;

  &:hover,
  &:focus {
    // Level 2 - acceptable for states
    color: $color-primary;
  }

  &--active {
    // Level 2 - acceptable for modifiers
    color: $color-primary;
    font-weight: $font-weight-bold;
  }
}

Acceptable Nesting

.component {
  // Direct child pseudo-elements
  &::before,
  &::after {
    content: '';
  }

  // State modifiers
  &:hover,
  &:focus,
  &:active {
    // State styles
  }

  // BEM modifiers
  &--variant {
    // Modifier styles
  }

  // Media queries
  @include respond-to('md') {
    // Responsive styles
  }
}

Functions

Color Functions

@function tint($color, $percentage) {
  @return mix(white, $color, $percentage);
}

@function shade($color, $percentage) {
  @return mix(black, $color, $percentage);
}

// Usage
.element {
  background: tint($color-primary, 20%);
  border-color: shade($color-primary, 10%);
}

Unit Conversion

@function px-to-rem($px, $base: 16) {
  @return ($px / $base) * 1rem;
}

@function rem-to-px($rem, $base: 16) {
  @return ($rem / 1rem) * $base * 1px;
}

// Usage
.element {
  font-size: px-to-rem(18); // 1.125rem
  padding: px-to-rem(24);   // 1.5rem
}

Spacing Function

@function spacing($multiplier) {
  @return $spacing-unit * $multiplier;
}

// Usage
.element {
  margin-bottom: spacing(2); // 16px
  padding: spacing(3);       // 24px
}

Extend and Placeholders

Use Placeholders Over Classes

// Define placeholder
%button-base {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: $spacing-sm $spacing-md;
  border: none;
  border-radius: $border-radius-md;
  font-family: inherit;
  font-size: $font-size-base;
  font-weight: $font-weight-bold;
  text-decoration: none;
  cursor: pointer;
  transition: all $transition-base;

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

// Extend placeholder
.btn-primary {
  @extend %button-base;
  background: $color-primary;
  color: white;

  &:hover:not(:disabled) {
    background: darken($color-primary, 10%);
  }
}

.btn-secondary {
  @extend %button-base;
  background: transparent;
  color: $color-primary;
  border: 2px solid $color-primary;

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

Loops and Iteration

Generate Utility Classes

// Spacing utilities
$spacing-directions: (
  '': '',
  't': '-top',
  'r': '-right',
  'b': '-bottom',
  'l': '-left',
  'x': '-inline',
  'y': '-block'
);

@each $abbr, $direction in $spacing-directions {
  @for $i from 0 through 8 {
    .m#{$abbr}-#{$i} {
      margin#{$direction}: spacing($i);
    }
    .p#{$abbr}-#{$i} {
      padding#{$direction}: spacing($i);
    }
  }
}

// Color utilities
@each $name, $color in $colors {
  .text-#{$name} {
    color: $color;
  }
  .bg-#{$name} {
    background-color: $color;
  }
  .border-#{$name} {
    border-color: $color;
  }
}

Performance Best Practices

  • Avoid overly specific selectors; aim for specificity of 0-1-0 (single class)
  • Never use !important except for utility classes
  • Minimize use of @extend across files (can cause bloat)
  • Use @use and @forward instead of @import (deprecated)
  • Compile with source maps in development, without in production
  • Use autoprefixer for vendor prefixes instead of manual prefixes

Modern SCSS Features

Module System

// _variables.scss
$primary: #3498db;

// _mixins.scss
@use 'variables' as vars;

@mixin themed-button {
  background: vars.$primary;
}

// main.scss
@use 'mixins';

.button {
  @include mixins.themed-button;
}

Built-in Modules

@use 'sass:math';
@use 'sass:color';
@use 'sass:list';
@use 'sass:map';
@use 'sass:string';

.element {
  width: math.div(100%, 3);
  background: color.adjust($color-primary, $lightness: 10%);
}

Code Style

  • Use 2 spaces for indentation
  • Use single quotes for strings
  • Add a space after colons in declarations
  • Add a space before opening braces
  • Put closing braces on new lines
  • Separate rule sets with blank lines
  • Order properties logically (positioning, box model, typography, visual, misc)
  • Comment complex calculations and non-obvious code

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.