Content Reveal
Title shown, content hidden: on hover the title slides up while the text panel rises from the card's bottom edge — pure CSS, zero JavaScript.
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. Cards has 41 effects, including 6 free. Explore the category →
3 usage examples



How it works
The card (.card-content-reveal) relies on overflow: hidden paired with position: relative. The .card-content panel is absolutely positioned at the bottom (bottom: 0) and starts at translateY(100%) — entirely below the lower edge, hidden by the clipped overflow.
On :hover, two simultaneous transform .4s transitions fire: the title (.card-reveal-title, with z-index: 1) shifts up 30 px (translateY(-30px)) to clear the lower area; the content panel returns to translateY(0) and slides up from the card's bottom edge.
The content panel carries a background: linear-gradient(transparent, rgba(0,0,0,.9)) that ensures text readability regardless of the card's background color. The transparent-to-dark gradient hides nothing before hover.
The effect is 100% declarative CSS — the js field is empty. It can be applied directly via CSS classes in any framework (React, Vue, Svelte) or any CMS that outputs static HTML.
Accessibility
- prefers-reduced-motion not implemented: the code contains no
@media (prefers-reduced-motion: reduce)block. Transitions continue even when the OS requests reduced motion. Add manually:@media (prefers-reduced-motion: reduce) { .card-content-reveal .card-reveal-title, .card-content-reveal .card-content { transition: none } }. - Keyboard navigation absent: the reveal only fires on
:hover. A<div>is not focusable by default. Addtabindex="0"on the container and mirror the:hoverrules under:focus-withinfor keyboard users. - Hidden content readable by screen readers: text inside
.card-content(attranslateY(100%)) is in the DOM and is neitherdisplay:nonenorvisibility:hidden— it remains accessible to assistive technologies even in its hidden state. - Panel contrast: the
rgba(0,0,0,.9)gradient behind white text delivers a ratio ≥ 7:1 (WCAG AAA) on the revealed panel. - Semantic structure: the container is a plain
<div>with no role. Replace it with<article>or<li>as appropriate and add anaria-labeloraria-labelledbypointing to.card-reveal-title.
Browser compatibility
Uses only CSS Transitions and absolute positioning — no JavaScript, no Canvas, no recent browser API required.
Without CSS Transitions support, the card shows its resting state (centered title, blue gradient background). The content in .card-content remains in the DOM but is not visually accessible on hover — no JS error, the page stays functional.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="card-demo card-content-reveal">
<span class="card-reveal-title">Visible title</span>
<!-- Content revealed on hover -->
<div class="card-content">
<p>Any text or HTML</p>
</div>
</div>
<!-- Note: card-demo provides the 280×180 px demo dimensions.
In production, replace it with your own sizing class. -->
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| --bg-card | #12121a | Card background color when no gradient is defined. Override this CSS variable on the card element for a solid-color look. |
| --radius-xl | 1rem | Corner radius of the card. Set to 0 for sharp edges or 1.5rem for heavily rounded corners. |
| background (`.card-content-reveal`) | linear-gradient(135deg, #3b82f6, #1d4ed8) | Card background gradient. Replace with your brand colors, a solid color, or a CSS image. |
| transition duration (`.card-reveal-title`, `.card-content`) | .4s | Reveal animation duration. .2s = instant response, .6s = slow cinematic fade. |
| translateY (`:hover .card-reveal-title`) | -30px | How far the title shifts up on hover. Adjust to match your font size and card height (e.g. -40px for a taller card). |
| background (`.card-content`) | linear-gradient(transparent, rgba(0,0,0,.9)) | Scrim behind the revealed panel. Reduce final opacity (rgba(0,0,0,.6)) for a more transparent look on light-colored cards. |
| padding (`.card-content`) | 20px | Inner padding of the revealed content panel. Increase to 28px for more breathing room, reduce to 12px on small cards. |
FAQ
:hover state and the second tap dismisses it. This works but can feel unintuitive. For a better mobile experience, add a JS touchstart listener that toggles an .is-revealed class on the card, and mirror the :hover rules under .card-content-reveal.is-revealed.tabindex="0" to the .card-content-reveal container, then in your CSS change the :hover selectors to :hover, :focus-within. The card will then reveal on click or keyboard focus, with no extra JavaScript needed.transition property: add opacity .4s to .card-content, set opacity: 0 at rest and opacity: 1 on hover. The fade overlays the translation for a softer reveal.