Efekty hover dodają interaktywności do stron WordPress bez JavaScriptu. Poniżej znajdziesz praktyczne wzorce fade in / fade out na obrazkach: od jednego przejścia opacity, przez nakładkę z tekstem, aż po warianty bezpieczne dla klawiatury i prefers-reduced-motion. Wszystkie przykłady działają w motywie klasycznym i w bloku Custom HTML w edytorze bloków.
Kiedy używać transition, a kiedy animation
Dla hover wystarczy transition. Animacja (@keyframes) ma sens dopiero wtedy, gdy efekt ma się zapętlać albo startować przy wejściu na stronę, a nie przy najechaniu kursorem. W portfolio, galeriach produktów WooCommerce i kartach usług prawie zawsze wygrywa transition: jest tańsza w utrzymaniu, łatwiejsza do zsynchronizowania z design systemem i mniej podatna na „miganie” przy skrolowaniu.
Praktyczna zasada z wdrożeń motywów: trzymaj czas w przedziale 200–300 ms. Poniżej 150 ms efekt jest prawie niewidoczny. Powyżej 400 ms użytkownik ma wrażenie, że interfejs „muli”. Krzywa ease-in-out daje najbardziej naturalne wejście i wyjście dla opacity; ease-out sprawdza się, gdy element ma szybko „wpaść” w stan hover, a wolniej wracać.
| Wzorzec | Narzędzie CSS | Typowy czas | Uwaga praktyczna |
|---|---|---|---|
| Przygaszenie zdjęcia | transition: opacity | 200–300 ms | Najtańszy i najbezpieczniejszy |
| Nakładka z CTA | opacity + transform | 250–350 ms | Hover na kontenerze, nie na samym img |
| Zmiana tła pod obrazkiem | opacity na img | 200–300 ms | Tło musi być w DOM przed fade |
| Wejście przy scrollu | animation / Intersection Observer | 400–600 ms | Osobny temat, nie mylić z hover |
Podstawowy kod HTML
Kontener jest obowiązkowy. Bez niego nie zrobisz nakładki, a sam obrazek bez wrappera łatwo „ucieknie” poza układ siatki w motywie.
<div class="image-container">
<img src="obrazek.jpg" alt="Opis zdjęcia produktu" class="fade-image" width="800" height="600" loading="lazy">
</div>Atrybuty width i height ograniczają CLS. loading="lazy" zostaw poza pierwszym ekranem. Alt ma opisywać treść, nie efekt wizualny - „zdjęcie z fade” w altcie nic nie daje czytnikowi ekranu.
CSS dla efektu fade
Najprostszy wariant: obrazek lekko traci krycie po najechaniu. To dobry start do galerii i miniaturek w figure.
.image-container {
position: relative;
overflow: hidden;
}
.fade-image {
display: block;
width: 100%;
height: auto;
opacity: 1;
transition: opacity 0.3s ease-in-out;
}
.fade-image:hover {
opacity: 0.7;
}W WordPressie wrzuć to do style.css motywu potomnego albo do bloku CSS w Motywy → Dostosuj → Dodatkowy CSS. Unikaj edycji plików wtyczek i motywu nadrzędnego: aktualizacja nadpisze zmiany.
Fade z nakładką tekstu
Częsty wzorzec w portfolio: zdjęcie przygasza, a na środku pojawia się podpis albo „Zobacz projekt”. Hover musi siedzieć na kontenerze - inaczej kursor na tekście „gubi” stan i efekt miga.
.image-container {
position: relative;
display: inline-block;
max-width: 100%;
}
.image-container img {
display: block;
width: 100%;
height: auto;
transition: opacity 0.3s ease;
}
.image-container::after {
content: "Zobacz więcej";
position: absolute;
inset: 0;
display: grid;
place-items: center;
opacity: 0;
transition: opacity 0.3s ease;
color: #fff;
font-weight: 600;
background: rgba(0, 0, 0, 0.35);
pointer-events: none;
}
.image-container:hover img,
.image-container:focus-within img {
opacity: 0.55;
}
.image-container:hover::after,
.image-container:focus-within::after {
opacity: 1;
}Jeśli tekst ma być linkiem, nie pakuj go w ::after. Użyj prawdziwego <a> lub <span> w HTML - czytniki ekranu i SEO lepiej znoszą treść w DOM niż w pseudoelementach.
Fade ze zmianą koloru tła
Gdy chcesz, żeby spod zdjęcia „przeświecał” kolor marki, ustaw tło na wrapperze i animuj tylko img.
.fade-bg-image {
background-color: #0a0a0a;
display: inline-block;
line-height: 0;
}
.fade-bg-image img {
display: block;
transition: opacity 0.3s ease;
}
.fade-bg-image:hover img {
opacity: 0.5;
}Kolor tła warto trzymać w zmiennej CSS motywu (--brand-overlay), żeby nie duplikować hexów w dziesięciu regułach.
Wariant z transform (lekki „zoom”)
Sam fade bywa płaski. Połączenie opacity z lekkim skalowaniem wygląda nowocześnie, o ile nie przesadzisz ze skalą - powyżej 1.05 widać „rozjechanie” na krawędziach.
.zoom-fade {
overflow: hidden;
}
.zoom-fade img {
transform: scale(1);
opacity: 1;
transition:
transform 0.3s ease-out,
opacity 0.3s ease-out;
}
.zoom-fade:hover img {
transform: scale(1.04);
opacity: 0.85;
}overflow: hidden na kontenerze jest krytyczne: bez niego powiększony obrazek nachodzi na sąsiednie karty w siatce CSS Grid / Flexbox.
Dostępność: klawiatura i reduced motion
Hover nie działa na telefonie tak jak na desktopie. Na touch urządzeniu pierwszy tap często tylko „odpala” stan, drugi dopiero klika. Dlatego ważne elementy (CTA, tytuł projektu) muszą być widoczne także bez hovera albo po fokusie.
@media (prefers-reduced-motion: reduce) {
.fade-image,
.image-container img,
.image-container::after,
.zoom-fade img {
transition: none !important;
}
}
.image-container a:focus-visible {
outline: 2px solid #10b981;
outline-offset: 3px;
}Checklist przed wdrożeniem:
- Czy CTA jest czytelne bez hovera na mobile?
- Czy fokus klawiatury pokazuje ten sam stan co hover (
:focus-within)? - Czy
prefers-reduced-motionwyłącza płynne przejścia? - Czy kontrast tekstu na nakładce spełnia WCAG AA (zwykle białe na półprzezroczystym czarnym)?
- Czy efekt nie ukrywa jedynej informacji o produkcie (cena, status, etykieta „wyprzedane”)?
WordPress: gdzie wstawić kod bezpiecznie
| Miejsce | Zalety | Ryzyko |
|---|---|---|
Motyw potomny style.css | Pełna kontrola, Git | Wymaga FTP / SSH lub edytora motywów |
| Dodatkowy CSS w Customizerze | Szybkie, bez deployu | Łatwo zgubić przy migracji |
| Blok „Dodatkowy CSS” w Site Editor | Działa w block themes | Trudniej wersjonować |
| Mała wtyczka własna | Czyste odseparowanie | Nadmiar dla 20 linii CSS |
Dla stron klientów trzymam takie efekty w motywie potomnym albo w lekkim mu-pluginie z jednym plikiem CSS. Customizer jest dobry na prototyp, słaby na produkcję z wieloma środowiskami.
W Gutenbergie możesz owinąć obrazek w grupę z klasą dodatkową (zoom-fade) przez panel Zaawansowane → Dodatkowa klasa CSS. Nie buduj osobnego bloku tylko po to, żeby dodać transition.
Typowe błędy, które widzę w audytach
- Transition na
background-image- przeglądarki tego płynnie nie animują; animujopacitywarstwy lub osobny overlay. - Hover na samym
imgwewnątrz linka - przy przejściu kursora na sąsiedni padding efekt znika. - Brak
display: blockna obrazku - zostaje szczelina baseline i „skacze” layout. - Animowanie
width/heightzamiasttransform- gorsza wydajność, więcej reflow. - Kilka różnych czasów (0.2s, 0.45s, 0.8s) w jednym widoku - wygląda niespójnie.
- Ukrywanie całego tekstu tylko przez
opacity: 0bez alternatywy dla mobile.
Checklista wdrożeniowa (15 minut)
- Dodaj kontener z jedną klasą utility używaną w całym motywie.
- Ustaw jedną długość transition (np. 0.28s) i trzymaj ją wszędzie.
- Podłącz
:hoveri:focus-withindo tej samej reguły. - Dodaj media query dla
prefers-reduced-motion. - Sprawdź kartę na desktopie, tablecie i telefonie.
- Zweryfikuj Lighthouse Accessibility i kontrast nakładki.
- Zapisz zmianę w Git z krótkim opisem „hover fade gallery cards”.
Częste pytania
Czy potrzebuję jQuery albo GSAP do fade na hover?
Nie. Do prostego fade wystarczy CSS Transitions. Biblioteki animacji mają sens przy sekwencjach scroll-triggered, timeline’ach reklamowych albo synchronizacji z dźwiękiem. Na karcie bloga albo w siatce portfolio jQuery tylko dokłada wagę.
Dlaczego efekt miga, gdy najadę na tekst na obrazku?
Bo hover jest podpięty pod dziecko (`img`), a nie pod rodzica. Przenieś selektor na `.image-container:hover` albo użyj `:focus-within`. Dzięki temu cały obszar karty trzyma jeden stan.
Jak zrobić fade między dwoma obrazkami?
Ułóż dwa `img` absolutnie w tym samym kontenerze i przełączaj opacity między nimi. Alternatywnie użyj jednego obrazka i zmieniaj `src` dopiero po zakończeniu transition - inaczej zobaczysz „skok” bez fade.
Czy transition psuje Core Web Vitals?
Sam fade opacity / transform jest tani dla GPU. Problemem jest animowanie layoutu (szerokość, top, left) oraz zbyt ciężkie filtry. Trzymaj się opacity i transform, unikaj animacji na dużych, nieprzeskalowanych PNG.
Co z Internet Explorerem?
IE 10 i 11 obsługują podstawowe transitions. Prefiksy `-webkit-` nie są już potrzebne w nowoczesnych przeglądarkach. Jeśli ktoś nadal wymaga IE 9, fade i tak nie zadziała płynnie - zostaw statyczny stan bez transition.
Przykład z bloku Galeria w Gutenbergie
W edytorze bloków najprostsza ścieżka wygląda tak: wstawiasz Galerię albo Siatkę, nadajesz grupie klasę zoom-fade, a reguły CSS trzymasz w motywie potomnym. Nie doklejaj inline style do każdego obrazka - przy kolejnej edycji treści klient nadpisze efekt albo zostawi niespójne czasy transition.
Dla motywów klasycznych opartych o ACF i powtarzalne karty (case study, zespół, produkty) trzymaj markup kontenera w partialu PHP albo w bloku ACF. Jedna klasa utility, wiele miejsc użycia. Gdy design system ma tokeny czasu (--motion-fast, --motion-base), podłącz transition do zmiennej zamiast hardcodować 0.3s w pięciu plikach.
Na sklepach WooCommerce fade na miniaturze kategorii bywa mylący, jeśli jednocześnie pokazuje się badge „promocja” tylko na hover. Badge musi być widoczny bez najechania - inaczej użytkownik mobilny w ogóle go nie zobaczy. To samo dotyczy etykiet „nowość” i stanów niedostępności.
Podsumowanie
Fade in / fade out na hover to kilkanaście linii CSS, ale w skali całego motywu liczy się spójność: jeden czas, jeden easing, hover na kontenerze, fokus klawiatury i szacunek dla prefers-reduced-motion. Zacznij od samego opacity, dopiero potem dorzuć nakładkę albo lekki zoom. Po wdrożeniu przejrzyj trzy szablony: stronę główną, archiwum i pojedynczy wpis - niespójny hover w jednym z nich od razu zdradza brak design systemu.
Same przejścia CSS to najtańsza część roboty; trudniej utrzymać spójność efektów w całym motywie. Gdy takich detali robi się kilkadziesiąt, warto oprzeć je na jednym, utrzymywanym motywie. Jeśli potrzebujesz audytu frontendu albo wdrożenia design systemu w WordPressie, napisz przez formularz kontaktowy na wppoland.com.







