Styling with CSS & SCSS
Introduction
Section titled “Introduction”Now that we have our component structure, let’s add styles following the NETWRIX design system. This section covers SCSS best practices, foundation tokens, and responsive design patterns.
SCSS File Location
Section titled “SCSS File Location”Create: src/scss/framework/components/card/_c--card-test.scss
Why this path?
framework/components/- Framework-level reusable componentscard/- Component type category_c--card-test.scss- Partial SCSS file (underscore prefix)
Complete SCSS Implementation
Section titled “Complete SCSS Implementation”@use "sass:map";@use "@scss/framework/_var/_vars.scss" as *;@use "@scssUtilities/utilities.scss";@use "@scssFoundation/foundation.scss";@use "@scssComponents/btn/_c--btn-a.scss";
.c--card-test { @extend .u--position-relative; @extend .u--display-block; @extend .u--width-100; @extend .u--height-100; border: 1px solid map.get($color-options, f); padding: $measure * 5 $measure * 4; text-decoration: none; transition: border-color $time-a $ease-standard-a;
@media all and ($viewport-type: $tablets) { padding: $measure * 4 $measure * 3; }
@media all and (hover: hover) and (pointer: fine) { &:is(a), &:is(button) { &:hover { border-color: map.get($color-options, c); } } }
&__media-wrapper { @extend .u--display-flex; @extend .u--align-items-center; @extend .u--justify-content-center; @extend .f--sp-d; height: $measure * 10; background-color: map.get($color-options, l); border-radius: $measure;
&__media { @extend .u--display-block; width: 80px; height: 80px; object-fit: cover; border-radius: 50%; } }
&__wrapper { @extend .u--display-flex; @extend .u--flex-direction-column; gap: $measure * 2;
&__title { @extend .f--font-d; @extend .f--color-a; @extend .u--font-semibold; }
&__subtitle { @extend .f--font-f; @extend .f--color-h; } }}Understanding the SCSS
Section titled “Understanding the SCSS”Imports
Section titled “Imports”@use "sass:map"; // Sass map utilities@use "@scss/framework/_var/_vars.scss" as *; // Foundation variables@use "@scssUtilities/utilities.scss"; // Utility classes@use "@scssFoundation/foundation.scss"; // Foundation systems@use "@scssComponents/btn/_c--btn-a.scss"; // Btn componentWhy these imports?
sass:map- Access color palettes viamap.get()_vars.scss- Contains$color-options,$measure,$time-*,$ease-*utilities.scss- Provides utility classes (.u--display-flex, etc.)foundation.scss- Typography, spacing, grid systems (.f--font-d,.f--sp-d, etc.)
📚 Learn more about Foundation Variables
Foundation Tokens
Section titled “Foundation Tokens”Color System
Section titled “Color System”border: 1px solid map.get($color-options, f); // Light borderbackground-color: map.get($color-options, l); // Light gray backgroundborder-color: map.get($color-options, c); // Hover colorColor Reference:
map.get($color-options, a)- Navy (primary)map.get($color-options, c)- Brand colormap.get($color-options, f)- Light bordermap.get($color-options, h)- Text graymap.get($color-options, l)- Light background
📚 Complete color reference in Chapter 2-1: Colors
Spacing System
Section titled “Spacing System”padding: $measure * 5 $measure * 4; // 40px top/bottom, 32px left/rightgap: $measure * 2; // 16px gapheight: $measure * 10; // 80px heightborder-radius: $measure; // 8px radiusThe $measure variable equals 8px - all spacing uses this base unit for consistency.
Common Spacing Multipliers:
$measure * 1= 8px$measure * 2= 16px$measure * 3= 24px$measure * 4= 32px$measure * 5= 40px$measure * 7= 56px$measure * 10= 80px
📚 Complete spacing reference in Chapter 2-3: Spacing
Typography System
Section titled “Typography System”@extend .f--font-d; // Font size D (typically ~24px)@extend .f--font-f; // Font size F (typically ~16px)@extend .u--font-semibold; // Font weight 600Typography Scale:
.f--font-a- Largest (70px).f--font-b- Very large (60px).f--font-c- Large (48px).f--font-d- Medium-large (32px).f--font-e- Medium (24px).f--font-f- Base (18px).f--font-g- Small (16px)
📚 Complete typography reference in Chapter 2-2: Typography
Timing & Easing
Section titled “Timing & Easing”transition: border-color $time-a $ease-standard-a;// $time-a = 0.3s// $ease-standard-a = cubic-bezier(0.4, 0.0, 0.2, 1)Timing Variables:
$time-a- Standard (0.3s)$time-b- Fast (0.2s)$time-c- Slow (0.5s)
Easing Variables:
$ease-standard-a- Standard$ease-in-out- Smooth in/out$ease-out- Smooth out
Utility Classes via @extend
Section titled “Utility Classes via @extend”@extend .u--position-relative; // position: relative@extend .u--display-flex; // display: flex@extend .u--width-100; // width: 100%@extend .u--font-semibold; // font-weight: 600@extend .f--sp-d; // Spacing bottom (margin-bottom)Benefits of @extend:
- ✨ Reuses existing CSS rules (smaller bundle)
- 🎯 Maintains consistency across components
- 🔄 Easy to update globally
When to use @extend vs writing CSS:
- @extend - Common patterns (display, position, width, flex, etc.)
- Direct CSS - Component-specific styles (custom padding, borders, transitions, etc.)
Responsive Design
Section titled “Responsive Design”@media all and ($viewport-type: $tablets) { padding: $measure * 4 $measure * 3; // Reduce padding on tablets}Breakpoint Variables:
$mobile- Mobile phones (max-width: 767px)$tablets- Tablets portrait (max-width: 1023px)$tabletl- Tablets landscape (max-width: 1279px)$laptop- Laptops (max-width: 1439px)$desktop- Desktop screens (min-width: 1440px)
Usage Pattern:
// Mobile first - start with mobile styles.component { padding: $measure * 2; // Mobile
@media all and ($viewport-type: $tablets) { padding: $measure * 3; // Tablets }
@media all and ($viewport-type: $laptop) { padding: $measure * 5; // Desktop }}📚 Complete grid & breakpoints reference in Chapter 2-4: Grid
Hover States
Section titled “Hover States”@media all and (hover: hover) and (pointer: fine) { &:is(a), &:is(button) { &:hover { border-color: map.get($color-options, c); } }}Why the media query?
hover: hover- Device supports hover (excludes touch devices)pointer: fine- Device has precise pointer (mouse, trackpad)
This prevents hover states from getting “stuck” on touch devices where there’s no true hover capability.
Selector Breakdown:
&:is(a)- When component is a link&:is(button)- When component is a button&:hover- On hover state
Registering the SCSS
Section titled “Registering the SCSS”Add your component to the main style.scss:
File: src/scss/style.scss
// ... other imports
@use "@scssComponents/card/_c--card-a.scss";@use "@scssComponents/card/_c--card-b.scss";// ... other cards@use "@scssComponents/card/_c--card-test.scss"; // ← Add your card here
// ... rest of importsImport Order Guidelines:
- Keep alphabetical within each category
- Cards should be grouped together in the cards section
- Test components typically go at the end during development
- Remove test components before production
BEM Architecture Deep Dive
Section titled “BEM Architecture Deep Dive”.c--card-test { // Block-level styles border: 1px solid map.get($color-options, f); padding: $measure * 5 $measure * 4;}The block represents the component as a whole. It encapsulates all functionality and styling.
Elements
Section titled “Elements”.c--card-test { &__media-wrapper { // Element styles height: $measure * 10;
&__media { // Sub-element styles width: 80px; height: 80px; } }
&__wrapper { // Another element gap: $measure * 2; }}Elements are parts of the block that have no standalone meaning. They’re semantically tied to the block.
Modifiers (when needed)
Section titled “Modifiers (when needed)”.c--card-test { &--dark { background-color: map.get($color-options, a);
.c--card-test__wrapper__title { color: map.get($color-options, w); } }
&--compact { padding: $measure * 3 $measure * 2; }}Modifiers represent different states or variations of the block or element.
Usage:
<div class="c--card-test c--card-test--dark">SCSS Best Practices
Section titled “SCSS Best Practices”✅ Good Patterns
Section titled “✅ Good Patterns”1. Use Foundation tokens
border: 1px solid map.get($color-options, f);padding: $measure * 5;@extend .f--font-d;2. Use meaningful media queries
@media all and ($viewport-type: $tablets) { padding: $measure * 4;}3. Wrap hover states properly
@media all and (hover: hover) and (pointer: fine) { &:hover { transform: scale(1.05); }}4. Use BEM consistently
.c--card-test { &__media-wrapper { &__media { // Nested elements } }}❌ Anti-patterns
Section titled “❌ Anti-patterns”1. Don’t use magic numbers
// ❌ Badpadding: 37px 22px;
// ✅ Goodpadding: $measure * 5 $measure * 3;2. Don’t hardcode colors
// ❌ Badcolor: #1a1536;background: #f5f5f5;
// ✅ Goodcolor: map.get($color-options, a);background-color: map.get($color-options, l);3. Don’t skip responsive design
// ❌ Bad - Same on all devicespadding: 40px;
// ✅ Good - Responsivepadding: $measure * 5;
@media all and ($viewport-type: $tablets) { padding: $measure * 4;}4. Don’t write inefficient selectors
// ❌ Baddiv.c--card-test .wrapper .title { color: blue;}
// ✅ Good.c--card-test__wrapper__title { @extend .f--color-a;}Testing Your Styles
Section titled “Testing Your Styles”Visual Regression Checklist
Section titled “Visual Regression Checklist”- Component renders correctly on desktop
- Component renders correctly on tablet
- Component renders correctly on mobile
- Hover states work on desktop
- No hover issues on touch devices
- Colors match Figma design
- Spacing matches Figma design
- Typography matches Figma design
- Animations are smooth
- No layout shifts
Browser Testing
Section titled “Browser Testing”Test in these browsers:
- ✅ Chrome (latest)
- ✅ Firefox (latest)
- ✅ Safari (latest)
- ✅ Edge (latest)
- ✅ Mobile Safari (iOS)
- ✅ Chrome Mobile (Android)
Common SCSS Issues & Solutions
Section titled “Common SCSS Issues & Solutions”Issue 1: Styles not applying
Section titled “Issue 1: Styles not applying”Problem: SCSS compiled but styles don’t show
Solutions:
- Check if SCSS is imported in
style.scss - Verify BEM class names match exactly
- Clear browser cache (Cmd+Shift+R / Ctrl+Shift+F5)
- Check CSS specificity in DevTools
- Ensure SCSS file has underscore prefix:
_c--card-test.scss
Issue 2: Colors don’t match Figma
Section titled “Issue 2: Colors don’t match Figma”Problem: Colors look different from design
Solutions:
- Use
map.get($color-options, x)instead of hex values - Check Figma Developer Mode for exact color token
- Verify color variable is defined in
_vars.scss - Check if browser color profile affects rendering
Issue 3: Responsive breakpoints not working
Section titled “Issue 3: Responsive breakpoints not working”Problem: Styles don’t change at breakpoints
Solutions:
- Use correct breakpoint variables:
$tablets,$laptop, etc. - Ensure media query format:
@media all and ($viewport-type: $tablets) - Test at actual breakpoint widths in DevTools
- Check for CSS conflicts with higher specificity
Issue 4: Transitions feel sluggish
Section titled “Issue 4: Transitions feel sluggish”Problem: Animations are too slow or too fast
Solutions:
- Use foundation timing:
$time-a,$time-b,$time-c - Use proper easing:
$ease-standard-a,$ease-in-out - Only animate transform and opacity for best performance
- Test on slower devices
Next Steps
Section titled “Next Steps”Great! Your component is now styled. Next, let’s add interactive behavior with JavaScript.
👉 Continue to Adding JavaScript Behavior to learn how to animate your component with GSAP.