
☝️ For everyone who just reads this on their phone: until this week, the start page showed you the same handful of articles four times in a row (a ticker, a slider, a tab box, a “trending” list, a “featured” grid) before the actual list began. Now you get the five newest posts, one after the other, and that’s it. The desktop view hasn’t changed. If you noticed nothing: perfect, that was the plan. 📱
Every theme looks great in its demo on a 27-inch monitor. Mine (MoreNews) has a busy magazine-style front page: a news ticker, a “Main News” slider, a tab box with Latest / Popular / Update, a “Trending Now” column, “Featured Posts”, and at the very bottom “You may have missed”. On a desktop these sit side by side and look like a newspaper. On a phone, the theme stacks every single block on top of the next. With a blog that publishes a few posts a week, that means one thing: the same five posts, again and again, for several screen lengths, before the real list even starts.
Same workflow as always: I make the calls, my AI co-admin (Claude) took the screenshots, wrote the CSS, ran the measurements and dug into why the first measurement looked worse instead of better.

🧹 The fix: one media query, six selectors
No plugin, no child theme, no template override. Just a few lines of CSS in WordPress’s “Additional CSS”, which lives in the database and not in the theme folder, so a theme update can’t wipe it:
@media screen and (max-width: 1024px) {
body.home .banner-exclusive-posts-wrapper,
body.home .aft-main-banner-section,
body.home .af-main-banner-featured-posts,
body.home .above-footer-widget-section,
body.home #secondary .widget:has(.wp-block-latest-posts),
body.home #secondary .widget:has(.wp-block-archives-list) { display: none !important; }
}
- Why 1024 px? That’s exactly where MoreNews switches its banner columns to full width and starts stacking them. Above that, tablets in landscape get the normal desktop layout, which works fine there.
- Why
body.home? Only the front page (and its page 2, 3, …) is affected. Single posts keep their sidebar and the “You may have missed” block. - Why
:has()for the sidebar? Widget IDs like#block-3change the moment someone reorders the sidebar. “The widget that contains a Latest Posts block” doesn’t. All current browsers support:has(). - What’s left on a phone: the five newest posts (WordPress’s “Blog pages show at most” is set to 5), page navigation, search and categories.
And because this blog is managed by Ansible, the CSS isn’t only in the database: the playbook writes it on every run. If somebody “tidies up” the Customizer, the next run puts it back.
📉 The plot twist: the page got slower
Less stuff on the page, so it must be faster, right? Lighthouse (mobile, three runs each) disagreed. The performance score dropped from 93–95 to 91, and the time until the largest element is visible (LCP) went from 2.6 s up to 3.4 s.
The reason is a nice lesson in how LCP works. Before, the largest thing on a phone screen was the slider image, and a small must-use plugin I’d written two days earlier made sure that image loaded immediately and with high priority. Now the slider is hidden, and the largest element is the image of the first post in the list. The theme marks every image loading="lazy" (“load it later, when it’s about to scroll into view”), including that one. So the browser waited 2.1 seconds before even asking for it. Meanwhile my plugin was still dutifully prioritising the image of a slider nobody could see.
Fix number one: on the front page, the plugin now gives “load now, high priority” to the first image inside the main post list (WordPress’s in_the_loop() tells the list apart from the banner, because the banner is rendered before the loop starts). The first banner image only gets “load now” without the priority boost, for desktop visitors who still see it.
🕵️ The attribute WordPress quietly ate
After fix number one the image loaded eagerly, but the fetchpriority="high" attribute simply wasn’t in the HTML. When we called the same code from the command line, it was there. On the live page it wasn’t.
The culprit: the theme echoes every post thumbnail through wp_kses_post(), WordPress’s HTML sanitiser. Its list of allowed <img> attributes doesn’t include fetchpriority, so the attribute was silently stripped. No warning, no log entry. Which also means the “performance fix” from two days earlier had only ever worked halfway. One small filter that adds fetchpriority to the allowed attributes, and it finally arrives in the browser.
📊 The numbers
| Lighthouse, mobile | Before | Only hidden | Final |
|---|---|---|---|
| Performance score | 93–95 | 91 | 95–96 |
| Largest Contentful Paint | 2.6 s | 3.4 s | 2.7 s |
| Total Blocking Time | 150–200 ms | 44 ms | 30–50 ms |
| Data transferred | 731 KB | 569 KB | 569 KB |
| Wait before the LCP image is requested | – | 2.1 s | 0.7 s |
| Desktop score | 100 | 100 | 100 |
So how much faster is it? Honestly: the big picture appears about as fast as before. Under Lighthouse’s deliberately slow test connection, most of those 2.7 seconds are the server’s answer (0.6 s) and the image download itself. What really changed: 22 % less data, because hidden blocks never load their images, and about four times less JavaScript blocking time, so the page reacts sooner when you tap or scroll. And, the main point: you’re reading the newest post after zero scrolling instead of three screens.
🖼️ Smaller images for phones? Not worth it
The obvious next idea: serve phones smaller images. I checked, and decided against it.
- Phones and desktops currently get the same file: a 768 px wide AVIF, typically 30–70 KB. The image optimiser creates only one size for its AVIF versions; the WebP fallback for older browsers already comes in four sizes.
- A phone that is 412 “CSS pixels” wide has a pixel density of about 2.6, which means roughly 1,070 real pixels. For a sharp, screen-wide image it needs at least as many pixels as the desktop column does. 768 px is already slightly below that, and only looks fine because AVIF is so efficient.
- Only old, low-density phones would benefit. Best case: maybe 20–30 KB per image, about 100 KB on the whole front page. Not worth extra image sizes on disk and more moving parts.
The real bottleneck was never the image size. It was that the most important image was told to wait.
🤦 Bonus: I banned myself (again)
For the “before” screenshot we rendered a saved copy of the old front page. That copy still pointed to minified CSS and JavaScript files that the optimisation plugin had thrown away minutes earlier when we cleared its cache. One page load, a burst of “404 Not Found” in a few seconds, and fail2ban did exactly what it was built for: it decided my home IP was a scanner and locked it out, web and SSH alike. Working as intended. Mildly embarrassing. Tea and patience were applied. ☕
🎨 Which theme, and what happens if I ever switch?
This blog runs MoreNews by AF themes, a classic (non-block) magazine theme. That matters, because most of what I did above is tied to it. If I switch themes one day, here’s what comes along and what doesn’t:
| Change | Survives a theme switch? | Why |
|---|---|---|
| “5 posts per page” | ✅ Yes | A WordPress setting (Settings → Reading), not a theme setting. |
The fetchpriority allow-list |
✅ Yes | Lives in a must-use plugin, which doesn’t care about the theme. Harmless if the new theme doesn’t need it. |
| “First list image loads first” | ✅ Mostly | Uses only core WordPress (in_the_loop(), the standard wp-post-image class). Works with any theme that shows featured images in the post list. |
| Font preloading | ❌ No | Points to the font files inside the MoreNews folder. A new theme has other fonts: update the paths or drop it. |
| Hiding the banner blocks | ❌ No | Class names like .aft-main-banner-section exist only in MoreNews. A new theme has its own blocks with its own names. |
| Hiding Recent Posts / Archive | ⚠️ Partly | The block classes come from WordPress core and stay the same. #secondary as the sidebar ID is common, but not guaranteed. |
| The “Additional CSS” itself | ❌ Not automatically | WordPress stores it per theme. After a switch, the new theme starts with an empty box (the old CSS is still kept for MoreNews). |
So the idea transfers 1:1: decide what a phone visitor really needs, measure, make the most important image load first. The selectors have to be rewritten. That takes maybe ten minutes with the browser’s developer tools: switch to phone view, right-click the block you don’t want, look up its class name.
And with a modern block theme you wouldn’t use CSS at all: in the Site Editor you can simply remove such blocks from the front-page template. That’s even better than display: none, because a hidden block is still part of the HTML the browser downloads, while a removed one is simply never sent.
📂 Where the code lives
Both changes are part of the Ansible role that manages this blog, in roles/blog_wordpress/tasks/main.yml: the task “Set custom CSS” for the mobile layout, and “Deploy performance hints mu-plugin” for the image priority and the fetchpriority allow-list.




