CSS Best Practices
Write clean, maintainable, performant CSS that professional teams love to work with.
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-visibleinstead of:focus:focusshows the ring even when clicking with a mouse (which many users find ugly).:focus-visibleonly shows the focus ring when navigating by keyboard — the best of both worlds..sr-onlyutility 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-pattern | Problem | Fix |
|---|---|---|
* { 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-boxapplied globally - ✓ CSS variables for all colours, spacing, fonts
- ✓ No
!important - ✓ Class-only selectors (no IDs for styling)
- ✓ Mobile-first media queries
- ✓
remfor font sizes - ✓
:focus-visiblestyled - ✓ 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-onlyfor 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:
- Note what the anti-pattern is
- Explain why it is a problem
- 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")