A Single-Element Approach to Fancy Image Decorations

The challenge: decorate an <img> element using only that single tag — no wrapper <div>s, no pseudo-elements, just the image itself. On the surface, that seems overly restrictive. But CSS properties such as border, box-shadow, outline, and especially background leave a surprising amount of room for creative styling.

The core trick is to create space around the image with padding or border, then draw decorations inside that space using background gradients. Because gradients support hard color stops, they can form geometric shapes, not just smooth transitions. Combined with clip-path, masking, and even outline, you can build frames, borders, and hover animations — all with minimal CSS.

Building a Simple Frame with One Gradient

The first example uses a single gradient, a transparent border, and padding to create a frame effect around the image:

img {
  --s: 10px; /* control the size */
  padding: var(--s);
  border: calc(2 * var(--s)) solid #0000;
  outline: 1px solid #000;
  outline-offset: calc(-1 * var(--s));
  background: conic-gradient(from 90deg at 1px 1px, #0000 25%, #000 0);
}

By declaring both padding and a transparent border, you control where the gradient appears. The trick hinges on the default values of two background properties: background-clip: border-box and background-origin: padding-box.

  • The gradient is drawn relative to the padding area.
  • The same repetition of the gradient extends behind the transparent border.
  • Once the border color is transparent, that repeated segment becomes visible as the frame.

Adding an outline with a negative offset places a square accent near the top of the frame. That single gradient plus outline keeps the code lean. A gradient-only alternative is possible but requires a more verbose syntax to replicate the outline effect. A separate variation uses clip-path instead of outline, trimming the gradient into four sharp corners rather than a full perimeter.

--b: 5px; /* border thickness */
background: conic-gradient(from 90deg at top var(--b) left var(--b), #0000 90deg, darkblue 0) 0 0;
background-size: 50px 50px; 
background-repeat: no-repeat;

The Corner-Only Frame

This effect uses four separate gradients, each drawn at 50×50px and anchored to one corner of the image. The trick is creating a hard stop so only part of the gradient is visible. The spec-compliant way to write a hard stop is:

#0000 25%, darkblue 25%

But there's a shorter form. Per the CSS Images spec, a color stop whose position is set to 0 is automatically bumped up to the largest previous stop position. Writing 0 in place of the second percentage yields the same hard stop, saving a few characters. So the gradient declaration reads:

#0000 25%, darkblue 0

Repeated for all four corners with CSS variables to hold the color and size, the code becomes compact:

img {
  --b: 5px; /* border thickness */
  --c: #0000 90deg, darkblue 0; /* define the color here */
  padding: 10px;
  background:
    conic-gradient(from 90deg  at top    var(--b) left  var(--b), var(--c)) 0 0,
    conic-gradient(from 180deg at top    var(--b) right var(--b), var(--c)) 100% 0,
    conic-gradient(from 0deg   at bottom var(--b) left  var(--b), var(--c)) 0 100%,
    conic-gradient(from -90deg at bottom var(--b) right var(--b), var(--c)) 100% 100%;
  background-size: 50px 50px; /* adjust border length here */
  background-repeat: no-repeat;
}

On hover, the gradient size increases to cover the whole image area. A value of 51% rather than 50% ensures a small overlap and avoids any hairlines at the seams during animation:

img:hover {
  background-size: 51% 51%;
}

Two-Gradient Frame with Complex Animation

A second hover effect uses just two gradients and a more involved animation. Each gradient initially sits partially off-canvas, then slides into position to form the top and right edges, then expands upward to complete the frame. This requires the gradient width to exceed 100% — typically 200% — because of how background-position behaves. With only 100% width, the gradient cannot be shifted far enough off-canvas to animate in from outside the visible area.

img {
  --b: 8px;  /* border thickness*/
  --s: 60px; /* size of the corner*/
  --g: 14px; /* the gap*/
  --c: #EDC951; 

  padding: calc(var(--b) + var(--g));
  background-image:
    conic-gradient(from  90deg at top    var(--b) left  var(--b), #0000 25%, var(--c) 0),
    conic-gradient(from -90deg at bottom var(--b) right var(--b), #0000 25%, var(--c) 0);
  background-position:
    var(--_p, 0%) var(--_p, 0%),
    calc(100% - var(--_p, 0%)) calc(100% - var(--_p, 0%));
  background-size: var(--s) var(--s);
  background-repeat: no-repeat;
  transition: 
    background-position .3s var(--_i,.3s), 
    background-size .3s calc(.3s - var(--_i, .3s));
}
img:hover {
  background-size: calc(100% - var(--g)) calc(100% - var(--g));
  --_p: calc(var(--g) / 2);
  --_i: 0s;
}

Note the convention of internal variables: names prefixed with an underscore, like --_i or --_p, mark variables used solely to keep the code DRY. Public variables such as --b, --c, or --g are for you to fine-tune the frame's thickness, color, and spacing.

Showing the same image of two classic cars three times to illustrate the CSS variables used in the code.

The Frame Reveal

Here, on hover, lines draw themselves around the image and then disappear in the opposite direction on mouse-out. The implementation applies the same gradient four times, each positioned to cover only one side at a time.

An even more visually interesting variant uses two conic gradients. On hover, one gradient animates into place to cover the top and right edges, shifting position first, then expanding its height. The second gradient mirrors this for the left and bottom edges by swapping the size and position values:

img {
  --c: #8A9B0F; /* the border color */
  --b: 10px; /* the border thickness*/
  --g: 5px;  /* the gap */

  padding: calc(var(--g) + var(--b));
  --_g: #0000 25%, var(--c) 0;
  background: 
    conic-gradient(from 180deg at top    var(--b) right var(--b), var(--_g))
     var(--_i, 200%) 0 / 200% var(--_i, var(--b))  no-repeat,
    conic-gradient(            at bottom var(--b) left  var(--b), var(--_g))
     0 var(--_i, 200%) / var(--_i, var(--b)) 200%  no-repeat;
  transition: .3s, background-position .3s .3s;
  cursor: pointer;
}
img:hover {
  --_i: 100%;
  transition: .3s, background-size .3s .3s;
}

Both gradients are near-identical, just positioned and sized differently. Each is set to 200% width, which again relates to the background-position travel distance.

Angled Frame Illusion

The final hover trick doesn't draw a frame — it distorts an existing one. The trick is that straight lines only appear to become angled. In reality, each of the four gradients animates independently, sliding across its edge. Where two adjacent gradients meet, they briefly overlap, creating the illusion of a diagonal corner. When the overflow is hidden and the outline removed, it looks like the frame rotates:

img {
  --g: 4px; /* the gap */
  --b: 12px; /* border thickness*/
  --c: #669706; /* the color */

  padding: calc(var(--g) + var(--b));
  --_c: #0000 0 25%, var(--c) 0 50%;
  --_g1: repeating-linear-gradient(90deg ,var(--_c)) repeat-x;
  --_g2: repeating-linear-gradient(180deg,var(--_c)) repeat-y;
  background:
    var(--_g1) var(--_p, 25%) 0, 
    var(--_g2) 0 var(--_p, 125%),
    var(--_g1) var(--_p, 125%) 100%, 
    var(--_g2) 100% var(--_p, 25%);
  background-size: 200% var(--b), var(--b) 200%;
  transition: .3s;
}
img:hover {
  --_p: 75%;
}
Similar logic drives a variation that uses repeating gradients and velocity changes on mouse-out. If you can trace one gradient's path, the other three follow the same choreography at different sides.

Why Gradients?

The constraint of one <img> tag limits your toolbox to roughly four CSS features: border, outline, box-shadow, and background. Among these, gradients embedded in background-image are by far the most programmable option. They deliver patterns, geometry, and animation surfaces without semantics or markup cost. Any reader who wants the full mechanical detail on gradient patterns and background-position quirks should review: