WordPress and <style> tags

WordPress.com strips <style> tags from posts. Here’s how to work around that and create beautifully styled technical articles with custom typography, code blocks, and layout components — without a self-hosted installation.

If you’ve ever tried to write a technical blog post on WordPress.com and found the default styling lacking — ugly code blocks, no control over fonts, tables that look like spreadsheets from 2005 — you’ve hit the platform’s biggest limitation. WordPress.com sanitizes post HTML aggressively, stripping out <style> and <link> tags for security reasons.

This post documents the approach I use to get full control over article styling on a WordPress.com Premium plan, without needing a self-hosted WordPress installation.

The Problem

WordPress.com’s block editor (Gutenberg) gives you paragraphs, headings, images, and code blocks. But the built-in styling is generic — it inherits your theme’s defaults, which are designed for broad appeal, not for technical writing. Specifically:

Code blocks use your theme’s monospace font with minimal contrast. No syntax highlighting, no language labels, no dark background that signals “this is code” to a scanning reader.

Tables get basic browser defaults — no header styling, inconsistent padding, no visual hierarchy between header and data rows.

Callout boxes don’t exist natively. You can use a Quote block, but it looks like a quote, not like a tip or warning. There’s no way to add a colored left border with a label.

Typography is locked to your theme. If your theme uses a system font stack, every post looks like a Google Doc.

The obvious fix — adding a <style> block to your post HTML — doesn’t work. WordPress.com strips it on save.

The Solution: Additional CSS + Custom HTML Blocks

The approach splits styling from content across two places:

Additional CSS
site-wide styles
+
Custom HTML Block
post content with classes
=
Styled Post
fonts, colors, layout

Additional CSS lives in the WordPress Customizer (Appearance → Customize → Additional CSS, or via the Site Editor on block themes). It’s injected into every page’s <head> as a legitimate <style> block. WordPress.com allows this on paid plans (Personal and above) because it’s a controlled environment — you’re editing a dedicated CSS field, not injecting arbitrary HTML into post content.

Custom HTML blocks in the post editor accept raw HTML with class attributes. WordPress.com doesn’t strip class from elements, so your post HTML can reference any class defined in Additional CSS.

The result: your CSS lives in one place and applies to all posts. Your post content is clean, semantic HTML with descriptive class names. No inline styles, no duplication, no fighting the sanitizer.

Setting Up the CSS

I scope everything under a single wrapper class — .rss-post — to avoid polluting the rest of the site. Every selector starts with .rss-post, so the styles only apply inside posts that use the wrapper div.

Fonts

The CSS imports three fonts from Google Fonts via @import:

css@import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,600;1,6..72,400&family=JetBrains+Mono:wght@400;500&family=DM+Sans:wght@400;500;600&display=swap');

Newsreader is an optical-size serif that works beautifully for body text — it adjusts its weight and contrast based on font size, so headings and body text both look sharp without manual tweaking. JetBrains Mono is a purpose-built coding font with ligatures and distinct characters for 0/O and 1/l/I. DM Sans handles UI elements like labels, table headers, and info box titles — places where a clean sans-serif reads better than a serif.

The accent system

A single accent color (#e07a2f, a warm amber) ties the design together. It appears in four places: the left border on headings, the left border on info boxes, the info box label text, and link hover states. This creates visual consistency without overwhelming the page.

css.rss-post h2 {
  position: relative;
  padding-left: 1.1rem;
}
.rss-post h2::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.15em;
  bottom: 0.15em;
  width: 3.5px;
  background: #e07a2f;
  border-radius: 2px;
}

The ::before pseudo-element creates the accent bar. This is one of the things you can’t do with inline styles — pseudo-elements only work in stylesheets, which is exactly why Additional CSS is necessary.

Code blocks

The default WordPress code block is functional but visually flat. The custom CSS gives code blocks a dark background (#1e1e2e, matching the Catppuccin Mocha palette), a subtle border, and generous padding. A floating language label in the top-right corner uses a <span class="label"> inside the <pre> block:

css.rss-post pre {
  background: #1e1e2e;
  color: #cdd6f4;
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.82rem;
  line-height: 1.7;
  padding: 1.4rem 1.6rem;
  border-radius: 8px;
  border: 1px solid #313244;
}

.rss-post pre .label {
  position: absolute;
  top: 0; right: 0;
  font-family: 'DM Sans', sans-serif;
  font-size: 0.62rem;
  text-transform: uppercase;
  color: #6c7086;
  background: #313244;
  padding: 0.2em 0.8em;
  border-radius: 0 8px 0 6px;
}

Inline code gets a light warm background (#edebe6) that’s visible without being distracting.

Info boxes

Tips, warnings, and gotchas use the .infobox class — a light background with an amber left border and an uppercase label:

Example This is what an info box looks like. The label is a <strong> element styled with uppercase text and the accent color. The background is a warm off-white that distinguishes it from the main content without creating harsh contrast.

The HTML for this is minimal:

html<div class="infobox">
  <strong>Tip</strong>
  Your message here.
</div>

Flow diagrams

For simple architecture or process diagrams, the .flow class creates a horizontal flex layout with dark boxes and arrow separators:

Step 1
Step 2
Step 3

The .accent modifier highlights one box in amber. On mobile, the flex container wraps naturally.

Writing a Post

The workflow for creating a styled post is:

1. Create a new post in the WordPress editor.

2. Add a Custom HTML block (not a Paragraph, not a Code block). Click the + button, search for “Custom HTML”.

3. Paste your HTML wrapped in <div class="rss-post">. Use standard HTML tags — <h2>, <p>, <pre><code>, <table> — with the custom classes where needed (.infobox, .flow, .label, .lead).

4. Preview and publish. The Additional CSS applies automatically.

Important Use a single Custom HTML block for the entire post, not multiple blocks. If you mix Custom HTML blocks with regular Paragraph or Heading blocks, the regular blocks won’t be inside the .rss-post wrapper and won’t get the custom styling.

Why Not Use the Block Editor Natively?

A reasonable question. Gutenberg’s blocks do offer some styling — you can set colors, font sizes, and spacing per block. But there are real limitations:

No custom fonts. You’re limited to what your theme provides plus WordPress.com’s font library. Want JetBrains Mono for code? Not an option through the block editor.

No pseudo-elements. The accent bar on headings uses ::before. There’s no block editor control for that.

No reusable component patterns. An info box with a colored border, background, and styled label would need manual per-block styling every time. With CSS classes, it’s one <div class="infobox">.

No code block theming. The built-in Code block doesn’t support dark themes, language labels, or custom fonts.

Consistency. When all styling comes from a single CSS file, every post looks consistent. Per-block styling drifts over time.

Available Components

Here’s a quick reference for the CSS classes available in the current stylesheet:

ClassElementPurpose
.rss-post<div>Wrapper — all styles are scoped under this
.lead<p>Subtitle / intro paragraph in muted gray
.infobox<div>Tip / warning / note callout box
.flow<div>Horizontal flow diagram container
.flow-box<div>Individual box in a flow diagram
.flow-box.accent<div>Highlighted (amber) flow box
.flow-arrow<span>Arrow between flow boxes
.label<span>Language label inside <pre> blocks
.tag-list<ul>Horizontal tag/category pills

The Tradeoff

This approach is not without downsides. You’re writing raw HTML instead of using the visual editor, which is slower and more error-prone. The post editor’s preview won’t show the custom styles (you need to use the site preview or publish as draft). And if you ever change themes, the Additional CSS carries over but may need adjustments to avoid conflicts with the new theme’s styles.

For me, the tradeoff is worth it. I write technical content with code blocks, tables, and diagrams. The default WordPress.com styling doesn’t serve that content well, and this approach gives me full control without needing to self-host WordPress or pay for a Business plan with plugin access.

One CSS file. Clean HTML with classes. Posts that actually look the way you want them to.

  • WordPress
  • CSS
  • Web Design
  • Blogging
  • Technical Writing

Leave a comment

Discover more from /root

Subscribe now to keep reading and get access to the full archive.

Continue reading