Cutting Out Postage Stamp Edges

A postage-stamp effect on an image needs no extra markup. Two gradients and a filter are enough, provided you give the image breathing room with padding. A background gradient fills that padded area, and two gradients combine to punch circles along the edges: a radial-gradient() cuts each circular notch while a linear-gradient() defines the straight portions of the border between notches.

The key detail is the round value on the background repetition. Without it, the notch pattern would have an awkward partial gap wherever the image’s dimensions don’t divide evenly. The CSS specification notes that round rescales the repeated image so it always fits a whole number of times inside the background positioning area, guaranteeing the notches align cleanly on every side regardless of the image’s aspect ratio.

Masking a Circular Frame

A different treatment uses circles that ring the image rather than cutting into it. Again, a radial-gradient() with the round value lays down the pattern. The harder part is creating the transparent gap between that ring of circles and the image itself.

The mask property solves that. The mask combines a linear-gradient(), which reveals the image area, with a conic-gradient() that reveals 20px on each side of the image — the frame thickness. The region between those two gradients is masked out, producing the visual transparent gap. Using two differently-natured gradients side by side inside a mask is the core idea you can carry into many decorative effects.

Inner Border and a Font-Size Trick

You can also create a transparent border that sits inside the image’s own bounds. Same structural approach: a linear-gradient() for the inner area and a conic-gradient() for the outer edges, with space between them masked away to form the transparent border.

Two things are worth noting here. First, the conic-gradient() syntax differs from what you saw with the frame effect, despite producing the same outer shape. Many gradient configurations lead to identical results; which syntax you choose is a matter of style and constraints. Smaller code with fewer gradients and calculations is a reasonable objective, but occasionally a more verbose version is preferable for the flexibility it buys you in variable-driven designs.

Second, the border can shift on hover by animating a custom property indirectly. The distance of the border from the image’s edge is stored in --d, initially 1em. That length gets modified by a second custom property --_d, so that movements in the border can be triggered even though the underlying property controlling it is not directly animatable. The mechanism is based on a change in font-size: if font-size is 0, then 1em is also zero and no shift occurs. Setting font-size to a nonzero value forces all em-based lengths to rescale, effectively allowing a variable change to update an otherwise static distance. This trick remains useful since @property-powered custom properties have incomplete browser support.

Reusing Masks for a Gradient Frame Reveal

Hover reveals for images can also be repurposed for gradient frames. The reveal mechanism stays exactly the same as with a solid-colored frame, but two modifications expand it considerably. The mask configuration from the earlier postage-stamp example is repurposed by moving its gradient stack from the background property into the mask property. Then, in the background itself, a repeating-linear-gradient() generates the stripes of the now-revealed frame.

A Multi-Step Frame Animation

Beyond two-state reveals, you can build multi-step frame animations where edges light up one after another. A three-step effect progressively exposes the bottom edge, then the left and right edges, and finally the top edge.

The first step grows the bottom edge by changing the background-size of a linear gradient. Since a single gradient covers both an edge that paints during this step and a top edge that stays hidden until the last step, one gradient serves two purposes. The second step introduces a second gradient to draw the sides, this time controlled via background-position.

The final step calls on the mask property to delay the top edge’s appearance. Two conic gradients inside the mask have a width of zero so they cover everything except the top region. Sliding their position on hover leaves only that top area to fill. You can combine mask and background work over a three-step animation without abandoning the single-element constraint. A more advanced version, containing a four-step variant, follows the same logic: dissect the movement each gradient contributes to understand how the complete animation is assembled.

The same baseline tactic — pairing gradients that draw a decoration with mask shapes that selectively hide parts of an image — extends to many of the effects you can build from a single <img> tag. The range of achievable results is expansive enough that further demos, like animations that seem to break and reassemble an image, need no change in technique.