Manoj
Back to Blog
·6 min read

CSS Fundamentals That Actually Matter

Forget the fancy frameworks for a minute. These are the CSS concepts you need to genuinely understand before reaching for any tool.

cssfrontendfundamentals

CSS Fundamentals That Actually Matter

I've seen developers jump straight into Tailwind or styled-components without understanding what's happening underneath. It works — until it doesn't. Then they're stuck staring at a layout that refuses to behave, with no idea why.

This post covers the CSS concepts I wish someone had drilled into me earlier. Not everything — just the stuff that actually trips people up in real projects.

The Box Model Isn't Optional

Every element on the page is a box. You've heard this before. But do you actually think in boxes when you're debugging layout issues?

/* The default box model is unintuitive */
.card {
  width: 300px;
  padding: 20px;
  border: 1px solid #ccc;
}
/* Actual rendered width: 300 + 20*2 + 1*2 = 342px. Surprised? */

This catches everyone at some point. The fix is simple, and you should set it globally on every project:

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

With border-box, padding and border are included inside the declared width. A 300px element stays 300px. Period.

Specificity: Why Your Styles Get Ignored

CSS specificity isn't random. It follows a scoring system, and once you learn it, you stop fighting the cascade.

Here's the hierarchy, from weakest to strongest:

  1. Type selectorsdiv, p, span → (0, 0, 1)
  2. Class selectors.card, .active → (0, 1, 0)
  3. ID selectors#header → (1, 0, 0)
  4. Inline stylesstyle="..." → wins over all the above
  5. !important — nuclear option, overrides everything
/* Specificity: (0, 1, 0) */
.nav-link {
  color: blue;
}

/* Specificity: (0, 2, 0) — wins because two classes > one class */
.nav .nav-link {
  color: red;
}

/* Specificity: (1, 0, 0) — ID always beats classes */
#main-nav {
  color: green;
}

The practical rule: keep specificity low and consistent. The moment you start using IDs for styling or sprinkling !important around, you're building a house of cards. Classes are the sweet spot — specific enough to target what you need, flat enough to override when you need to.

The Cascade: Source Order Matters

When two selectors have the same specificity, the one that comes later in the stylesheet wins.

.button {
  background: blue;
}

/* This wins — same specificity, but declared later */
.button {
  background: green;
}

This is why the order of your CSS imports matters. If you load a reset stylesheet after your component styles, the reset will override your work. It's not a bug — that's the cascade doing exactly what it's supposed to.

Display, Position, and the Flow of Things

The display property controls how an element participates in layout. This is where most layout confusion starts.

Block vs Inline:

  • block — takes full width, stacks vertically (div, p, h1)
  • inline — only takes as much width as needed, sits in a line (span, a, strong)
  • inline-block — inline flow but respects width/height (useful for buttons, badges)

Position:

.tooltip {
  position: absolute;
  top: 100%;
  left: 0;
}

absolute positions an element relative to its nearest positioned ancestor — not the page. This is the #1 gotcha. If no ancestor has position: relative, it falls back to the viewport. So the fix is always:

.tooltip-wrapper {
  position: relative; /* Creates the positioning context */
}

I can't count how many times I've debugged a tooltip or dropdown that appeared in the wrong place, only to find a missing position: relative on the parent.

Margin Collapse — The Weird One

Vertical margins between adjacent block elements collapse into a single margin. The larger one wins.

.heading {
  margin-bottom: 30px;
}

.paragraph {
  margin-top: 20px;
}

/* Gap between them: 30px, NOT 50px */

This only happens vertically, and only with block elements in normal flow. It doesn't happen with flexbox children, grid children, or elements with padding/border between them. Once you know the rule, it stops being mysterious.

Flexbox: Learn This, Use This

Flexbox handles one-dimensional layouts. It's the tool you'll reach for most often.

.navbar {
  display: flex;
  justify-content: space-between; /* Horizontal distribution */
  align-items: center; /* Vertical centering */
  gap: 1rem; /* Spacing between children */
}

The mental model: the parent (display: flex) controls how children are laid out. Children can override their own behavior with flex-grow, flex-shrink, and flex-basis, but start with the parent properties — they solve 80% of layout needs.

A few combos I use constantly:

/* Center anything */
.center {
  display: flex;
  justify-content: center;
  align-items: center;
}

/* Push last item to the right */
.header {
  display: flex;
  align-items: center;
  gap: 1rem;
}
.header .logout {
  margin-left: auto;
}

/* Equal-width columns */
.grid-row {
  display: flex;
}
.grid-row > * {
  flex: 1;
}

CSS Grid: When Flexbox Isn't Enough

Grid handles two-dimensional layouts — rows AND columns simultaneously.

.dashboard {
  display: grid;
  grid-template-columns: 250px 1fr;
  grid-template-rows: 60px 1fr;
  min-height: 100vh;
}

.sidebar {
  grid-row: 1 / -1; /* Span all rows */
}

My rule of thumb: if you're only controlling items in one direction (a row of buttons, a stack of cards), use flexbox. If you need to control both rows and columns at once (a page layout, a dashboard grid, an image gallery), use grid.

They also compose well together — Grid for the page skeleton, Flexbox for component internals.

Responsive Design: Mobile-First

Start with the smallest screen, then layer on complexity:

.card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

@media (min-width: 640px) {
  .card-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

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

Mobile-first isn't just a convention — it produces cleaner CSS. Your base styles are simple, and breakpoints only add rules. Going desktop-first means your base is complex and breakpoints undo things, which gets messy quickly.

Units: When to Use What

  • rem — for font sizes, spacing, anything that should scale with the root font size
  • em — for component-relative sizing (padding relative to its own font size)
  • px — for borders, shadows, tiny details that shouldn't scale
  • % — for fluid widths relative to the parent
  • vh/vw — for viewport-relative sizing (use sparingly, dvh is more reliable on mobile)

Don't overthink this. Use rem as your default, px for small fixed values, and you'll be fine 90% of the time.

Wrapping Up

None of this is glamorous. You won't see "I understand the box model" on anyone's LinkedIn. But these fundamentals are what separate someone who writes CSS from someone who actually controls their layouts. Every framework and utility library is built on top of these concepts — if you understand them, picking up any CSS tool becomes straightforward.