Before learning any specific naming convention, the deeper shift is mental: stop asking 'how do I style this part of the page' and start asking 'how do I style this reusable component, wherever it ends up placed'.
1Page-First vs Component-First Thinking
Page-first CSS answers 'what does the hero section need to look like'. Component-first CSS answers 'what does a primary button need to look like, regardless of where it's used'. The practical difference shows up immediately in selector choice: page-first reaches for .hero .button; component-first reaches for .button--primary.
This distinction matters because pages change constantly — sections get reordered, redesigned, or removed — while well-designed components tend to outlive any single page layout. Coupling styles to a page's structure means every redesign forces a CSS rewrite; coupling styles to the component itself means the component simply gets moved.
2Self-Containment Is The Test Of A Real Component
A useful diagnostic: copy a component's markup and CSS into a completely blank HTML page with no other stylesheet loaded. If it renders correctly, it's genuinely self-contained. If it depends on layout rules, resets, or variables defined elsewhere that aren't part of its own component file, it has hidden coupling that will eventually cause a bug when the component is moved or reused.
This doesn't mean components can't consume shared design tokens (colors, spacing scales) — those are meant to be global. The distinction is between depending on a shared *vocabulary* (fine) versus depending on a specific *page's structure* (fragile).
3Modifiers Prevent Component Sprawl
Without a modifier convention, visual variation tends to produce component sprawl: .button, .secondary-button, .large-secondary-button, each a near-complete copy of the last with one or two properties changed. Every shared property now has to be kept in sync by hand across every fork.
The modifier pattern — a base class plus one or more --modifier classes — solves this structurally. .button.button--secondary.button--large composes three independent concerns (base, color variant, size variant) instead of requiring a combinatorial explosion of forked component names.
4Step-by-Step Breakdown
Stop Styling Pages. Start Styling Components.. Page-first CSS styles a specific location — 'the button in the hero section'. Component-first CSS styles a reusable unit — 'a primary button' — that happens to be placed in the hero. That single mindset shift is the foundation every modern CSS methodology and component library is built on.
A Component Is A Self-Contained, Reusable Unit. A component owns its own styling completely: its internal spacing, typography, and colors are defined relative to itself, not to whatever page it happens to be dropped into. A well-built .card component looks correct in a grid, a sidebar, or a modal, without any page-specific CSS propping it up.
Component Self-Containment. What does it mean for a CSS component to be 'self-contained'?
- →All its CSS lives in exactly one file, regardless of styling logic
- →Its appearance doesn't depend on page-specific context or surrounding selectors
- →It uses no CSS classes at all
Avoid Location-Dependent Selectors. The classic anti-pattern is targeting a component based on where it happens to sit: .hero .button instead of .button--primary. The moment that button needs to appear in a footer or a modal, the location-based selector either doesn't apply or has to be duplicated — component-scoped selectors avoid this entirely.
Location Coupling. Why does .hero .button { ... } fail as a reusable component style?
- →It's syntactically invalid CSS
- →It only applies inside .hero, so the same button styled elsewhere needs duplicated CSS
- →Descendant selectors are always too slow to use
Variants Extend, They Don't Fork. A real component system handles visual variation through modifier classes layered on a base class, not by copy-pasting a near-identical new component. .button plus .button--secondary shares 90% of its rules with the primary variant, instead of duplicating every property in a separate .secondary-button component.
Variants Over Forks. What's the advantage of .button--secondary extending .button over creating an entirely separate .secondary-button component?
- →It just results in a shorter class name
- →It shares the base component's styles and only overrides what genuinely differs
- →There's no real difference between the two approaches
Component Mindset Installed. You now think in components rather than pages: self-contained, free of location-dependent selectors, and extended through modifier classes rather than forked into near-duplicates. This mental model is the direct foundation for BEM, the naming convention up next.
Style A Self-Contained Component. A component's styles are scoped to its own class, independent of where it's placed.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Self-Contained Components Keep Focus And ARIA Attributes Consistent Everywhere They're Used
When a component's interactive behavior and accessibility attributes travel with it as a unit, moving it to a new page can't accidentally strip the focus styles or ARIA wiring a page-specific override might have depended on.
2Modifier Classes Should Never Remove Base Accessibility Affordances
A `.button--icon-only` modifier that removes visible text must still preserve (or add) an accessible name — modifiers change appearance, not the contract that the component remains usable by assistive technology.
SEO Implications
- 1
Reusable Components Reduce Duplicate CSS Across A Site's Total Payload
When variation is handled via modifiers rather than component forks, the browser downloads and parses meaningfully less redundant CSS across pages that reuse the same components.
- 2
Component-First CSS Pairs Naturally With Component-Based Frameworks' Code-Splitting
Frameworks like React or Vue can lazy-load a component's CSS alongside its JavaScript only when that component is actually rendered, which is far cleaner when the component's styles are genuinely self-contained.
Best Practices
Ask 'Would This Render Correctly On A Blank Page' For Every New Component
This single mental test catches hidden coupling to page-specific selectors or missing shared tokens before the component ships and gets reused somewhere it silently breaks.
Reach For A Modifier Class Before Creating A New Component Name
If 90% of the styles match an existing component, it's a variant, not a new component — express it as `.component--modifier` to keep the shared base in sync automatically.
Frequent Bugs
A button looks correct on the homepage but loses its styling when moved into a modal.
The original rule was scoped with a location-dependent selector like `.homepage .button`. Replace it with a component-scoped class that doesn't depend on placement.
The codebase has five near-identical button components, each maintained separately.
Consolidate into one `.button` base class with modifier classes (`--secondary`, `--large`) to express the variations that actually differ.
Real-World Examples
Consolidating Forked Button Components
A team discovered four separate button components (PrimaryButton, SecondaryButton, LargeButton, IconButton) that shared 80% of their CSS, and merged them into one base component with modifier classes.
.button { /* shared base */ }
.button--secondary { /* override */ }
.button--large { /* override */ }