A forum that “works on mobile” is not a second style. XenForo already collapses to a single column, grows hit targets, and tucks secondary chrome into menus — that sentence is in the official PWA chapter. Discourse already splits a lot of its CSS by @media. Your job is to stop fighting those systems: use the style properties that already know about narrow viewports, test the composer and the quote control with a thumb, and be careful with “make it full width” components that look great on a 24-inch panel and bunched on a phone you never opened.
This is not the CSS / Less primer. That article is selectors, @xf- tokens, and extra.less. This one assumes you can write a rule. It is also not the typography file-hosting walkthrough, and it is not a second dark mode style. A “mobile-only dark style” is two chooser entries and a second bug. Responsive is width. Variations are palette.
Official XenForo sources: style properties (including layout / header / page width groups), Setup → PWA setup (responsive is listed as a PWA feature, not as a separate product), the public board as a guest. Discourse sources: notebook / Meta practice on @media splitting, the discourse-full-width-component, and the known-flaky quoting popup on mobile. If a breakpoint number is not on those pages, this article will not invent a magic 768px as “the XenForo breakpoint.” Inspect your compiled sheet.
What “responsive” means on a forum
Members do not say “responsive.” They say:
- The post button is under my thumb.
- The quote popup vanished.
- The sidebar ate the thread on my tablet.
- The line of text is 40 centimetres long on my ultrawide and I cannot read it.
- I cannot hit Reply without opening Report.
Translate those into work:
| Complaint | Actual job |
|---|---|
| “The theme is not mobile” | You added a fixed width, a hover-only control, or a second style |
| “Buttons are tiny” | Touch targets. 44 CSS pixels is the usual accessibility floor, not a XenForo setting name |
| “The menu is a mess” | Mobile nav / off-canvas. Stop adding tabs |
| “Desktop looks empty / bunched left” | Max-width versus full-width. Discourse has a named component for this |
| “Composer is unusable” | Fixed headers + 100vh + on-screen keyboard. Test on a real phone |
Performance is part of this page because a 3 MB first screen is not “responsive” just because the columns stack. Images and webfonts have their own articles. Here: do not load desktop-only chrome on a phone if you can avoid it, and do not ship a hover gallery as the only way to reach Edit.
XenForo: properties, then a little Less
Start in Appearance → Styles → [child] → Style properties. Groups that usually own width and header behaviour (labels vary by style; open them, do not assume a vendor’s names):
- Page / content width — the measure of the main column. A readable thread is not “as wide as the monitor.” Somewhere in the 1100–1400 px content range is where most Default-descended styles already live. Raising this to 2000 px because you bought an ultrawide is how post lines become unreadably long. If you need more room for a portal hero, that is a page-only sheet, not a global measure change.
- Header and navigation / Header/logo row — sticky header, logo space, nav density. A tall sticky header plus a sticky staff bar plus a sticky thread title is how a phone has 120 pixels of thread left.
- Node / forum list — row padding, icon column. The nodes article already warned: a 96-pixel crest in a 40-pixel gutter overflows the title on mobile.
- Responsive / breakpoint switches if your style exposes them. Default XenForo has public CSS that collapses sidebars and moves nav into an off-canvas menu. Vendor frameworks add more toggles. Use a toggle before you write
@media.
Then, and only then, extra.less.
XenForo’s public markup already has hooks. Inspect a phone-width window (DevTools device mode is a start; a real phone is the exam). You will see classes and off-canvas markup (.offCanvasMenu on stock). Do not clone PAGE_CONTAINER to “build a mobile header.” You will own that merge forever.
A safe pattern when a property does not exist:
@media (max-width: 650px) {
.p-staffBar {
// example only — inspect your compiled breakpoint first
font-size: @xf-fontSizeSmall;
}
}
Two honesty rules:
- Use the breakpoint XenForo already used, not a number you like. Inspect the core rule that hides the desktop nav. Match that width so your hide and theirs happen together.
- Tokens, not hex. Same as the primer. A mobile-only
#fffstill breaks the dark variation.
If @xf-fontSizeSmall is not on your style, use a unit or skip the tweak. Do not invent tokens.
Mobile nav is a menu, not a second sitemap
On a narrow viewport XenForo moves secondary items into menus. Official PWA feature list: single column, larger hit targets, secondary chrome in menus. Your job is to stop adding tabs.
Practical rules:
- The public nav should fit the things a first-week member needs: forums, what’s new, one custom item you can defend. Landing and onboarding links can live in the footer or a notice.
- Staff links belong in the staff bar, not in the public nav. Guests should not get a tap target for a room they cannot see. CSS
display: noneis not permissions. - Mega-menus and hover flyouts are desktop toys. If a section is only reachable on
:hover, it does not exist on a phone. Provide a tap path. - Off-canvas should close on navigate and on the first backdrop tap. If a vendor style traps focus badly, that is a vendor bug — test before you buy, and test after every parent update.
PWA is adjacent, not the same job. Setup → PWA setup wants HTTPS, a ≤12-character short title, and 192 / 512 icons. That makes the site installable and enables push. It does not fix a 72-pixel sticky header. Do the layout work first.
The composer on a phone
Most “our forum is unusable on mobile” tickets are the reply box, not the forum list.
Test this on a real device, both variations, as a throwaway Registered user:
- Open a long thread. Tap Reply.
- Does the composer sit under a sticky header? Can you see what you type once the keyboard is up?
- Attach an image. Does the client-side resize path still work, or does the keyboard + file picker leave you in a broken overlay?
- Preview, then post. Find Edit on your own post. Find Quote on someone else’s.
- Open a conversation (mail pipe is a different article; the UI is this one). Same composer questions.
Fixes that stay in CSS / properties:
- Reduce stacked sticky bars on small viewports. One sticky is a gift. Three is a bug.
- Do not set
height: 100vhon overlays without testing the keyboard. Mobilevhincludes area the keyboard then covers. - Grow the tap target on
.message-attributioncontrols if you customized them down to 12 px text with 4 px padding. Restore padding. Do not only enlarge the glyph.
Fixes that are not CSS:
- A member on a 2018 Android with three add-on JS composers. Disable the extra editors on staging and retest stock.
- A vendor “mobile message layout” that hides user info you still need for moderation. Check staff flow, not just the pretty guest screenshot.
Discourse: @media is how the product thinks
Discourse theme CSS is already organized around viewport. Notebook / community practice: split custom CSS by @media. Desktop rules in a desktop query, mobile rules in a mobile query. A 400-line unscoped theme sheet that assumes a 1440 px window will punch a phone in the face.
Operational split (use Discourse’s own breakpoints when you can see them in core; do not invent a third set):
/* Mobile-first additions */
.custom-hero { padding: 12px; }
@media (min-width: 768px) {
.custom-hero { padding: 24px 32px; }
}
Theme components are how Discourse shares that CSS. Install fewer of them. Each component is a CSS + JS bundle someone else upgrades on their schedule.
discourse-full-width-component
This is the named component in the notebook. It lets the topic list and topic view use more of a wide desktop. That is a real job: Discourse’s default measure can look bunched left on a 27-inch monitor, with a large empty gutter that feels like unused product.
Use it when:
- Your members actually sit at wide screens (an internal IT community, a sim-racing league with triple monitors).
- You have looked at a topic, not just the latest list. Full width on a 200-character post is a long lonely line.
- You still have a max-width on prose if the component lets the column grow past comfortable reading. A full-bleed frame with a readable measure is the goal. A 220-character line is not “modern.” It is tiring.
Do not use it when:
- Most of the community is on phones. They will never see the win and they will see any CSS the component leaks downward.
- You have not tested the topic map, the timeline, and the composer at that width.
After install: latest, a long topic, a short topic, user card, composer, both light and dark if you ship them. Theme components break on upgrade more often than core. Pin a known-good version if your host lets you.
Quoting is flaky on mobile
Notebook fact, stated plainly: the quoting popup is flaky on mobile. Members select text, expect the Quote / Reply balloon, and get nothing — or get it once, then never again until refresh.
What you should do:
- Document a fallback in the welcome / first-month habits: the Quote button on the post, or reply and paste. Do not pretend the balloon is guaranteed.
- Do not install a second quoting component to “fix” core. You now have two balloons.
- Test before you blame a theme. Stock theme, no components, Android Chrome and iOS Safari. If stock fails, it is the known issue. If only your theme fails, your CSS (overflow,
user-select, a transform on a parent) is a suspect. - Moderators quoting from the queue should use the desktop or the post menu, not a selection balloon, when they are on a phone on the bus.
This is not a XenForo ticket. XenForo’s quote is a post-menu action plus selected-text quote on desktop. Still test it. Do not assume it.
Touch targets, thumbs, and the 44-pixel floor
Neither official XenForo nor the Discourse notebook publishes “our buttons are 44×44.” WCAG / platform HIG energy is still the right floor: about 44×44 CSS pixels for anything a thumb must hit. Spacing between targets matters as much as size. Two 44 px buttons that share a 2 px gap are one fat-finger error.
Audit on a real phone:
| Control | Common failure | Fix |
|---|---|---|
| Post / Reply / Create topic | Pretty, padded-down vendor button | Restore property padding |
| Quote / Like / Edit / Report | Icon-only row packed in the attribution | More padding; consider hiding Report behind a menu on mobile (core often already does) |
| Nav hamburger | Hit area is the 16 px glyph | Pad the button, not just the icon |
| Checkbox / reaction | Tiny native box | Do not shrink form controls in extra.less |
| Off-canvas close | A 12 px × in the corner | Bigger target + backdrop tap |
| Pagination | Page numbers as 10 px links | Use the core pager; stop restyling it to “minimal” |
Hover-only is the other half of touch. Tooltips that contain the only copy of a permission error, :hover dropdowns with no :focus-within equivalent, drag handles with no button alternative — those are desktop assumptions. If staff must drag to reorder, give them a desktop. Do not tell a moderator their only sort tool is a hover handle.
Wide desktops without a second product
Ultrawide members will ask you to “use the whole screen.” Resist the urge to remove max-width globally.
Readable prose has a measure. Classic typography (and every newspaper) keeps line length in a range the eye can scan. A forum post is prose. A server list table is not. A media grid is not.
So:
- Thread bodies and articles stay on a readable measure. XenForo’s default content width is already in that business. Discourse’s default is too. Full-width components should still protect the post column.
- Indexes, galleries, dashboards can go wider. A portal hero, a media strip, a two-column widget row — those jobs want horizontal space. Scope the exception: a page-only
<xf:css>on XenForo, a theme component or a category-specific class on Discourse. - Sidebars. XenForo can move the sidebar below content on small viewports (core behaviour). On a tablet in portrait, “below” can still be a long widget stack the member never asked for. Hide or shorten guest widgets on small widths rather than shipping the entire What’s online stack under every thread.
A child-style exception for a page node:
// only if you scoped it to that page's wrapper
.template-page_view .p-body-inner {
max-width: 1200px;
}
Class names vary. Inspect. Do not copy a guessed template- class from a blog comment.
Performance is a viewport problem
A phone on LTE is the honest test. The CWV article owns guest cache, WebP, and icon sprites. This page only names the layout mistakes that show up as mobile performance:
- A desktop hero slider on the index that loads four 2000 px JPEGs before the thread list. Use one image, sized, or no slider.
- A custom webfont you did not subset (typography).
- A theme component that injects a third-party script for a mobile menu you already had.
preloadof desktop-only images.- Sticky everything, which costs main-thread work on scroll (INP), not just space.
Cloudflare “speed” toggles that rewrite JavaScript are a security / ops and a Discourse-specific landmine (disable JS minify / JS modifications or Discourse breaks). They are not a mobile optimization.
A 45-minute pass on an existing board
- Phone, guest, forum list. Can you reach What’s new and a forum in three taps?
- Phone, throwaway Registered, open a thread, Reply, type a sentence with the keyboard up, attach a small image, post.
- Quote via the post menu. Try selected-text quote. Note if it fails; do not “fix” it with a plugin on the same day.
- Tablet portrait. Is the sidebar under the thread? Is that acceptable? Hide one widget if not.
- Desktop at 1440 px and at 2560 px. Is the measure readable? If Discourse looks bunched, then evaluate discourse-full-width-component. If XenForo looks bunched, raise the page width a notch, not to infinity.
- Both XenForo variations. Dark + small + sticky header is where contrast dies.
- Staff account: moderation menu, Edit, move, merge. If staff tools require hover, give staff a desktop SOP.
- Optional: PageSpeed on a guest phone profile. If LCP is a hero image, that is the image article, not a breakpoint.
Three sessions: guest, member, staff. Two widths: ~390 px and your actual desktop. One real device. DevTools is a lie detector you should still confirm on glass.
What usually looks like a responsive bug
| Symptom | Likely cause | First move |
|---|---|---|
| Horizontal scroll on a thread | A table, image, or pre without overflow | max-width: 100% on images; wrap pre; don’t paste 20-column tables |
| Double headers | Vendor mobile header + core header | Disable one |
| Composer under keyboard | 100vh overlay + sticky header |
Test on device; reduce stickies |
| Quote balloon missing (Discourse) | Known flake, or your overflow/transform | Stock theme test; teach post-menu quote |
| “Full width” bunched left | You expected the component to change latest only | Read what the component actually wraps |
| Nav empty on phone | You hid .p-nav globally to “clean desktop” |
Scope the hide to a min-width query |
| Dark mode unreadable on phone | Hex in mobile @media |
Tokens; see the primer |
What this is not
It is not a second XenForo style named “Mobile.” Official responsive layout is already the product. A second style is a second outdated-template queue.
It is not PWA setup. HTTPS, icons, short title, push — notifications.
It is not permission to fork PAGE_CONTAINER for a custom hamburger. Child extra.less + properties +, on Discourse, a small theme component.
It is not a Core Web Vitals deep dive. Link and leave: speed.
Checklist
- No second “mobile style.” One style, properties +
@media. - Page / content max-width is readable on a 27-inch monitor (prose measure protected).
- Sticky chrome: one bar on small viewports.
- Public nav is a short list; the rest is off-canvas or footer.
- Touch targets ~44 px and spaced; no hover-only critical actions.
- Composer tested on a real phone with the keyboard up.
- Discourse custom CSS split by
@media. - discourse-full-width-component only after a wide-desktop look at topics, not just latest.
- Quoting: post-menu path documented; balloon treated as best-effort on mobile.
- Images
max-width: 100%; no desktop-only hero on the phone first screen. - Guest + member + staff, ~390 px + desktop, both XenForo variations.
Takeaways
- XenForo is already responsive. You break it with fixed widths, hover-only UI, stacked stickies, and a second style. Properties first, a small
@mediain the childextra.lesssecond. - Discourse wants CSS split by
@media. The full-width component fixes a real bunched-left desktop. It is not a substitute for a readable measure, and it will not repair the quoting popup. - The quoting popup is flaky on mobile (notebook). Teach the post-menu quote. Do not install a second balloon.
- Touch is padding and spacing, not a new icon font. Test the composer on glass.
- Wide and narrow are different jobs. Let indexes go wide. Keep posts readable. Measure both.
Open the board on your phone before you buy a “mobile theme.” If you can post a reply with one thumb, you are most of the way there.

