Dodawanie slugu strony nadrzędnej do klas body w WordPressie

Dodawanie slugu strony nadrzędnej do klas body w WordPressie

Ostatnio zweryfikowano: 22 września 2026
6 min czytania
Poradnik
Full-stack developer
500+ projektów WP

Projektowanie rozbudowanych serwisów korporacyjnych, portali edukacyjnych czy dokumentacji technicznej na WordPressie niemal zawsze opiera się o hierarchiczną strukturę stron (post_parent). Poszczególne sekcje serwisu (na przykład cała gałąź podstron pod adresem /uslugi/ lub /baza-wiedzy/) wymagają często odmiennego traktowania wizualnego: dedykowanego układu nagłówka, wyróżniającego koloru akcentującego, innego tła paska bocznego czy ukrycia wybranych elementów interfejsu.

WordPress generuje w znaczniku <body> obszerną listę klas pomocniczych poprzez funkcję body_class(). Niestety, w kontekście relacji hierarchicznych silnik oferuje domyślnie jedynie klasy bazujące na identyfikatorach numerycznych (np. page-id-142). Opieranie arkuszy stylów CSS na sztywnych numerach ID utrudnia przenoszenie kodu między środowiskami (staging, produkcja) i wymusza stałe poprawianie selektorów. Rozwiązaniem tego problemu jest automatyczne wstrzykiwanie slugów stron nadrzędnych i przodków do tablicy klas body_class.

#1. Ograniczenia domyślnej funkcji body_class()

Funkcja body_class() wywoływana w pliku nagłówka szablonu (header.php) lub ewaluowana wewnętrznie przez motywy blokowe FSE (theme.json i szablony HTML) odpowiada za dostarczenie kontekstu przeglądarce.

Gdy użytkownik odwiedza stronę pod adresem /uslugi/migracje/bazy-danych/, silnik generuje zestaw klas zbliżony do poniższego:

<body class="page-template-default page page-id-142 page-child parent-pageid-98 logged-in wp-custom-logo">

Zauważmy istotne braki semantyczne:

  • Klasa parent-pageid-98 informuje jedynie, że rodzic ma identyfikator 98. W arkuszu CSS kod .parent-pageid-98 .header nic nie mówi osobie analizującej kod po roku.
  • W przypadku migracji bazy danych między środowiskiem deweloperskim a produkcyjnym, identyfikator strony rodzica może ulec zmianie (np. na 104), co natychmiast zrywa powiązania stylów CSS.
  • Całkowicie brakuje informacji o “dziadku”, czyli nadrzędnej sekcji /uslugi/. Jeżeli chcemy nadać wspólny styl wszystkim podstronom w całym dziale usług, selektory oparte o pojedynczego rodzica stają się bezużyteczne.

#2. Architektura rozwiązania: filtr body_class i get_post_ancestors()

WordPress udostępnia potężny filtr body_class, który przekazuje tablicę aktualnie wygenerowanych klas i oczekuje zwrócenia tablicy wynikowej.

Zamiast pisać własne zapytania SQL czy wielokrotnie wywoływać get_post(), wykorzystamy wbudowaną w WordPress core funkcję get_post_ancestors(). Przyjmuje ona obiekt posta lub jego identyfikator i zwraca tablicę ID wszystkich przodków w porządku od bezpośredniego rodzica aż do najwyższego szczebla hierarchii.

#Gotowy kod do wdrożenia (functions.php lub wtyczka funkcyjna)

/**
 * Dodaje semantyczne slugi rodzica i wszystkich przodków do klas znacznika body.
 *
 * Obsługuje standardowe strony oraz dowolne hierarchiczne typy wpisów (CPT).
 *
 * @param array<string> $classes Tablica klas wygenerowanych przez WordPress.
 * @return array<string> Zmodyfikowana tablica klas.
 */
function wppoland_dodaj_slugi_przodkow_do_body_class( array $classes ): array {
    // Upewniamy się, że znajdujemy się na pojedynczym wpisie lub stronie
    if ( ! is_singular() ) {
        return $classes;
    }

    $post_id = get_queried_object_id();
    if ( ! $post_id ) {
        return $classes;
    }

    $post_obj = get_post( $post_id );
    if ( ! ( $post_obj instanceof \WP_Post ) ) {
        return $classes;
    }

    // Dodajemy slug bieżącej strony z czytelnym prefiksem
    $classes[] = sanitize_html_class( 'page-slug-' . $post_obj->post_name );

    // Sprawdzamy, czy typ wpisu obsługuje hierarchię i czy posiada rodzica
    if ( is_post_type_hierarchical( $post_obj->post_type ) && $post_obj->post_parent > 0 ) {
        
        // 1. Bezpośredni rodzic
        $parent_obj = get_post( $post_obj->post_parent );
        if ( $parent_obj instanceof \WP_Post ) {
            $classes[] = sanitize_html_class( 'parent-' . $parent_obj->post_name );
        }

        // 2. Wszyscy przodkowie w drzewie (rodzic, dziadek, pradziadek)
        $ancestors = get_post_ancestors( $post_id );
        if ( ! empty( $ancestors ) && is_array( $ancestors ) ) {
            foreach ( $ancestors as $ancestor_id ) {
                $ancestor_obj = get_post( $ancestor_id );
                if ( $ancestor_obj instanceof \WP_Post ) {
                    $classes[] = sanitize_html_class( 'ancestor-' . $ancestor_obj->post_name );
                }
            }

            // 3. Korzeń sekcji (najwyższy przodek w hierarchii)
            $root_id = end( $ancestors );
            if ( $root_id && $root_id !== $post_obj->post_parent ) {
                $root_obj = get_post( $root_id );
                if ( $root_obj instanceof \WP_Post ) {
                    $classes[] = sanitize_html_class( 'section-root-' . $root_obj->post_name );
                }
            }
        }
    }

    // Usuwamy ewentualne duplikaty i puste wartości
    return array_unique( array_filter( $classes ) );
}
add_filter( 'body_class', 'wppoland_dodaj_slugi_przodkow_do_body_class' );

#3. Efekt w kodzie HTML

Po zaimplementowaniu powyższej funkcji, dla podstrony o ścieżce: https://domena.pl/uslugi/infrastruktura/optymalizacja-mysql/

Znacznik <body> w wyjściowym dokumencie HTML otrzyma przejrzysty, przewidywalny zestaw klas:

<body class="page-template-default page page-id-214 page-slug-optymalizacja-mysql parent-infrastruktura ancestor-infrastruktura ancestor-uslugi section-root-uslugi">

Zwróćmy uwagę na precyzję:

  • page-slug-optymalizacja-mysql: Umożliwia zaadresowanie wyłącznie tej konkretnej strony.
  • parent-infrastruktura: Pozwala ostylować bezpośrednie podstrony działu infrastruktury.
  • ancestor-uslugi: Daje dostęp do wszystkich elementów potomnych w sekcji usług, niezależnie od poziomu zagłębienia.
  • section-root-uslugi: Wyraźnie wskazuje główny węzeł sekcji nadrzędnej.

#4. Praktyczne zastosowanie w CSS i SCSS

Dzięki semantycznym klasom arkusze stylów stają się w pełni deklaratywne i łatwe w audycie.

#Przykład A: Różnicowanie kolorystyki sekcji

/* Domyślny akcent dla całej sekcji Usługi */
body.ancestor-uslugi {
    --color-accent: #0066cc;
    --sidebar-bg: #f8fafc;
}

/* Specjalny motyw dla działu Bezpieczeństwo */
body.ancestor-bezpieczenstwo {
    --color-accent: #dc2626;
    --sidebar-bg: #fef2f2;
}

/* Wyróżnienie nagłówków artykułów w wybranym dziale */
body.ancestor-baza-wiedzy .article-header h1 {
    font-family: var(--font-mono);
    letter-spacing: -0.02em;
}

#Przykład B: Ukrywanie elementów w wybranej gałęzi

Częstym wymaganiem biznesowym jest ukrycie formularza kontaktowego lub baneru promocyjnego na podstronach konkretnego działu (np. w sekcji prawnej lub regulaminowej):

/* Ukrycie globalnego baneru CTA na wszystkich podstronach w sekcji /regulaminy/ */
body.ancestor-regulaminy .global-cta-banner {
    display: none;
}

#5. Integracja z motywami blokowymi (Full Site Editing / Gutenberg)

W nowoczesnym WordPressie (wersje 6.x i 7.x) dominują motywy oparte na blokach (FSE). Chociaż plik theme.json pozwala na definiowanie globalnych stylów bloków, nie posiada wbudowanego mechanizmu warunkowania stylów w zależności od hierarchii adresu URL.

Dodanie slugów przodków do body_class rozwiązuje tę lukę. Możemy tworzyć warianty bloków lub dopisywać reguły w pliku style.css motywu blokowego, korzystając z nowoczesnej pseudoklasy CSS :where() lub :is():

/* Zmiana odstępów kontenera głównego tylko w sekcji dokumentacji */
:where(body.ancestor-dokumentacja) .wp-block-group.is-layout-constrained {
    max-width: 1440px;
    padding-left: 2rem;
    padding-right: 2rem;
}

Użycie :where() obniża specyficzność selektora do zera, co oznacza, że ewentualne style blokowe edytowane przez administratora w edytorze wizualnym Gutenberga nie zostaną przypadkowo zablokowane.

#6. Aspekty bezpieczeństwa i higieny kodu

Przy modyfikacji atrybutów HTML generowanych po stronie serwera należy pamiętać o dwóch kluczowych zasadach:

  1. Obowiązkowa sanityzacja: Tytuły lub slugi wpisów mogą teoretycznie zawierać znaki niedozwolone w specyfikacji selektorów CSS (np. nieprawidłowo skonfigurowane znaki diakrytyczne czy symbole). Zastosowanie sanitize_html_class() eliminuje to ryzyko.
  2. Eliminacja błędów w pętli: Zawsze sprawdzaj, czy funkcja get_post() zwróciła poprawną instancję \WP_Post. Jeśli podstrona odwołuje się do nieistniejącego rodzica (np. w wyniku uszkodzenia spójności bazy danych po niedokładnym imporcie), odwołanie do $parent_obj->post_name wywołałoby błąd PHP Notice: Trying to get property of non-object.

#Podsumowanie

Wzbogacenie tablicy body_class o slugi stron nadrzędnych i przodków to zaledwie kilkadziesiąt linijek kodu PHP, które całkowicie eliminują konieczność stosowania sztywnych identyfikatorów numerycznych w CSS. Architektura ta jest bezpieczna, odporna na migracje bazy danych i kompatybilna z hierarchicznymi typami wpisów CPT.

Jeśli rozwijasz niestandardowy motyw korporacyjny, skalujesz zaawansowany portal lub porządkujesz dług techniczny w warstwie szablonów, sprawdź, jak prowadzimy profesjonalne programowanie i wsparcie techniczne WordPress.

Następny krok

Przekuj artykuł w realne wdrożenie

Pod tym wpisem dokładam linki, które domykają intencję użytkownika i prowadzą dalej w strukturze serwisu.

Chcesz wdrożyć ten temat na swojej stronie?

Jeśli chcesz przełożyć wiedzę z artykułu na działającą stronę, sklep albo przebudowę serwisu, przygotuję konkretny zakres prac.

Powiązany klaster

Sprawdź inne usługi WordPress i bazę wiedzy

Wzmocnij swój biznes dzięki profesjonalnemu wsparciu technicznemu w kluczowych obszarach ekosystemu WordPress.

Dlaczego domyślny WordPress nie dodaje slugów rodziców do znacznika body?#
WordPress domyślnie dodaje klasy ogólne (page, page-template-default) oraz klasę opartą o identyfikator numeryczny (np. page-id-84). Brak slugów wynika z chęci zachowania minimalnej wielkości kodu HTML oraz zapobiegania rozrostowi klas przy głębokich strukturach drzewiastych.
Jaka jest różnica między bezpośrednim rodzicem a przodkiem w strukturze stron?#
Bezpośredni rodzic (parent) to strona znajdująca się dokładnie jeden poziom wyżej w hierarchii (wskazywana przez pole post_parent). Przodkowie (ancestors) obejmują wszystkich nadrzędnych poprzedników w drzewie, aż do strony głównej sekcji (np. rodzic, dziadek, pradziadek).
Czy to rozwiązanie działa również dla własnych typów wpisów (Custom Post Types)?#
Tak, pod warunkiem że dany typ wpisu został zarejestrowany z parametrem hierarchical => true (podobnie jak standardowe strony). W kodzie wystarczy użyć warunku is_singular() w połączeniu z funkcją is_post_type_hierarchical().
Jak prawidłowo sanityzować dodawane klasy CSS?#
Każdą wartość tekstową dodawaną do tablicy klas należy przepuścić przez funkcję sanitize_html_class(). Zapewnia to usunięcie spacji, znaków specjalnych i potencjalnie niebezpiecznych sekwencji, które mogłyby uszkodzić atrybut class w HTML.

Potrzebujesz FAQ dopasowanego do branży i rynku? Przygotujemy wersję pod Twoje cele biznesowe.

Porozmawiajmy

Polecane artykuły