Skip to content

Styling with CSS & SCSS

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.


Create: src/scss/framework/components/card/_c--card-test.scss

Why this path?

  • framework/components/ - Framework-level reusable components
  • card/ - Component type category
  • _c--card-test.scss - Partial SCSS file (underscore prefix)

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

@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 component

Why these imports?

  • sass:map - Access color palettes via map.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


border: 1px solid map.get($color-options, f); // Light border
background-color: map.get($color-options, l); // Light gray background
border-color: map.get($color-options, c); // Hover color

Color Reference:

  • map.get($color-options, a) - Navy (primary)
  • map.get($color-options, c) - Brand color
  • map.get($color-options, f) - Light border
  • map.get($color-options, h) - Text gray
  • map.get($color-options, l) - Light background

📚 Complete color reference in Chapter 2-1: Colors


padding: $measure * 5 $measure * 4; // 40px top/bottom, 32px left/right
gap: $measure * 2; // 16px gap
height: $measure * 10; // 80px height
border-radius: $measure; // 8px radius

The $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


@extend .f--font-d; // Font size D (typically ~24px)
@extend .f--font-f; // Font size F (typically ~16px)
@extend .u--font-semibold; // Font weight 600

Typography 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


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

@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.)

@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


@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

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 imports

Import 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

.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.

.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.

.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">

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

1. Don’t use magic numbers

// ❌ Bad
padding: 37px 22px;
// ✅ Good
padding: $measure * 5 $measure * 3;

2. Don’t hardcode colors

// ❌ Bad
color: #1a1536;
background: #f5f5f5;
// ✅ Good
color: map.get($color-options, a);
background-color: map.get($color-options, l);

3. Don’t skip responsive design

// ❌ Bad - Same on all devices
padding: 40px;
// ✅ Good - Responsive
padding: $measure * 5;
@media all and ($viewport-type: $tablets) {
padding: $measure * 4;
}

4. Don’t write inefficient selectors

// ❌ Bad
div.c--card-test .wrapper .title {
color: blue;
}
// ✅ Good
.c--card-test__wrapper__title {
@extend .f--color-a;
}

  • 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

Test in these browsers:

  • ✅ Chrome (latest)
  • ✅ Firefox (latest)
  • ✅ Safari (latest)
  • ✅ Edge (latest)
  • ✅ Mobile Safari (iOS)
  • ✅ Chrome Mobile (Android)

Problem: SCSS compiled but styles don’t show

Solutions:

  1. Check if SCSS is imported in style.scss
  2. Verify BEM class names match exactly
  3. Clear browser cache (Cmd+Shift+R / Ctrl+Shift+F5)
  4. Check CSS specificity in DevTools
  5. Ensure SCSS file has underscore prefix: _c--card-test.scss

Problem: Colors look different from design

Solutions:

  1. Use map.get($color-options, x) instead of hex values
  2. Check Figma Developer Mode for exact color token
  3. Verify color variable is defined in _vars.scss
  4. 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:

  1. Use correct breakpoint variables: $tablets, $laptop, etc.
  2. Ensure media query format: @media all and ($viewport-type: $tablets)
  3. Test at actual breakpoint widths in DevTools
  4. Check for CSS conflicts with higher specificity

Problem: Animations are too slow or too fast

Solutions:

  1. Use foundation timing: $time-a, $time-b, $time-c
  2. Use proper easing: $ease-standard-a, $ease-in-out
  3. Only animate transform and opacity for best performance
  4. Test on slower devices

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.