gap — spacing without margin hacks
Originally a Grid-only property, gap now works in Flexbox too. It adds space between items without affecting the outer edges — no more negative margins or :last-child { margin: 0 } hacks.
/* Old approach — messy */
.nav-items > * + * { margin-left: 16px; }
/* Modern approach */
.nav-items {
display: flex;
gap: 16px; /* applies between items only */
}
/* Different row and column gaps in grid */
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px 16px; /* row-gap column-gap */
}
The key benefit: gap doesn't add space before the first item or after the last, which means no :first-child or :last-child overrides and no wrapper negative-margin tricks.
object-fit — images that don't distort
If you've ever needed a card thumbnail that fills its container without distorting, object-fit: cover is the answer. It works on <img> and <video> elements the same way background-size: cover works on backgrounds.
/* Card thumbnail — fills the space, crops to fit */
.card-image {
width: 100%;
height: 200px;
object-fit: cover;
object-position: center top; /* crop from the top */
}
/* Product photo — show the full image with letterboxing */
.product-photo {
width: 300px;
height: 300px;
object-fit: contain;
background: #f5f5f5;
}
Pair with object-position to control which part of the image stays visible when cropping. By default it centers, but object-position: top is useful for portraits where the face is near the top.
isolation — fixing stacking context bugs
This property creates a new stacking context for an element without changing its z-index. It's the fix for two of CSS's most confusing bugs: z-index layers from a child element leaking out of their parent, and mix-blend-mode bleeding into sibling elements it shouldn't.
Without isolation, a button inside a modal with z-index: 50 can end up layered behind content outside the modal that has z-index: 1. Adding isolation: isolate to the modal makes all z-index values inside it self-contained.
/* The modal and its contents form their own stacking world */
.modal {
isolation: isolate;
}
/* z-index inside .modal is relative to .modal only */
.modal-overlay { z-index: 1; }
.modal-content { z-index: 2; }
.modal-close-btn { z-index: 3; }
/* This prevents .hero-image's blend mode from
affecting the .card next to it */
.card {
isolation: isolate;
}
.card .hero-image {
mix-blend-mode: multiply;
}
caret-color — branded text cursors
This one is small but has disproportionate impact on how polished a design feels. It changes the color of the blinking text cursor (caret) in inputs and textareas. On most sites the cursor is the browser's default — usually black or blue. Matching it to your brand color is a tiny detail that makes your forms feel like they belong.
input, textarea {
caret-color: #7c6fff; /* matches your brand accent */
}
/* Or apply to all focusable content globally */
:root {
caret-color: #7c6fff;
}
/* Hide the caret entirely (for custom cursors) */
.custom-input {
caret-color: transparent;
}
scroll-snap-type — native carousels
Most carousel implementations I've seen in the wild use a JavaScript library (Swiper, Slick, Embla) when CSS scroll snapping would handle 90% of the use case. scroll-snap-type on the container and scroll-snap-align on the children creates a smooth, touch-friendly carousel with zero JavaScript.
.carousel {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
gap: 16px;
/* Hide scrollbar but keep functionality */
scrollbar-width: none;
}
.carousel::-webkit-scrollbar { display: none; }
.carousel-item {
flex-shrink: 0;
width: 80%;
scroll-snap-align: center;
border-radius: 12px;
}
mandatory means the scroll always snaps to an item boundary — it never rests between items. Use proximity instead if you want it to snap only when the user stops near a snap point.
writing-mode — vertical text done right
Rotating text with transform: rotate(-90deg) is the common approach, but it doesn't affect the element's layout — the element still takes up horizontal space, causing overflow and layout bugs. writing-mode changes how text flows and interacts with the layout system entirely.
/* Table row header — text reads bottom to top */
.table-row-header {
writing-mode: vertical-rl;
transform: rotate(180deg); /* flip to read upward */
text-align: center;
}
/* Sidebar label */
.sidebar-label {
writing-mode: vertical-lr;
letter-spacing: 0.12em;
text-transform: uppercase;
font-size: 0.7rem;
}
/* Important: width/height swap — height defines the
text direction's "width", width defines its "height" */
mix-blend-mode — creative image overlays
This brings Photoshop-style blending to the web. Place a colored overlay on top of an image with mix-blend-mode: multiply and the image shows through, tinted by your color. It's how you get cohesive image grids where every photo looks like it belongs to the same brand palette.
.image-card {
position: relative;
isolation: isolate; /* prevent bleed to siblings */
}
.image-card img {
display: block;
width: 100%;
filter: grayscale(100%); /* make it monochrome first */
}
.image-card::after {
content: '';
position: absolute;
inset: 0;
background: #7c6fff;
mix-blend-mode: multiply;
opacity: 0.6;
}
Common modes worth knowing: multiply darkens (good for tinting), screen lightens (good for glows), overlay increases contrast, and color applies just the hue without changing luminosity.
user-select — copy UX control
Controls whether the user can select text by clicking and dragging. none prevents selection entirely — useful for UI labels, buttons, and icons that people accidentally select while trying to click. all selects the entire element's text on a single click, which is great for code snippets and copy-to-clipboard targets.
/* Prevent accidental text selection on UI elements */
button, label, nav, .icon {
user-select: none;
-webkit-user-select: none;
}
/* Select entire code block on a single click */
.code-snippet {
user-select: all;
-webkit-user-select: all;
cursor: pointer;
}
/* Explicitly allow selection (overrides parent's none) */
.article-body {
user-select: text;
}
Preventing text selection on large sections of a page is a significant accessibility and usability problem. Users can't copy text they might want to quote, translate, or save. Use it only on UI chrome (buttons, icons, navigation labels) — never on body content.
The common thread
Every property in this list has been available in all major browsers for several years. None of them require a polyfill or a build step. The reason developers miss them isn't browser support — it's that CSS has grown faster than the tutorials and courses that teach it. The properties you learned in 2018 are still valid, but the toolbox has tripled in size since then.
Make a habit of checking MDN or the CSS spec when you find yourself reaching for JavaScript or a clever workaround. Modern CSS probably has a native answer.
Explore CSS tools visually
Use CSSTools.io to experiment with gradients, shadows, blend modes, filters, and more — live preview with one-click code export.
Open CSS Tools →