[KongchangAI]
· 2 min read· 1,373 words

CSS Conditional Border-Radius Trick: Auto-Square Corners When Elements Touch the Edge

CSS Conditional Border-Radius Trick: Auto-Square Corners When Elements Touch the Edge

A pure CSS conditional border-radius approach using clamp and viewport/container units that auto-zeros at the edge.

The article presents a pure CSS trick for conditional border-radius: rounded corners while floating, square corners when touching the edge. The core formula calc(100vw - 100%) makes the radius vary with the element's distance from the viewport edge and zero out at the edge. Since that difference creates a soft transition zone, it's multiplied by a large coefficient and wrapped in clamp() to lock values between 0 and the design cap, switching instantly at the threshold. It also covers scrollbar-induced 100vw偏差 mitigated with scrollbar-gutter: stable, and using cqi units with container-type so the threshold follows the container boundary, falling back to the viewport when no container exists. No JavaScript required—ideal for full-width mobile cards, modals, and adaptive layouts.

Elements keep rounded corners while floating in the viewport, but snap to square corners once they touch the screen edge—and you can achieve this with pure CSS, no JavaScript resize listeners needed. A frontend trick demo from YouTube showcases a conditional border-radius approach built on clamp() and viewport units—the logic is clean, but the details are worth unpacking.

Starting with percentage radii: why they fall short

The most direct approach is to give border-radius a percentage value, say 20%. Percentage radii scale with element size, so when the element gets narrower the corners shrink too, looking "adaptive." But the problem is that percentage radii are affected by both element width and height, producing weird elliptical corners when the aspect ratio isn't 1:1, and the scaling is completely uncontrolled.

The demo notes that the percentage approach "leads to strange sizes"—it scales, but not in a way that aligns with your goal of zeroing out the radius when the element hits the edge.

Percentage radii scale with element size but the dimensions are uncontrollable

When border-radius uses percentages, the horizontal radius is computed as a percentage of the element's width and the vertical radius as a percentage of its height. That is, border-radius: 20% is equivalent to border-radius: 20% / 20%, with different reference bases in each direction. When the element's aspect ratio isn't 1:1, the absolute radii in the two directions differ, so the corners render as elliptical arcs rather than circular ones. Even if the element is square, a percentage radius always occupies a fixed proportion of the side length and can't hold a constant visual corner size as the element widens—let alone implement position-dependent conditional logic like "zero out when touching the edge."

The core formula: 100vw minus 100%

The real breakthrough is subtracting the element width from the viewport width:

border-radius: calc(100vw - 100%);

The meaning of this expression is intuitive: when the element width approaches the viewport width, the difference approaches 0 and the corners disappear; the narrower the element, the larger the difference and the more pronounced the corners. When the element hugs both sides of the screen, the radius naturally drops to zero, and while it floats in the middle it stays round.

Slowly widening and narrowing the browser window, you can see the radius continuously grow and shrink until it hits zero the moment the element touches the edge. The direction is right, but the transition zone is too "soft"—the radius gradually shrinks over a distance rather than switching decisively.

The radius enters a gradually shrinking transition zone near the edge

Using clamp with an exponent to eliminate the transition band

To make the radius switch with a "click," you need to flatten that soft transition. The way to do it is to stuff the difference into clamp():

border-radius: clamp(0px, (100vw - 100%) * 9999, 24px);

Multiplying the middle value by a huge coefficient (the demo uses an exponential form, and the author notes that * 9999 works identically) makes the difference shoot to the upper limit the instant it leaves the edge, so the radius essentially turns on or off right at the threshold of touching/leaving the edge, with no fuzzy gradient zone in between.

After multiplying by a large coefficient, the radius switches directly at the threshold

The three parameters of clamp() lock in the minimum radius (0), the dynamic computed value, and the maximum radius (your desired cap). This structure guarantees that no matter how you resize, the radius never exceeds the design range.

The offset problem caused by scrollbars

Viewport units have a classic trap: 100vw includes the width occupied by the scrollbar. On some operating systems, the scrollbar overlays the content, causing the radius to hit zero slightly before the visual edge—it looks like it only switches once it "ducks under the scrollbar."

The mitigation the author offers is scrollbar-gutter: stable, which reserves fixed space for the scrollbar so that the vw calculation aligns with the actual visible area. A caveat: this property isn't fully supported in all browsers, and the author himself stresses that you should test it in practice. On Chrome, when used together with scrollbar-gutter: stable, the switch point lands where expected.

After stabilizing the scrollbar gutter, the switch point aligns with the visible edge

scrollbar-gutter: stable is a property in the CSS Overflow module for controlling the scrollbar gutter. By default, when page content is shorter than one screen the scrollbar doesn't appear, and when content grows longer the scrollbar suddenly appears and squeezes the layout, causing horizontal content jump (layout shift). The stable value makes the browser always reserve space for the scrollbar—even when no scrollbar is currently needed, the gutter stays present, avoiding layout jitter. In this example, once the gutter is reserved, the difference between 100vw and the actual content area width no longer includes the scrollbar width, so the radius-zeroing threshold lands precisely on the visible edge. There's also a stable both-edges value that reserves symmetric space on both sides, suited to centered layouts.

Container query units: not just for the viewport

If the element doesn't fill the viewport but sits inside a container (say, each item in a card list), using vw goes wrong—the radius stays until the element touches the viewport edge, not the container edge.

The fix is to declare container-type on the parent and replace vw in the formula with the container query unit cqi (the container inline-size unit in the logical direction; cqw is equivalent in most scenarios). Then the radius switch threshold follows the container boundary, and the radius drops to zero when the element touches the container edge.

.parent { container-type: inline-size; }
.child {
  border-radius: clamp(0px, (100cqi - 100%) * 9999, 24px);
}

When no container is defined, cqi falls back to the viewport size, so the same snippet works in both scenarios without writing two sets of rules.

container-type is the foundational declaration for container queries, with common values inline-size, size, and normal. inline-size means the element becomes a queryable container in the inline direction (the width in horizontal writing mode), and children can use units like cqw (1% of container width), cqi (1% of container inline size), and cqmin/cqmax; size establishes size containment in both the inline and block directions, which is more expensive but supports block-level units like cqh and cqb. Note that declaring container-type establishes a containment context for the element, which may affect how its own size is computed—so it's usually added to a parent wrapper with a defined size, not directly to an element that needs to adapt to its content.

The practical value of this approach

Typical use cases for conditional border-radius include full-width mobile cards, modals, and adaptive layouts that are "full-bleed on small screens, inset on large screens." Traditional approaches either hard-switch at breakpoints with media queries—causing jumps near the breakpoint—or measure element width with JS and change styles dynamically, adding runtime overhead.

Using clamp() with viewport/container units hands the decision logic to the CSS engine: one line of code, responding continuously to window resizing. The cost is needing to understand unit semantics and scrollbar behavior, and to validate against your target browsers. For UI details where you want "one less layer of JS," this trick is worth adding to your toolbox.

One-line Summary

A pure CSS conditional border-radius approach using clamp and viewport/container units that auto-zeros at the edge.

Paragraph Summary

The article introduces a pure CSS conditional border-radius trick: rounded corners while floating, square corners when touching the edge. The core formula is calc(100vw - 100%), which makes the radius vary with the element's distance from the viewport edge and zero out at the edge. But that difference produces a soft transition zone, so it's multiplied by a large coefficient and put into clamp() to lock values between 0 and the design cap, making the radius switch instantly at the threshold. The article also discusses scrollbar-induced 100vw calculation偏差, mitigated with scrollbar-gutter: stable; and using cqi units with container-type inside containers so the threshold follows the container boundary, falling back to the viewport when no container exists. The approach needs no JavaScript and suits adaptive scenarios like full-width mobile cards and modals.

#CSS conditional border-radius#clamp()#viewport units#container query units#scrollbar-gutter
Share:

Related articles