Overview
The hover system provides a consistent “lift” interaction for cards, links, and button-cards: smooth scale + shadow, a color overlay fade, and synchronized text / heading / icon color shifts. Themes only change tokens (colors); the behavior and timing remain consistent.
- Pick one theme: use
hover-*on the interactive element (example:hover-dark). - Bulk apply: use
child-hover-*on a parent to auto-hover all direct children. - Opt out: add
no-hoverto a direct child to prevent hover behavior. - Keyboard users: hover visuals also trigger on
:focus-visibleand:focus-within. - Icons are decorative by default: add
aria-hidden="true"to icon elements.
Primary and Secondary buttons Have specific styles
See: Standard Button Styles
Samples
Parent mode
Use on an element
Add exactly one hover-* class to the interactive element (usually an <a> card).
Use this when you want to control hover behavior per card or per link.
Example (icon card link)
<a href="images.html" class="btn card-lifted hover-dark flex-v middle">
<i class="icon icon--arrow-right item-t-r" aria-hidden="true"></i>
<h6>Ratio Calc</h6>
<i class="fs-lg icon icon--images" aria-hidden="true"></i>
</a>
Example (content card link)
<a href="#" class="card-lifted btn-card in-dwn-sm hover-dark">
<h4>Student Leadership and Success Studies</h4>
<p>Promotes holistic student development and advances students...</p>
<i class="fa-solid fa-arrow-up-right" aria-hidden="true"></i>
</a>
Use <a href="..."> for navigation. Use <button type="button"> for in-page actions.
Avoid non-interactive containers (like <div>) pretending to be buttons.
Use on a parent (auto-apply)
Add a child-hover-* class to a parent container. Every direct child becomes hoverable automatically.
Use this for grids/lists where every item should behave the same.
Example
<div class="grid grid-cols-1 md:grid-cols-2 gap-md child-hover-dark">
<a href="#" class="card-lifted btn-card in-dwn-sm">
<h4>Card 1</h4>
<p>This card hovers because it is a direct child.</p>
<i class="fa-solid fa-arrow-up-right" aria-hidden="true"></i>
</a>
<a href="#" class="card-lifted btn-card in-dwn-sm no-hover">
<h4>Opt-out</h4>
<p>This card does not hover because it has .no-hover.</p>
<i class="fa-solid fa-arrow-up-right" aria-hidden="true"></i>
</a>
</div>
Parent mode targets .parent > *. If you wrap cards in extra containers, the wrapper becomes the hover target.
Keep the hoverable card as the direct child of the parent.
Theme options
Theme classes only set tokens. Behavior and timing remain identical.
Element themes
hover-greenhover-wolverinehover-darkhover-softhover-super-green
Parent themes
child-hover-greenchild-hover-wolverinechild-hover-darkchild-hover-softchild-hover-super-green
Theme tokens
Themes customize hover by setting tokens (CSS variables). Use these consistently so all themes behave the same.
| Token | Controls | Notes |
|---|---|---|
--hover-bg |
Overlay background | Applied on ::before |
--hover-text |
Hover text color | Applied to the hoverable element |
--hover-headline |
Heading color on hover | Targets h1–h6 inside |
--hover-scale |
Scale amount | Default: 1.05 |
--hover-shadow |
Lift shadow | Default: subtle; dark uses deeper shadow |
Create a theme
Add a new theme by defining token values. Keep the theme token set consistent. Always create both an element theme and a parent theme.
/* Theme tokens only (behavior stays identical) */
.hover-sunrise,
.child-hover-sunrise{
--hover-bg: var(--color-valley-green);
--hover-headline: var(--color-white);
--hover-text: var(--color-white);
--hover-scale: 1.05;
--hover-shadow: 0 8px 26px rgba(0,0,0,0.18);
}
- Use simple names that reflect the visual intent:
hover-dark,hover-soft,hover-super-green. - Avoid state-like names (example: “hover-active”).
- Keep themes scoped to token values only (no behavior changes).
Using with .anim
The animation system uses transform with !important. Hover also uses transform.
If both are on the same element, hover scale can break unless the compatibility patch is present.
If an element has both .anim and a hover-* class, the Hover + Anim compat patch must be present
and loaded after the animation CSS.
Compat patch (must load after animation CSS)
/* HOVER + ANIM COMPAT PATCH (NO MARKUP CHANGES) */
:where([class^="hover-"], [class*=" hover-"]).anim,
:where([class^="child-hover-"], [class*=" child-hover-"]) > *:not(.no-hover).anim{
transition-property: opacity, filter !important;
}
:where([class^="hover-"], [class*=" hover-"]).anim{
transform: scale(1) !important;
}
:where([class^="child-hover-"], [class*=" child-hover-"]) > *:not(.no-hover).anim{
transform: scale(1) !important;
}
:where([class^="hover-"], [class*=" hover-"]).anim:is(:hover, :focus-visible, :focus-within){
transform: scale(var(--hover-scale)) !important;
}
:where([class^="child-hover-"], [class*=" child-hover-"]) > *:not(.no-hover).anim:is(:hover, :focus-visible, :focus-within){
transform: scale(var(--hover-scale)) !important;
}
Slide/zoom entrance effects also rely on transform. If you need slide/zoom entrance motion and hover scale on the
exact same element, transforms will compete. Prefer entrance motion on a child wrapper and hover on the parent interactive element.
Accessibility + motion
Keyboard behavior
- Hover visuals also apply on
:focus-visibleand:focus-within. - The focus outline is theme-aware and must remain visible.
- Use meaningful labels on the interactive element (not icon-only).
Reduced motion
prefers-reduced-motionremoves scale and shortens transitions.@media (hover:none)disables hover-scale to avoid sticky hover on touch.
Avoid
| Avoid | Why | Use instead |
|---|---|---|
Multiple hover-* themes on one element |
Conflicting tokens; unpredictable results | Pick one theme per element |
| Hover as the only sign a card is clickable | Touch users may never see hover | Use card styling that reads clickable by default |
.anim + hover-* without compat patch |
Transform conflicts break scale timing | Include the patch after animation CSS |
Using child-hover-* but wrapping cards in extra containers |
Parent mode targets direct children only | Make the card the direct child |
Dark Mode - needs work
It seems that you have reduce motion enabled!