015 —Frontend
Inline CSS and embedded fonts: 2010s habits that aged well
Eleven years on, the parts of a 2014 WordPress theme still doing useful work are the ones we used to mock: inline CSS, self-hosted fonts, plain JavaScript.
The site is from 2014. The agency that built it folded years ago. The new owner pays €40 a month for hosting, hates Webflow on principle, and asked us to add a cookie banner without breaking anything. Open style.css in the theme and there it is: a 2,100-line stylesheet, hand-rolled, no preprocessor, no build step. Open the header template and the font is being served from /wp-content/themes/dockside/fonts/inter-v3/ with five weights and a sane font-display: swap declaration. Eleven years later, on a clean Lighthouse pass, this site scores 98 on performance.
Nobody planned for this. The developer who wrote it was doing what was normal in 2014. And yet the things that aged badly in this codebase are not the inline CSS or the embedded fonts. They are the jQuery slider plugin, the third-party CDN that no longer resolves, and the Google Maps embed that now demands an API key. The boring parts, the parts we used to apologise for, are doing the heavy lifting.
The inline CSS heuristic that survived
If you wrote CSS between 2008 and 2016, you were taught to keep it out of HTML. Specificity wars, separation of concerns, the cascade. Then around 2018 a different argument started winning: critical CSS belongs in <style> tags in the document head, because the browser cannot paint anything until that stylesheet arrives. Tailwind made inline classes respectable. Next.js shipped CSS-in-JS by default for a while. The pendulum swung.
What did not change is the underlying physics. A round trip to fetch style.css still costs you a paint. A site from 2014 that put its layout-critical inline CSS in the head, even by accident, will outperform a site from 2024 that pulls a 60 KB Tailwind bundle from a CDN with no Cache-Control header. The 2014 developer was not optimising for Largest Contentful Paint. They just did not know any better and could not be bothered with Sass. The result is the same.
The MDN reference for <style> still warns against using it for anything except scoped critical CSS. That warning has not changed in fifteen years. The advice was right then and is right now, and the developers who ignored it in the messy direction (everything inline, nothing external) accidentally landed closer to current best practice than the developers who built elaborate Sass pipelines they then walked away from.
Self-hosted fonts before it was cool
For a long stretch, roughly 2012 to 2020, the default move was to load fonts from Google Fonts. One line in the head, no fuss. Then GDPR happened. The Munich regional court ruling in January 2022 made it explicit: serving a Google-hosted font from a German visitor's browser transmits their IP address to Google without consent, and that is a data transfer under Article 44. Every German agency suddenly scrambled to self-host.
The legacy sites we work with had already done this. Not because they were privacy-conscious, but because in 2014 a lot of developers did not trust third-party CDNs and just downloaded the font files. The .htaccess block they wrote looked like this:
<FilesMatch "\.(woff|woff2|ttf|eot|otf)$">
Header set Cache-Control "max-age=31536000, public, immutable"
Header set Access-Control-Allow-Origin "*"
</FilesMatch>
That is still a correct configuration in 2026. The immutable directive is from 2017, so it usually was not there originally, but the rest of it has been valid since Apache 2.2. The font files themselves, sitting in /wp-content/themes/<theme>/fonts/, still ship with no privacy footprint, no third-party DNS lookup, and no surprise outage when Google decides to rename a weight.
Vanilla JS as a forward-compatibility strategy
The other 2010s habit that aged well is plain, framework-free JavaScript. Not by virtue, by accident. A site that used jQuery 1.11 in 2014 will mostly still work in 2026, because the DOM API has been remarkably stable. A site that used Angular 1.x, Backbone, or Ember in 2014 is unmaintainable now without a full rewrite. The line between platform and framework matters here. Browsers honour their old contracts. Frameworks do not.
The places this shows up most are the small interaction layers: a mobile menu toggle, a tab switcher, a form validator. Five lines of addEventListener from 2015 are still five lines of addEventListener in 2026. Five lines of Backbone View.extend are an archaeological dig.
The lesson is not "never use a framework". It is that any abstraction with a release cycle shorter than your client's hosting contract is a future migration project. The browser API surface runs on a different release cycle. It has not had a meaningful breaking change in a decade.
What the 2020s habit set got wrong
The default frontend in 2026 looks roughly like this: Next.js, Tailwind, a component library, a headless CMS, Vercel. It is a productive stack for greenfield work. It is also a stack with eight moving parts, six of which will publish a breaking change in the next 24 months. None of these parts are individually wrong. The aggregate is fragile in a way that a 2014 WordPress theme with hand-written CSS is not.
This is not nostalgia. The 2014 site has its own problems: no responsive image strategy, an XSS-prone comment form, no CSP header. The point is narrower. The specific habits we mocked at the time, inline CSS, embedded fonts, plain JS, turned out to be the layers that survived the longest because they touched the parts of the platform that change the slowest.
If you are about to rebuild one of these sites, the most useful question is not "what framework should we use" but "which of this thing's current habits are accidentally future-proof, and which broke first." The answer is usually the opposite of what the original developer would have predicted.
Where this leaves the audit
When we built Pier we ran into this exact pattern: agencies would dock with a 2014 WordPress install, expect to spend two days untangling the CSS, and find the CSS was the cleanest thing in the codebase. The way we ended up handling it was to make every file edit reversible from version history, with the MySQL editor sitting beside the file tree so you can poke at the inline CSS without committing to a refactor.
The smallest thing you could do today: open one legacy site you maintain, grep the theme for fonts.gstatic.com, and if you find a reference, decide in the next ten minutes whether it stays. The font files are almost certainly already on disk.
— Questions —
Should I refactor inline CSS in a legacy site on principle?
Usually no. If it renders fast and works, leave it. Refactor when you have a concrete reason like a redesign or accessibility audit, not because the pattern looks old.
Is self-hosting fonts still better than Google Fonts in 2026?
For European traffic, yes. Self-hosting removes a DNS lookup, gives you full Cache-Control headers, and avoids GDPR exposure. Browsers no longer share font caches across origins anyway.
What is the fastest way to audit a legacy theme for third-party calls?
Grep the theme directory for 'https://' and '//'. Most outbound calls are font CDNs, analytics, and abandoned plugins. Each one is a future outage waiting to happen.
Does jQuery still belong on a 2026 site?
On a new site, no. On a 2014 site that already loads it for a working menu, removing it is a refactor risk with no user-visible benefit. Leave it unless it blocks a security update.