Buttons✨ Premium

Slide Reveal Icon

An arrow hidden in a zero-width span; on hover, its max-width goes to 24 px, its opacity to 1 and an 8 px gap opens — the button widens by ≈ 18 px to the right in 300 ms, pure CSS.

CSSHoverIcon

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Buttons has 27 effects, including 2 free. Explore the category →

3 usage examples

Explore destinations — travel site landing page — Slide Reveal Icon example 1

① Explore destinations — travel site landing page

WhenPhotographic or gradient hero: headline, subtitle, one entry button.
WhyThe unfolding arrow promises movement: it's the quintessential “go” button. Green evokes responsible travel; darkened, it holds the contrast. On a hero, the 18 px widening disturbs nothing.
Settings16 px text, padding: 17px 24px 17px 36px, #065f46 → #047857 gradient, 18 px SVG arrow (max-width: 26px).
Read more — article card — Slide Reveal Icon example 2

② Read more — article card

WhenArticle grid: image, title, excerpt, a read button aligned left in each card.
WhyThe arrow appearing on hover signals navigation (you're leaving the grid) without cluttering the card at rest. Aligned left, the button grows rightward into free space: no neighbor is displaced.
SettingsCompact button (padding: 10px 14px 10px 18px, 13.5 px), gap: 6px on hover, border-radius: 8px, .25s duration.
Continue — step of a setup wizard — Slide Reveal Icon example 3

③ Continue — step of a setup wizard

WhenMulti-step wizard: progress indicator, a field or a choice, Back and Continue buttons.
WhyThe right arrow is the natural glyph for “next”; revealing it on hover keeps the button sober until it's chosen. Place it at the right end of the row so it grows toward the margin.
Settingspadding: 13px 20px 13px 26px, 14.5 px, icon (U+2192, thinner than ➔), max-width: 20px, dark gradient for contrast.

How it works

.btn-icon-reveal is a display: inline-flex; align-items: center button with gap: 0 and a padding-right: 20px shorter than the left one (32 px inherited from .demo-btn). Its label is a text node, the icon a <span class="btn-icon">➔</span> placed after it. linear-gradient(135deg, #059669, #10b981) background, white text, transition: .3s on all properties.

At rest, .btn-icon is collapsed: max-width: 0; overflow: hidden; opacity: 0. It takes zero pixels of width and the gap: 0 leaves no space between the text and it: the button seems to hold only a label, slightly off-center to the right (20 px of padding against 32).

On :hover, three values change over .3s: max-width: 24px and opacity: 1 on the icon, gap: 8px and padding-right: 16px on the button. Measured in Chromium: the ➔ arrow measures 14.3 px at 16 px font size, so the button goes from 119.6 to 137.9 px — +8 (gap) +14.3 (icon) −4 (padding) = +18.3 px. The 24 px max-width is only a cap: the real width is the glyph's.

max-width, gap and padding are layout properties: the button really grows and pushes whatever follows it (or re-centers if it is centered). The icon appears sliding in from the left because its container opens left to right — there is no transform.

Accessibility

  • Insufficient contrast as shipped: white text on the #059669 → #10b981 gradient = 3.8:1 and 2.5:1, below the 4.5:1 AA threshold — the light half even falls below 3:1. Switch to linear-gradient(135deg, #065f46, #047857) (7.7:1 and 5.5:1).
  • Screen readers: the arrow is a text character (, U+2794) and is part of the accessible name — announced “heavy wide-headed rightwards arrow” after the label by VoiceOver. The shipped HTML doesn't hide it: add aria-hidden="true" on .btn-icon.
  • prefers-reduced-motion not handled: the 18 px widening over 300 ms is a layout movement that displaces neighbors. Add @media (prefers-reduced-motion: reduce) { .btn-icon-reveal, .btn-icon-reveal .btn-icon { transition: none } } — the icon appears at once.
  • Keyboard: no :hover for Tab navigation. Duplicate both hover rules under .btn-icon-reveal:focus-visible and .btn-icon-reveal:focus-visible .btn-icon. The native outline is not removed and follows the widening.
  • Touch: on iOS the expanded state sticks after a tap; the arrow stays visible until the next touch (@media (hover: hover)). Without hover, the icon is never seen on mobile: it must carry no information. Target ≈ 50 px tall, above the 44 px minimum.

Browser compatibility

Requires display: inline-flex with the gap property on a flex container, and transitions of max-width, gap, padding and opacity. No JavaScript, zero dependencies.

Chrome 84+✓ Full
Firefox 63+✓ Full
Safari 14.1+✓ Full
Edge 84+✓ Full
Mobile iOS 14.5+✓ Static (no hover)
Android Chrome✓ Static (no hover)

Without gap on flex (Safari before 14.1, Chrome before 84), the icon appears glued to the text, without the 8 px space; everything else works. Without transitions, the arrow and the widening pop in at once on hover.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
max-width (CSS — :hover .btn-icon) 24px Width cap of the expanded icon. Match it to the real width of the glyph or SVG (14 px for ➔ at 16 px): a value too large lengthens the transition without showing anything.
gap (CSS — .btn-icon-reveal 0 / :hover 8px) 0 → 8px Text–icon spacing. Its opening is part of the widening; 4px tightens, 12px detaches the arrow from the word.
padding-right (CSS — .btn-icon-reveal 20px / :hover 16px) 20 → 16px Partial compensation of the widening (−4 px). Use 12px to absorb more, or match the left padding at rest for a centered label.
transition (CSS — .btn-icon-reveal and .btn-icon) .3s Unfolding duration. .2s = brisk; .45s = slow, the widening then reads as a movement in its own right.
opacity (CSS — .btn-icon 0 / :hover 1) 0 → 1 Fade of the icon during the opening. Remove the opacity transition for a crisp arrow that “comes out” of the edge, or combine with transform: translateX(-4px) → 0 for a slide.
Icon (HTML — span.btn-icon) ➔ (U+2794) The glyph depends on the font and the system (sometimes rendered as a color emoji). An inline 16 × 16 px SVG with fill: currentColor is identical everywhere — give it width: 16px; flex: none.
background (CSS — .btn-icon-reveal) linear-gradient(135deg, #059669, #10b981) Button color. For AA contrast with white text: #065f46 → #047857.

FAQ

That's the code's behavior: max-width, gap and padding change the layout width, by about +18 px. Three solutions. Place the button at the end of a row (right-aligned) or in a container wider than itself: it grows into empty space. Reserve the room from rest: .btn-icon { max-width: 24px; opacity: 0; transform: translateX(-8px) } with a fixed gap: 8px, then animate only opacity and transform on hover — the arrow slides into place without changing the width. Or set the button's min-width to its expanded width.
(U+2794) is a Dingbats character: its shape comes from the system's fallback font, and some mobile browsers render it as a color emoji. Replace it with an inline SVG: <svg class="btn-icon" width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path d="M2 8h10M8 4l4 4-4 4" stroke="currentColor" stroke-width="2" fill="none"/></svg>, keeping the .btn-icon rules (max-width, overflow: hidden, opacity) and adding flex: none so it doesn't get squeezed.
Place the span.btn-icon before the label in the HTML and swap the paddings: padding-left: 20px at rest, 16px on hover, fixed padding-right: 32px. The container still opens left to right (max-width grows from the span's left edge), so the icon seems to unroll toward the text. For the opposite unrolling, add direction: rtl on the span alone.