W serwisach członkowskich, sklepach WooCommerce i portalach B2B pasek narzędzi WordPressa (#wpadminbar) pojawia się dla każdego zalogowanego użytkownika, także dla klienta bez dostępu do kokpitu. Na frontendzie wygląda jak obcy panel deweloperski: linki do pulpitu, powiadomienia o aktualizacjach, menu użytkownika. Ten tutorial pokazuje, jak wyłączyć go poprawnie po stronie PHP, dlaczego manage_options bije porównanie stringu roli, jak złożyć 32 px marginesu na html i dlaczego samo CSS to antypatern.
Materiał jest napisany pod polskie wdrożenia produkcyjne: motyw potomny albo mu-plugin, WooCommerce z rolą customer, staging z osobnym kontem testowym. Nie wymaga wtyczki „hide admin bar”; jeden filtr i checklista regresji wystarczą.
Problem: pasek dla każdego zalogowanego
WordPress domyślnie pokazuje admin bar każdemu użytkownikowi z sesją. Decyzja ma sens w blogu z kilkoma autorami, którzy edytują wpisy z frontu. W sklepie, akademii online albo strefie klienta ta sama decyzja psuje UX:
- Subskrybent widzi skróty do ekranów, których i tak nie otworzy.
- Klient WooCommerce dostaje czarny pasek nad koszykiem i nagłówkiem.
- Motyw z
position: stickyna headerze zaczyna „pływać” o 32 px względem viewportu. - Testerzy raportują „pustą przestrzeń” albo „złamany sticky”, choć winny jest
margin-topna elemenciehtml.
Ukrycie paska to nie kosmetyka. To świadome odcięcie warstwy administracyjnej od warstwy produktu.
Filtr show_admin_bar zamiast show_admin_bar(false) w akcji
Oficjalny punkt zaczepienia to filtr show_admin_bar. Funkcja show_admin_bar() ustawia flagę globalną, ale filtr jest tym, czego używa rdzeń przy decyzji o renderze paska i o wstrzyknięciu stylów.
Minimalny, czytelny wariant:
/**
* Pokaż admin bar tylko użytkownikom z manage_options.
*/
add_filter( 'show_admin_bar', 'wppoland_show_admin_bar_for_managers' );
function wppoland_show_admin_bar_for_managers( $show ) {
if ( is_admin() ) {
return $show;
}
return current_user_can( 'manage_options' );
}Co się tu dzieje:
- Filtr dostaje aktualną wartość bool i ją zwraca albo nadpisuje.
- W kokpicie (
is_admin()) nie ruszamy decyzji WordPressa. - Na frontendzie zwracamy wynik
current_user_can( 'manage_options' ).
Starszy wzorzec z after_setup_theme + show_admin_bar( false ) nadal działa, ale filtr jest czytelniejszy w debugowaniu (kolejność priorytetów, inne wtyczki nadpisujące ten sam hook) i lepiej dokumentowany w Codexie / DevHub.
manage_options kontra string roli „administrator”
current_user_can() sprawdza capability, nie nazwę roli. Rola administratora w WordPressie ma capability manage_options (patrz rola Administrator i Capabilities API). Porównanie current_user_can( 'administrator' ) jest mylące:
- String
'administrator'to slug roli, nie capability. W wielu instalacjach ten warunek „przypadkiem” przechodzi przez legacy path w mapowaniu ról, ale nie jest to kontrakt, na którym warto budować. - Wtyczki membership / LMS często dodają własne role z częściowym dostępem do kokpitu. String roli ich nie obejmuje, a capability
edit_postsalbomanage_woocommercemoże. - Multisite: super admin i admin sieci mają inny zestaw uprawnień niż lokalny admin bloga. Capability jest bliżej intencji „kto może zarządzać stroną”.
Praktyczna macierz decyzji:
| Cel | Warunek |
|---|---|
| Tylko pełny admin witryny | current_user_can( 'manage_options' ) |
| Redaktorzy i wyżej widzą pasek | current_user_can( 'edit_pages' ) albo edit_others_posts |
| Ukryj tylko dla klientów sklepu | negacja current_user_can( 'customer' ) nie - lepiej lista ról lub ! current_user_can( 'edit_posts' ) |
| Multisite: tylko super admin | is_super_admin() |
Jeśli naprawdę potrzebujesz listy ról (np. ukryj dla subscriber, customer, contributor), czytaj $user->roles, ale trzymaj tę listę w stałej i testuj po każdej zmianie wtyczek ról:
add_filter( 'show_admin_bar', function ( $show ) {
if ( is_admin() || ! is_user_logged_in() ) {
return $show;
}
$user = wp_get_current_user();
$hide_for = array( 'subscriber', 'customer', 'contributor' );
if ( array_intersect( $hide_for, (array) $user->roles ) ) {
return false;
}
return $show;
} );Dla typowego sklepu albo strefy klienta wystarczy wariant z manage_options.
Collapse 32 px: margin-top na html
Gdy pasek jest widoczny, WordPress ładuje admin-bar.min.css i ustawia html { margin-top: 32px !important; } (46 px na wąskich viewportach). Gdy filtr zwraca false, skrypt i style paska nie powinny się załadować. Czasem jednak:
- inna wtyczka wymusza CSS paska,
- motyw w customizerze zakłada stałe 32 px,
- Full Site Editing (FSE) trzyma sticky header z
top: 0, a wcześniej ktoś dodał body classadmin-barręcznie.
Objawy: szara dziura pod górą viewportu, sticky nawigacja przesunięta o wysokość paska, sticky CTA „ucieka” pod admin barem, którego już nie ma.
Diagnoza w DevTools:
- Zaloguj się jako subskrybent.
- Sprawdź, czy w DOM jest
#wpadminbar. - Sprawdź computed
margin-topnahtmlibody. - Sprawdź, czy klasa
admin-barsiedzi na<body>.
Jeśli filtr działa, a margin zostaje, usuń go warunkowo albo napraw źródło CSS. Nie doklejaj ślepo html { margin-top: 0 !important; } dla wszystkich - zniszczysz layout adminów, którzy pasek nadal widzą.
Bezpieczny kompromis w motywie potomnym:
/* Tylko gdy WordPress nie dodał klasy admin-bar */
body:not(.admin-bar) {
/* sticky / fixed elementy zakładają top: 0 względem viewportu */
}Albo w PHP upewnij się, że filtr leci wcześnie (priorytet domyślny 10 jest OK; unikaj late hooks typu wp_footer).
Mu-plugin: izolacja od motywu
Kod w functions.php motywu znika przy zmianie skórki. W sklepach i portalach klienta logika „kto widzi pasek” należy do produktu, nie do wyglądu. Must-use plugin (wp-content/mu-plugins/) ładuje się zawsze, przed zwykłymi wtyczkami, bez przełącznika w UI.
Przykład pliku wp-content/mu-plugins/wppoland-admin-bar.php:
<?php
/**
* Plugin Name: WPPoland Admin Bar Policy
* Description: Frontend admin bar tylko dla manage_options.
* Author: wppoland.com
*/
add_filter( 'show_admin_bar', static function ( $show ) {
if ( is_admin() ) {
return $show;
}
return current_user_can( 'manage_options' );
}, 10 );Zalety mu-pluginu w tym przypadku:
- Przetrwa wymianę motywu i staging → produkcja bez kopiowania
functions.php. - Nie da się wyłączyć przypadkiem z listy wtyczek.
- Łatwy do wrzucenia do repozytorium infrastruktury (Ansible, image Dockera, Git deploy).
Wady: brak GUI, trzeba dokumentować w README projektu, debug wymaga SSH lub menedżera plików. Dla agencji to zwykle zaleta.
Jeśli zespół nie używa mu-plugins, trzymaj kod w małej własnej wtyczce w wp-content/plugins/, ale nie w motywie sklepu, który wymieniasz co sezon.
Antypatern: ukrywanie samym CSS
#wpadminbar { display: none !important; } wygląda na szybki fix i jest częstą radą na forach. To antypatern z kilku powodów:
- DOM i JS zostają. Pasek nadal się buduje, eventy hover/klik nadal wiszą, skrypty
admin-barnadal ładują się w<head>. - Margin 32 px zostaje.
html { margin-top: 32px !important; }pochodzi z tego samego arkusza. Ukryjesz pasek, zostawisz dziurę. - Dostępność. Elementy focusable w ukrytym pasku nadal mogą złapać Tab, zwłaszcza przy niedbałym
visibilityalboopacity. - False sense of security. Użytkownik bez
manage_optionsnadal „ma” warstwę admin UI w HTML. Filtr PHP jej nie renderuje.
CSS ma sens wyłącznie jako warstwa awaryjna przy motywach, które hardcodują odstęp, po poprawnym filtrze PHP. Nigdy jako jedyne rozwiązanie.
FSE, sticky headery i klasy body
Bloki motywów Full Site Editing (Twenty Twenty-Four i pochodne) często używają sticky grupy nawigacji. Gdy body.admin-bar jest obecne, wiele motywów klasycznych dodaje top: 32px do fixed/sticky. Motywy blokowe bywają mniej przewidywalne: niektóre polegają wyłącznie na marginie html, inne na własnym CSS.
Scenariusze do przetestowania ręcznie:
- Admin zalogowany, frontend - pasek widoczny, sticky header pod paskiem, brak overlapu.
- Klient zalogowany, frontend - brak
#wpadminbar, brak klasyadmin-bar, sticky flush z górą viewportu. - Wylogowany - jak klient, bez sesji.
- Mobile - WordPress używa wyższego offsetu (46 px) przy wąskim viewportcie; po ukryciu paska sticky nie może zostawiać 46 px „powietrza”.
- Edytor pełnoekranowy / Site Editor - nie ruszaj paska w
is_admin(); filtr z wcześniejszego przykładu już to zabezpiecza.
Jeśli sticky header w FSE nadal skacze po wdrożeniu filtra, sprawdź w Global Styles / Additional CSS motywu reguły typu body.admin-bar .wp-block-group.is-position-sticky. Usuń zależność od klasy albo uzależnij offset wyłącznie od obecności #wpadminbar.
Na stagingu warto porównać trzy viewporty obok siebie: desktop z adminem, desktop z klientem, telefon z klientem. Różnica w getBoundingClientRect().top sticky nawigacji od razu pokazuje, czy offset 32/46 px nadal „żyje” w CSS mimo braku paska.
Wariant legacy: after_setup_theme
Dla kompatybilności ze starszymi snippetami (i z naszym historycznym kodem) zostawiam wariant imperatywny. Działa, byle warunek opierał się o capability:
add_action( 'after_setup_theme', 'wppoland_remove_admin_bar' );
function wppoland_remove_admin_bar() {
if ( ! current_user_can( 'manage_options' ) && ! is_admin() ) {
show_admin_bar( false );
}
}Zalecenie na 2026: preferuj filtr show_admin_bar. Funkcja show_admin_bar( false ) jest OK jako skrót, ale filtr daje jeden punkt kontroli, gdy kilka wtyczek walczy o widoczność.
Gdzie nie wkładać logiki
- Motyw rodzica - aktualizacja nadpisze plik.
- Customizer „Additional CSS” - wracamy do antypaternu CSS.
- Inline w szablonie page - zadziała tylko na jednej stronie.
- Wtyczka „snippet manager” bez eksportu do Git - kolejny single point of failure przy migracji.
Mu-plugin albo mała wtyczka wersjonowana w repo projektu to domyślny wybór dla produkcji.
Test regresji w 10 minut
- Utwórz użytkownika z rolą Subskrybent (albo Customer w WooCommerce).
- W oknie prywatnym zaloguj się jako on, otwórz stronę główną i koszyk.
- Potwierdź: brak
#wpadminbar,bodybezadmin-bar,margin-topnahtmlrówny 0. - Zaloguj się jako Administrator: pasek wraca, sticky header nie nachodzi na treść.
- Wejdź do
/wp-admin/jako admin: kokpit bez zmian. - (Opcjonalnie) tymczasowo nadaj subskrybentowi capability
manage_optionsprzez wtyczkę ról i sprawdź, że pasek wraca - potwierdza to, że filtr czyta capability, nie slug roli.
Dobre praktyki
- Używaj filtra
show_admin_bar, nie samego CSS. - Warunek buduj na capability (
current_user_can), zwyklemanage_options. - Nie ruszaj zachowania w
is_admin(). - Po ukryciu sprawdź collapse 32 px / 46 px i sticky headery FSE.
- Kod trzymaj w mu-pluginie albo własnej wtyczce, nie w motywie sklepu.
- Dokumentuj politykę w README projektu, żeby kolejny deploy nie „naprawił” paska z powrotem przez CSS.
Podsumowanie
Ukrycie paska administratora dla nie-administratorów to mały filtr PHP z dużym wpływem na UX sklepu i strefy klienta. Filtr show_admin_bar plus manage_options daje przewidywalną politykę; lista stringów ról zostaje dla wyjątków. Po wyłączeniu paska zawsze weryfikuj margin na html i sticky w FSE. CSS-only to usterka czekająca na regresję. Mu-plugin izoluje regułę od rotacji motywów.
Po wdrożeniu zapisz w changelogu projektu: kto widzi pasek, gdzie leży plik (mu-plugin vs wtyczka), oraz datę weryfikacji na produkcyjnych rolach. Przy kolejnym audycie UX wystarczy powtórzyć checklistę z sekcji testów zamiast zgadywać, która wtyczka „naprawiła” pasek z powrotem.
Jeśli budujesz portal klienta albo sklep na WordPressie i potrzebujesz spójnej warstwy UX poza samym paskiem, zobacz usługi developera WordPress.







