Home / Snippets / Animation & Motion /

Border draw

A border that traces itself on hover using pseudo-elements and scaleX/scaleY transitions.

Hover me
Widely Supported
animationno-js

Quick implementation

.border-draw {
  position: relative;
  display: inline-block;
  padding: 1rem 2rem;
}

/* Top and bottom edges via ::before / ::after */
.border-draw::before,
.border-draw::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  height: 2px;
  background: oklch(0.52 0.22 265);
  transition: transform 0.35s ease;
}
.border-draw::before {
  top: 0;
  transform-origin: left;
  transform: scaleX(0);
}
.border-draw::after {
  bottom: 0;
  transform-origin: right;
  transform: scaleX(0);
}

/* Left and right edges via sibling spans (or extra pseudo on a wrapper) */
.border-draw .bd-left,
.border-draw .bd-right {
  position: absolute;
  width: 2px;
  top: 0; bottom: 0;
  background: oklch(0.52 0.22 265);
  transition: transform 0.35s ease 0.35s;
}
.border-draw .bd-left  { left: 0;  transform-origin: bottom; transform: scaleY(0); }
.border-draw .bd-right { right: 0; transform-origin: top;    transform: scaleY(0); }

/* Trigger on hover */
.border-draw:hover::before,
.border-draw:hover::after  { transform: scaleX(1); }
.border-draw:hover .bd-left,
.border-draw:hover .bd-right { transform: scaleY(1); }

@media (prefers-reduced-motion: reduce) {
  .border-draw::before,
  .border-draw::after,
  .border-draw .bd-left,
  .border-draw .bd-right { transition: none; }
}

Prompt this to your LLM

Includes role, constraints, two framework variants, and edge cases to handle.

You are a senior frontend engineer building a CSS interaction library.

Goal: A border-draw hover effect — the border traces around an element on hover using only CSS, no JavaScript.

Technical constraints:
- Use ::before and ::after pseudo-elements for the top and bottom edges.
- Use two additional positioned elements (spans or a wrapper's children) for the left and right edges.
- Animate using transform: scaleX(0→1) and scaleY(0→1) with transform-origin to control draw direction.
- Sequence the vertical edges slightly after the horizontal ones using transition-delay.
- Use oklch() for border color, not hex or rgba().
- Include @media (prefers-reduced-motion: reduce) to disable transitions.

Framework variant (pick one):
A) Vanilla CSS — utility class applied to any inline or block element with two child spans for vertical edges.
B) React component — wraps children, injects the required spans and styles automatically.

Edge cases to handle:
- Works on both inline and block-level elements.
- Does not shift surrounding content (position: absolute edges, not border-width changes).
- Color and thickness should be CSS custom-property overrides.
- Ensure the parent has position: relative.

Return CSS and minimal HTML markup.

Why this matters in 2026

Border draw is a staple hover effect for navigation items, CTAs, and feature cards. Done in pure CSS — no JavaScript mousemove listeners — it is lightweight, accessible, and performant. The sequenced timing creates a satisfying "trace" feel that draws the eye without overwhelming motion-sensitive users.

The logic

Each edge is a thin rectangle anchored to one side of the container with position: absolute. The horizontal edges (top and bottom) start at transform: scaleX(0) and scale to 1 on hover; transform-origin controls which end leads the draw. The vertical edges follow with a transition-delay equal to the horizontal duration, creating a clockwise trace. Because only transform changes, no layout or paint work occurs — just compositing.

Accessibility & performance

@media (prefers-reduced-motion: reduce) sets transition: none on all edge elements, so the border appears instantly on hover rather than animating. transform is compositor-only — no layout recalculation triggered. Because the edges are purely decorative, they carry no semantic meaning; if you rely on this effect to convey focus state, pair it with a visible :focus-visible style using outline rather than the draw animation alone.