D
P
0
← All articles Baca dalam Bahasa Indonesia

WordPress & PHP in Production

`body_class()` Adds `search-results` to `<body>`, and My Whole Search Page Got Squished

· · 4 min read
`body_class()` Adds `search-results` to `<body>`, and My Whole Search Page Got Squished

Most of the bugs I write up here can be traced to one wrong line. This one could not. Every line was correct, and an entire page still fell apart, because the class name I picked happened to be one WordPress generates on its own. Fifteen minutes to fix, but the shape of it was worth writing down.

The symptom: one page squished, everything else fine

I was porting a client's static site into a custom WordPress theme. The brief was a faithful port, so the original markup and CSS came across almost untouched and my job was mostly wiring it into the loop and the template system.

Every page rendered correctly. Home was fine, service pages were fine, the post archive was fine. Then I tried the search box.

The search results page came out narrow. Not just the results list: the header, the hero, the footer, all of it crammed into one thin column in the middle of the viewport with wide empty gutters on both sides. No other page behaved that way. Only this one.

The width itself felt familiar. Around 820px, and that number was mine. I had written it for the search results list, not for an entire page.

The root cause: WordPress happened to use my class name

My search template had a block like this:

<div class="search-results">
  <?php while ( have_posts() ) : the_post(); ?>
    <?php get_template_part( 'template-parts/content', 'search' ); ?>
  <?php endwhile; ?>
</div>

With about the simplest CSS you would expect:

.search-results {
  max-width: 820px;
  margin: 0 auto;
}

And in header.php, exactly like every other WordPress theme:

<body <?php body_class(); ?>>

That is where the collision lives. body_class() does not only print the classes you pass into it. It generates a set of contextual classes describing the page being rendered, and on a search results page one of the classes it adds is search-results. So the body tag came out roughly like this:

<body class="search search-results logged-in ...">

Which means that on this page, and only this page, two elements matched the selector .search-results: my results wrapper, and <body> itself. A max-width: 820px landing on <body> pinches the entire document, including everything that has nothing to do with search.

It also explains why every other page survived. Outside of search, WordPress never puts that class on the body, so my rule only ever hit the element I meant.

The slightly embarrassing part is that devtools had been showing me the answer from the first second. I glanced at the class list on <body>, assumed all of it was mine, and moved on.

The fix: rename the class instead of patching the selector

The first instinct is obviously to tighten the selector so it cannot reach the body:

main .search-results {
  max-width: 820px;
  margin: 0 auto;
}

That does make the symptom go away, but the trap stays armed. The name still collides, and it only takes one new rule written without the main prefix to bring the whole thing back, possibly months later and possibly written by someone else.

So I took the boring, safer route and renamed the wrapper.

<div class="search-list">
.search-list {
  max-width: 820px;
  margin: 0 auto;
}

Done. WordPress never generates search-list, so that selector can only ever match an element I created myself.

One more option crossed my mind and got discarded fast: stripping the generated class with a filter.

// Do not do this.
add_filter( 'body_class', function ( $classes ) {
    return array_diff( $classes, [ 'search-results' ] );
} );

Those body classes are part of a contract that plugins and other stylesheets rely on to target page types. Removing one means breaking somebody else's code to rescue my own careless naming.

Check the names before you pick one

Since then I have added a small step whenever I name a layout class in a theme. Open the page in question, read the class attribute on <body>, then search the theme CSS for those names:

grep -rn "\.search-results" wp-content/themes/your-theme/

If anything comes back that is not a deliberate body level rule, you have a collision waiting to happen.

The names body_class() produces tend to sound generic, and that is precisely the danger. search-results, search-no-results, error404, home, blog, archive, single, page-template. Every one of them is a perfectly natural thing to call your own wrapper.

What I took away