In a codebase where each UI component ships with its own stylesheet file, which kinds of CSS legitimately belong in the shared global stylesheet instead, and which do not?
answer
- would it die with the component?
- must exist once for the document
- inheritance is what makes base rules work
- deletion is the real constraint
- page-specific fixes are not global
basics
~20 sGlobal CSS holds what must exist exactly once for the whole document: the reset, @font-face declarations, custom-property definitions on :root, base element typography, and a small utility set. Anything describing one component belongs in that component's file.
solid answer
~50 sThe useful test is: **if this component were deleted tomorrow, should this rule disappear with it?** If yes, the rule belongs in the component's own file. If no, it is global. That puts four things at document scope: the reset, `@font-face` declarations (a font family has to be registered once for the document), design-token custom properties declared on `:root` so every element in the tree can read them, and base element styling — `body` typography, default link colour, focus-visible styling — which works by inheriting down to everything. A short utility set can join them. What does *not* belong is anything selecting a component's internals, any page-specific fix, and any override written to defeat a component from outside. Those are the rules nobody dares delete two years later, because it is no longer obvious what they were propping up.
code
css · 33 lines/* global.css - loaded once, before any component styles */
@font-face {
font-family: "AppSans";
src: url("/fonts/app-sans.woff2") format("woff2");
font-display: swap;
}
:root {
--color-text: #1a1a1a;
--color-link: #0b5fff;
--space-3: 1rem;
}
body {
margin: 0;
font-family: "AppSans", system-ui, sans-serif;
line-height: 1.5;
color: var(--color-text);
}
a {
color: var(--color-link);
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}go deeper
Know that some CSS is document-wide — the reset, fonts, tokens, body typography — and that anything naming a specific component goes in that component's file. Give the reset and :root tokens as concrete examples.
Explain the mechanism behind each global category: why @font-face registers once per document, why custom properties are declared on :root to be visible everywhere, and why base element rules reach everything through inheritance.
Frame the split as a deletion strategy, not tidiness — CSS never errors on an unused rule, so matching a file's lifetime to what it styles is the only reliable way to keep the sheet shrinkable. Handle the grey zones: layout compositions and page-scoped fixes.
Own the global stylesheet as a shared contract: define who reviews additions to it, why its size should stay roughly flat while component count grows, and how the entry file's order encodes the project's override rules for every team.
## The question that actually decides it Every architecture debate about global CSS collapses into one test: > If the component this rule was written for were deleted tomorrow, should this rule disappear too? "Yes" means the rule is component-scoped and belongs in the component's file, where deleting the component deletes the CSS as a side effect. "No" means it is genuinely document-level and belongs in the global stylesheet. The test is worth applying literally, because the failure mode of a global stylesheet is not that it is badly written — it is that it becomes undeletable. ## What genuinely belongs at document scope **The reset or normalize layer.** It exists to flatten browser defaults for the whole document. There is nothing per-component about it. **`@font-face` declarations.** An `@font-face` rule registers a family name for the document; it is not applied to any element. Declaring it once, globally, is the only sane arrangement — duplicating it in component files gives you multiple definitions of the same family name. **Design tokens as custom properties.** A custom property is inherited, and is only visible to the element it is declared on and that element's descendants. Declaring the palette and spacing scale on `:root` — the `<html>` element — makes them visible everywhere: ```css :root { --color-surface: #ffffff; --color-text: #1a1a1a; --space-3: 1rem; } ``` Theme variants that redefine those same properties (a `[data-theme="dark"]` block, a `prefers-color-scheme` media query) also belong here, because they are redefinitions of the same document-level contract. **Base element styling.** Body typography, the default link colour, `:focus-visible` styling, heading sizes. These are written against bare element selectors and reach everything through inheritance and through matching every instance of the element. A component file cannot express "all paragraphs in the app". **A small utility set, and print rules.** If the project has utilities (`.visually-hidden` is the archetype), they are shared by definition. `@media print` adjustments to the document as a whole live here too. ## What belongs in the component file Everything that names the component. Its layout, its states, its variants, its internal spacing, its media-query adjustments, and its `:hover`/`:focus`/`[aria-expanded]` rules. Media queries in particular are frequently misfiled: teams collect every breakpoint rule into a `responsive.css` at the end of the global sheet, which guarantees that deleting a component leaves its breakpoint rules behind. Keep the breakpoint next to the rule it modifies. ## The grey zone: layout and page-specific CSS Two categories fit neither bucket cleanly. **Layout compositions** — the app shell grid, the standard page container width, the stack/cluster primitives — describe *arrangement* rather than any single component. Give them their own layer of files (`layout/`), between base and components. They are shared, but they are not base styling, and mixing them into `base.css` is how a base file grows to a thousand lines. **Page-specific styling** — the one tweak the pricing page needs — should go in a file named after that page and loaded only for it, never appended to the global sheet. A page-scoped file is deletable when the page is; a rule appended to `global.css` with the comment `/* pricing page fix */` is not. ## Why the split is really about deletion CSS has no compile-time link between a rule and the markup it styles. Nothing errors when a selector matches nothing. That means the *only* reliable mechanism for removing dead CSS is structural: put the rule in a file whose lifetime matches the thing it styles. Colocation is not a tidiness preference — it is the deletion strategy. Every rule you move into the global sheet is a rule you have opted into keeping forever, because proving it unused later requires searching the entire codebase rather than deleting one folder. ## A workable order The global entry point ends up looking like a short, ordered list — reset, then base, then tokens (or tokens first, if base rules consume them), then layout, then utilities — with components composed in by the build. The order is itself a design artifact: it encodes which rules are allowed to override which, and a reviewer can read the whole architecture from that one file. The healthy signal is that the global stylesheet is roughly *stable in size*. Components come and go weekly; the reset, the tokens and the base typography should change only when the design system changes. A global file that grows every sprint is telling you that component-scoped rules are being filed at document scope.
- Where do layout rules — the app shell grid, the standard page container width — belong under this split?In their own layer of files between base and components. They are shared, so they are not component-scoped, but they describe arrangement rather than document defaults, so folding them into `base.css` bloats the file that should be the most stable one in the project. A `layout/` folder keeps them findable and keeps base honest.
- A designer asks for a one-off spacing tweak that applies only to the pricing page. Where does that CSS go?In a stylesheet named for that page and loaded only with it — not appended to the global sheet. The point is lifetime: when the pricing page is redesigned or removed, its CSS goes with it. A page fix filed globally becomes a rule no one can safely delete, because nothing records which page it was propping up.
- Why can't a component's own stylesheet declare the design tokens it needs, so the component is fully self-contained?It can declare them, but they would only be visible on that component's subtree, so two components would hold independent copies of the same palette and could drift. Custom properties inherit downward from where they are declared, which is exactly why the shared vocabulary is declared once on `:root` and components only *consume* it with `var()`.
- How would you tell, reviewing a pull request, that a rule has been filed in the global stylesheet when it shouldn't have been?Look at the selector. If it names a component (`.card__footer`), names a page, or is an override written to defeat a component from outside, it is misfiled — it will die with something and therefore belongs with that thing. Global rules should select bare elements, `:root`, or a genuinely shared utility class.
saying these in an interview costs you the question
- Puts every media query in one global responsive.css file
- Adds page-specific fixes to the bottom of global.css
- Thinks global CSS is faster because it's a single file
- Declares design tokens separately in each component file
- Treats the base layer as a dumping ground for shared rules