CSS Architecture & Modern Techniques

Writing CSS that works is easy. Writing CSS that scales — CSS that a team can maintain, extend, and debug six months later — is a different skill entirely. This lesson teaches you how to think about CSS structurally, name things consistently, and use modern native features that make your stylesheets easier to manage without any build tools or preprocessors.

📌 What you'll learn
  • What CSS architecture is and why it matters
  • How to use BEM naming conventions
  • How to organize CSS files logically (pure CSS)
  • How specificity and the cascade really work
  • CSS custom properties, utility classes, and component-based thinking
  • Modern features: @layer and logical properties

1. What is CSS Architecture?

CSS architecture is the practice of organizing, naming, and structuring your CSS so that it stays predictable and maintainable as your project grows. It is not a single tool or framework — it is a set of decisions about how you write your styles.

Think of it like building a house. You could pile bricks randomly and eventually have four walls, or you could follow a plan that makes the house safe, extensible, and readable by any contractor who comes after you.

Problems with unstructured CSS

When CSS has no architecture these problems appear quickly:

✗ What goes wrong
  • Specificity wars — selectors keep getting more specific to override each other
  • Duplication — the same color value is written 40 times across 10 files
  • Fragile overrides — changing one rule breaks three unrelated pages
  • No clear ownership — nobody knows where a style lives or who wrote it
  • Dead code — styles for components that no longer exist pile up
✓ What architecture solves
  • Consistent naming makes selectors predictable and low-specificity
  • CSS variables centralize repeated values in one place
  • Component isolation means changes stay local
  • File structure tells everyone exactly where to look
  • Unused styles are easy to spot and delete

2. Naming Conventions — BEM

BEM stands for Block, Element, Modifier. It is a naming system that makes CSS class names self-documenting — you can read a class name and immediately understand what it styles and how it relates to the rest of the UI.

Block

.card

A standalone UI component. The top-level "thing". Can exist on its own.

Element

.card__title

A part inside a block. Uses double underscore. Cannot exist outside its block.

Modifier

.card--active

A variation or state of a block or element. Uses double hyphen.

BEM in practice

/* ── Block ── */
.card {
  background: white;
  border-radius: 8px;
  padding: 1.5rem;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

/* ── Elements (parts inside the block) ── */
.card__image {
  width: 100%;
  border-radius: 6px 6px 0 0;
}

.card__title {
  font-size: 1.25rem;
  font-weight: 700;
  margin-bottom: 0.5rem;
}

.card__body {
  color: #5a6c7d;
  line-height: 1.6;
}

.card__footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 1rem;
}

/* ── Modifiers (variants of the block) ── */
.card--featured {
  border: 2px solid #3498db;
}

.card--compact {
  padding: 0.75rem;
}

/* ── Element modifier ── */
.card__title--large {
  font-size: 1.75rem;
}

And the matching HTML:

<!-- Default card -->
<article class="card">
  <img class="card__image" src="photo.jpg" alt="Product">
  <h2 class="card__title">Product Name</h2>
  <p class="card__body">Short description here.</p>
  <div class="card__footer">
    <span>$29</span>
    <button class="btn btn--primary">Buy</button>
  </div>
</article>

<!-- Featured variant — just add the modifier class -->
<article class="card card--featured">
  <h2 class="card__title card__title--large">Special Offer</h2>
  <p class="card__body">...</p>
  <div class="card__footer">...</div>
</article>
💡 BEM rules to remember
  • Block names should describe what the component is: .navbar, .modal, .avatar
  • Elements describe their role inside the block: .navbar__link, .modal__close
  • Modifiers describe a state or variant: --active, --disabled, --large
  • Never go more than two levels deep: .card__body__text is a sign you need a new block
⚠️ Common BEM mistakes
  • Nesting BEM in CSS — BEM class names are already flat. Never write .card .card__title; just use .card__title alone.
  • Three-level elements.card__body__paragraph is wrong. Create a new block or re-think the structure.
  • Using BEM for layout — Grid containers and page-level wrappers don't need BEM. BEM is for reusable components.

3. File & Style Organization

Even without SCSS or a build tool you can split your CSS across multiple files linked in order from <head>. Each file has a single responsibility — the same principle good code always follows.

css/
  base.css  — resets, body font, :root variables
  layout.css  — grid, containers, page sections
  components.css  — buttons, cards, navbar, modals…
  utilities.css  — .mt-1, .text-center, .hidden…
  pages.css  — (optional) page-specific overrides

Link them in your HTML in this exact order — it matters for the cascade:

<head>
  <link rel="stylesheet" href="css/base.css">
  <link rel="stylesheet" href="css/layout.css">
  <link rel="stylesheet" href="css/components.css">
  <link rel="stylesheet" href="css/utilities.css">
</head>

What goes in each file

base.css — the foundation

/* base.css */
:root {
  --color-primary:  #3498db;
  --color-accent:   #2ecc71;
  --color-text:     #2c3e50;
  --color-bg:       #ffffff;

  --space-xs: 0.5rem;
  --space-sm: 1rem;
  --space-md: 1.5rem;
  --space-lg: 2rem;
  --space-xl: 3rem;

  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 16px;

  --font-body: system-ui, sans-serif;
  --font-code: 'Consolas', monospace;
}

*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html { font-size: 16px; scroll-behavior: smooth; }

body {
  font-family: var(--font-body);
  color: var(--color-text);
  background-color: var(--color-bg);
  line-height: 1.6;
}

layout.css — page structure

/* layout.css */
.container {
  width: 100%;
  max-width: 1200px;
  margin-inline: auto;
  padding-inline: var(--space-md);
}

.page-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-lg);
}

@media (min-width: 768px) {
  .page-grid { grid-template-columns: 1fr 3fr; }
}

components.css — reusable UI

/* components.css */
.btn {
  display: inline-flex;
  align-items: center;
  padding: 0.6em 1.4em;
  border: 2px solid transparent;
  border-radius: var(--radius-sm);
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  transition: background-color 0.2s, transform 0.15s;
}
.btn:hover { transform: translateY(-1px); }

.btn--primary {
  background-color: var(--color-primary);
  color: #fff;
}
.btn--primary:hover { background-color: #2980b9; }

.btn--outline {
  background-color: transparent;
  border-color: var(--color-primary);
  color: var(--color-primary);
}

utilities.css — one-job helper classes

/* utilities.css */
.mt-1 { margin-top: var(--space-xs); }
.mt-2 { margin-top: var(--space-sm); }
.mt-3 { margin-top: var(--space-md); }

.text-center  { text-align: center; }
.text-right   { text-align: right; }

.font-bold    { font-weight: 700; }

.hidden       { display: none; }
.sr-only {            /* visible to screen readers only */
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
}

4. CSS Specificity & the Cascade

When two rules target the same element, the browser uses specificity to decide which wins. Understanding specificity stops you from reaching for !important every time something doesn't apply.

Specificity scoring

Every selector has a three-number score: (IDs, Classes, Elements). Higher score wins.

Live specificity breakdown
p
0ID
0cls
1elm
= (0, 0, 1) — lowest
.card
0ID
1cls
0elm
= (0, 1, 0)
.card .card__title
0ID
2cls
0elm
= (0, 2, 0)
#header .nav a
1ID
1cls
1elm
= (1, 1, 1) — very high
color: red !important
ID
cls
elm
= beats everything — avoid

The cascade order

When specificity is equal, the rule that appears later in the file wins.

1
Browser defaults
Every browser ships with a built-in stylesheet (user agent styles)
2
Your base.css
Resets and root variables override the browser defaults
3
Components / layout
Component styles come next in the link order
4
Utilities
Utility classes are last — they win on equal specificity, which is intentional
!
!important
Overrides everything. Hard to debug. Almost always a sign of an architecture problem.

Strategies for managing specificity

  • Stay flat. Use single class selectors (.card) not chains (section div .card).
  • Avoid ID selectors in CSS. IDs have a specificity of (1,0,0) which is very hard to override without escalating.
  • Never use !important in component or layout CSS. It is occasionally acceptable in utilities (e.g. .hidden { display: none !important; }) where you genuinely want it to always win.
  • Use :where() to write zero-specificity selectors in resets: :where(h1, h2, h3) { margin: 0; }
/* ✗ Bad — high specificity chains */
#page-header nav ul li a { color: blue; }

/* ✓ Good — single class, easy to override */
.nav__link { color: blue; }

/* ✓ Zero-specificity reset using :where() */
:where(h1, h2, h3, h4) {
  margin: 0;
  line-height: 1.3;
}

5. CSS Custom Properties (Variables)

CSS custom properties let you define values once and reuse them everywhere. They are live — changing a variable instantly updates every place it is used. They cascade and inherit like regular CSS properties, and they work natively in every modern browser.

Defining and using variables

/* Define on :root to make globally available */
:root {
  /* Colors */
  --color-primary:    #3498db;
  --color-primary-dk: #2471a3;
  --color-success:    #2ecc71;
  --color-danger:     #e74c3c;
  --color-text:       #2c3e50;
  --color-bg:         #ffffff;

  /* Spacing scale */
  --space-1: 0.25rem;   /*  4px */
  --space-2: 0.5rem;    /*  8px */
  --space-3: 1rem;      /* 16px */
  --space-4: 1.5rem;    /* 24px */
  --space-5: 2rem;      /* 32px */
  --space-6: 3rem;      /* 48px */

  /* Typography */
  --font-size-sm: 0.875rem;
  --font-size-md: 1rem;
  --font-size-lg: 1.25rem;
  --font-size-xl: 1.5rem;

  /* Border radii */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-full: 9999px;

  /* Shadows */
  --shadow-sm: 0 1px 3px rgba(0,0,0,0.12);
  --shadow-md: 0 4px 12px rgba(0,0,0,0.15);
}

/* Use them with var() */
.btn--primary {
  background-color: var(--color-primary);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
}

.btn--primary:hover {
  background-color: var(--color-primary-dk);
}

Dark mode with one variable swap

The real power of CSS variables shows when implementing themes. You redefine the variables, and every component updates automatically:

:root {
  --color-bg:      #ffffff;
  --color-text:    #2c3e50;
  --color-surface: #f8f9fa;
  --color-border:  #e1e4e8;
}

/* Dark mode — swap the values, not the rules */
[data-theme="dark"] {
  --color-bg:      #1a1a1a;
  --color-text:    #e8e8e8;
  --color-surface: #2d2d2d;
  --color-border:  #404040;
}

/* Components use variables — automatically themed */
body {
  background-color: var(--color-bg);
  color: var(--color-text);
}

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
}
--primary
--success
--danger
--warning
--purple
--text
💡 var() fallback values

You can provide a fallback in case the variable is not set:

color: var(--color-text, #333);
⚠️ Common mistakes with variables
  • Defining variables inside a component selector instead of :root — they won't be available globally.
  • Naming variables by their value (--blue) instead of purpose (--color-primary). When you change the primary color to green, --blue becomes confusing.

6. Utility Classes

A utility class does exactly one thing. Instead of writing custom CSS for every small tweak, you compose existing utility classes directly in HTML. This approach is popularized by Tailwind CSS, but you can implement a useful subset in plain CSS.

/* utilities.css */

/* Spacing */
.mt-0 { margin-top: 0; }
.mt-1 { margin-top: var(--space-2); }    /* 8px  */
.mt-2 { margin-top: var(--space-3); }    /* 16px */
.mt-3 { margin-top: var(--space-4); }    /* 24px */

.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: var(--space-2); }
.mb-2 { margin-bottom: var(--space-3); }

.p-1  { padding: var(--space-2); }
.p-2  { padding: var(--space-3); }
.p-3  { padding: var(--space-4); }

/* Text */
.text-center  { text-align: center; }
.text-right   { text-align: right; }
.text-sm      { font-size: var(--font-size-sm); }
.text-lg      { font-size: var(--font-size-lg); }
.font-bold    { font-weight: 700; }

/* Display */
.flex         { display: flex; }
.flex-col     { flex-direction: column; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.gap-1        { gap: var(--space-2); }
.gap-2        { gap: var(--space-3); }

/* Colors */
.text-primary { color: var(--color-primary); }
.text-muted   { color: var(--text-secondary, #6c757d); }

/* Visibility */
.hidden       { display: none !important; }
.invisible    { visibility: hidden; }

Using utilities alongside component classes:

<!-- The card component provides structure,
     utilities handle spacing tweaks without touching the CSS -->
<article class="card mt-3">
  <h2 class="card__title text-lg font-bold">Hello</h2>
  <p class="card__body text-muted">Some text.</p>
  <div class="card__footer flex justify-between items-center">
    <span class="text-sm">Posted today</span>
    <a href="#" class="btn btn--primary">Read more</a>
  </div>
</article>

Pros

  • You rarely need to write new CSS for layout tweaks
  • HTML is self-documenting — styles are visible right there
  • Small, predictable changes with no side effects
  • CSS file size stabilises as the project grows

Cons

  • HTML can get verbose with many classes
  • Duplication moves to HTML instead of CSS
  • Not a substitute for proper component structure
  • Can be misused to avoid thinking about design
📌 Rule of thumb

Use component classes for the core structure and appearance of a UI element. Use utilities for spacing, typography, and alignment tweaks that vary from context to context. Never build a whole component out of utilities alone — that leads to the duplication problem in HTML.

7. Component-Based Thinking

A component is a self-contained, reusable piece of UI. Component-based CSS means each component owns its own styles and does not depend on where it appears on the page.

Rules for component CSS

  • A component should look the same whether it appears in a sidebar or a modal
  • Style with classes, never with tag selectors inside a component
  • Do not reference page-level IDs or ancestor selectors inside a component
  • Responsive behaviour belongs inside the component, not outside it
/* ✗ Bad — styles depend on page location */
.sidebar .card { padding: 0.5rem; }
#dashboard .card h2 { font-size: 1rem; }

/* ✓ Good — component is self-contained */
.card { padding: var(--space-4); }
.card--compact { padding: var(--space-2); }   /* modifier for tight contexts */
.card__title { font-size: var(--font-size-lg); }

Thinking in components — a checklist

  • Can I drop this HTML + CSS into a different page and have it work? (It should.)
  • Is the component styling free of parent selectors?
  • Do variants use modifier classes rather than duplicate rules?
  • Are all magic numbers replaced with CSS variables?

8. Layout & Structure Best Practices

Avoid deeply nested selectors

/* ✗ Bad — brittle and hard to maintain */
body main section.articles div.wrapper article.post h2.title a {
  color: blue;
}

/* ✓ Good — one predictable class */
.post__title-link { color: var(--color-primary); }

Prefer class-based styling

Styling with element type selectors causes unintended side effects. Use classes for everything that needs custom styles.

/* ✗ Risky — affects every h2 on the page */
h2 { font-size: 2rem; color: navy; }

/* ✓ Safe — only affects cards */
.card__title { font-size: 1.25rem; color: var(--color-text); }

/* ✓ Acceptable — in base.css as a reset */
h1, h2, h3 { line-height: 1.3; margin-bottom: 0.5em; }

Keep styles predictable

  • One class should always produce the same visual result, regardless of context
  • Avoid relying on sibling or child combinators (+, ~, >) outside of base resets
  • Group related rules together — don't scatter a component's styles across the file

9. Responsive & Scalable CSS

CSS that hardcodes pixel values breaks on different screens and at different font sizes. Writing flexible CSS from the start avoids the painful retrofitting of responsive styles later.

Use flexible units

/* ✗ Hardcoded — breaks at different font sizes and screens */
.card {
  width: 320px;
  padding: 24px;
  font-size: 16px;
}

/* ✓ Flexible — scales naturally */
.card {
  width: 100%;            /* fills its container */
  max-width: 40rem;       /* caps at reasonable size */
  padding: var(--space-4);
  font-size: var(--font-size-md);
}
Unit Relative to Best used for
remRoot font size (16px default)Spacing, font sizes, component sizing
emParent element font sizePadding/margin that should scale with the component's own text
%Parent container sizeWidths and heights relative to parent
vw / vhViewport width/heightFull-screen sections, hero images, viewport-relative type
chWidth of "0" characterReadable line lengths (max-width: 65ch)
clamp()Viewport + min/max boundsFluid typography and spacing

Fluid typography with clamp()

/* Font grows from 1rem at narrow screens to 1.5rem at wide */
h1 {
  font-size: clamp(1.5rem, 4vw, 2.5rem);
}

/* Padding grows with the viewport */
.section {
  padding-block: clamp(var(--space-4), 6vw, var(--space-6));
}

Mobile-first media queries

/* Start with mobile — add complexity upward */
.card-grid {
  display: grid;
  grid-template-columns: 1fr;          /* 1 column on mobile */
  gap: var(--space-3);
}

@media (min-width: 600px) {
  .card-grid { grid-template-columns: 1fr 1fr; }     /* 2 columns */
}

@media (min-width: 960px) {
  .card-grid { grid-template-columns: repeat(3, 1fr); } /* 3 columns */
}

10. Optional Modern Features

CSS @layer — explicit cascade control

@layer lets you declare a cascade layer order. Styles in a later layer win, regardless of specificity within that layer. This makes specificity battles almost disappear.

/* Declare layer order first */
@layer base, components, utilities;

@layer base {
  /* low-priority foundational styles */
  body { font-family: system-ui, sans-serif; }
  a { color: inherit; }
}

@layer components {
  /* component styles — override base */
  .btn { padding: 0.5em 1em; background: var(--color-primary); }
}

@layer utilities {
  /* utilities always win over components */
  .hidden { display: none; }
  .text-center { text-align: center; }
}
📌 Browser support

@layer is supported in all modern browsers (Chrome 99+, Firefox 97+, Safari 15.4+). It is safe to use in new projects today.

Logical properties — writing direction agnostic CSS

Physical properties like margin-left and padding-top are tied to screen direction. Logical properties adapt automatically for right-to-left languages and vertical writing modes.

Physical property Logical equivalent Meaning
margin-left / rightmargin-inlineBoth inline (horizontal) sides
margin-top / bottommargin-blockBoth block (vertical) sides
padding-leftpadding-inline-startStart of inline axis
padding-rightpadding-inline-endEnd of inline axis
widthinline-sizeSize along the inline axis
heightblock-sizeSize along the block axis
/* Old way — direction-specific */
.container {
  margin-left: auto;
  margin-right: auto;
  padding-top: 2rem;
  padding-bottom: 2rem;
  padding-left: 1.5rem;
  padding-right: 1.5rem;
}

/* Logical way — works for LTR and RTL */
.container {
  margin-inline: auto;
  padding-block: 2rem;
  padding-inline: 1.5rem;
}

11. Bad vs Good: Side-by-Side

Here is the same card component written two ways. Both produce the same visual output, but only one is maintainable.

✗ Messy CSS
div div.card h2 {
  font-size: 20px;
  color: #2c3e50 !important;
  font-weight: bold;
}

.card p {
  color: #5a6c7d;
  font-size: 14px;
}

div.card {
  background: white;
  padding: 24px;
  border: 1px solid #e1e4e8;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

.card a.btn {
  background: #3498db;
  color: white !important;
  padding: 8px 16px;
  text-decoration: none;
  border-radius: 4px;
  display: inline-block;
}
✓ Structured CSS
/* In components.css */
.card {
  background: var(--color-bg);
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}

.card__title {
  font-size: var(--font-size-lg);
  font-weight: 700;
  color: var(--color-text);
}

.card__body {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}

/* In components.css — shared button */
.btn--primary {
  background: var(--color-primary);
  color: #fff;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  text-decoration: none;
  display: inline-block;
}

Key differences in the structured version:

  • No !important — specificity is managed through flat class selectors
  • No hardcoded values — everything references a CSS variable
  • No tag-based selectors inside components — only BEM class names
  • The .btn--primary class is reusable anywhere, not locked to cards

12. Refactoring Example

Let's walk through converting a poorly written navbar into structured CSS step by step.

Step 1 — Start: the messy original
/* ✗ Original — tangled, hardcoded, hard to maintain */
#main-nav {
  background: #1a1a2e;
  padding: 0 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 60px;
}

#main-nav .logo {
  color: white;
  font-size: 22px;
  font-weight: 800;
  text-decoration: none;
}

#main-nav ul {
  list-style: none;
  display: flex;
  gap: 10px;
  margin: 0;
  padding: 0;
}

#main-nav ul li a {
  color: rgba(255,255,255,0.75);
  text-decoration: none;
  padding: 6px 12px;
  font-size: 15px;
}

#main-nav ul li a:hover {
  color: white !important;
  background: rgba(255,255,255,0.1);
  border-radius: 4px;
}
Step 2 — Identify the components

We have: a navbar block, with a logo element, a links list element, and each link element. The hover state is a modifier/state.

Step 3 — Apply BEM + variables
/* base.css — add nav-specific variables */
:root {
  --nav-bg:        #1a1a2e;
  --nav-height:    60px;
  --nav-link-color:       rgba(255,255,255,0.75);
  --nav-link-hover-color: #ffffff;
  --nav-link-hover-bg:    rgba(255,255,255,0.1);
}

/* components.css — navbar component */
.navbar {
  background-color: var(--nav-bg);
  padding-inline: var(--space-4);
  display: flex;
  align-items: center;
  justify-content: space-between;
  block-size: var(--nav-height);
}

.navbar__logo {
  color: #fff;
  font-size: var(--font-size-lg);
  font-weight: 800;
  text-decoration: none;
}

.navbar__links {
  list-style: none;
  display: flex;
  gap: var(--space-2);
}

.navbar__link {
  color: var(--nav-link-color);
  text-decoration: none;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-sm);
  transition: color 0.2s, background-color 0.2s;
}

.navbar__link:hover,
.navbar__link--active {
  color: var(--nav-link-hover-color);
  background-color: var(--nav-link-hover-bg);
}
Step 4 — Updated HTML
<nav class="navbar">
  <a href="/" class="navbar__logo">MySite</a>
  <ul class="navbar__links">
    <li><a href="/" class="navbar__link navbar__link--active">Home</a></li>
    <li><a href="/about" class="navbar__link">About</a></li>
    <li><a href="/contact" class="navbar__link">Contact</a></li>
  </ul>
</nav>

What we gained:

  • No ID selector — the navbar can now be used multiple times or in any context
  • No !important — hover works naturally via flat specificity
  • Active state is a modifier class (--active) not an JS-injected inline style
  • All values come from variables — theming is one variable change away
  • Logical properties (padding-inline, block-size) used for better RTL support

13. Mini Challenge

Challenge: Style a Profile Card

Given the HTML below, write CSS following all the principles from this lesson: BEM naming, CSS variables, flat selectors, and no !important.

<!-- Starter HTML — do not change this -->
<article class="profile-card">
  <div class="profile-card__avatar">
    <img src="avatar.jpg" alt="Jane Doe">
  </div>
  <div class="profile-card__info">
    <h2 class="profile-card__name">Jane Doe</h2>
    <p class="profile-card__role">Frontend Developer</p>
    <p class="profile-card__bio">Building clean, accessible UIs one component at a time.</p>
  </div>
  <div class="profile-card__actions">
    <a href="#" class="btn btn--primary">Follow</a>
    <a href="#" class="btn btn--outline">Message</a>
  </div>
</article>
Define all colors and sizes as CSS variables in :root
Use BEM class names for every element (already done in HTML)
Make the avatar a circle using border-radius
No !important, no ID selectors, no tag selectors in component rules
Add a hover state to the card (lift it with box-shadow)
Add a modifier .profile-card--compact with reduced padding
Make it responsive — stack to a single column below 480px
💡 Starter CSS skeleton
:root {
  /* your variables here */
}

.profile-card { /* ... */ }
.profile-card:hover { /* ... */ }
.profile-card--compact { /* ... */ }

.profile-card__avatar { /* ... */ }
.profile-card__avatar img { /* ... */ }

.profile-card__name { /* ... */ }
.profile-card__role { /* ... */ }
.profile-card__bio  { /* ... */ }

.profile-card__actions { /* ... */ }

/* Responsive */
@media (max-width: 480px) {
  .profile-card { /* ... */ }
}

14. Quick Reference Summary

Principle Rule
NamingUse BEM: .block__element--modifier
SpecificityKeep selectors flat — one class per rule whenever possible
!importantAvoid in components; acceptable only in utilities
IDs in CSSNever use for styling — only for JS hooks
ValuesAll colors, spacing, radii → CSS variables in :root
File structurebase → layout → components → utilities (in link order)
ComponentsSelf-contained; no parent selectors; variants via modifiers
ResponsiveMobile-first; use rem, %, clamp(); avoid px for sizes
NestingNever more than 2 levels deep in CSS
ModernUse @layer and logical properties in new projects
Key takeaway

Good CSS architecture is not about following rules for their own sake. It is about making your code readable and changeable. If a teammate (or future you) can open your stylesheet, find the right rule in under 30 seconds, and change it without fear — you have succeeded.