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
opacityogtransformpå 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/mouseleaveog 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:
- Elementet er fullt synlig (
opacity: 1). - Ved hover går verdien mot
0.7over 300 ms. - Timing-funksjonen
ease-in-outmyker start og stopp. - Når hover opphører, kjører overgangen tilbake til
1med 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>, ellertabindex="0"med tastaturhåndtering). - Ikke fjern
outlineuten å 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:
filemtimegir cache-bust når filen endres, uten manuell versjonsbump hver gang.- Handle
theme-hover-fadekan 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:
opacitytransform(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
- Sjekk at start- og sluttverdi faktisk er forskjellige.
- Bekreft at
transitionsitter på base-regelen, ikke bare på:hover. Overgangen hentes fra stilen elementet går over til, så uten den mangler retur-overgangen. - Se etter andre regler som setter
transition: noneglobalt. - Kontroller
prefers-reduced-motioni OS. - 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
transitionbare 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: 0er 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 medpointer-eventsog ta den ut av tabulatorrekkefølgen. - Opacity alene skjuler ikke innhold for skjermlesere. Til det brukes
hidden-attributtet,visibilityellerdisplay. - Enhver verdi under
1lager en ny stacking context. Et overlay medz-indexinne i et kort som toner til0.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:
transitionendutlø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-propertyfjernes ellerdisplaysettes tilnone, kommer ingentransitionend. - Utløses
transitioncancel, for eksempel fordi pekeren forlater elementet midt i fade-en, kommer ingentransitionendfor 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).
| Funksjon | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
transition | 26 | 16 | 9 | 12 |
:focus-within | 60 | 52 | 10.1 | 79 |
:focus-visible | 86 | 85 | 15.4 | 86 |
prefers-reduced-motion | 74 | 63 | 10.1 | 79 |
@media (hover) | 38 | 64 | 9 | 12 |
transition-behavior | 117 | 129 | 17.4 | 117 |
@starting-style | 117 | 129 | 17.5 | 117 |
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 gjernetransform), ikke layout-egenskaper. - Bruk
transitionfor tilstand,animationfor 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-stylenå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.







