Senior Tips & Tricks
The hard-won habits that keep a Sass codebase fast, small and pleasant years in.
A closing grab-bag of practices that separate a codebase you enjoy maintaining from one you dread. None are exotic; all are the kind of thing you wish someone had told you earlier.
Keep the output honest
- @extend sparingly. Placeholders (
%name) with@extendshare styles with zero duplication, but they hoist and reorder selectors in ways that surprise people. For parameterised reuse, prefer a mixin; for a shared base class, prefer a real class. - Mixins duplicate; that's fine. A mixin repeats its declarations at each call site. gzip loves repetition, so the real cost is smaller than it looks — optimise for clarity first.
- Emit custom properties for anything runtime. Colours, spacing a component might re-theme — go through
var(). Keep pure build-time values as Sass.
Keep the source maintainable
- One source of truth. Every value traces back to a token map. A raw hex in a component file is a bug waiting to drift.
- Private members. Prefix helpers with
-or_(@function -clamp()) so@forwardwon't leak your internals. - Never
@import. It's deprecated and slated for removal —@use/@forwardonly.
Example
@use 'sass:map';
// Private helper: the leading '-' keeps it out of @forward
@function -contrast($bg) {
@return if(lightness($bg) > 55%, #111, #fff);
}
// Placeholder + @extend: shared base, no duplication...
%card-base {
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,.12);
}
.card { @extend %card-base; }
.panel { @extend %card-base; }
// -> .card, .panel { border-radius: 8px; box-shadow: ... }
// ...vs a mixin when you need a parameter
@mixin surface($bg) {
background: $bg;
color: -contrast($bg); // auto-readable text
}
.tag--brand { @include surface(#CC6699); }
.tag--neutral { @include surface(#f3f4f6); }When to use it
- A senior developer uses @debug to inspect a computed Sass map value mid-compilation rather than guessing why a generated color looks wrong.
- A team creates a single _index.scss in each folder using @forward so every internal file changes require only one update to the public API, not every consumer.
- A developer uses CSS custom properties for runtime-switchable values and Sass variables only for compile-time constants, cleanly separating the two concerns.
More examples
Using @debug to inspect values
Prints the computed values of Sass expressions to the terminal during compilation, acting as a printf-style debugger for Sass logic.
@use 'sass:color';
$brand: #2563eb;
$hover: color.adjust($brand, $lightness: -10%);
@debug 'brand: #{$brand}';
@debug 'hover: #{$hover}';
// Outputs to stderr during compilation — remove before shippingFolder index for stable public API
An index file hides the internal file structure; adding or renaming component partials never breaks external consumers.
// components/_index.scss
@forward 'buttons';
@forward 'cards';
@forward 'forms';
// Other files just write:
// @use '../components';
// regardless of how many files are inside components/Sass vars for constants, custom props for runtime
Uses a Sass variable for the fixed breakpoint pixel value and a CSS custom property for spacing that can shift responsively at runtime.
// Sass variable — compile-time constant, never changes at runtime
$breakpoint-md: 768px;
// CSS custom property — can be changed by JS or media queries at runtime
:root {
--spacing: 1rem;
}
@media (min-width: $breakpoint-md) {
:root { --spacing: 1.5rem; }
}
Discussion