Last updated on August 11th, 2025 at 11:56 am
Have you ever noticed that special characters like ®, ™, and © appear inconsistently across different fonts on your website? Sometimes they’re oversized, misaligned, or just don’t blend well with the surrounding text. This inconsistency can disrupt the visual harmony of the design, and make for unhappy customers. So here’s how we fix symbols style and consistency in wordpress.
Index
The Problem
Special characters such as the registered trademark (®), trademark (™), and copyright (©) symbols can render differently depending on the font used. Some fonts may display these symbols larger or smaller than the surrounding text, leading to a jarring visual experience.
The Solution
To ensure consistent styling of these special characters across your website, in Fuel LAB® we normally use a combination of CSS and JavaScript. The idea is to wrap these symbols in a <span> with a specific class and then apply uniform styling to that class.
You can also just download the wp_code snippet here, if you don’t need the the TreeWalker edit 🙂
Step 1: Define the CSS for your special characters
First, add the following CSS to your stylesheet to style the special characters uniformly:
.reg-symbol {
font-size: 0.55em;
position: relative;
top: -0.4em;
line-height: 0;
vertical-align: baseline;
font-weight: normal;
display: inline-block;
}
This CSS sets a consistent size and vertical alignment for the symbols, ensuring they blend seamlessly with the surrounding text.
Step 2: Implement the JavaScript
Next, use the following JavaScript to automatically wrap the special characters in the defined <span>:
<script>
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll("p, h1, h2, h3, h4, h5, h6, span, li, a").forEach(el => {
el.childNodes.forEach(node => {
if (node.nodeType === Node.TEXT_NODE && node.textContent.match(/[®™©]/)) {
const replacedHTML = node.textContent
.replace(/®/g, '<span class="reg-symbol">®</span>')
.replace(/™/g, '<span class="reg-symbol">™</span>')
.replace(/©/g, '<span class="reg-symbol">©</span>');
const spanWrapper = document.createElement('span');
spanWrapper.innerHTML = replacedHTML;
el.replaceChild(spanWrapper, node);
}
});
});
});
</script>
This script waits for the DOM to load, then searches through specified elements for text nodes containing the special characters. When found, it wraps them in a <span> with the reg-symbol class.
What if it doesn’t work?
There are a number of situations where your ® mark is included in an inner structure of the block, as an example bold tags. This applies to <b>, <strong>, <i>, <span>, ecc
In this case, you can try a different approach using Tree Walker
document.addEventListener("DOMContentLoaded", function () {
const elements = document.querySelectorAll("p, h1, h2, h3, h4, h5, h6, span, li, a");
elements.forEach(el => {
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null, false);
let node;
while ((node = walker.nextNode())) {
if (node.textContent.match(/[®™©]/)) {
const replacedHTML = node.textContent
.replace(/®/g, '<span class="reg-symbol">®</span>')
.replace(/™/g, '<span class="reg-symbol">™</span>')
.replace(/©/g, '<span class="reg-symbol">©</span>');
const spanWrapper = document.createElement('span');
spanWrapper.innerHTML = replacedHTML;
node.parentNode.replaceChild(spanWrapper, node);
}
}
});
});What if the symbols are still rendering the same
In this case, it could be your theme builder or page builder is injecting after the DOM is ready the dynamic classes on spans, p, headings and such. In that case, you might want to try a different approach (tree walker), such as the following example I’ve made for Divi:
<script>
(function () {
// Regex to detect any of the target symbols
const SYMBOL_RE = /[®™©]/;
const SPLIT_RE = /[®™©]/g;
// Root selector where processing will start
// Change this if you want to limit scope, e.g., '.et_builder_inner_content'
const rootSelector = 'body';
/**
* Processes a single text node and replaces ®™© with <span class="reg-symbol">...</span>
* while keeping the original parent element and its attributes intact.
*/
function processTextNode(node) {
if (!node || !node.parentNode) return;
const parent = node.parentNode;
const tag = parent.nodeName;
// Skip unwanted parent tags and text already inside .reg-symbol
if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'TEXTAREA' || parent.closest('.reg-symbol')) return;
const txt = node.nodeValue;
if (!SYMBOL_RE.test(txt)) return; // Skip if no symbol found
// Create a temporary fragment to hold the new node structure
const frag = document.createDocumentFragment();
let last = 0;
// Go through each match and replace it with a span
txt.replace(SPLIT_RE, (match, idx) => {
// Append text before the symbol
if (idx > last) frag.appendChild(document.createTextNode(txt.slice(last, idx)));
// Create the span for the symbol
const span = document.createElement('span');
span.className = 'reg-symbol';
span.textContent = match; // Safe text insertion (avoids HTML injection)
frag.appendChild(span);
last = idx + 1;
return match;
});
// Append any remaining text after the last symbol
if (last < txt.length) frag.appendChild(document.createTextNode(txt.slice(last)));
// Replace the original text node with our fragment
parent.replaceChild(frag, node);
}
/**
* Processes all eligible text nodes within a given root element.
* Uses TreeWalker to find text nodes and avoids script/style/textarea.
*/
function processContainer(root) {
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode(n) {
if (!n.parentNode) return NodeFilter.FILTER_REJECT;
const p = n.parentNode;
const tag = p.nodeName;
if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'TEXTAREA') return NodeFilter.FILTER_REJECT;
return SYMBOL_RE.test(n.nodeValue) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
}
});
const toProcess = [];
let node;
while ((node = walker.nextNode())) {
toProcess.push(node);
}
// Process after collection to avoid breaking the walker
toProcess.forEach(processTextNode);
}
/**
* Initializes the script:
* - Processes the initial content
* - Sets up a MutationObserver to handle dynamically inserted content
*/
function init() {
const root = document.querySelector(rootSelector);
if (!root) return;
// Process existing content
processContainer(root);
// Observe for future changes (Divi animations, AJAX, lazy load, etc.)
const obs = new MutationObserver(mutations => {
for (const m of mutations) {
if (m.type === 'characterData') {
processTextNode(m.target);
} else if (m.type === 'childList') {
m.addedNodes.forEach(n => {
if (n.nodeType === 3) {
processTextNode(n); // Direct text node
} else if (n.nodeType === 1) {
processContainer(n); // Element: process all its text nodes
}
});
}
}
});
obs.observe(root, {
childList: true, // Watch for new/removed elements
characterData: true, // Watch for text changes
subtree: true // Include all descendants
});
}
// Run after page load to ensure we beat late scripts that change the DOM
if (document.readyState === 'complete') {
setTimeout(init, 0);
} else {
window.addEventListener('load', () => setTimeout(init, 0));
}
})();
</script>Conclusion
If you’re working with wordpress, this is a pretty stable way to ensure that special characters like ®, ™, and © are styled consistently across your website, regardless of the fonts used. Of course feel free to edit the css part of the snippet accordingly to your design choices.

Pietro Mingotti is an Italian neural science researcher, entrepreneur and technical marketing specialist, best known as the founder and owner of Fuel LAB®, a leading digital marketing and technical marketing agency based in Italy, operating worldwide. With a passion for science, creativity, innovation, and technology, Pietro has established himself as a thought leader in the field of technical marketing and data science and has helped numerous companies achieve their goals.