Jak dodać własny kursor na stronie w CSS?

Detale robią różnicę. Naprawdę.

Klientka wchodzi na stronę i jeszcze zanim przeczyta pierwsze zdanie – już czuje czy ta strona jest dopracowana czy nie. Własny kursor to jeden z tych elementów które działają podprogowo – nikt nie myśli „o, jaki fajny kursor”, ale każdy czuje że strona jest wyjątkowa, spójna, przemyślana.

I nie, to nie jest rozwiązanie dla każdego projektu. Ale w odpowiednim kontekście – strona portfolio, agencja kreatywna, butikowy sklep z produktami premium, strona dla fotografa czy projektantki – własny kursor może być tym jednym detalem który sprawia że strona zapada w pamięć.

W tym artykule pokażę Ci jak to zrobić przez czysty CSS, przez JavaScript dla bardziej zaawansowanych efektów i jak to wdrożyć w WordPress. Bez wtyczek, bez ciężkich bibliotek, z pełną kontrolą nad efektem.

Czym tak naprawdę jest kursor w CSS?

Zanim przejdziemy do kodu, warto zrozumieć jak przeglądarka obsługuje kursor.

Przeglądarka domyślnie pokazuje kilka typów kursorów w zależności od kontekstu – strzałkę na zwykłych elementach, rączkę na linkach i przyciskach, krzyżyk tekstowy przy zaznaczaniu tekstu, kółko ładowania gdy coś się wczytuje. Wszystko to kontroluje właściwość CSS cursor.

I ta właściwość przyjmuje nie tylko słowa kluczowe jak pointer czy default – przyjmuje też własny plik graficzny przez funkcję url(). To właśnie ta możliwość otwiera drzwi do własnego kursora.

body {
    cursor: url('kursor.png'), auto;
}

Drugi parametr po przecinku – auto – to fallback. Jeśli przeglądarka nie obsługuje pliku albo plik się nie załaduje, użyje domyślnego kursora systemowego. Ten fallback jest obowiązkowy – bez niego CSS jest nieprawidłowy.

Formaty plików i ograniczenia przeglądarek

Zanim zaczniesz tworzyć kursor, warto wiedzieć z czym pracujesz. Nie każdy format działa wszędzie i są konkretne limity wielkości.

Format .cur to natywny format kursora Windows. Obsługiwany przez wszystkie przeglądarki bez wyjątku. Możesz go stworzyć w dedykowanych edytorach jak IcoFX albo przez konwertery online.

Format PNG to najwygodniejsza opcja – tworzysz w Canvie, Figmie, Photoshopie, eksportujesz i gotowe. Obsługiwany przez Chrome, Edge i Firefox. Safari ma z nim problemy – dlatego zawsze podajesz fallback.

Format SVG – obsługiwany przez Chrome i Firefox, nie przez Safari. Lekki i skalowalny, ale najwęższe wsparcie przeglądarek.

Limity wielkości – to ważne. Przeglądarki mają różne limity maksymalnego rozmiaru niestandardowego kursora. Chrome i Firefox obsługują do 128x128px. Praktycznie jednak kursory powyżej 64x64px wyglądają już przesadnie. Optymalny rozmiar to 32x32px albo 64x64px.

Punkt aktywny (hotspot) – to miejsce na kursorze które „klika”. Dla standardowej strzałki to lewy górny róg (0,0). Możesz to zmienić w definicji kursora:

cursor: url('kursor.png') 16 16, auto;

Liczby 16 16 oznaczają że punkt aktywny jest w środku kursora 32x32px. Przy kursorze w kształcie koła – środek jako hotspot jest naturalny. Przy kursorze w kształcie strzałki – lewy górny róg (0 0).

Podstawowa implementacja – własna grafika jako kursor

Zacznijmy od najprostszego przypadku – podmieniasz domyślny kursor na własny plik graficzny.

/* Kursor dla całej strony */
body {
    cursor: url('kursor.png'), auto;
}

/* Kursor rączki dla linków i przycisków */
a,
button,
[role="button"] {
    cursor: url('kursor-klik.png'), pointer;
}

/* Kursor tekstowy przy zaznaczaniu */
p,
h1, h2, h3,
article {
    cursor: url('kursor-tekst.png'), text;
}

Trzy selektory, trzy kursory – każdy dopasowany do kontekstu. Klientka przeglądająca stronę widzi spójny system kursorów który pasuje do estetyki marki.

Jak wgrać kursor do WordPress? Przez FTP do katalogu motywu potomnego – np. wp-content/themes/twoj-motyw-child/images/kursor.png. W CSS ścieżkę podajesz relatywnie do pliku CSS albo absolutnie:

cursor: url('/wp-content/themes/twoj-motyw-child/images/kursor.png'), auto;

Albo przez functions.php gdzie masz dostęp do funkcji WordPressa:

add_action('wp_head', 'wlasny_kursor_css');
function wlasny_kursor_css() {
    $uri = get_stylesheet_directory_uri();
    echo '<style>
        body {
            cursor: url("' . $uri . '/images/kursor.png"), auto;
        }
        a, button {
            cursor: url("' . $uri . '/images/kursor-klik.png"), pointer;
        }
    </style>';
}

Obsługa wszystkich przeglądarek – właściwa kolejność

Żeby kursor działał wszędzie gdzie to możliwe, podajesz kilka formatów w kolejności od najbardziej preferowanego:

body {
    cursor:
        url('kursor.svg'),    /* Chrome, Firefox */
        url('kursor.png'),    /* Chrome, Firefox, Edge */
        url('kursor.cur'),    /* wszystkie przeglądarki */
        auto;                 /* ostateczny fallback */
}

Przeglądarka bierze pierwszy format który obsługuje. Safari skoczy od razu do .cur albo do auto. Chrome i Firefox użyją SVG.

Ukryj domyślny kursor i zrób własny przez JavaScript

CSS cursor: url() ma jedno duże ograniczenie – kursor jest grafiką i nie możesz go animować, dodać mu rozmytego śladu, sprawić żeby reagował na hover elementów inaczej niż przez podmianę pliku.

Jeśli chcesz naprawdę efektowny kursor – taki z animacją, ze świecącym kółkiem, z efektem magnetycznego przyciągania do przycisków – musisz zbudować go przez JavaScript.

Zasada jest prosta: ukrywasz systemowy kursor przez CSS i zastępujesz go własnym elementem HTML który podąża za myszą.

Krok 1 – ukryj systemowy kursor

* {
    cursor: none !important;
}

!important jest potrzebne żeby nadpisać wszystkie inne reguły cursor – rączkę na linkach, strzałkę na elementach, kursor tekstowy.

Krok 2 – utwórz element kursora

.custom-cursor {
    position: fixed;
    top: 0;
    left: 0;
    width: 20px;
    height: 20px;
    background: #0444ac;
    border-radius: 50%;
    pointer-events: none;  /* ważne - kursor nie może blokować kliknięć */
    transform: translate(-50%, -50%);
    transition: transform 0.1s ease;
    z-index: 99999;
    mix-blend-mode: difference; /* opcjonalnie - efekt inwersji kolorów */
}

Krok 3 – JavaScript który śledzi mysz

const cursor = document.createElement('div');
cursor.classList.add('custom-cursor');
document.body.appendChild(cursor);

document.addEventListener('mousemove', function(e) {
    cursor.style.left = e.clientX + 'px';
    cursor.style.top  = e.clientY + 'px';
});

To podstawowy kursor który podąża za myszą. Prosto i działa.

Efekt opóźnienia – kursor który „ciągnie się” za myszą

Jeden z najpopularniejszych efektów kursorów na stronach portfolio. Zamiast natychmiastowego śledzenia myszy – kursor jakby płynął za nią z lekkim opóźnieniem. Sprawia wrażenie że ma masę, że jest fizyczny.

const cursor = document.querySelector('.custom-cursor');
let mouseX = 0, mouseY = 0;
let cursorX = 0, cursorY = 0;
const speed = 0.1; // im mniejsza wartość, tym większe opóźnienie

document.addEventListener('mousemove', function(e) {
    mouseX = e.clientX;
    mouseY = e.clientY;
});

function animujKursor() {
    // Interpolacja liniowa (lerp) - kursor "dogania" mysz
    cursorX += (mouseX - cursorX) * speed;
    cursorY += (mouseY - cursorY) * speed;

    cursor.style.left = cursorX + 'px';
    cursor.style.top  = cursorY + 'px';

    requestAnimationFrame(animujKursor);
}

animujKursor();

requestAnimationFrame sprawia że animacja jest płynna i zsynchronizowana z odświeżaniem ekranu – żadnych szarpnięć, żadnych problemów z wydajnością.

Wartość speed = 0.1 oznacza że kursor co klatkę przesuwa się o 10% odległości do myszy. Im mniejsza wartość tym większe opóźnienie – 0.05 daje bardzo leniwy kursor, 0.2 prawie natychmiastowy.

Kursor który reaguje na hover – efekt powiększenia przy linkach

Klasyczny i elegancki efekt – kursor zmienia rozmiar gdy najedziesz na link albo przycisk. Informuje użytkownika że element jest klikalny, bez potrzeby zmiany kursora na rączkę.

css

.custom-cursor {
    position: fixed;
    width: 12px;
    height: 12px;
    background: #0444ac;
    border-radius: 50%;
    pointer-events: none;
    transform: translate(-50%, -50%);
    transition: width 0.3s ease,
                height 0.3s ease,
                background 0.3s ease;
    z-index: 99999;
}

.custom-cursor.hover {
    width: 40px;
    height: 40px;
    background: transparent;
    border: 2px solid #0444ac;
}

javascript

const cursor = document.querySelector('.custom-cursor');

document.addEventListener('mousemove', function(e) {
    cursor.style.left = e.clientX + 'px';
    cursor.style.top  = e.clientY + 'px';
});

// Dodaj klasę hover gdy mysz jest nad linkiem lub przyciskiem
const klikalneElementy = document.querySelectorAll('a, button, [role="button"], .klikalne');

klikalneElementy.forEach(el => {
    el.addEventListener('mouseenter', () => cursor.classList.add('hover'));
    el.addEventListener('mouseleave', () => cursor.classList.remove('hover'));
});

Efekt: mały wypełniony punkt na co dzień, duże puste kółko nad elementami klikalnymi. Przejście między stanami przez CSS transition jest płynne.

Kursor z efektem mix-blend-mode – inwersja kolorów

Ten efekt jest naprawdę efektowny i zaskakująco prosty do osiągnięcia.

.custom-cursor {
    position: fixed;
    width: 30px;
    height: 30px;
    background: white;
    border-radius: 50%;
    pointer-events: none;
    transform: translate(-50%, -50%);
    z-index: 99999;
    mix-blend-mode: difference;
}

mix-blend-mode: difference sprawia że kursor miesza się z tłem przez odjęcie kolorów. Biały kursor na ciemnym tle wygląda jak jasna plama. Biały kursor na jasnym tle wygląda jak ciemna plama. Na kolorowym tle – daje efekt inwersji kolorów.

To jeden z tych efektów które wyglądają dużo drożej niż są w implementacji.

Kursor z cząsteczkami – efekt śladu za kursorem

Bardziej zaawansowany efekt – za kursorem ciągnie się ślad małych cząsteczek które znikają.

function stworzCzasteczke(x, y) {
    const czasteczka = document.createElement('div');
    czasteczka.style.cssText = `
        position: fixed;
        width: 6px;
        height: 6px;
        background: #0444ac;
        border-radius: 50%;
        pointer-events: none;
        left: ${x}px;
        top: ${y}px;
        transform: translate(-50%, -50%);
        z-index: 99998;
        transition: opacity 0.5s ease, transform 0.5s ease;
    `;
    document.body.appendChild(czasteczka);

    // Animuj i usuń
    setTimeout(() => {
        czasteczka.style.opacity = '0';
        czasteczka.style.transform = 'translate(-50%, -50%) scale(0)';
    }, 50);

    setTimeout(() => czasteczka.remove(), 600);
}

let ostatniaX = 0, ostatniaY = 0;

document.addEventListener('mousemove', function(e) {
    // Twórz cząsteczkę tylko gdy kursor się poruszył o minimum 5px
    // (bez tego przy powolnym ruchu jest ich za dużo)
    const dx = e.clientX - ostatniaX;
    const dy = e.clientY - ostatniaY;
    if (Math.sqrt(dx*dx + dy*dy) > 5) {
        stworzCzasteczke(e.clientX, e.clientY);
        ostatniaX = e.clientX;
        ostatniaY = e.clientY;
    }
});

Jak wdrożyć w WordPress

Masz kilka opcji w zależności od tego co wolisz.

Opcja 1 – przez Insert Headers and Footers

CSS wklejasz w sekcję Header, JavaScript w sekcję Footer. Prosto i bez dotykania plików motywu.

Opcja 2 – przez functions.php w motywie potomnym

add_action('wp_enqueue_scripts', 'wlasny_kursor_skrypty');
function wlasny_kursor_skrypty() {
    // Wgraj własny plik JS
    wp_enqueue_script(
        'wlasny-kursor',
        get_stylesheet_directory_uri() . '/js/kursor.js',
        [],
        '1.0',
        true // załaduj w footerze
    );

    // Wgraj własny plik CSS
    wp_enqueue_style(
        'wlasny-kursor-styl',
        get_stylesheet_directory_uri() . '/css/kursor.css'
    );
}

Tworzysz dwa pliki w motywie potomnym: /js/kursor.js z JavaScriptem i /css/kursor.css ze stylami kursora. Czyste, profesjonalne i zgodne z WordPress best practices.

Opcja 3 – inline przez wp_head

add_action('wp_head', 'wlasny_kursor_inline');
function wlasny_kursor_inline() { ?>
    <style>
        * { cursor: none !important; }
        .custom-cursor {
            position: fixed;
            width: 20px;
            height: 20px;
            background: #0444ac;
            border-radius: 50%;
            pointer-events: none;
            transform: translate(-50%, -50%);
            z-index: 99999;
        }
    </style>
<?php }

add_action('wp_footer', 'wlasny_kursor_js');
function wlasny_kursor_js() { ?>
    <script>
        const cursor = document.createElement('div');
        cursor.classList.add('custom-cursor');
        document.body.appendChild(cursor);
        document.addEventListener('mousemove', e => {
            cursor.style.left = e.clientX + 'px';
            cursor.style.top  = e.clientY + 'px';
        });
    </script>
<?php }

Ważne – własny kursor tylko na desktop

To jeden z najczęstszych błędów – zapominanie że na urządzeniach mobilnych i tabletach nie ma myszki. Własny kursor nie ma sensu na touch devices, a cursor: none może powodować problemy.

Zawsze ograniczaj własny kursor do urządzeń z myszką:

/* Własny kursor TYLKO na urządzeniach nie-dotykowych */
@media (pointer: fine) {
    * {
        cursor: none !important;
    }
    .custom-cursor {
        display: block;
    }
}

/* Na urządzeniach dotykowych - ukryj element kursora */
@media (pointer: coarse) {
    .custom-cursor {
        display: none;
    }
}

Media query pointer: fine wykrywa urządzenia ze wskaźnikiem o dużej precyzji – czyli myszkę. pointer: coarse to ekrany dotykowe.

W JavaScript dodaj analogiczne sprawdzenie:

// Uruchom własny kursor tylko jeśli jest mysz
if (window.matchMedia('(pointer: fine)').matches) {
    // cały kod kursora tutaj
}

Typowe błędy przy własnym kursorze

Brak pointer-events: none – jeśli zapomnisz dodać tę właściwość do elementu kursora, będzie on blokował kliknięcia. Klientka klika link – klika kursor zamiast linka. Zawsze dodawaj pointer-events: none do elementu kursora.

Za duży kursor – kursor 100x100px to za dużo. Zakrywa treść, jest irytujący. Trzymaj się 20-40px dla eleganckiego efektu.

Brak fallbacka w CSS cursor: url() – bez fallbacka auto na końcu CSS jest nieprawidłowy i przeglądarka może zignorować całą regułę.

Własny kursor na mobile – jak już pisałam wyżej. Zawsze sprawdzaj pointer: fine.

Za bardzo skomplikowany efekt śladu – tworzenie setek elementów DOM co kilka milisekund zabija wydajność strony. Jeśli robisz ślad cząsteczek – ogranicz częstotliwość tworzenia nowych elementów i zawsze usuwaj stare przez remove().

Kursor znika za modalnymi i popupami – jeśli popup ma z-index: 9999, a kursor ma z-index: 9998, kursor chowa się pod popupem. Daj kursorowi z-index: 999999 albo dynamicznie zarządzaj z-indexem.

Kiedy własny kursor to dobry pomysł – a kiedy nie

Własny kursor ma sens gdy jest częścią spójnej koncepcji wizualnej strony – nie jako ozdobnik dla ozdobnika.

Dobrze sprawdza się przy stronach portfolio fotografów i ilustratorów, stronach agencji kreatywnych, butikowych sklepach z produktami luksusowymi, landing page’ach dla konkretnych kampanii, stronach eventowych i kulturalnych.

Nie sprawdza się przy blogach edukacyjnych i informacyjnych – odciąga uwagę od treści. Przy sklepach WooCommerce z dużym asortymentem – kursor powinien być maksymalnie transparentny. Przy stronach korporacyjnych gdzie liczy się zaufanie i profesjonalizm w tradycyjnym sensie.

Zasada jest prosta: jeśli pierwszą myślą po wdrożeniu jest „ale fajne”, a drugą „ale czy to nie przeszkadza” – prawdopodobnie przeszkadza. Własny kursor powinien być odczuwalny podprogowo, nie pierwszoplanowy.

Kilka słów na zakończenie

Własny kursor to jedno z tych rozwiązań które kosztuje kilkanaście linijek kodu, a może naprawdę wyróżnić stronę wizualnie.

Zacznij od prostego cursor: url() jeśli potrzebujesz tylko własnej grafiki. Jeśli chcesz efektu z opóźnieniem albo hover – JavaScript z requestAnimationFrame. Jeśli chcesz czegoś naprawdę efektownego – mix-blend-mode: difference za darmo daje wow efekt.

Pamiętaj o mobile, pamiętaj o fallbackach, pamiętaj o pointer-events: none.

I zawsze pytaj: czy ten kursor służy stronie, czy strona służy kursorowi?

Dołącz do Przystani Specek – miejsca, gdzie kobiety uczą się budować własne strony, sklepy, platformy kursowe i marki online - bez stresu, w zgodzie z sobą, ze wsparciem mentorki i cudownej społeczności innych Specek.👉 Kliknij i wejdź na pokład

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *