CSS Tips & Tricks Worth Knowing

Sun Mar 01 · 4 min read

Stop using fixed font sizes

Most developers set font sizes with px or rem and then add media query breakpoints to scale them. There's a better way.

h1 {
  font-size: clamp(1.5rem, 4vw + 1rem, 3rem);
}

clamp(min, preferred, max) gives you fluid typography that scales smoothly with the viewport — no media queries, no layout shifts at breakpoints. The 4vw + 1rem preferred value grows with the viewport but never goes below 1.5rem or above 3rem.

aspect-ratio is finally here

Before this property existed, the padding-top hack was the standard:

/* old way */
.video-wrapper {
  padding-top: 56.25%; /* 9/16 */
  position: relative;
}

Now:

.video-wrapper {
  aspect-ratio: 16 / 9;
  width: 100%;
}

Works for images, videos, cards, avatars — anything you want to keep proportional. Combine it with object-fit: cover on images for perfectly cropped thumbnails.

CSS custom properties with fallbacks

Custom properties (CSS variables) support a fallback value as the second argument to var().

.button {
  background: var(--btn-color, #39FF14);
  padding: var(--btn-padding, 0.5rem 1rem);
}

This is useful for component theming — the component works out of the box with defaults, but consumers can override by setting the custom property. No class variants needed.

You can also do computed values:

:root {
  --space-unit: 0.25rem;
}
 
.card {
  padding: calc(var(--space-unit) * 6); /* 1.5rem */
  gap: calc(var(--space-unit) * 4);     /* 1rem */
}

:is() for cleaner selectors

Grouping selectors gets messy fast:

/* verbose */
h1 a:hover,
h2 a:hover,
h3 a:hover {
  color: green;
}

With :is():

:is(h1, h2, h3) a:hover {
  color: green;
}

:is() takes the specificity of its most specific argument. If you want zero specificity (useful for resets), use :where() instead — same syntax, no specificity cost.

Container queries replace most media queries

Media queries respond to the viewport. Container queries respond to the parent element. This is the right mental model for components.

.card-wrapper {
  container-type: inline-size;
}
 
.card {
  display: flex;
  flex-direction: column;
}
 
@container (min-width: 400px) {
  .card {
    flex-direction: row;
  }
}

Now the card layout responds to its container, not the screen. Drop the same component into a narrow sidebar or a wide main area — it adapts correctly without touching the CSS.

Logical properties for international layouts

Physical properties like margin-left and padding-right break in RTL (right-to-left) languages. Logical properties adapt automatically.

/* physical — breaks in RTL */
.text {
  margin-left: 1rem;
  border-right: 1px solid;
}
 
/* logical — works in any direction */
.text {
  margin-inline-start: 1rem;
  border-inline-end: 1px solid;
}

The cheat sheet:

  • margin-leftmargin-inline-start
  • margin-rightmargin-inline-end
  • padding-toppadding-block-start
  • widthinline-size
  • heightblock-size

If you're not building for RTL, this still reads more semantically — "start" and "end" are layout concepts, "left" and "right" are physical directions.

gap works in Flexbox too

Most developers know gap from Grid. It works in Flexbox just as well and is far cleaner than adding margins to children.

/* old way — fragile */
.list > * + * {
  margin-left: 1rem;
}
 
/* clean */
.list {
  display: flex;
  gap: 1rem;
}

No need to handle first/last child exceptions or use negative margins on the parent. Just use gap.

Scroll snap for carousels

Native CSS scroll snap gives you smooth, snapping carousels without JavaScript.

.carousel {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  gap: 1rem;
}
 
.carousel-item {
  flex: 0 0 80%;
  scroll-snap-align: start;
}

Add -webkit-overflow-scrolling: touch for smoother iOS momentum scrolling. For most use cases this beats a JS carousel library.

place-items shorthand

place-items is shorthand for align-items and justify-items together.

/* centering a child — the old way */
.parent {
  display: grid;
  align-items: center;
  justify-items: center;
}
 
/* one line */
.parent {
  display: grid;
  place-items: center;
}

The fastest way to center anything. Works for both Grid and Flexbox (though in Flex it only sets align-items).

@layer for predictable cascade control

When you mix third-party CSS with your own, specificity conflicts are inevitable. @layer lets you define an explicit cascade order.

@layer reset, base, components, utilities;
 
@layer reset {
  * { box-sizing: border-box; margin: 0; }
}
 
@layer base {
  a { color: inherit; text-decoration: none; }
}
 
@layer utilities {
  .text-green { color: #39FF14; }
}

Layers declared later win. Styles outside any layer always win over layered styles. This eliminates most specificity debugging sessions.

Closing thoughts

CSS has grown into a genuinely powerful language. The properties above — clamp, container queries, logical properties, @layer — aren't obscure tricks. They're the foundation of maintainable, resilient CSS.

Learn the mental models behind them and you'll write less CSS, fight the cascade less, and ship layouts that hold up in the real world.