Enkel inn- og uttoning ved hovring med CSS3

Enkel inn- og uttoning ved hovring med CSS3

Sist verifisert: 15. oktober 2026
16 min lesetid
Veiledning

I jQuery-tiden ble hover-fade ofte løst med .animate() eller .fadeTo(). I 2026 er det unødvendig for enkle UI-tilstander: nettleseren kan interpolere opacity direkte med CSS. Resultatet er mindre skript, færre layout-thrash-risikoer og en effekt som følger systemets preferanser når du kobler inn prefers-reduced-motion.

Denne artikkelen er en praktisk front-end-oppskrift for WordPress-tema og Gutenberg. Du får en minimal fade på hover, deretter transition versus animation, tastaturvennlig :focus-visible, redusert bevegelse, korrekt wp_enqueue_style, typiske blokk-CSS-feller, og hvorfor compositor-egenskaper holder 60 fps mer forutsigbart enn width eller top.

#CSS transitions eller JavaScript for hover-effekter

JavaScript er riktig når tilstanden avhenger av nettverk, skjema-validering eller komplekse sekvenser. For «mus over bilde, demp litt» er CSS raskere å lese og billigere å kjøre.

Konkrete fordeler i praksis:

  • Ingen event-lyttere som må ryddes opp i spa-lignende admin-skjermer.
  • Nettleseren kan behandle opacity og transform på compositor-tråden når laget er etablert.
  • Samme regelsett fungerer for musepekere og for fokus når du speiler tilstanden med :focus-visible.
  • Du unngår race conditions mellom mouseenter / mouseleave og CSS som allerede animerer.

Unntaket er når du må synkronisere med et skriptstyrt lag (for eksempel en tilpasset lightbox som setter aria-hidden). Da kan du fortsatt bruke CSS for selve fade-en og bare bytte en klasse fra JavaScript.

#Fade bilde ved hover med CSS opacity

Start med et bilde eller en knapp. Sett startverdien eksplisitt, deklarer hvilken egenskap som skal interpoleres, og endre verdien i :hover.

.hover-image {
  opacity: 1;
  transition: opacity 0.3s ease-in-out;
}

.hover-image:hover {
  opacity: 0.7;
}

Hva som skjer steg for steg:

  1. Elementet er fullt synlig (opacity: 1).
  2. Ved hover går verdien mot 0.7 over 300 ms.
  3. Timing-funksjonen ease-in-out myker start og stopp.
  4. Når hover opphører, kjører overgangen tilbake til 1 med samme regel.

opacity er en tallverdi mellom 0 og 1. Den påvirker hele elementet inklusive barn. Hvis bare bakgrunnen skal dempes, er background-color med rgba() eller color-mix() ofte mer presist enn å dempe hele kortet.

#Transition versus animation

transition og animation er ikke utskiftbare verktøy. Velg etter hva som utløser bevegelsen.

Bruk transition når:

  • En egenskap endrer seg mellom to tilstander (hover, fokus, åpen/lukket klasse).
  • Du vil at nettleseren skal interpolere fra nåværende verdi til en ny verdi.
  • Effekten skal snu midtveis hvis brukeren flytter musen vekk.

Bruk @keyframes / animation når:

  • Bevegelsen skal kjøre uten brukerinteraksjon (lasting, forsiktig pulse, intro).
  • Du trenger flere nøkkelrammer, forsinkelser per steg, eller animation-iteration-count.
  • Tilstanden ikke er en enkel endring fra A til B.
/* Transition: drevet av tilstandsendring */
.btn {
  opacity: 1;
  transition: opacity 0.2s ease-out;
}
.btn:hover {
  opacity: 0.85;
}

/* Animation: tidsstyrt sekvens */
@keyframes fade-in-once {
  from { opacity: 0; }
  to { opacity: 1; }
}
.hero-title {
  animation: fade-in-once 0.4s ease-out both;
}

En vanlig feil i temaer er å «animere» hover med evig animation og deretter kjempe mot animation-fill-mode. For hover er transition nesten alltid renere.

#Hover-effekt med focus-visible for tastatur

:hover alene er utilstrekkelig for tastaturbrukere. Speil den visuelle tilbakemeldingen med :focus-visible, slik at Tab-navigasjon får samme fade uten at hvert museklikk etterlater et synlig fokusring-spor.

.card-media {
  opacity: 1;
  outline: none;
  transition: opacity 0.25s ease, box-shadow 0.25s ease;
}

.card-media:hover,
.card-media:focus-visible {
  opacity: 0.75;
}

.card-media:focus-visible {
  box-shadow: 0 0 0 3px color-mix(in srgb, CanvasText 40%, transparent);
}

Praktiske merknader:

  • Bruk :focus-visible, ikke bare :focus, hvis du vil unngå fokusmarkering etter museklikk i moderne nettlesere.
  • Sørg for at det fokuserbare elementet faktisk kan motta fokus (<a>, <button>, eller tabindex="0" med tastaturhåndtering).
  • Ikke fjern outline uten å erstatte den. Fade alene er ikke nok kontrast for fokusindikator.

På berøringsskjermer er :hover sticky i noen nettlesere. Hold fade-en subtil (0.7-0.9), så «låst» hover ikke ser ut som en feil.

#Hvordan slå av hover-animasjon med prefers-reduced-motion

Noen brukere har bedt systemet om mindre bevegelse. Respekter det med en media query som kortslutter overganger og animasjoner.

@media (prefers-reduced-motion: reduce) {
  .hover-image,
  .card-media,
  .btn {
    transition: none;
    animation: none;
  }
}

Du kan også forkorte varigheten i stedet for å nullstille alt, men for fade på hover er none greit: tilstanden bytter fortsatt, bare uten interpolasjon.

Test i macOS (innstillingen for redusert bevegelse under Tilgjengelighet) og i Windows 11 (animasjonseffekter under Tilgjengelighet og visuelle effekter). Verifiser at fokusmarkeringen fortsatt er synlig når overgangen er av.

#CSS transition-egenskaper forklart

#Transition-property

Angi alltid konkrete egenskaper. transition: all er fristende, men den animerer også ting du ikke tenkte på (for eksempel border-width eller font-size fra en annen regel) og koster mer.

/* Foretrekk */
transition-property: opacity, transform;

/* Unngå i UI-komponenter */
transition-property: all;

#Transition-duration

For grensesnitt ligger typisk 150-300 ms i den sonen der effekten merkes uten å føles treg. Under 100 ms kan se ut som et hardt kutt. Over 400 ms begynner hover å føles tregt på desktop.

#Transition-timing-function

  • ease-out: rask start, myk landing, ofte best på hover-ut.
  • ease-in-out: symmetrisk mykhet, grei på opacity-fade.
  • linear: sjelden ønskelig for UI, mer for fremdriftsindikatorer.
  • cubic-bezier(...): når merkevaren har en egen bevegelseskurve.

#Transition-delay

Forsinkelse er nyttig når et underordnet lag skal vente på et overordnet. På enkel bilde-fade er delay vanligvis 0s; ellers føles UI tregt.

#CSS transition kortform med flere egenskaper

Kortformen er property duration timing-function delay:

.hover-image {
  transition: opacity 0.3s ease-in-out 0s;
}

/* Flere egenskaper */
.card-img {
  transition:
    opacity 0.25s ease-out,
    transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

Hold rekkefølgen konsistent i hele temaet. Når én komponent bruker langt format og en annen kortformat med andre verdier, blir bevegelsesspråket rotete.

#Avanserte hover-mønstre uten jQuery

#Fade pluss skalering

transform: scale() kombineres godt med opacity fordi begge kan compositor-akselereres.

.card-img {
  opacity: 1;
  transform: scale(1);
  transition:
    opacity 0.3s ease-in-out,
    transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.card-img:hover,
.card-img:focus-visible {
  opacity: 0.9;
  transform: scale(1.04);
}

Unngå å animere width / height for zoom. Det tvinger layout og paint på hver frame.

#Fade på bilde inne i en beholder

Når hover-området er større enn bildet (hele kortet), sett :hover på beholderen:

.image-container img {
  opacity: 1;
  transition: opacity 0.3s ease-in-out;
}

.image-container:hover img,
.image-container:focus-within img {
  opacity: 0.7;
}

:focus-within dekker tastaturfokus på lenken inne i kortet.

#WordPress: enqueue stilarket riktig

Legg ikke fade-CSS som en tilfeldig <style>-blokk i header.php hvis du kan unngå det. Bruk temaets enqueue-API slik at avhengigheter, versjonering og lasting blir forutsigbare.

<?php
declare(strict_types=1);

add_action('wp_enqueue_scripts', static function (): void {
    $theme_version = wp_get_theme()->get('Version');
    $css_path = get_theme_file_path('assets/css/hover-fade.css');
    $css_ver = file_exists($css_path) ? (string) filemtime($css_path) : $theme_version;

    wp_enqueue_style(
        'theme-hover-fade',
        get_theme_file_uri('assets/css/hover-fade.css'),
        [],
        $css_ver
    );
});

Hvorfor dette mønsteret:

  • filemtime gir cache-bust når filen endres, uten manuell versjonsbump hver gang.
  • Handle theme-hover-fade kan dequeques eller overstyres i child themes.
  • Du unngår duplikater hvis en blokk eller et plugin også prøver å injisere samme regler.

For blokk-temaer (FSE) kan du fortsatt enqueue et lite utility-ark, eller legge utilitariske klasser i theme.json der det passer. Store hover-regler med media queries hører fortsatt hjemme i CSS-filer.

Hvis effekten bare trengs på én mal, beting enqueue med is_singular() / is_page_template() for å holde kritisk CSS mindre.

#Hvorfor fade ikke virker i Gutenberg-blokker

Gutenberg endrer DOM-strukturen mer enn klassiske maler. Fade som «virket i Classic» feiler ofte av disse grunnene:

#1. Feil selector på wrapper

Blokker pakkes ofte i .wp-block-image, .wp-block-group eller figure/figcaption. En regel som treffer img:hover direkte treffer ikke når hover skjer på lenke-wrapper rundt bildet.

/* For snevert i mange blokk-oppsett */
.wp-block-image img:hover {
  opacity: 0.7;
}

/* Mer robust */
.wp-block-image a:hover img,
.wp-block-image a:focus-visible img,
.wp-block-image figure:hover img {
  opacity: 0.7;
}

#2. Inline styles og content-bilder

Bildestiler satt i editoren (style="width:...") konkurrerer ikke nødvendigvis med opacity, men !important i tema-reset eller plugin-CSS kan det. Hold specificiteten lav og konsistent; unngå !important for fade.

#3. Additional CSS versus blokk-stylesheet

«Ekstra CSS» under Utseende og Tilpass er greit for prototyper. For produksjon: egen fil via enqueue, eller blokk-spesifikk stil registrert med wp_enqueue_block_style, slik at reglene lastes bare der blokken brukes.

#4. Interaktivitet og nested blokker

Gruppe-blokker med lenker inni kan trigge hover på forelder mens fokus er på barn. Test både mus og Tab. :focus-within på kort-wrapper løser mange av disse tilfellene.

#5. Editor versus frontend

Canvas i editoren laster ikke alltid temaets frontend-CSS. Hvis kunden «ikke ser fade i editor», er det forventet med mindre du også enqueue’er stilene via enqueue_block_assets eller add_editor_style. Dokumenter skillet: effekt på live-side, ikke nødvendigvis i canvas.

#Hvilke CSS-egenskaper er raske å animere

Når du animerer egenskaper som trigger layout (width, height, top, left, margin) eller dyr paint (box-shadow på enorme flater uten lag), risikerer du jank på mobil.

Foretrekk for fade / micro-interaction:

  • opacity
  • transform (translate, scale, rotate)

Vær forsiktig med:

  • filter: blur() på store bilder (kan være dyrt)
  • animerte box-shadow-verdier på hele viewport-seksjoner
  • transition: all

Du trenger sjelden will-change: opacity på hvert bilde i et rutenett. will-change reserverer minne; bruk det midlertidig på elementer som faktisk animerer ofte, eller stol på at nettleseren promoterer laget under transition.

Mål med Performance-panelet: se etter lange frames under hover på en liste med mange kort. Hvis layout thrashing dukker opp, er det nesten alltid feil egenskap som animeres.

#Feilsøking når fade ikke synes

  1. Sjekk at start- og sluttverdi faktisk er forskjellige.
  2. Bekreft at transition sitter på base-regelen, ikke bare på :hover. Overgangen hentes fra stilen elementet går over til, så uten den mangler retur-overgangen.
  3. Se etter andre regler som setter transition: none globalt.
  4. Kontroller prefers-reduced-motion i OS.
  5. I WordPress: bekreft at stilarket er enqueue’t (View Source / Network) og at selector matcher den renderte blokk-DOM-en.

#Hvilken transition-regel gjelder når hover starter og slutter

CSS Transitions Module Level 1 er fortsatt et W3C Working Draft, sist publisert 8. januar 2026, og ikke en ferdig anbefaling. Spesifikasjonen sier at varighet, forsinkelse og timing-funksjon hentes fra elementets after-change style, altså stilen elementet er på vei over til. Når pekeren går inn over bildet, gjelder transition fra :hover-regelen. Når pekeren forlater bildet, gjelder transition fra basisregelen.

Det gir to praktiske konsekvenser:

  • Står transition bare i :hover, toner bildet ned, men hopper tilbake uten overgang når pekeren forlater det, fordi basisregelen ikke har noen overgang.
  • Du kan gi inngang og utgang ulik fart med vilje, for eksempel rask respons inn og roligere retur.
.hover-image {
  opacity: 1;
  transition: opacity 0.3s ease-out; /* brukes på vei tilbake */
}

.hover-image:hover {
  opacity: 0.7;
  transition-duration: 0.15s; /* brukes på vei inn */
}

Spesifikasjonen dekker også tilfellet der pekeren forlater elementet før fade-en er ferdig. Avsnitt 3.1 om raskere reversering av avbrutte overganger forklarer at en retur med full varighet over en kortere strekning virker skjev. Derfor forkortes returovergangen med en egen faktor (reversing shortening factor), og hele timing-funksjonen brukes fortsatt, så returen hopper ikke inn midt i kurven. Du trenger altså ikke JavaScript for å regne ut gjenstående tid. Kilde: CSS Transitions Module Level 1.

#Regnes en opacity-fade som bevegelse etter WCAG

WCAG 2.2 suksesskriterium 2.3.3 Animation from Interactions (nivå AAA) krever at bevegelsesanimasjon som utløses av interaksjon kan slås av, med mindre animasjonen er nødvendig for funksjonen eller informasjonen. W3Cs forklaringsdokument for 2.3.3 avgrenser begrepet: endringer i farge eller opacity som ikke endrer opplevd størrelse, form eller posisjon, regnes ikke som bevegelsesanimasjon. En errata har fjernet uskarphet fra dette unntaket, så filter: blur() hører ikke lenger med.

En ren opacity-fade faller dermed utenfor 2.3.3, mens transform: scale() endrer opplevd størrelse og faller innenfor. Det gir en mer presis regel for redusert bevegelse enn å slå av alt:

@media (prefers-reduced-motion: reduce) {
  .card-img {
    transform: none;
    transition: opacity 0.2s ease-out;
  }
}

MDN beskriver samme grep i dokumentasjonen for prefers-reduced-motion: en skaleringsanimasjon tones ned til en dissolve-animasjon, som ikke utløser vestibulære plager. Verdien reduce evalueres som sann, så @media (prefers-reduced-motion) betyr det samme som @media (prefers-reduced-motion: reduce).

For fokus gjelder et annet kriterium. 2.4.7 Focus Visible (nivå AA) krever en synlig fokusindikator, og MDN viser i tillegg til 1.4.11 Non-text Contrast, som krever minst 3:1 kontrast for indikatoren. En opacity-endring på et bilde oppfyller sjelden det alene, derfor trenger fokus en egen outline eller box-shadow.

#Hvorfor opacity 0 ikke skjuler et element

MDN-dokumentasjonen for opacity peker på tre ting som ofte overses i hover-effekter:

  • Et element med opacity: 0 er usynlig, men fortsatt en del av DOM. Det tar imot pekerhendelser og kan få fokus hvis det ligger i tabulatorrekkefølgen. En skjult overlay-knapp kan altså klikkes og fokuseres. Gjør den synlig når den får fokus, eller slå av pekerhendelser med pointer-events og ta den ut av tabulatorrekkefølgen.
  • Opacity alene skjuler ikke innhold for skjermlesere. Til det brukes hidden-attributtet, visibility eller display.
  • Enhver verdi under 1 lager en ny stacking context. Et overlay med z-index inne i et kort som toner til 0.9, kan derfor havne i en annen stablingsrekkefølge enn forventet.

Skal bare bakgrunnen dempes, anbefaler MDN en bakgrunnsfarge med alfakanal i stedet for opacity på hele elementet. Ligger det tekst over bildet, må kontrasten mellom tekst og bakgrunn fortsatt holde etter fade-en.

#Fade inn ved første visning med @starting-style

En transition starter bare når en verdi endres. MDN forklarer at overganger som standard ikke utløses ved elementets første stiloppdatering, eller når display går fra none til en synlig verdi. Et kort som settes inn i DOM med opacity: 1, toner derfor ikke inn av seg selv.

@starting-style definerer verdiene overgangen skal starte fra. For elementer som veksler til og fra display: none, trengs i tillegg transition-behavior: allow-discrete, fordi display har diskret animasjonstype. Ved overgang til none bytter verdien først ved 100 prosent av varigheten, så elementet er synlig gjennom hele fade-en.

.notice {
  opacity: 1;
  transition: opacity 0.3s ease-out;
  transition: opacity 0.3s ease-out, display 0.3s allow-discrete;
}

.notice.is-hidden {
  display: none;
  opacity: 0;
}

@starting-style {
  .notice {
    opacity: 0;
  }
}

Den første transition-linjen er med vilje: MDN skriver egenskapen to ganger, slik at nettlesere uten støtte for allow-discrete fortsatt animerer opacity. @starting-style har samme spesifisitet som den opprinnelige regelen og må derfor stå etter den. Begge funksjonene er merket Baseline 2024 hos MDN. Eldre nettlesere viser elementet uten fade, som er en akseptabel reserve for en ren visuell overgang.

#Reagere på ferdig fade med transitionend

Når JavaScript bare bytter en klasse og CSS gjør selve fade-en, kan skriptet lytte på transitionend for å vite når overgangen er ferdig, for eksempel før det setter hidden eller flytter fokus. MDN-siden om transitionend og spesifikasjonen gir fire regler for når hendelsen faktisk kommer:

  • transitionend utløses i begge retninger: når elementet når hover-tilstanden, og når det er helt tilbake.
  • Fjernes overgangen før den er ferdig, for eksempel fordi transition-property fjernes eller display settes til none, kommer ingen transitionend.
  • Utløses transitioncancel, for eksempel fordi pekeren forlater elementet midt i fade-en, kommer ingen transitionend for den overgangen.
  • Er både varighet og forsinkelse 0s, finnes ingen overgang, og ingen av overgangshendelsene utløses.

Den siste regelen gir oftest feil i praksis. Når prefers-reduced-motion setter transition: none, venter et skript som rydder opp i transitionend, for alltid. Filtrer på propertyName, og ha en vei videre når ingen overgang kjører.

const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;

// I CSS: .is-fading-out { opacity: 0; }
function hideNotice(el) {
  el.classList.add('is-fading-out');
  if (reduce) {
    el.hidden = true;
    return;
  }
  el.addEventListener('transitionend', function onEnd(event) {
    if (event.propertyName !== 'opacity') return;
    el.removeEventListener('transitionend', onEnd);
    el.hidden = true;
  });
}

#Hover på berøringsskjerm med media query hover

Mediefunksjonen hover fra Media Queries Level 4 tester om den primære inndataenheten kan holde pekeren over elementer. Ifølge MDN gir den verdien none når primær inndata ikke kan holde over i det hele tatt eller ikke på en praktisk måte, for eksempel mobiler som emulerer hover med et langt trykk. Legg hover-regelen bak @media (hover: hover) når en fastlåst hover på mobil er et problem, og la :focus-visible stå utenfor, så tastaturfokus fortsatt gir samme tilstand.

@media (hover: hover) {
  .card-media:hover {
    opacity: 0.75;
  }
}

.card-media:focus-visible {
  opacity: 0.75;
}

#Nettleserstøtte for transition, focus-visible og prefers-reduced-motion

Tabellen viser første versjon med støtte uten prefiks, hentet fra MDN browser-compat-data versjon 8.1.6 (oktober 2026).

FunksjonChromeFirefoxSafariEdge
transition2616912
:focus-within605210.179
:focus-visible868515.486
prefers-reduced-motion746310.179
@media (hover)3864912
transition-behavior11712917.4117
@starting-style11712917.5117

Internet Explorer støtter transition fra versjon 10, men ingen av de andre funksjonene i tabellen. Safari hadde transition med prefikset -webkit- fra versjon 3.1.

Én konsekvens av tabellen er lett å overse. I en nettleser uten :focus-visible, for eksempel Safari før 15.4, ignoreres hele regelen .card-media:hover, .card-media:focus-visible, også hover-delen. MDN forklarer i artikkelen om selektorlister at én ukjent selektor gjør hele listen ugyldig. Skriv hover og fokus som to separate regler, eller pakk dem i :is(), som tar imot en tilgivende selektorliste.

#Oppsummering

CSS-overganger er den moderne, vedlikeholdbare måten å gjøre hover-fade på i WordPress-temaer.

Ta med deg:

  • Animer opacity (og gjerne transform), ikke layout-egenskaper.
  • Bruk transition for tilstand, animation for tidsstyrte sekvenser.
  • Speil hover med :focus-visible / :focus-within.
  • Respekter prefers-reduced-motion.
  • Enqueue CSS via wp_enqueue_style, og tilpass selectorer til Gutenberg-wrappers.
  • Hold varighet rundt 0,2-0,3 s for UI.
  • Bruk @starting-style når et element skal tone inn ved første visning.

Med det på plass trenger du ikke jQuery for en dempet bilde-hover, bare tydelige tilstander og et stilark som faktisk lastes. Når du senere utvider til flere komponenter, gjenbruk samme varighet og timing-funksjon i hele temaet, så føles grensesnittet konsekvent for både musebrukere og tastaturnavigasjon.

Neste steg

Gjør artikkelen om til faktisk implementering

Denne blokken styrker intern lenking og sender leseren videre til de mest relevante tjenestene og innholdet.

Vil du få dette implementert på nettstedet ditt?

Hvis du vil gjøre kunnskapen i artikkelen om til konkrete forbedringer, redesign eller en tydelig leveranseplan, kan jeg ta det videre.

Utforsk andre WordPress-tjenester og kunnskapsbase

Styrk virksomheten din med profesjonell teknisk støtte innen kjerneområdene i WordPress-økosystemet.

Ofte stilte spørsmål

Praktiske svar for å bruke temaet i faktisk arbeid.

Hvordan lager du en jevn CSS-overgang ved hover?#
Bruk transition-egenskapen med en konkret animerbar egenskap som opacity eller transform, en kort varighet på 0.2s til 0.3s, og en timing-funksjon som ease eller ease-in-out.
Hvordan toner du ned et bilde ved hover med CSS?#
Sett standardtilstanden til opacity 1, endre den på :hover, for eksempel til 0.7, og legg til transition: opacity 0.3s ease-in-out for en jevn overgang.
Hvorfor bør du bruke CSS i stedet for JavaScript til hover-effekter?#
CSS-hover-effekter er lettere, enklere å vedlikeholde og som regel jevnere, fordi nettleseren kan optimalisere overganger uten ekstra JavaScript-arbeid.
Hvordan endrer du fade out-effekten ved mouseover med CSS?#
Legg til transition: opacity 0.3s ease-in-out på elementet, og sett en lavere opacity-verdi på :hover-pseudoklassen. Nettleseren håndterer den jevne overgangen automatisk, uten JavaScript.

Trenger du FAQ tilpasset bransje og marked? Jeg lager en versjon som støtter dine forretningsmål.

Ta kontakt

Relaterte artikler