Development Workflow and Tooling

1. Sass Linting with stylelint Configuration

Configuration Plugin/Rule Purpose Example Rule
stylelint-scss SCSS-specific rules Enforce SCSS syntax at-rule-no-unknown
Standard Config stylelint-config-standard-scss Recommended rules Comprehensive base
Order Plugin stylelint-order Property ordering Consistent structure
BEM Plugin stylelint-selector-bem-pattern BEM validation Naming conventions
Custom Rules Project-specific config Team standards Brand compliance

Example: Complete stylelint configuration for SCSS

// .stylelintrc.json
{
  "extends": [
    "stylelint-config-standard-scss",
    "stylelint-config-prettier-scss"
  ],
  "plugins": [
    "stylelint-scss",
    "stylelint-order"
  ],
  "rules": {
    // SCSS-specific rules
    "scss/at-rule-no-unknown": true,
    "scss/at-import-partial-extension": "never",
    "scss/dollar-variable-pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$",
    "scss/percent-placeholder-pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$",
    "scss/at-mixin-pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$",
    "scss/at-function-pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$",
    "scss/selector-no-redundant-nesting-selector": true,
    "scss/no-duplicate-dollar-variables": true,
    "scss/no-duplicate-mixins": true,
    "scss/operator-no-newline-after": true,
    "scss/operator-no-unspaced": true,
    "scss/dimension-no-non-numeric-values": true,
    
    // Property ordering
    "order/properties-order": [
      "position",
      "top",
      "right",
      "bottom",
      "left",
      "z-index",
      "display",
      "flex",
      "flex-direction",
      "justify-content",
      "align-items",
      "width",
      "height",
      "margin",
      "padding",
      "border",
      "background",
      "color",
      "font",
      "text-align",
      "transition",
      "transform"
    ],
    
    // General rules
    "color-hex-length": "short",
    "color-named": "never",
    "declaration-no-important": true,
    "max-nesting-depth": 3,
    "selector-max-id": 0,
    "selector-max-compound-selectors": 4,
    "selector-max-specificity": "0,4,0",
    "selector-class-pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*(__[a-z0-9]+(-[a-z0-9]+)*)?(--[a-z0-9]+(-[a-z0-9]+)*)?$",
    
    // Comments
    "comment-empty-line-before": [
      "always",
      {
        "except": ["first-nested"],
        "ignore": ["stylelint-commands"]
      }
    ],
    
    // Disabled rules
    "no-descending-specificity": null,
    "selector-pseudo-class-no-unknown": [
      true,
      {
        "ignorePseudoClasses": ["global", "local"]
      }
    ]
  },
  "ignoreFiles": [
    "node_modules/**",
    "dist/**",
    "build/**",
    "*.min.css"
  ]
}

// package.json scripts
{
  "scripts": {
    "lint:scss": "stylelint '**/*.scss'",
    "lint:scss:fix": "stylelint '**/*.scss' --fix",
    "lint:scss:report": "stylelint '**/*.scss' --formatter json --output-file stylelint-report.json"
  }
}

Example: Custom stylelint rules for team standards

// .stylelintrc.js - Advanced configuration
module.exports = {
  extends: ['stylelint-config-standard-scss'],
  plugins: [
    'stylelint-scss',
    'stylelint-order',
    'stylelint-selector-bem-pattern'
  ],
  rules: {
    // BEM pattern enforcement
    'plugin/selector-bem-pattern': {
      preset: 'bem',
      componentName: '[A-Z]+',
      componentSelectors: {
        initial: "^\\.{componentName}(?:-[a-z]+)*$",
        combined: "^\\.combined-{componentName}-[a-z]+$"
      },
      utilitySelectors: "^\\.util-[a-z]+$"
    },
    
    // Custom SCSS variable naming
    "scss/dollar-variable-pattern": [
      "^(_)?[a-z][a-z0-9]*(-[a-z0-9]+)*$",
      {
        message: "Expected variable to be kebab-case (use _ prefix for private)",
        ignore: ["global"]
      }
    ],
    
    // Enforce @use over @import
    'scss/at-import-no-partial-leading-underscore': true,
    'scss/load-no-partial-leading-underscore': true,
    'at-rule-disallowed-list': ['import'],
    
    // Mixin and function standards
    'scss/at-mixin-argumentless-call-parentheses': 'always',
    'scss/at-else-closing-brace-newline-after': 'always-last-in-chain',
    'scss/at-else-closing-brace-space-after': 'always-intermediate',
    'scss/at-else-empty-line-before': 'never',
    'scss/at-if-closing-brace-newline-after': 'always-last-in-chain',
    'scss/at-if-closing-brace-space-after': 'always-intermediate',
    
    // Color management
    'color-function-notation': 'modern',
    'color-hex-case': 'lower',
    'scss/dollar-variable-colon-space-after': 'always',
    'scss/dollar-variable-colon-space-before': 'never',
    
    // Property ordering with groups
    'order/properties-order': [
      {
        groupName: 'positioning',
        properties: ['position', 'top', 'right', 'bottom', 'left', 'z-index']
      },
      {
        groupName: 'box-model',
        properties: ['display', 'flex', 'grid', 'width', 'height', 'margin', 'padding']
      },
      {
        groupName: 'typography',
        properties: ['font-family', 'font-size', 'line-height', 'color', 'text-align']
      },
      {
        groupName: 'visual',
        properties: ['background', 'border', 'border-radius', 'box-shadow', 'opacity']
      },
      {
        groupName: 'animation',
        properties: ['transition', 'animation', 'transform']
      }
    ],
    
    // Limit complexity
    'max-nesting-depth': [
      3,
      {
        ignore: ['blockless-at-rules', 'pseudo-classes']
      }
    ],
    
    // Project-specific rules
    'declaration-property-value-disallowed-list': {
      '/^border/': ['none'],
      'transition': ['/all/']
    },
    
    // Disable for SCSS features
    'at-rule-no-unknown': null,
    'function-no-unknown': null
  }
};

// VS Code settings.json integration
{
  "stylelint.enable": true,
  "stylelint.validate": ["css", "scss"],
  "editor.codeActionsOnSave": {
    "source.fixAll.stylelint": true
  }
}

Example: Stylelint ignore patterns and overrides

// .stylelintrc.json with overrides
{
  "extends": "stylelint-config-standard-scss",
  "rules": {
    "max-nesting-depth": 3,
    "selector-max-id": 0
  },
  "overrides": [
    {
      // Stricter rules for components
      "files": ["src/components/**/*.scss"],
      "rules": {
        "max-nesting-depth": 2,
        "selector-class-pattern": "^[a-z][a-z0-9]*(__[a-z0-9]+)?(--[a-z0-9]+)?$"
      }
    },
    {
      // Relaxed rules for utilities
      "files": ["src/utilities/**/*.scss"],
      "rules": {
        "declaration-no-important": null,
        "max-nesting-depth": 1
      }
    },
    {
      // Legacy code (gradual migration)
      "files": ["src/legacy/**/*.scss"],
      "rules": {
        "max-nesting-depth": null,
        "selector-max-id": null,
        "at-rule-disallowed-list": null
      }
    }
  ],
  "ignoreFiles": [
    "**/*.min.css",
    "**/vendor/**",
    "**/node_modules/**"
  ]
}

// .stylelintignore
node_modules/
dist/
build/
*.min.css
vendor/
coverage/

# Inline ignore comments in SCSS
.legacy-component {
  /* stylelint-disable-next-line selector-max-id */
  #legacy-id {
    color: red;
  }
}

.exception {
  /* stylelint-disable declaration-no-important */
  color: blue !important;
  /* stylelint-enable declaration-no-important */
}

2. Prettier Formatting and Code Style

Setting Option Recommendation Reason
printWidth 80-120 characters 100 Readability
singleQuote true/false true Consistency
tabWidth 2/4 spaces 2 Standard SCSS
trailingComma none/es5/all es5 Git diffs
bracketSpacing true/false true Readability

Example: Prettier configuration for SCSS

// .prettierrc.json
{
  "printWidth": 100,
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": true,
  "quoteProps": "as-needed",
  "trailingComma": "es5",
  "bracketSpacing": true,
  "arrowParens": "always",
  "endOfLine": "lf",
  "overrides": [
    {
      "files": "*.scss",
      "options": {
        "singleQuote": false,
        "parser": "scss"
      }
    },
    {
      "files": "*.json",
      "options": {
        "printWidth": 80,
        "tabWidth": 2
      }
    }
  ]
}

// .prettierignore
node_modules/
dist/
build/
coverage/
*.min.css
*.min.js
package-lock.json
yarn.lock
pnpm-lock.yaml

// package.json scripts
{
  "scripts": {
    "format": "prettier --write '**/*.{scss,css,js,jsx,ts,tsx,json,md}'",
    "format:check": "prettier --check '**/*.{scss,css,js,jsx,ts,tsx,json,md}'",
    "format:scss": "prettier --write '**/*.scss'"
  },
  "devDependencies": {
    "prettier": "^3.1.0"
  }
}

Example: EditorConfig for consistent formatting

// .editorconfig - Works with Prettier and most editors
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.{js,jsx,ts,tsx,json}]
indent_style = space
indent_size = 2

[*.{scss,css}]
indent_style = space
indent_size = 2
quote_type = double

[*.md]
max_line_length = off
trim_trailing_whitespace = false

[{package.json,.prettierrc,.stylelintrc}]
indent_style = space
indent_size = 2

[*.yml]
indent_style = space
indent_size = 2

// Example SCSS before Prettier
.button{background-color:#0066cc;color:white;padding:10px 20px;&:hover{background-color:darken(#0066cc,10%);}}

// After Prettier
.button {
  background-color: #0066cc;
  color: white;
  padding: 10px 20px;

  &:hover {
    background-color: darken(#0066cc, 10%);
  }
}

Example: Integration with stylelint and Prettier

// Combine stylelint + Prettier for best results

// 1. Install dependencies
npm install -D prettier stylelint stylelint-config-prettier-scss

// 2. .stylelintrc.json
{
  "extends": [
    "stylelint-config-standard-scss",
    "stylelint-config-prettier-scss"  // Disables conflicting rules
  ],
  "rules": {
    // Your custom rules
  }
}

// 3. package.json scripts
{
  "scripts": {
    "lint": "npm run lint:scss && npm run format:check",
    "lint:scss": "stylelint '**/*.scss'",
    "lint:fix": "npm run lint:scss:fix && npm run format",
    "lint:scss:fix": "stylelint '**/*.scss' --fix",
    "format": "prettier --write '**/*.{scss,css,js,json}'",
    "format:check": "prettier --check '**/*.{scss,css,js,json}'"
  }
}

// 4. VS Code settings.json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.stylelint": true
  },
  "[scss]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnSave": true
  },
  "stylelint.enable": true,
  "stylelint.validate": ["css", "scss"],
  "prettier.requireConfig": true
}

// 5. Workflow
// 1. Stylelint fixes structural/logical issues
// 2. Prettier formats the code aesthetically
// 3. Both run automatically on save in VS Code
// 4. Pre-commit hooks enforce on all commits

3. VS Code Extensions and IntelliSense

Extension ID Features Priority
SCSS IntelliSense mrmlnc.vscode-scss Autocomplete, go-to-def Essential
Stylelint stylelint.vscode-stylelint Linting errors/warnings Essential
Prettier esbenp.prettier-vscode Code formatting Essential
Color Highlight naumovs.color-highlight Visual color preview Recommended
CSS Peek pranaygp.vscode-css-peek Class definition lookup Recommended

Example: VS Code workspace settings for SCSS

// .vscode/settings.json
{
  // Editor
  "editor.tabSize": 2,
  "editor.insertSpaces": true,
  "editor.formatOnSave": true,
  "editor.formatOnPaste": false,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.stylelint": true,
    "source.organizeImports": false
  },
  
  // SCSS specific
  "[scss]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.suggest.insertMode": "replace",
    "editor.quickSuggestions": {
      "other": true,
      "comments": false,
      "strings": true
    }
  },
  
  // Stylelint
  "stylelint.enable": true,
  "stylelint.validate": ["css", "scss", "sass"],
  "stylelint.snippet": ["css", "scss"],
  "css.validate": false,  // Disable default CSS validation
  "scss.validate": false, // Let stylelint handle it
  
  // Prettier
  "prettier.requireConfig": true,
  "prettier.useEditorConfig": true,
  
  // SCSS IntelliSense
  "scss.scannerDepth": 30,
  "scss.scannerExclude": [
    "**/.git",
    "**/node_modules",
    "**/bower_components"
  ],
  "scss.implicitlyLabel": "(implicitly)",
  
  // Color decorators
  "editor.colorDecorators": true,
  
  // Emmet
  "emmet.includeLanguages": {
    "scss": "css"
  },
  "emmet.syntaxProfiles": {
    "scss": "css"
  },
  
  // Files
  "files.associations": {
    "*.scss": "scss"
  },
  "files.exclude": {
    "**/.git": true,
    "**/node_modules": true,
    "**/.DS_Store": true,
    "**/dist": true,
    "**/build": true
  },
  
  // Search
  "search.exclude": {
    "**/node_modules": true,
    "**/dist": true,
    "**/*.min.css": true
  }
}
// .vscode/extensions.json
{
  "recommendations": [
    // Essential for SCSS development
    "stylelint.vscode-stylelint",
    "esbenp.prettier-vscode",
    "mrmlnc.vscode-scss",
    
    // Enhanced developer experience
    "naumovs.color-highlight",
    "pranaygp.vscode-css-peek",
    "csstools.postcss",
    
    // General productivity
    "editorconfig.editorconfig",
    "usernamehw.errorlens",
    "christian-kohler.path-intellisense",
    
    // Git integration
    "eamodio.gitlens",
    "mhutchie.git-graph"
  ],
  "unwantedRecommendations": [
    "hookyqr.beautify",  // Use Prettier instead
    "HookyQR.beautify"
  ]
}

// .vscode/tasks.json - Build tasks
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Watch SCSS",
      "type": "shell",
      "command": "npm run watch:scss",
      "group": {
        "kind": "build",
        "isDefault": true
      },
      "isBackground": true,
      "problemMatcher": {
        "owner": "scss",
        "fileLocation": ["relative", "${workspaceFolder}"],
        "pattern": {
          "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
          "file": 1,
          "line": 2,
          "column": 3,
          "severity": 4,
          "message": 5
        },
        "background": {
          "activeOnStart": true,
          "beginsPattern": "^Compiling",
          "endsPattern": "^Compiled"
        }
      }
    },
    {
      "label": "Lint SCSS",
      "type": "shell",
      "command": "npm run lint:scss",
      "group": "test",
      "presentation": {
        "reveal": "always",
        "panel": "new"
      }
    },
    {
      "label": "Format SCSS",
      "type": "shell",
      "command": "npm run format:scss",
      "group": "none"
    }
  ]
}

Example: Custom VS Code snippets for SCSS

// .vscode/scss.code-snippets
{
  "SCSS Mixin": {
    "prefix": "mixin",
    "body": [
      "@mixin ${1:name}($2) {",
      "  $3",
      "}"
    ],
    "description": "Create a SCSS mixin"
  },
  
  "SCSS Function": {
    "prefix": "function",
    "body": [
      "@function ${1:name}($2) {",
      "  @return $3;",
      "}"
    ],
    "description": "Create a SCSS function"
  },
  
  "SCSS Use Module": {
    "prefix": "use",
    "body": "@use '${1:module}' as ${2:alias};",
    "description": "Import SCSS module with @use"
  },
  
  "SCSS Forward Module": {
    "prefix": "forward",
    "body": "@forward '${1:module}';",
    "description": "Forward SCSS module"
  },
  
  "SCSS Media Query": {
    "prefix": "media",
    "body": [
      "@media (min-width: ${1:768px}) {",
      "  $2",
      "}"
    ],
    "description": "Create a media query"
  },
  
  "SCSS For Loop": {
    "prefix": "for",
    "body": [
      "@for \\${1:i} from ${2:1} through ${3:10} {",
      "  $4",
      "}"
    ],
    "description": "Create a @for loop"
  },
  
  "SCSS Each Loop": {
    "prefix": "each",
    "body": [
      "@each \\${1:item} in ${2:list} {",
      "  $3",
      "}"
    ],
    "description": "Create an @each loop"
  },
  
  "BEM Block": {
    "prefix": "bem-block",
    "body": [
      ".${1:block} {",
      "  $2",
      "  ",
      "  &__${3:element} {",
      "    $4",
      "  }",
      "  ",
      "  &--${5:modifier} {",
      "    $6",
      "  }",
      "}"
    ],
    "description": "Create a BEM block structure"
  },
  
  "Responsive Mixin": {
    "prefix": "responsive",
    "body": [
      "@mixin responsive(\\$breakpoint) {",
      "  @if \\$breakpoint == mobile {",
      "    @media (max-width: 767px) { @content; }",
      "  } @else if \\$breakpoint == tablet {",
      "    @media (min-width: 768px) and (max-width: 1023px) { @content; }",
      "  } @else if \\$breakpoint == desktop {",
      "    @media (min-width: 1024px) { @content; }",
      "  }",
      "}"
    ],
    "description": "Create a responsive mixin"
  }
}

4. Git Hooks and Pre-commit Validation

Tool Hook Type Action Purpose
Husky pre-commit Run linters Quality gate
lint-staged pre-commit Lint staged files only Fast validation
commitlint commit-msg Validate commit messages Clean history
pre-push pre-push Run full test suite Prevent broken pushes
prepare-commit-msg commit template Auto-add ticket number Traceability

Example: Husky and lint-staged setup

// 1. Install dependencies
npm install -D husky lint-staged

// 2. Initialize husky
npx husky install
npm pkg set scripts.prepare="husky install"

// 3. package.json configuration
{
  "scripts": {
    "prepare": "husky install",
    "lint:scss": "stylelint '**/*.scss'",
    "format": "prettier --write"
  },
  "lint-staged": {
    "*.scss": [
      "stylelint --fix",
      "prettier --write"
    ],
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,md,yml}": [
      "prettier --write"
    ]
  },
  "devDependencies": {
    "husky": "^8.0.0",
    "lint-staged": "^15.0.0",
    "stylelint": "^16.0.0",
    "prettier": "^3.0.0"
  }
}

// 4. Create pre-commit hook
npx husky add .husky/pre-commit "npx lint-staged"

// .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

echo "🔍 Running pre-commit checks..."
npx lint-staged

// 5. Create commit-msg hook (optional)
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'

// .commitlintrc.json
{
  "extends": ["@commitlint/config-conventional"],
  "rules": {
    "type-enum": [
      2,
      "always",
      [
        "feat",
        "fix",
        "docs",
        "style",
        "refactor",
        "perf",
        "test",
        "chore"
      ]
    ],
    "subject-case": [2, "always", "sentence-case"]
  }
}

// Example commit message:
// feat: add new button component styles
// fix: resolve Safari flexbox layout issue
// style: format SCSS files with Prettier

Example: Advanced pre-commit workflow

// .husky/pre-commit - Comprehensive checks
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

echo "🚀 Running pre-commit hooks..."

# 1. Run lint-staged for quick fixes
echo "📝 Linting and formatting staged files..."
npx lint-staged

# 2. Check for TODO/FIXME comments (optional warning)
echo "🔍 Checking for TODO/FIXME..."
git diff --cached --name-only --diff-filter=ACM | \
  xargs grep -n -E "TODO|FIXME" && \
  echo "⚠️  Warning: TODO/FIXME found in staged files" || true

# 3. Run type checking (if TypeScript)
if [ -f "tsconfig.json" ]; then
  echo "🔷 Type checking..."
  npm run type-check
fi

# 4. Check bundle size (optional)
# npm run size-check

echo "✅ Pre-commit checks passed!"

// package.json - Extended lint-staged config
{
  "lint-staged": {
    "*.scss": [
      "stylelint --fix",
      "prettier --write",
      "git add"
    ],
    "*.{css,scss}": [
      // Custom script to check for vendor prefixes
      "node scripts/check-prefixes.js"
    ],
    "package.json": [
      // Sort package.json
      "sort-package-json",
      "prettier --write"
    ]
  }
}

// scripts/check-prefixes.js
const fs = require('fs');
const path = require('path');

// Check if files contain manual vendor prefixes
// (should use autoprefixer instead)
process.argv.slice(2).forEach(file => {
  const content = fs.readFileSync(file, 'utf8');
  const vendorPrefixes = /-webkit-|-moz-|-ms-|-o-/g;
  
  if (vendorPrefixes.test(content)) {
    console.error(` Found vendor prefixes in ${file}`);
    console.error('   Use autoprefixer instead of manual prefixes');
    process.exit(1);
  }
});

console.log('✅ No manual vendor prefixes found');

// .husky/pre-push - Run tests before push
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

echo "🧪 Running tests before push..."

# Run full test suite
npm run test

# Run full linting (not just staged)
npm run lint

# Check build
npm run build

echo "✅ All pre-push checks passed!"

Example: Custom validation scripts

// scripts/validate-scss.js - Custom SCSS validation
const fs = require('fs');
const path = require('path');
const glob = require('glob');

// Custom rules beyond stylelint
const rules = {
  // Check for @import usage (should use @use)
  noImport: {
    pattern: /@import\s+['"][^'"]+['"]/g,
    message: 'Use @use instead of @import'
  },
  
  // Check for hardcoded colors (should use variables)
  noHardcodedColors: {
    pattern: /(?<!\/\/.*)(#[0-9a-fA-F]{3,6}|rgba?\([^)]+\))/g,
    exclude: /\$|@/,
    message: 'Use color variables instead of hardcoded values'
  },
  
  // Check for z-index values (should use map)
  zIndexMap: {
    pattern: /z-index:\s*\d+/g,
    message: 'Use z-index from $z-index map'
  }
};

// Validate files
const files = glob.sync('src/**/*.scss');
let hasErrors = false;

files.forEach(file => {
  const content = fs.readFileSync(file, 'utf8');
  const lines = content.split('\n');
  
  Object.entries(rules).forEach(([ruleName, rule]) => {
    lines.forEach((line, index) => {
      if (rule.pattern.test(line)) {
        if (!rule.exclude || !rule.exclude.test(line)) {
          console.error(
            `❌ ${file}:${index + 1} - ${rule.message}`
          );
          console.error(`   ${line.trim()}`);
          hasErrors = true;
        }
      }
    });
  });
});

if (hasErrors) {
  console.error('\n❌ SCSS validation failed!');
  process.exit(1);
}

console.log('✅ SCSS validation passed!');

// Add to package.json
{
  "scripts": {
    "validate:scss": "node scripts/validate-scss.js",
    "pre-commit": "npm run validate:scss && lint-staged"
  }
}

5. Continuous Integration and Deployment

Platform Configuration Steps Artifacts
GitHub Actions .github/workflows/*.yml Lint, test, build CSS files, reports
GitLab CI .gitlab-ci.yml Pipeline stages Build artifacts
CircleCI .circleci/config.yml Workflow jobs Compiled CSS
Jenkins Jenkinsfile Declarative pipeline Distribution files
Vercel/Netlify Build commands Auto-deployment Static assets

Example: GitHub Actions workflow

// .github/workflows/scss-ci.yml
name: SCSS CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  lint-and-test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [18.x, 20.x]
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run stylelint
        run: npm run lint:scss
      
      - name: Check Prettier formatting
        run: npm run format:check
      
      - name: Run custom SCSS validation
        run: npm run validate:scss
      
      - name: Build SCSS
        run: npm run build:scss
      
      - name: Run tests
        run: npm test
      
      - name: Upload build artifacts
        uses: actions/upload-artifact@v3
        with:
          name: css-build-${{ matrix.node-version }}
          path: dist/css/
      
      - name: Generate stylelint report
        if: always()
        run: npm run lint:scss:report
      
      - name: Upload lint report
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: stylelint-report
          path: stylelint-report.json
  
  size-check:
    runs-on: ubuntu-latest
    needs: lint-and-test
    
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20.x'
      
      - run: npm ci
      - run: npm run build:scss
      
      - name: Check bundle size
        uses: andresz1/size-limit-action@v1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          build_script: build:scss
  
  deploy:
    runs-on: ubuntu-latest
    needs: [lint-and-test, size-check]
    if: github.ref == 'refs/heads/main'
    
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20.x'
      
      - run: npm ci
      - run: npm run build:scss
      
      - name: Deploy to production
        run: npm run deploy
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

Example: GitLab CI pipeline

// .gitlab-ci.yml
image: node:20

cache:
  paths:
    - node_modules/

stages:
  - install
  - lint
  - build
  - test
  - deploy

install:
  stage: install
  script:
    - npm ci
  artifacts:
    paths:
      - node_modules/
    expire_in: 1 hour

lint:scss:
  stage: lint
  script:
    - npm run lint:scss
    - npm run format:check
  artifacts:
    reports:
      codequality: stylelint-report.json
    when: always

validate:scss:
  stage: lint
  script:
    - npm run validate:scss
  allow_failure: false

build:scss:
  stage: build
  script:
    - npm run build:scss
  artifacts:
    paths:
      - dist/css/
    expire_in: 1 week

test:unit:
  stage: test
  script:
    - npm run test
  coverage: '/Coverage: \d+\.\d+%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

deploy:production:
  stage: deploy
  script:
    - npm run deploy
  only:
    - main
  environment:
    name: production
    url: https://example.com
  when: manual

deploy:staging:
  stage: deploy
  script:
    - npm run deploy:staging
  only:
    - develop
  environment:
    name: staging
    url: https://staging.example.com

Example: Build optimization and caching

// package.json - Optimized build scripts
{
  "scripts": {
    "prebuild": "npm run clean",
    "build": "npm run build:scss && npm run optimize:css",
    "build:scss": "sass src/scss:dist/css --style=compressed --source-map",
    "build:scss:dev": "sass src/scss:dist/css --style=expanded --source-map",
    "watch:scss": "sass src/scss:dist/css --watch --style=expanded --source-map",
    "optimize:css": "npm run autoprefixer && npm run purgecss",
    "autoprefixer": "postcss dist/css/*.css --use autoprefixer -d dist/css",
    "purgecss": "purgecss --css dist/css/*.css --content 'src/**/*.html' --output dist/css",
    "clean": "rm -rf dist/css",
    "deploy": "npm run build && npm run upload",
    "upload": "aws s3 sync dist/css s3://my-bucket/css --cache-control max-age=31536000"
  },
  "devDependencies": {
    "sass": "^1.70.0",
    "postcss": "^8.4.0",
    "postcss-cli": "^11.0.0",
    "autoprefixer": "^10.4.0",
    "purgecss": "^5.0.0"
  }
}

// postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer')({
      overrideBrowserslist: [
        '> 1%',
        'last 2 versions',
        'not dead'
      ]
    }),
    require('cssnano')({
      preset: ['default', {
        discardComments: {
          removeAll: true
        },
        normalizeWhitespace: true
      }]
    })
  ]
};

// CI cache configuration
// GitHub Actions
- name: Cache dependencies
  uses: actions/cache@v3
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

// GitLab CI
cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - node_modules/
    - .npm/

6. Package Manager Integration (npm, yarn, pnpm)

Manager Installation Lock File Advantages
npm npm install package-lock.json Built-in, universal
Yarn Classic yarn install yarn.lock Faster, offline cache
Yarn Berry yarn install yarn.lock PnP, zero-installs
pnpm pnpm install pnpm-lock.yaml Disk efficient, strict
Bun bun install bun.lockb Fastest, built-in

Example: Complete package.json for SCSS project

{
  "name": "my-scss-project",
  "version": "1.0.0",
  "description": "SCSS-powered web application",
  "main": "index.js",
  "scripts": {
    "dev": "concurrently \"npm:watch:*\"",
    "watch:scss": "sass src/scss:dist/css --watch --style=expanded --source-map",
    "build": "npm run clean && npm run build:scss && npm run optimize",
    "build:scss": "sass src/scss:dist/css --style=compressed --no-source-map",
    "optimize": "postcss dist/css/*.css --use autoprefixer cssnano -d dist/css",
    "lint": "npm run lint:scss && npm run format:check",
    "lint:scss": "stylelint 'src/**/*.scss'",
    "lint:scss:fix": "stylelint 'src/**/*.scss' --fix",
    "format": "prettier --write 'src/**/*.{scss,js,json,md}'",
    "format:check": "prettier --check 'src/**/*.{scss,js,json,md}'",
    "test": "jest",
    "clean": "rm -rf dist/css",
    "prepare": "husky install",
    "size": "size-limit",
    "analyze": "sass src/scss:dist/css --style=expanded --embed-sources"
  },
  "dependencies": {},
  "devDependencies": {
    "sass": "^1.70.0",
    "sass-loader": "^14.0.0",
    "postcss": "^8.4.32",
    "postcss-cli": "^11.0.0",
    "autoprefixer": "^10.4.16",
    "cssnano": "^6.0.2",
    "stylelint": "^16.1.0",
    "stylelint-config-standard-scss": "^12.0.0",
    "stylelint-config-prettier-scss": "^1.0.0",
    "stylelint-scss": "^6.0.0",
    "stylelint-order": "^6.0.4",
    "prettier": "^3.1.1",
    "husky": "^8.0.3",
    "lint-staged": "^15.2.0",
    "concurrently": "^8.2.2",
    "size-limit": "^11.0.1",
    "@size-limit/file": "^11.0.1"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ],
  "size-limit": [
    {
      "path": "dist/css/main.css",
      "limit": "50 KB"
    }
  ],
  "engines": {
    "node": ">=18.0.0",
    "npm": ">=9.0.0"
  }
}

Example: pnpm workspace configuration

// pnpm-workspace.yaml
packages:
  - 'packages/*'
  - 'apps/*'

// package.json (root)
{
  "name": "monorepo",
  "private": true,
  "scripts": {
    "dev": "pnpm -r --parallel dev",
    "build": "pnpm -r build",
    "lint": "pnpm -r lint",
    "test": "pnpm -r test"
  },
  "devDependencies": {
    "sass": "^1.70.0",
    "stylelint": "^16.0.0",
    "prettier": "^3.0.0"
  }
}

// packages/design-system/package.json
{
  "name": "@company/design-system",
  "version": "1.0.0",
  "main": "dist/index.css",
  "scripts": {
    "dev": "sass src:dist --watch",
    "build": "sass src:dist --style=compressed",
    "lint": "stylelint 'src/**/*.scss'"
  },
  "devDependencies": {
    "sass": "workspace:*",
    "stylelint": "workspace:*"
  }
}

// apps/website/package.json
{
  "name": "@company/website",
  "version": "1.0.0",
  "dependencies": {
    "@company/design-system": "workspace:*"
  },
  "devDependencies": {
    "sass": "workspace:*"
  }
}

// .npmrc (pnpm configuration)
shamefully-hoist=false
strict-peer-dependencies=true
auto-install-peers=true
resolution-mode=highest

// Commands
pnpm install                    # Install all dependencies
pnpm -r build                   # Build all packages
pnpm --filter @company/website dev   # Run dev in specific package
pnpm -r --parallel dev          # Run dev in all packages

Example: Monorepo with Yarn workspaces

// package.json (root)
{
  "private": true,
  "workspaces": [
    "packages/*",
    "apps/*"
  ],
  "scripts": {
    "dev": "yarn workspaces foreach -pi run dev",
    "build": "yarn workspaces foreach -t run build",
    "lint": "yarn workspaces foreach run lint",
    "test": "yarn workspaces foreach run test"
  },
  "devDependencies": {
    "sass": "^1.70.0",
    "stylelint": "^16.0.0",
    "prettier": "^3.0.0",
    "lerna": "^8.0.0"
  }
}

// .yarnrc.yml (Yarn Berry)
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.0.2.cjs
enableGlobalCache: true

// Commands
yarn install                    # Install all dependencies
yarn workspace @company/design-system build
yarn workspaces foreach build   # Build all packages
yarn workspaces foreach -pi run dev  # Parallel interactive dev

// Alternative: Using Lerna with Yarn
// lerna.json
{
  "version": "independent",
  "npmClient": "yarn",
  "useWorkspaces": true,
  "packages": [
    "packages/*",
    "apps/*"
  ],
  "command": {
    "version": {
      "message": "chore(release): publish %s"
    }
  }
}

// Commands with Lerna
lerna bootstrap                 # Install dependencies
lerna run build                 # Run build in all packages
lerna run build --scope=@company/design-system
lerna publish                   # Publish packages

Development Workflow Best Practices

  • Linting: Use stylelint with SCSS plugins, enforce consistent naming and structure
  • Formatting: Prettier for automatic code formatting, integrate with editor and pre-commit
  • VS Code: Install SCSS IntelliSense, stylelint, and Prettier extensions for best DX
  • Git hooks: Use Husky + lint-staged to enforce quality before commits
  • CI/CD: Automate linting, testing, and building in GitHub Actions or GitLab CI
  • Package managers: Use pnpm for monorepos, enable caching and strict dependency resolution
  • Build optimization: Compress with Sass, add autoprefixer, purge unused CSS
  • Monorepo strategy: Share SCSS utilities across packages, use workspace dependencies
Note: A robust development workflow with automated linting, formatting, and testing catches errors early and maintains code quality. Integrate these tools into your editor and CI/CD pipeline for a seamless developer experience.