Home Module 04 CSS Best Practices

1. Introduction

You have learned the mechanics of CSS — now learn how to write CSS that is clean, maintainable, and professional. These are the habits that separate junior developers from senior developers when it comes to CSS.

This lesson is a comprehensive checklist of CSS best practices, covering:

  • Code style and formatting
  • Selector strategy
  • Performance considerations
  • Responsive and accessible CSS
  • Debugging CSS
  • What NOT to do

2. The 15 CSS Best Practices

1. Always use box-sizing: border-box

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

2. Use CSS variables for all tokens

:root {
  --color-primary: #2563eb;
  --spacing-md: 1rem;
  --radius-md: 8px;
}

Never repeat the same colour or spacing value more than once. Always create a variable.

3. Never use !important unless forced

/* Wrong */
.btn { color: white !important; }

/* Fix the specificity problem instead */
.navbar .btn { color: white; }

4. Keep selector specificity flat

/* Bad — very high specificity, hard to override */
div#main .content ul li a.nav-link { }

/* Good — single class, easy to override */
.nav-link { }

5. Write mobile-first CSS

/* Start with mobile styles */
.card { padding: 16px; }

/* Enhance for larger screens */
@media (min-width: 768px) {
  .card { padding: 24px; }
}

6. Use shorthand properties wisely

/* Good shorthand */
margin: 16px 24px;
border: 1px solid #ccc;

/* Avoid shorthand that resets unintended properties */
/* background shorthand resets background-size, position etc */
/* Use individual properties if you only mean to change one thing */
background-color: #2563eb; /* explicit is clearer */

7. Avoid magic numbers

/* Bad — what is 237px? */
.hero { height: 237px; }

/* Good — explain with a variable or comment */
.navbar { height: 64px; }
.hero { min-height: calc(100vh - 64px); /* full screen minus navbar */ }

8. Use relative units for font sizes

/* Bad — ignores user preferences */
h1 { font-size: 32px; }

/* Good — scales with browser settings */
h1 { font-size: 2rem; }

9. Always style :focus for keyboard accessibility

/* Never just remove the outline */
* { outline: none; } /* BAD — breaks keyboard navigation */

/* Style it nicely instead */
:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
}

10. Use logical properties for internationalisation

/* Traditional — breaks with RTL languages */
margin-left: 16px;
padding-left: 24px;

/* Logical — works in LTR and RTL */
margin-inline-start: 16px;
padding-inline-start: 24px;

11. Prefer transform and opacity for animations

/* Bad — causes layout reflow (slow) */
.btn:hover { width: 120px; left: 10px; }

/* Good — GPU-accelerated (fast) */
.btn:hover { transform: scale(1.05); opacity: 0.9; }

12. Use transition for smooth state changes

/* Add transitions to the base state, not the hover state */
.btn {
  background: blue;
  transition: background 0.2s ease, transform 0.1s ease;
}
.btn:hover {
  background: darkblue;
  transform: translateY(-1px);
}

13. Validate your CSS

Use the W3C CSS Validator (jigsaw.w3.org/css-validator) to check for syntax errors. Also use browser DevTools to check for invalid property warnings.

14. Test in multiple browsers

Check your pages in Chrome, Firefox, and Safari at minimum. Use caniuse.com to check property support before using new CSS features.

15. Use DevTools to debug

Right-click any element → Inspect. The Styles panel shows all applied styles and where they come from. The Computed panel shows the final values. The Box Model diagram shows margins, padding, and dimensions visually.

3. Real World Example

Professional CSS codebases consistently apply these patterns. For instance, the Tailwind CSS source code uses CSS variables for every design token. Large teams at companies like GitHub, Stripe, and Shopify have internal CSS style guides that enforce these same principles across hundreds of contributors.

When you follow these practices, your CSS becomes easy to:

  • Read — other developers understand it immediately
  • Maintain — changing a value updates everything that uses it
  • Debug — issues are isolated and predictable
  • Scale — adding features doesn't break existing ones

4. Code Example — Professional CSS Template

/* ================================================
   style.css — Project: [Project Name]
   Author: [Your Name]
   Last updated: [Date]
================================================ */

/* ---- Reset ---- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }
body { min-height: 100vh; line-height: 1.5; }
img, video { display: block; max-width: 100%; }
input, button, textarea, select { font: inherit; }

/* ---- Variables ---- */
:root {
  /* Colours */
  --color-primary:    #2563eb;
  --color-primary-dk: #1d4ed8;
  --color-text:       #1e293b;
  --color-text-muted: #64748b;
  --color-bg:         #f8fafc;
  --color-white:      #ffffff;
  --color-border:     #e2e8f0;
  --color-success:    #16a34a;
  --color-danger:     #dc2626;
  --color-warning:    #d97706;

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

  /* Typography */
  --font-body: 'Inter', system-ui, sans-serif;
  --text-sm:   0.875rem;
  --text-base: 1rem;
  --text-lg:   1.125rem;
  --text-xl:   1.25rem;
  --text-2xl:  1.5rem;
  --text-3xl:  2rem;

  /* Borders */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-full: 999px;

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

/* ---- Base Typography ---- */
body {
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, h5, h6 {
  line-height: 1.2;
  color: var(--color-text);
}

/* ---- Accessibility ---- */
:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ---- Layout ---- */
.container {
  width: 90%;
  max-width: 1200px;
  margin: 0 auto;
}

/* ---- Utilities ---- */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

5. Code Breakdown

File header comment
Always add a comment block at the top identifying the file, project, author, and date. In a team, this helps everyone understand what the file is for.
-webkit-font-smoothing: antialiased
Makes text render more cleanly on macOS and iOS. Only affects WebKit browsers (Chrome/Safari on Mac). Makes fonts look thinner and crisper on retina displays.
:focus-visible instead of :focus
:focus shows the ring even when clicking with a mouse (which many users find ugly). :focus-visible only shows the focus ring when navigating by keyboard — the best of both worlds.
.sr-only utility class
Makes content visually hidden but accessible to screen readers. Used for labels and text that provide context for assistive technology but would be redundant visually.
Spacing as a scale
Using a consistent spacing scale (--space-4 = 1rem, --space-6 = 1.5rem) creates visual rhythm. Elements spaced from the same scale always look harmonious together.

6. CSS Anti-Patterns to Avoid

Anti-patternProblemFix
* { outline: none } Destroys keyboard accessibility Style :focus-visible attractively
!important everywhere Specificity wars, impossible to maintain Fix specificity at the root
Inline styles in HTML Cannot reuse, hard to maintain Use class-based external CSS
Hard-coded colours everywhere Changing brand colour = editing 200 lines CSS variables
Deeply nested selectors High specificity, fragile Single-level class selectors
Animating width/height Forces layout reflow — jank Animate transform/opacity
Not testing multiple browsers Works in Chrome, breaks in Safari Test regularly, use caniuse.com

7. Best Practices Summary Checklist

  • box-sizing: border-box applied globally
  • ✓ CSS variables for all colours, spacing, fonts
  • ✓ No !important
  • ✓ Class-only selectors (no IDs for styling)
  • ✓ Mobile-first media queries
  • rem for font sizes
  • :focus-visible styled
  • ✓ Transitions on base state, not just hover
  • ✓ Animations using transform/opacity
  • ✓ Consistent naming convention (BEM or similar)
  • ✓ Validated with W3C CSS Validator
  • ✓ Tested in Chrome, Firefox, Safari
  • ✓ Comments for sections and non-obvious code
  • ✓ No inline styles
  • .sr-only for screen-reader-only text

8. Practice Exercise

Audit a previous CSS file you have written and find violations of the best practices above. For each violation found:

  1. Note what the anti-pattern is
  2. Explain why it is a problem
  3. Fix it

Target: fix at least 5 issues. Common ones to look for:

  • Missing box-sizing: border-box
  • Hard-coded hex values that could be variables
  • Class names that describe appearance, not purpose
  • Missing or removed focus styles
  • Fixed pixel font sizes instead of rem

9. Assignment — CSS Module Capstone

Build a complete product landing page applying all CSS best practices from Modules 03 and 04. The page must include:

  • A fixed navigation bar
  • A hero section with a background gradient and centred content
  • A three-column features section
  • A pricing table with three cards (one featured)
  • A contact section with a form
  • A footer

CSS requirements:

  • CSS reset applied
  • CSS variables for all design tokens
  • BEM naming for all components
  • No !important, no inline styles, no ID selectors
  • Responsive at both mobile (360px) and desktop (1200px)
  • Focus styles present
  • Smooth button hover transitions

10. Interview Questions

Q1: What are some common CSS anti-patterns you should avoid?

Answer: Overusing !important, using ID selectors for styling (high specificity), deeply nested selectors, inline styles, removing focus outlines without replacement, using fixed pixel font sizes instead of rem, animating layout-triggering properties like width/height instead of transform/opacity.

Q2: How do you ensure CSS scales well in a large project?

Answer: Use CSS custom properties for design tokens (colours, spacing, fonts), follow a naming convention like BEM, organise CSS into logical sections, keep specificity flat (class selectors only), write mobile-first, and use a CSS reset for consistency.

Q3: Why should you animate transform instead of width/height?

Answer: Animating width, height, top, or left triggers layout reflow — the browser has to recalculate all affected elements' positions. This is expensive and causes janky animations. transform and opacity are GPU-accelerated and do not trigger layout reflow, giving smooth 60fps animations.

Q4: What is :focus-visible and why use it instead of :focus?

Answer: :focus-visible only applies when the browser determines focus was received via keyboard navigation (not mouse click). This means keyboard users see focus rings (essential for accessibility) while mouse users don't see them on every click. It's a much better user experience than either always showing or never showing the focus ring.

Q5: How do you debug CSS when styles are not applying as expected?

Answer: Open browser DevTools (F12), click on the element, check the Styles panel which shows all applied rules and crossed-out overridden rules. Check the Computed panel for the actual final values. Look for specificity conflicts — the rule with higher specificity wins. Check if the selector is correct, if the file is linked properly, and use the Box Model diagram for layout issues.

11. Additional Resources

  • W3C CSS Validator — jigsaw.w3.org/css-validator
  • Can I Use — caniuse.com (browser compatibility)
  • MDN DevTools docs — search "MDN browser developer tools"
  • Google Chrome DevTools — search "Chrome DevTools CSS"
  • CSS-Tricks — CSS Best Practices (search "css-tricks css best practices")