ShaharAmir
← Back to Blog
JavaScript6 min read

CSS Features That Will Change How You Write Styles in 2026

From container queries to customizable selects — the CSS features shipping now that replace JavaScript and simplify your code.

S
Shahar Amir

CSS3

CSS has evolved dramatically. Features that required JavaScript libraries or hacky workarounds are now native to the language. Here are the most impactful CSS features you should be using in 2026.

Container Queries

Media queries respond to the viewport. Container queries respond to the parent container. This is huge for component-based design.

.card-container {
  container-type: inline-size;
  container-name: card;
  padding: 20px;
  background: #1a1a2e;
  border-radius: 12px;
  resize: horizontal;
  overflow: auto;
  min-width: 200px;
  max-width: 500px;
}

.card {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  padding: 20px;
  border-radius: 8px;
  color: white;
}

.card h3 { margin: 0 0 10px; font-size: 1.2rem; }
.card p { margin: 0; opacity: 0.9; }

/* When container is wider than 350px */
@container card (min-width: 350px) {
  .card {
    display: flex;
    align-items: center;
    gap: 20px;
  }
  .card h3 { margin: 0; }
}

Why it matters: Build truly reusable components that adapt to wherever they're placed — sidebar, main content, modal — without viewport hacks.

CSS Nesting

Native nesting is here. No Sass, no PostCSS, just CSS.

css
12345678910111213141516171819202122
/* Before: Repetitive selectors */
.card { background: white; }
.card:hover { transform: scale(1.02); }
.card .title { font-size: 1.5rem; }
.card .title:hover { color: blue; }
/* After: Native nesting */
.card {
background: white;
&:hover {
transform: scale(1.02);
}
.title {
font-size: 1.5rem;
&:hover {
color: blue;
}
}
}

The & represents the parent selector. You can nest pseudo-classes, pseudo-elements, and child selectors naturally.

The :has() Selector

The "parent selector" CSS never had — until now. Select elements based on what they contain.

.form-group {
  margin-bottom: 15px;
  padding: 15px;
  background: #f8f9fa;
  border-radius: 8px;
  border: 2px solid transparent;
  transition: all 0.2s;
}

/* Style parent based on child state! */
.form-group:has(.invalid) {
  background: #fff5f5;
  border-color: #e53e3e;
}

.form-group:has(input:focus) {
  border-color: #667eea;
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
}

label { display: block; margin-bottom: 5px; font-weight: 600; }
input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }
.invalid { border-color: #e53e3e; }
.error { color: #e53e3e; font-size: 0.85rem; margin-top: 5px; display: block; }

Real use cases:

  • Style form groups when inputs have errors
  • Hide empty states when content exists
  • Highlight table rows with checked boxes

Cascade Layers (@layer)

Control specificity without fighting it. Layers let you define the order styles are applied.

css
123456789101112131415161718
/* Define layer order - last wins */
@layer reset, base, components, utilities;
@layer reset {
* { margin: 0; padding: 0; box-sizing: border-box; }
}
@layer base {
h1 { font-size: 2rem; color: #1a1a2e; }
}
@layer components {
.card h1 { font-size: 1.5rem; } /* Beats base h1 */
}
@layer utilities {
.text-xl { font-size: 1.5rem !important; } /* Always wins */
}

Why it matters: No more !important wars. Third-party CSS can't override your utilities. Design systems become predictable.

@starting-style

Animate elements from display: none. Previously impossible without JavaScript.

.modal {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
  place-items: center;
}

.modal.open {
  display: grid;
}

.modal-content {
  background: white;
  padding: 30px;
  border-radius: 12px;
  transform: scale(1);
  opacity: 1;
  transition: transform 0.3s, opacity 0.3s;
}

/* Starting state when element appears */
@starting-style {
  .modal.open .modal-content {
    transform: scale(0.9);
    opacity: 0;
  }
}

button {
  padding: 12px 24px;
  background: #667eea;
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-size: 1rem;
}

Customizable elements can finally be styled.

css
123456789101112131415161718192021222324252627
select {
appearance: base-select;
}
/* Style every part */
select::picker(select) {
background: white;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
option {
padding: 12px 16px;
}
option:hover {
background: #f0f0ff;
}
/* Even add images to options! */
option::before {
content: '';
width: 20px;
height: 20px;
background-image: var(--flag);
}

Browser support: Chrome 135+. Use progressively — the standard