XenForo will not overwrite a template you customized. That sounds like a gift. It is how boards die on the first 2.3.x after someone “just tweaked” PAGE_CONTAINER in the Default style. The next core release ships a new wrapper. Your copy is older. The merge queue turns yellow. The public header is last year’s HTML with this year’s CSS. Members call it “the update broke the theme.” The update found the fork you left in the road.
This is the process article: Master stays untouched, the vendor parent stays a vendor parent, your child holds the logo and the CSS, extra.less holds experiments, template modifications beat direct edits, and Appearance → Outdated templates → Merge is how you digest an upgrade. Official sources: the styles manual (docs.xenforo.com/manual/appearance/styles and style properties), the template-modifications screen, the outdated-templates merge tool (blue = auto-merged, yellow = conflict). Community practice — extra.less as the upgrade-safe CSS bucket, child-of-vendor, UI.X “too many open files” — is labeled as community, because it is not in the official docs notebook.
This is not how to enable the 2.3 light/dark switch. That article already owns variations, the footer gadget, and prefers-color-scheme. This is not whether light or dark should win as a design. This is not a CSS primer and it is not a fonts tutorial. This is not FiveStyle as a historical skin. Stay here until the next point release is boring.
The only inheritance tree that survives
Official rule, still the one that matters:
- A style with no parent inherits the hidden Master style.
- A child inherits its parent unless you override a property or customize a template.
- Customizations in the child win. Uncustomized values keep flowing down when the parent updates.
Draw it like this and do not get creative:
Master style ← hidden, XenForo-owned, never open this
└─ Vendor parent ← UI.X 2, Legend, Gamez, Default, XenBase…
└─ Board ← YOUR child. Logo, chips, extra.less, TMS
Three forbidden moves:
- Customize Master. You will not see it in the public list. You can still reach it if you try. Do not. Master is the floor every style stands on. A customized Master is a landmine under every future import.
- Customize the vendor parent. The next vendor zip is a new parent, or an overwrite of this one. Your logo in the parent is a merge you will do again. The vendor’s 2.3.x templates will not include your header comment.
- Customize Default and call it production. Default is a parent you might want to keep clean so email, a staff style, or a future child can inherit stock. If Default is the only style you will ever serve, still add a child named
Boardand put work there. The extra click is cheaper than an outdated Default.
Official Appearance options sit next to this tree: which installed style is the Default style for visitors who have not chosen, and which style HTML email uses. Those are pointers, not parents. Point them at Board (public) and at a light style (email). Do not confuse the option labelled Default style with the style named Default style.
Create the child the official way, before you touch a chip:
- Appearance → Styles → Add style.
- Title
Board(or the brand name you will still understand in a year). - Parent = the vendor 2.3 style you actually serve, or Default if you are not buying a parent.
- Leave user-selectable off until you mean guests to pick it.
- Make it the public Default style only after the logo and both variations are readable.
Vendor import: install the 2.3 package as a new parent, keep Board pointed at it, merge the child’s outdated templates. Do not paste 2.2 extra.less onto a 2.3 parent and call it done. The upgrade article already sequenced that.
Style properties before any template
Official style-properties page: properties inherit unless you customize them on this style. Types: colours, text, numbers with units, switches, multiple-choice, and CSS groups (text / background / border / padding / extra CSS).
Color palette is the first group. It defines chips. Changing a chip replaces every use of that chip. There is a switch to set the style type Light or Dark. That switch tells intensify / mix / diminish which way to push. It is not “this style is the dark theme.” The switcher article owns the variation UI. The operational rule for this article: recolour chips on the child, not hex in a forked template.
Basic colors assign chips to roles. Official reference syntax:
@xf-paletteNeutral3
@xf-textColor
Prefix @xf- plus the property ID. In Default, Text color (textColor) uses @xf-paletteNeutral3. Change the chip, or point the role at a different chip. Do not search-and-replace #141414 across templates.
CSS-type groups — Header and navigation, Header/logo row, Node / forum list, buttons — are where a rebrand actually happens. Official walkthrough: open Header/logo row, point text at @xf-paletteColor2 and background at @xf-paletteColor5, save, load a public page. Logo upload lives here. That is branding. It is also how you avoid forking header to change a background.
Order of operations that keeps the merge queue empty:
- Child exists.
- Palette chips (both variations if enabled).
- Basic color roles.
- CSS-type groups you can see (header, nodes, buttons, overlays).
- Only then
extra.less. - Only then a template modification.
- Almost never a direct template customize.
If you jump to step 7 because “I know HTML,” you have volunteered for yellow merges until you leave the project.
extra.less — community standard, not an official chapter
The official styles manual in the notebook does not document extra.less. XenForo community threads and ThemeHouse install write-ups do, and every working 2.x board we have seen follows the same habit:
- Each style has a template named
extra.less. - Put custom CSS/LESS there, on the child.
- Core upgrades do not overwrite a child’s extra.less. That is the point. It is also why a novel in extra.less becomes your permanent debt — nothing will clean it up for you.
Community include, when the file becomes a novel:
<xf:include template="your_custom_file.less" />
Split by job (board_header.less, board_nodes.less), still on the child. Official 2.3 already unbundles page-specific CSS from the global sheet. A rule that only the portal needs should not live in global extra.less — put <xf:css src="…" /> at the top of that page template so you do not invalidate the global cache for a campaign. The performance article is why.
What belongs in extra.less:
- One-off selectors properties cannot reach (
node--id12, a single prefix colour, a hide on one widget key) - 2.3 node icons via
.m-faContent(@fa-var-…)after hiding the SVG — nodes article - Tiny spacing the CSS-type group almost did
What does not:
- A second copy of
app.less - Hard-coded
#111/#fffon surfaces both variations use @importof Google Fonts. Site policy and performance: self-host. Official 2.3 already self-hosts its UI fonts.- A paste of the vendor’s compiled CSS “so we have a backup”
extra.less is never variation-aware by itself. Use @xf-… tokens and official Less helpers xf-intensify / xf-diminish (they darken a light variation and lighten a dark one). Or write both colours on purpose. The switcher article has the ticket titled “dark mode broke extra.less.” The mistakes catalogue does too.
Custom fonts — not in the official docs
Typography sizes and family names exist as style properties. A file upload for a custom font is not in the official documentation we queried. Community practice is @font-face in the child’s extra.less, files you host on your own origin, then a style property or a token that names that family. Do not recommend a Google Fonts CDN. Do not invent an ACP “upload font” control this article did not see. If your vendor parent already exposes a font picker that reads files you placed on disk, use that picker on the child override, not an undocumented core screen.
Template modifications versus a direct edit
Official screen: Appearance → Template modifications.
A template modification is a find/replace (simple or regex) that runs against a core (or add-on) template at compile time. The core template stays uncustomized. Upgrades apply. Your find either still matches or it does not — you will see that on the modification list, not as a silent outdated clone. You can bind the modification to an add-on ID so uninstall removes it.
A direct template edit copies the template into the current style. Official: customized templates are not overwritten on upgrade. They become outdated. That is the mechanism. It is not a bug. It is XenForo refusing to destroy your HTML.
| Approach | Survives upgrade? | When to use it | Failure mode |
|---|---|---|---|
| Style property | Yes | Colours, logo, type scale, node-list density | You used hex instead of a chip |
| extra.less on the child | Yes (file stays) | CSS the property UI cannot say | Hex; global novel; 2.2 webfont icons |
| Template modification | Yes if the find still exists | Insert a wrapper, a data attribute, a widget hole | Silent no-match when 2.3 rewrote the markup |
| Direct customize on the child | No — it goes outdated | Last resort, small template, you will merge | Yellow conflict; you paste the old file back |
| Direct customize on vendor / Default / Master | Disaster | Never | You cannot take the next parent |
TMS into extra.less is a community pattern for shared tweaks across several colour children: one modification appends a line to extra.less, the children inherit the CSS, you do not maintain three extra.less novels. Use it when you truly have colour children. If you have variations inside one style — the 2.3 default — you do not need colour children at all. Read the switcher article again.
After every core, style, or add-on update:
- Appearance → Template modifications. Look for disabled rows and finds that no longer match. Fix the find. Do not add a second modification that “also” tries the old HTML.
- Appearance → Outdated templates. Open it even if the banner is easy to ignore.
Merge: blue and yellow
Official merge tool, the sentence people skip: check immediately; important functionality may be missing if you leave outdated templates sitting.
Appearance → Outdated templates → Merge.
- Blue = the tool applied that hunk automatically. Read it anyway. Auto-merge is not a design review.
- Yellow = conflict. The parent changed a line you also changed. You must pick, combine, or revert.
There is no third colour that means “safe to ignore until next quarter.”
A sane merge pass:
- Backup first. Always. Backup article. You cannot downgrade. Restore is the only undo.
- Merge on staging if the list is more than a handful of templates.
- Sort mentally:
extra.lessalmost never belongs here (it is yours).PAGE_CONTAINER,header,helper_js_global,forum_list,thread_vieware the ones that take the public site down. - For each yellow: open a diff. Ask “did I customize this to add a logo, or because I was bored?” Logo belongs in Header/logo row — revert the template and do it properly. Boredom: revert.
- If you cannot explain the custom hunk in one sentence, revert to parent and recreate the need as a property, extra.less, or a narrow TMS.
- Reload guest, Registered, staff. Both variations. Icon Usage Analyzer if you touched icons.
Community thread pattern when an outdated template “will not go away”: you still have a customize on the child and you think you merged. Revert the child copy so it inherits, then re-apply only the specific change — preferably not as another full-file customize.
Automatically mergeable templates can be batch-processed. Remaining templates need a human. “All automatically mergeable templates have been processed” is not “the site is fine.” It is “the easy ones are done.”
UI.X and “too many open files”
Community operations, ThemeHouse UI.X specifically: a style rebuild can exhaust the process file-descriptor limit. Symptom: rebuild fails partway, CSS half-applies, ACP throws an error that mentions too many open files. This is not “XenForo is broken.” It is a host limit meeting a framework that opens a lot of templates.
What people do in those threads:
- Raise
open_files_limit(community example: 50000) at the PHP-FPM / system level, then rebuild. - Rebuild from CLI instead of the web ACP so the web-server user is not the one hitting the cap.
This article will not invent a ThemeHouse button name we did not see. If your UI.X parent documents a CLI rebuild, use that. If it does not, the XenForo CLI (php cmd.php on the box) is how other heavy jobs already run — style rebuild from CLI is the same idea. Do this on staging first. Do not raise a production ulimit in the same hour you also flip Default style.
If you are choosing a parent and your host cannot raise file limits, a one-shot DohTheme skin (Gamez, Pad, and friends) is operationally quieter than UI.X. That is a hosting fact, not a design ranking. The FiveStyle review already put Legend versus Gamez in those terms.
Designer mode is not this article
Official $config['designer']['enabled'] = true exists. It is how style developers sync templates to the filesystem. Mentioning it so you do not think we forgot: it is advanced, it is how you get a messy src/styles/ tree on a production box if you flip it “to be safe,” and you do not need it to run a child + extra.less + TMS stack. Leave it off unless you are shipping a style.
Worked hour: logo, one rule, one modification
You serve Default. You want the city logo, a slightly tighter category strip, and a data-board="city" on PAGE_CONTAINER for a tiny JS hook an add-on already expects.
- Appearance → Styles → Add style. Title
Board. Parent Default. Not user-selectable yet. - Style properties → Header/logo row. Upload the logo. Background and text on
@xf-chips. - Enable variations if you want night mode. Recolour the alternate palette. Style types Light / Dark. Stop. Flip as a guest. If this is the only goal, you are done — switcher article.
- Child Templates → extra.less:
.block--category .block-header {
padding-top: @xf-paddingMedium;
padding-bottom: @xf-paddingMedium;
}
Tokens, not 12px you will hate on the dark half. Save. Hard-refresh. If it looks wrong, the property group Node / forum list might already expose this — delete the rule and use the property.
- Appearance → Template modifications → Add template modification. Template
PAGE_CONTAINER. Find the opening<htmltag (copy the real attribute list from the template — do not invent a short find that matches twice). Replace with the same tag plusdata-board="city". Simple find, not regex, until you must. Bind to no add-on, or to a dummy add-on you own, so you can find it later. Save. Confirm the modification applies (match count ≥ 1). - Do not customize
PAGE_CONTAINER. - Appearance options: Default style =
Board. HTML email = Default (light). - Three sessions. Then announce the logo.
Next 2.3.x: extra.less stays. The TMS either still matches or the modification list shows zero matches — you update the find in five minutes. Zero outdated templates. That is the whole point of the hour.
What usually looks like “the update broke us”
| Symptom | Likely cause | First move |
|---|---|---|
| Header is last year’s HTML | Customized PAGE_CONTAINER or header on Default / vendor |
Outdated templates; revert; logo belongs in properties |
| Dark variation is black-on-black | Hex in extra.less, or style type still Light | Search extra.less for #; read the switcher article |
| Node icons missing | 2.2 webfont CSS | Nodes article; .m-faContent; hide SVG |
| TMS did nothing after upgrade | Find string gone in 2.3 markup | Template modifications list; rewrite the find |
| Public CSS half-missing after UI.X rebuild | too many open files | Raise limit or CLI rebuild; staging |
| Email is a dark brick | Email uses the public dark style | Separate HTML email style |
| “I merged and it is still outdated” | Child copy still customized | Revert to inherit, re-apply as TMS / extra.less |
| Guest sees old logo | CDN or css.php cache |
Purge; performance |
| Staff say it looks fine | They never left the ACP | Guest + throwaway Registered + staff |
Configuration mistakes already told you that if you only test as admin, you ship admin’s board. Same rule after every merge.
Branding, light/dark, and the articles that own them
A logo and palette chips are a brand system. How those chips stay consistent on mail, notices, and a Discourse sister site is the branding article. Do not turn this process piece into a brand book.
Whether the public default should be light or dark, and how contrast fails on a newsroom versus a FiveM hub, is light vs dark. This article only insists that whatever you choose lives in variation-aware chips on a child.
How the member flips the two halves is dark mode. Do not install a third-party switcher. Do not create Board Dark as a sibling.
Add-ons that inject CSS
An add-on that ships its own Less is a second parent you do not control. After you install XenPorta, a cache add-on, or a listing add-on:
- Keep your overrides in the child’s extra.less, scoped to that add-on’s classes.
- Re-test private nodes. Some layout add-ons render a card from the title before they check View.
- Re-test both variations.
- After the add-on updates, check Template modifications and Outdated templates. Add-on templates go outdated the same way core templates do.
Do not customize the add-on’s templates in the vendor style. Child, or a TMS bound to that add-on ID.
A quarterly drill
Print this. Run it when 2.3.x lands, and once a quarter even if it does not.
- Staging from last night’s backup.
- Upgrade core / add-ons / vendor parent on staging only.
- Appearance → Template modifications — zero silent misses.
- Appearance → Outdated templates — merge blue, decide yellow, revert anything you cannot explain.
- Icon Usage Analyzer if icons moved.
- Guest, Registered, staff × both variations × forum list, thread, editor, profile, conversations, login, a notice, HTML email.
- Only then production, same order, with a restore point you have already tested.
If step 4 is fifty yellow templates, you did not fail the upgrade. You failed last year’s process. Spend the next quiet week reverting customizes into extra.less and TMS until the outdated list is empty. The following upgrade will be an hour.
Checklist
- Master never customized
- Vendor parent (or Default) never holds the logo or extra.less
- Public style is a child named something durable
- Palette and Basic colors use
@xf-chips - extra.less is on the child; includes used if it grew; no Google Fonts CDN
- Custom font, if any, is self-hosted
@font-face(community; not an official upload doc) - New HTML is a template modification, not a customized core file
- After every update: Template modifications list + Outdated templates merge (blue / yellow)
- UI.X rebuild: file-limit or CLI, staging first
- Designer mode off unless you ship a style
- HTML email style is readable
- Three sessions, both variations, after every merge
Takeaways
- The upgrade-safe stack is Master → vendor parent → your child. Customize only the child.
- Style properties, then extra.less (community standard, not an official chapter), then template modifications. Direct template edits become outdated on purpose.
- Appearance → Outdated templates → Merge: blue applied, yellow conflict. Check immediately.
- UI.X can hit too many open files; raise the limit or rebuild from CLI.
- The 2.3 variation switch is a different article. Do not fork a second style to get dark mode.
Leave the parent boring. Put the brand in the child. The next point release should be a merge you finish before coffee, not a redesign you announce as an outage.

