Naming conventions that scale
The $category-property-modifier pattern is the de facto standard in mature design systems. $color-primary-500 is superior to $blue because it communicates three things: type (color), role (primary), and variation (level 500 on the tonal scale).
Common mistake: hardcoded values without semantics. $spacing-24px is worse than $spacing-lg because the former becomes obsolete if you decide to change the value to 28px. The latter maintains the intention ('large spacing') independent of implementation.
For colors, adopt numeric scales (50-900) instead of subjective names (light/lighter/lightest). Material Design and Tailwind popularized this approach because it allows clear visual interpolation: $color-gray-400 is necessarily between 300 and 500. Reserve descriptive names for exceptional cases: $color-brand-primary is fine because that color has specific business meaning.
Organizing variables by abstraction layers
Structure in three levels: primitives, semantics, and components. Primitives are raw values: $blue-500: #3b82f6;. Semantics map usage: $color-primary: $blue-500;. Components apply context: $button-bg-primary: $color-primary;.
This enables global changes without touching components. If design requires primary to shift from blue to green, you modify one line ($color-primary: $green-500;) instead of find/replace in 50 files. Components continue using $button-bg-primary unchanged.
For typography, avoid loose variables like $font-size-button. Better: $button-text-size: $font-size-sm;. This keeps the button tied to the global typographic system. If you later decide that all 'sm' sizes should increase 1px, the button scales automatically. Think of design tokens as indirection layers that reduce coupling.
Patterns for responsive and theming
Breakpoints should be named variables, not magic numbers: $breakpoint-md: 768px;. This lets you create reusable mixins: @include respond-above($breakpoint-md) { ... }. Some teams prefer contextual names ($breakpoint-tablet) over generic ones (md), but generic ones are more resilient when 'tablet' definitions change.
For dark mode, don't duplicate the entire palette. Better: define semantic variables that change by context. $color-surface-base can be #ffffff in light mode and #1a1a1a in dark. Components use $color-surface-base without knowing which theme is active. Implement with CSS custom properties if you need runtime switching: --color-surface: #{$color-surface-base};.
Avoid inline responsive variables like $padding-mobile and $padding-desktop. Prefer a single $padding-component that changes value in media queries. Reduces variable count and keeps responsive logic encapsulated in the component.
Integration with design tokens and handoff
If working with Figma/Sketch, your SASS variables should mirror the design tokens from the design file. Tools like Style Dictionary or Theo automate conversion: design exports JSON tokens, build generates SCSS. This eliminates manual transcription and sync errors.
Recommended token format: JSON with metadata. Instead of {"blue-500": "#3b82f6"}, use {"blue-500": { "value": "#3b82f6", "type": "color", "category": "primitive" }}. Metadata enables validations (verify only colors are assigned to color properties) and auto-generated documentation.
For complex components, document variables in SCSS comments: /// @prop {length} $button-height-md - Default button height (40px). Tools like SassDoc parse this and generate documentation sites. In large teams, inline documentation is the only kind that stays current; external wikis go stale in weeks.