Lift

Raises the element slightly and deepens its shadow while the pointer is on it or a keyboard is inside it.

Example

View Code
<div class="cluster" data-gap="md">
    <article class="card lift" data-raised>
        <h3><a href="#lift-one" data-stretch>Hover me</a></h3>
        <p>The card rises and its shadow deepens.</p>
    </article>
    <article class="card lift" data-raised>
        <h3><a href="#lift-two" data-stretch>Or tab to me</a></h3>
        <p>A keyboard inside the card lifts it too.</p>
    </article>
</div>

When to use it

On something that can be pressed and is big enough for the movement to read: a card that is a link, a tile in a grid of choices, a panel that opens something. On a button it is noise — a button already has a pressed and a hovered state — and on a paragraph it is a lie, because nothing happens when you click it.

<article class="card lift" data-raised>
    <h3><a href="/yeti/" data-stretch>Yeti</a></h3>
    <p>for Websites</p>
</article>

How it works

A transition on translate and box-shadow, and one rule that raises the element --yeti-lift-distance and swaps in --yeti-lift-shadow. It answers three selectors: :hover, :focus-visible on the element itself, and :has(:focus-visible). The third is the one that matters most in practice, because the thing that takes focus is almost always inside — a card's stretched link, a button in a tile — and without it the lift would be an affordance only a pointer ever sees.

translate rather than transform, so an element that its own component already scales or rotates keeps that.

Accessibility

The lift sits on top of a focus ring; it never replaces one. The ring comes from the reset and nothing here touches it.

Reduced motion is answered differently here from everywhere else in Yeti, and on purpose. Everywhere else the animation runs once and ends, so collapsing its duration lands on the end state instantly, which is what was asked for. A hover has no end: its end state is a quarter of a rem up, so a collapsed duration would still move the element, just without the smooth part, and every pass of the pointer would jump the box out from under the cursor and drop it back on the way out. That is more startling than the animation it replaced. So --yeti-lift-distance goes to zero instead. The element does not move at all, the deeper shadow carries the whole hover, and the transition that is left has nothing to animate but that shadow.

Attributes

None. This is configured through its children and tokens only.

Children

No structural requirements.

Tokens

Token Description
--yeti-lift-ease The curve the lift runs on.
--yeti-lift-duration How long the lift takes; match a tinted hover to it.
--yeti-lift-distance How far the element rises; reduced motion takes it to zero.
--yeti-lift-shadow The shadow the element casts once lifted.

Accessibility

  • The lift answers :focus-visible on the element and on anything inside it, so a reader tabbing through a page of cards sees the same affordance a pointer gets. It is decoration on top of a focus ring, never a replacement for one; the ring comes from the reset and is untouched. Under reduced motion the element no longer moves and the deeper shadow carries the hover by itself.

Browser support

  • Used without guards: individual transform properties, :has()
  • Behind @supports: nothing

JavaScript

None. This component is CSS only.

Available since 7.0.0.