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-98informuje jedynie, że rodzic ma identyfikator 98. W arkuszu CSS kod.parent-pageid-98 .headernic 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:
- 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. - 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_namewywołałoby błąd PHPNotice: 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.





