What Is the CSS @supports Feature Query?

The CSS @supports feature query is a conditional rule that allows developers to test whether a web browser supports specific CSS property-value pairs or selectors before applying corresponding styles. By enabling native feature detection directly within stylesheets, @supports provides a reliable foundation for progressive enhancement, allowing modern styling techniques to be implemented without breaking layouts on older or non-compliant browsers.

How the @supports Rule Works

Often referred to as a "feature query," the @supports at-rule tests browser capabilities at runtime. If the browser parses and supports the specified declaration, the enclosed CSS block executes. If the declaration is unsupported, the browser skips the entire block.

This mechanism avoids the need for external JavaScript feature-detection libraries (such as Modernizr) for purely visual features, improving performance and keeping style-related logic inside CSS.

Basic Syntax and Examples

The syntax of @supports consists of the @supports keyword, a condition wrapped in parentheses containing a CSS declaration, and a block of rules to apply if the condition evaluates to true.

@supports (display: grid) {
  .container {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 1rem;
  }
}

In this example, the grid layout styles apply only if the browser supports display: grid.

Combining Conditions with Logical Operators

Feature queries support logical operators to create sophisticated detection logic: and, or, and not.

The and Operator

The and operator requires all specified declarations to be supported.

@supports (display: flex) and (backdrop-filter: blur(10px)) {
  .card {
    display: flex;
    backdrop-filter: blur(10px);
    background-color: rgba(255, 255, 255, 0.5);
  }
}

The or Operator

The or operator checks whether at least one of the conditions is supported, which is useful when dealing with vendor-prefixed properties or alternative implementations.

@supports (background: -webkit-named-image(i)) or (aspect-ratio: 16 / 9) {
  .video-container {
    aspect-ratio: 16 / 9;
  }
}

The not Operator

The not operator inverts the logic, applying styles only when a feature is unsupported.

@supports not (display: grid) {
  .container {
    float: left;
    width: 100%;
  }
}

When combining not with other operators, parentheses must explicitly define precedence:

@supports (display: flex) and (not (gap: 1rem)) {
  .flex-item {
    margin-right: 1rem;
  }
}

Testing for Selector Support

Beyond property-value declarations, modern implementations of @supports allow checking whether a browser recognizes specific CSS selectors using the selector() function.

@supports selector(:has(> .child)) {
  .parent:has(> .child) {
    border: 2px solid blue;
  }
}

This prevents complex relational selectors from failing silently or causing unexpected behavior in legacy rendering engines.

Progressive Enhancement Strategy

The recommended practice with @supports is to build a solid baseline layout using universally supported CSS outside of any @supports block, then layer advanced styling inside an @supports query.

/* Base styles for all browsers */
.gallery {
  display: block;
}

.gallery-item {
  display: inline-block;
  width: 30%;
  margin: 1%;
}

/* Enhanced layout for modern browsers */
@supports (display: grid) and (gap: 1rem) {
  .gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 1rem;
  }

  .gallery-item {
    width: auto;
    margin: 0;
  }
}

By structuring stylesheets this way, older user agents render a functional baseline, while newer browsers receive a richer visual experience without dependency overhead.