SVG✨ Premium

Circular Reveal

A hidden face that opens in a circle born under the pointer, then follows it while it stays on the card. Mouse, finger and keyboard.

JSClip-pathInteractive

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. SVG has 30 effects, including 3 free. Explore the category →

3 usage examples

An estate agency's neighbourhood map: prices per m² appear around the pointer — Circular Reveal example 1

① An estate agency's neighbourhood map: prices per m² appear around the pointer

WhenAn agency's “Prices by neighbourhood” page: price legend on the left, town map on the right. The map is dark and neutral; as soon as the mouse enters, a coloured disc is born under it and shows blocks tinted by average price per m², with a few figures. Moving around, the disc follows and sweeps the town.
WhyVisitors keep a readable map (streets, river, park) and only bring up the data where they are looking: they compare two nearby areas without switching maps or opening a filter.
SettingsTwo SVGs with the same geometry and .cvr-media: neutral map in .cvr-front, price map in .cvr-back. --cvr-w:100%, --cvr-h:100%, --cvr-size:72% (a disc that follows rather than a full page), --cvr-speed:.45s, --cvr-front:#151a20, --cvr-back:#f3ede4.
A design studio's full-screen menu: each item's background bursts from the entry point — Circular Reveal example 2

② A design studio's full-screen menu: each item's background bursts from the entry point

WhenA studio's open menu: five large items (Identities, Publishing, Spaces, Objects, Contact) with their project counts. Each row is a link; entering from the left makes the golden fill start on the left, entering from the right makes it start on the right.
WhyA fill that starts from the mouse makes the menu feel like it answers the gesture, without moving anything: labels stay put and readable.
SettingsThe root is the menu's <ul>; .cvr-card on each <a> (no tabindex). --cvr-w:100%, --cvr-h:48px, --cvr-front:transparent, --cvr-back:linear-gradient(100deg,#f6d365,#fda085), --cvr-back-color:#1b130c, --cvr-size:150% so the whole row is covered even from one end. The label is repeated in .cvr-back with aria-hidden="true".
E-reader settings: each theme tile fills with its colour from the pointer — Circular Reveal example 3

③ E-reader settings: each theme tile fills with its colour from the pointer

WhenThe “Display” panel of a reading app, opened below the current page: three tiles, Light, Sepia and Night. Hovering a tile fills it with the theme colour from the pointer as a preview; clicking applies the theme to the page above.
WhyThe preview happens inside the tile itself, where you aim, before committing: you see the paper tone without the reading page changing on every pass.
SettingsThree 156 × 78 px <button aria-pressed> in one root, --cvr-size:150%, and one colour per tile set on the card (--cvr-back:#fbf9f4, #f3e6cc, #0b0e14, with a matching --cvr-back-color). Choosing the theme on click is wired by the page, not by the effect; in the capture, the capture mouse's click selects “Light”.

How it works

The .cvr-card stacks a visible face, .cvr-front, and a face to uncover, .cvr-back. Both take any content: text, a list, a button, or an image with the .cvr-media class, which then covers the whole face. The hidden face is cut by clip-path: circle(0% at var(--cvr-x) var(--cvr-y)): a radius-less circle whose centre comes from two variables.

When the mouse comes in, the script measures the entry point inside the card (as a percentage), moves the circle centre there without animating it, then adds the .cvr-on class: the radius grows to --cvr-size (100% by default) in 0.4 s. So the circle bursts out exactly where the pointer arrives — from the left it opens on the left, from below it opens at the bottom. While hovering, each move updates the centre and the circle smoothly catches up. On leaving, it closes towards the point where the mouse went out.

With a finger, one tap opens the circle where you touched and a second tap closes it; a link or button tapped inside the card keeps its action. With the keyboard, focusing the card (:focus-visible) or a focusable element inside it (:has(:focus-visible)) opens it too, from the last known point (the centre if the mouse never came by).

The script handles every .cvr-card in the page's .fx-0622-root roots, each with its own coordinates set as inline style on the card: several cards in one root, or several copies of the effect, work without getting in each other's way. No name is added to window.

Accessibility

  • Keyboard: on Tab the card (tabindex="0" in the demo) opens and gets a #67e8f9 outline. On an <a> or <button>, drop tabindex.
  • Screen readers: the hidden face stays in the page and is read out. When it repeats the visible face (a duplicated menu label, say), put aria-hidden="true" on .cvr-back.
  • Reduced motion: under prefers-reduced-motion: reduce the circle is no longer animated; the hidden face shows at once, whole, then hides at once.
  • Touch: one tap opens from the finger, a second one closes. A finger sliding to scroll the page triggers nothing.
  • Without JavaScript: hover does nothing any more, but keyboard focus still opens the card (from the centre) and the hidden content remains readable by screen readers.

Browser compatibility

Measured in Chromium 151, Firefox 153 and Playwright's WebKit 26.5: 0 errors, two copies on the page (hovering the 2nd copy from the left: circle centre measured 7 to 9% from the left edge in all three engines), Tab, tap that opens then closes, immediate rendering with reduced motion, nothing overflowing at 390 px. :focus-visible and :has() set the minimum versions.

Chrome 105+✓ Full (measured)
Firefox 121+✓ Full (measured)
Safari 15.4+✓ Full (measured in Playwright's WebKit)
Edge 105+✓ Full (Chromium engine)
Safari iOS 15.4+✓ Tap checked in emulation, not on a real device
Chrome Android 105+✓ Full (Chromium engine, tap checked in emulation)

Before those versions, mouse and finger work in older versions that have the inset property (Chrome 87, Firefox 66, Safari 14.1: the versions that brought the inset property used to stack the faces); only keyboard opening is missing.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0622-root">
  <div class="cvr-card" tabindex="0">
    <span class="cvr-front">Move &amp; Hover</span>
    <div class="cvr-back">
      <span>Magic!</span>
    </div>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
--cvr-w 280px Card width, capped by its container (100% for a menu row or a map).
--cvr-h 180px Card height.
--cvr-radius 1rem Corner rounding.
--cvr-front and --cvr-front-color #1a1a24 and #fff Background and text of the visible face.
--cvr-back and --cvr-back-color linear-gradient(135deg,#06b6d4,#3b82f6) and #fff Background and text of the uncovered face; one value per card gives different colours in the same root.
--cvr-size 100% Final circle radius. At 100%, entering from a corner leaves the opposite corner hidden; 150% always covers everything; 60–75% gives a disc that follows the pointer.
--cvr-speed .4s Opening and closing time, and how smoothly the circle catches up with the pointer.
--cvr-bg and --cvr-min-h #0a0a0f and 300px Root background and minimum height (transparent and 0 to fit it into a page).
.cvr-on class set by the script Open state. The script adds it when the pointer enters or on tap; you can add it yourself, the circle then starts from the last known coordinates.

FAQ

Shape Clip Reveal always opens its circle in the centre of the card, in CSS only. Circular Reveal starts it under the pointer and makes it follow, which needs the small script provided. Pick the first for a calm, symmetrical reveal, this one when the direction of the gesture matters (menu, map, grid).
Yes: for a circle, 100% means the card's diagonal divided by √2, about 235 px for 280 × 180; from a corner you would need the whole diagonal (333 px). Set --cvr-size:150% on the root or the card so the circle always covers everything.
Yes: <img class="cvr-media" src="…" alt="…"> inside .cvr-back. It fills the face (object-fit: cover); text placed after it shows on top. The same works for an SVG, like the price map in the example.