I’ll build this as a strong SEO UI chunk for RX Theme: schema helpers, TOC, FAQ accordion, reading progress, breadcrumbs enhancement, copy-link buttons, lazy UI behaviors, and safe WordPress-friendly front-end code.
Yes friend — for this file:
/assets/static-js/static-chunks-js/chunk-049-seo-ui.js
Use it for SEO user-interface features, not heavy backend SEO. This file should improve user experience, engagement, structured-content interaction, readability, internal navigation, and article signals.
Copy this full code into:
rx-theme/assets/static-js/static-chunks-js/chunk-049-seo-ui.js
/*!
* RX Theme - SEO UI Chunk
* File: /assets/static-js/static-chunks-js/chunk-049-seo-ui.js
* Purpose: Advanced SEO-friendly UI enhancement for article pages, medical content, blog posts, docs, FAQs, TOC, breadcrumbs, reading progress, share tools, and UX signals.
* Author: RxHarun
*/
(function () {
'use strict';
/**
* ==========================================================
* RX SEO UI CONFIG
* ==========================================================
*/
const RX_SEO_UI = {
selectors: {
article: '.rx-article, article, .entry-content, .post-content, .rx-content',
contentArea: '.entry-content, .post-content, .rx-content, article',
headings: 'h2, h3',
tocContainer: '[data-rx-toc], .rx-toc',
faqContainer: '.rx-faq, [data-rx-faq]',
faqItem: '.rx-faq-item, .faq-item',
faqQuestion: '.rx-faq-question, .faq-question',
faqAnswer: '.rx-faq-answer, .faq-answer',
breadcrumb: '.rx-breadcrumb, .breadcrumbs, nav[aria-label="breadcrumb"]',
shareBox: '.rx-share-box, [data-rx-share]',
copyLinkButton: '[data-rx-copy-link]',
printButton: '[data-rx-print]',
backToTop: '[data-rx-back-to-top], .rx-back-to-top',
readingProgress: '[data-rx-reading-progress], .rx-reading-progress',
estimatedReadTime: '[data-rx-read-time], .rx-read-time',
updatedDate: '[data-rx-updated-date], .rx-updated-date',
searchInput: '[data-rx-content-search]',
glossaryTerm: '[data-rx-glossary-term]',
externalLinks: 'a[href^="http"]',
internalLinks: 'a[href^="/"], a[href*="' + window.location.hostname + '"]',
tables: '.entry-content table, .post-content table, article table',
images: '.entry-content img, .post-content img, article img',
videos: 'iframe[src*="youtube"], iframe[src*="vimeo"], video',
accordions: '[data-rx-accordion]',
jumpLinks: 'a[href^="#"]'
},
classes: {
active: 'is-active',
open: 'is-open',
hidden: 'is-hidden',
visible: 'is-visible',
current: 'is-current',
copied: 'is-copied',
enhanced: 'rx-enhanced',
tableWrap: 'rx-table-wrap',
imageWrap: 'rx-image-wrap',
externalLink: 'rx-external-link',
internalLink: 'rx-internal-link',
tocList: 'rx-toc-list',
tocItem: 'rx-toc-item',
tocLink: 'rx-toc-link',
tocLevel2: 'rx-toc-level-2',
tocLevel3: 'rx-toc-level-3',
progressBar: 'rx-progress-bar'
},
options: {
minHeadingsForToc: 3,
wordsPerMinute: 220,
scrollOffset: 90,
debounceDelay: 120,
throttleDelay: 80,
autoGenerateToc: true,
autoHeadingIds: true,
smoothScroll: true,
enableFaqAccordion: true,
enableProgressBar: true,
enableReadTime: true,
enableExternalLinkSafety: true,
enableTableWrap: true,
enableImageEnhance: true,
enableVideoLazyFrame: true,
enableCopyButtons: true,
enableContentSearch: true,
enableBackToTop: true,
enablePrint: true,
enableTocSpy: true,
enableSchemaHints: true
}
};
/**
* ==========================================================
* SMALL HELPERS
* ==========================================================
*/
const RX = {
qs(selector, parent = document) {
return parent.querySelector(selector);
},
qsa(selector, parent = document) {
return Array.prototype.slice.call(parent.querySelectorAll(selector));
},
hasClass(el, className) {
return el && el.classList.contains(className);
},
addClass(el, className) {
if (el) el.classList.add(className);
},
removeClass(el, className) {
if (el) el.classList.remove(className);
},
toggleClass(el, className, force) {
if (el) el.classList.toggle(className, force);
},
attr(el, name, value) {
if (!el) return null;
if (typeof value === 'undefined') return el.getAttribute(name);
el.setAttribute(name, value);
return value;
},
create(tag, className, text) {
const el = document.createElement(tag);
if (className) el.className = className;
if (text) el.textContent = text;
return el;
},
slugify(text) {
return String(text || '')
.toLowerCase()
.trim()
.replace(/&/g, ' and ')
.replace(/[^\w\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '');
},
uniqueId(base) {
let id = base || 'rx-section';
let i = 2;
while (document.getElementById(id)) {
id = base + '-' + i;
i++;
}
return id;
},
getText(el) {
return el ? el.textContent.replace(/\s+/g, ' ').trim() : '';
},
isVisible(el) {
if (!el) return false;
const style = window.getComputedStyle(el);
return style.display !== 'none' && style.visibility !== 'hidden' && el.offsetParent !== null;
},
debounce(fn, wait) {
let timer = null;
return function () {
const context = this;
const args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, wait);
};
},
throttle(fn, wait) {
let last = 0;
let timer = null;
return function () {
const now = Date.now();
const remaining = wait - (now - last);
const context = this;
const args = arguments;
if (remaining <= 0) {
clearTimeout(timer);
timer = null;
last = now;
fn.apply(context, args);
} else if (!timer) {
timer = setTimeout(function () {
last = Date.now();
timer = null;
fn.apply(context, args);
}, remaining);
}
};
},
copyText(text) {
if (navigator.clipboard && window.isSecureContext) {
return navigator.clipboard.writeText(text);
}
return new Promise(function (resolve, reject) {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.left = '-9999px';
textarea.style.top = '-9999px';
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
try {
document.execCommand('copy');
document.body.removeChild(textarea);
resolve();
} catch (error) {
document.body.removeChild(textarea);
reject(error);
}
});
},
safeUrl(url) {
try {
return new URL(url, window.location.origin);
} catch (error) {
return null;
}
},
prefersReducedMotion() {
return window.matchMedia &&
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
}
};
/**
* ==========================================================
* MAIN SEO UI MODULE
* ==========================================================
*/
const RxSeoUI = {
article: null,
headings: [],
tocLinks: [],
progressBar: null,
init() {
this.article = RX.qs(RX_SEO_UI.selectors.article);
if (!this.article) {
return;
}
RX.addClass(document.documentElement, 'rx-seo-ui-ready');
this.prepareHeadings();
this.buildTableOfContents();
this.setupTocScrollSpy();
this.setupSmoothJumpLinks();
this.setupReadingProgress();
this.setupEstimatedReadTime();
this.setupFaqAccordion();
this.setupBreadcrumbEnhancement();
this.setupShareTools();
this.setupCopyLinkButtons();
this.setupBackToTop();
this.setupPrintButtons();
this.setupContentSearch();
this.enhanceExternalLinks();
this.enhanceInternalLinks();
this.wrapTables();
this.enhanceImages();
this.enhanceVideos();
this.setupAccordionBlocks();
this.setupGlossaryTerms();
this.setupSchemaHints();
this.setupKeyboardUX();
this.observeContentChanges();
window.dispatchEvent(
new CustomEvent('rxSeoUiReady', {
detail: {
article: this.article,
headings: this.headings.length
}
})
);
},
/**
* ======================================================
* HEADING ID + ARTICLE OUTLINE
* ======================================================
*/
prepareHeadings() {
if (!RX_SEO_UI.options.autoHeadingIds) {
return;
}
const headings = RX.qsa(RX_SEO_UI.selectors.headings, this.article)
.filter(function (heading) {
return RX.getText(heading).length > 0;
});
headings.forEach(function (heading) {
if (!heading.id) {
const base = RX.slugify(RX.getText(heading)) || 'rx-heading';
heading.id = RX.uniqueId(base);
}
heading.setAttribute('tabindex', '-1');
heading.classList.add('rx-seo-heading');
});
this.headings = headings;
},
/**
* ======================================================
* AUTO TABLE OF CONTENTS
* ======================================================
*/
buildTableOfContents() {
if (!RX_SEO_UI.options.autoGenerateToc) {
return;
}
const tocContainer = RX.qs(RX_SEO_UI.selectors.tocContainer);
if (!tocContainer || this.headings.length < RX_SEO_UI.options.minHeadingsForToc) {
return;
}
if (RX.hasClass(tocContainer, RX_SEO_UI.classes.enhanced)) {
return;
}
const nav = RX.create('nav', 'rx-toc-nav');
nav.setAttribute('aria-label', 'Article table of contents');
const title = tocContainer.getAttribute('data-rx-toc-title') || 'Table of Contents';
const titleEl = RX.create('div', 'rx-toc-title', title);
const list = RX.create('ol', RX_SEO_UI.classes.tocList);
this.headings.forEach(function (heading) {
const level = heading.tagName.toLowerCase() === 'h3' ? 3 : 2;
const item = RX.create(
'li',
RX_SEO_UI.classes.tocItem + ' ' + (level === 3 ? RX_SEO_UI.classes.tocLevel3 : RX_SEO_UI.classes.tocLevel2)
);
const link = RX.create('a', RX_SEO_UI.classes.tocLink, RX.getText(heading));
link.href = '#' + heading.id;
link.setAttribute('data-rx-target', heading.id);
item.appendChild(link);
list.appendChild(item);
});
nav.appendChild(titleEl);
nav.appendChild(list);
tocContainer.innerHTML = '';
tocContainer.appendChild(nav);
RX.addClass(tocContainer, RX_SEO_UI.classes.enhanced);
this.tocLinks = RX.qsa('.' + RX_SEO_UI.classes.tocLink, tocContainer);
},
setupTocScrollSpy() {
if (!RX_SEO_UI.options.enableTocSpy || !this.headings.length || !this.tocLinks.length) {
return;
}
const updateActiveToc = RX.throttle(() => {
let currentId = null;
const offset = RX_SEO_UI.options.scrollOffset + 20;
for (let i = 0; i < this.headings.length; i++) {
const heading = this.headings[i];
const rect = heading.getBoundingClientRect();
if (rect.top <= offset) {
currentId = heading.id;
}
}
this.tocLinks.forEach(function (link) {
const isCurrent = link.getAttribute('data-rx-target') === currentId;
RX.toggleClass(link, RX_SEO_UI.classes.current, isCurrent);
if (isCurrent) {
link.setAttribute('aria-current', 'true');
} else {
link.removeAttribute('aria-current');
}
});
}, RX_SEO_UI.options.throttleDelay);
window.addEventListener('scroll', updateActiveToc, { passive: true });
window.addEventListener('resize', updateActiveToc, { passive: true });
updateActiveToc();
},
/**
* ======================================================
* SMOOTH SCROLL FOR JUMP LINKS
* ======================================================
*/
setupSmoothJumpLinks() {
if (!RX_SEO_UI.options.smoothScroll) {
return;
}
const links = RX.qsa(RX_SEO_UI.selectors.jumpLinks);
links.forEach(function (link) {
if (RX.hasClass(link, 'rx-smooth-ready')) {
return;
}
RX.addClass(link, 'rx-smooth-ready');
link.addEventListener('click', function (event) {
const hash = link.getAttribute('href');
if (!hash || hash === '#') {
return;
}
const targetId = decodeURIComponent(hash.slice(1));
const target = document.getElementById(targetId);
if (!target) {
return;
}
event.preventDefault();
const top = target.getBoundingClientRect().top + window.pageYOffset - RX_SEO_UI.options.scrollOffset;
window.scrollTo({
top: top,
behavior: RX.prefersReducedMotion() ? 'auto' : 'smooth'
});
target.focus({ preventScroll: true });
if (history.pushState) {
history.pushState(null, '', '#' + target.id);
}
});
});
},
/**
* ======================================================
* READING PROGRESS
* ======================================================
*/
setupReadingProgress() {
if (!RX_SEO_UI.options.enableProgressBar) {
return;
}
let progress = RX.qs(RX_SEO_UI.selectors.readingProgress);
if (!progress) {
progress = RX.create('div', 'rx-reading-progress');
progress.setAttribute('data-rx-reading-progress', '');
progress.innerHTML = '<span class="' + RX_SEO_UI.classes.progressBar + '" aria-hidden="true"></span>';
document.body.appendChild(progress);
}
this.progressBar = RX.qs('.' + RX_SEO_UI.classes.progressBar, progress) || progress;
const updateProgress = RX.throttle(() => {
const articleRect = this.article.getBoundingClientRect();
const articleTop = articleRect.top + window.pageYOffset;
const articleHeight = Math.max(this.article.offsetHeight - window.innerHeight, 1);
const scrolled = window.pageYOffset - articleTop;
const percentage = Math.max(0, Math.min(100, (scrolled / articleHeight) * 100));
this.progressBar.style.width = percentage + '%';
progress.setAttribute('aria-valuenow', Math.round(percentage));
}, RX_SEO_UI.options.throttleDelay);
progress.setAttribute('role', 'progressbar');
progress.setAttribute('aria-label', 'Reading progress');
progress.setAttribute('aria-valuemin', '0');
progress.setAttribute('aria-valuemax', '100');
window.addEventListener('scroll', updateProgress, { passive: true });
window.addEventListener('resize', updateProgress, { passive: true });
updateProgress();
},
/**
* ======================================================
* ESTIMATED READING TIME
* ======================================================
*/
setupEstimatedReadTime() {
if (!RX_SEO_UI.options.enableReadTime) {
return;
}
const readTimeEl = RX.qs(RX_SEO_UI.selectors.estimatedReadTime);
if (!readTimeEl) {
return;
}
const text = RX.getText(this.article);
const words = text.split(/\s+/).filter(Boolean).length;
const minutes = Math.max(1, Math.ceil(words / RX_SEO_UI.options.wordsPerMinute));
readTimeEl.textContent = minutes + ' min read';
readTimeEl.setAttribute('aria-label', 'Estimated reading time: ' + minutes + ' minutes');
RX.addClass(readTimeEl, RX_SEO_UI.classes.enhanced);
},
/**
* ======================================================
* FAQ ACCORDION
* ======================================================
*/
setupFaqAccordion() {
if (!RX_SEO_UI.options.enableFaqAccordion) {
return;
}
const faqContainers = RX.qsa(RX_SEO_UI.selectors.faqContainer);
faqContainers.forEach(function (container, containerIndex) {
if (RX.hasClass(container, RX_SEO_UI.classes.enhanced)) {
return;
}
const items = RX.qsa(RX_SEO_UI.selectors.faqItem, container);
items.forEach(function (item, index) {
const question = RX.qs(RX_SEO_UI.selectors.faqQuestion, item);
const answer = RX.qs(RX_SEO_UI.selectors.faqAnswer, item);
if (!question || !answer) {
return;
}
const qId = question.id || 'rx-faq-q-' + containerIndex + '-' + index;
const aId = answer.id || 'rx-faq-a-' + containerIndex + '-' + index;
question.id = qId;
answer.id = aId;
question.setAttribute('role', 'button');
question.setAttribute('tabindex', '0');
question.setAttribute('aria-controls', aId);
question.setAttribute('aria-expanded', RX.hasClass(item, RX_SEO_UI.classes.open) ? 'true' : 'false');
answer.setAttribute('role', 'region');
answer.setAttribute('aria-labelledby', qId);
if (!RX.hasClass(item, RX_SEO_UI.classes.open)) {
answer.hidden = true;
}
const toggle = function () {
const isOpen = RX.hasClass(item, RX_SEO_UI.classes.open);
RX.toggleClass(item, RX_SEO_UI.classes.open, !isOpen);
question.setAttribute('aria-expanded', !isOpen ? 'true' : 'false');
answer.hidden = isOpen;
};
question.addEventListener('click', toggle);
question.addEventListener('keydown', function (event) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
toggle();
}
});
});
RX.addClass(container, RX_SEO_UI.classes.enhanced);
});
},
/**
* ======================================================
* BREADCRUMB ENHANCEMENT
* ======================================================
*/
setupBreadcrumbEnhancement() {
const breadcrumbs = RX.qsa(RX_SEO_UI.selectors.breadcrumb);
breadcrumbs.forEach(function (nav) {
if (RX.hasClass(nav, RX_SEO_UI.classes.enhanced)) {
return;
}
nav.setAttribute('aria-label', nav.getAttribute('aria-label') || 'Breadcrumb');
const links = RX.qsa('a', nav);
const last = links[links.length - 1];
links.forEach(function (link) {
link.classList.add('rx-breadcrumb-link');
});
if (last && last.href === window.location.href) {
last.setAttribute('aria-current', 'page');
}
RX.addClass(nav, RX_SEO_UI.classes.enhanced);
});
},
/**
* ======================================================
* SHARE TOOLS
* ======================================================
*/
setupShareTools() {
const shareBoxes = RX.qsa(RX_SEO_UI.selectors.shareBox);
shareBoxes.forEach(function (box) {
if (RX.hasClass(box, RX_SEO_UI.classes.enhanced)) {
return;
}
const title = document.title;
const url = window.location.href;
const nativeBtn = RX.qs('[data-rx-native-share]', box);
if (nativeBtn && navigator.share) {
nativeBtn.hidden = false;
nativeBtn.addEventListener('click', function () {
navigator.share({
title: title,
url: url
}).catch(function () {
// User cancelled share. No action needed.
});
});
}
const shareLinks = RX.qsa('[data-rx-share-platform]', box);
shareLinks.forEach(function (link) {
const platform = link.getAttribute('data-rx-share-platform');
const encodedUrl = encodeURIComponent(url);
const encodedTitle = encodeURIComponent(title);
let href = '';
if (platform === 'facebook') {
href = 'https://www.facebook.com/sharer/sharer.php?u=' + encodedUrl;
} else if (platform === 'twitter' || platform === 'x') {
href = 'https://twitter.com/intent/tweet?url=' + encodedUrl + '&text=' + encodedTitle;
} else if (platform === 'linkedin') {
href = 'https://www.linkedin.com/sharing/share-offsite/?url=' + encodedUrl;
} else if (platform === 'whatsapp') {
href = 'https://api.whatsapp.com/send?text=' + encodedTitle + '%20' + encodedUrl;
} else if (platform === 'telegram') {
href = 'https://t.me/share/url?url=' + encodedUrl + '&text=' + encodedTitle;
} else if (platform === 'email') {
href = 'mailto:?subject=' + encodedTitle + '&body=' + encodedUrl;
}
if (href) {
link.href = href;
link.target = platform === 'email' ? '_self' : '_blank';
link.rel = platform === 'email' ? '' : 'noopener noreferrer';
}
});
RX.addClass(box, RX_SEO_UI.classes.enhanced);
});
},
/**
* ======================================================
* COPY CURRENT LINK / COPY SECTION LINK
* ======================================================
*/
setupCopyLinkButtons() {
if (!RX_SEO_UI.options.enableCopyButtons) {
return;
}
const buttons = RX.qsa(RX_SEO_UI.selectors.copyLinkButton);
buttons.forEach(function (button) {
if (RX.hasClass(button, RX_SEO_UI.classes.enhanced)) {
return;
}
const originalText = button.textContent;
button.addEventListener('click', function () {
const targetSelector = button.getAttribute('data-rx-copy-link');
let textToCopy = window.location.href;
if (targetSelector && targetSelector !== 'current') {
const target = document.querySelector(targetSelector);
if (target && target.id) {
textToCopy = window.location.origin + window.location.pathname + '#' + target.id;
}
}
RX.copyText(textToCopy)
.then(function () {
RX.addClass(button, RX_SEO_UI.classes.copied);
button.textContent = button.getAttribute('data-rx-copied-text') || 'Copied';
setTimeout(function () {
RX.removeClass(button, RX_SEO_UI.classes.copied);
button.textContent = originalText;
}, 1600);
})
.catch(function () {
button.textContent = 'Copy failed';
setTimeout(function () {
button.textContent = originalText;
}, 1600);
});
});
RX.addClass(button, RX_SEO_UI.classes.enhanced);
});
this.addHeadingCopyLinks();
},
addHeadingCopyLinks() {
this.headings.forEach(function (heading) {
if (RX.qs('.rx-heading-copy', heading)) {
return;
}
const btn = RX.create('button', 'rx-heading-copy', '#');
btn.type = 'button';
btn.setAttribute('aria-label', 'Copy link to this section');
btn.addEventListener('click', function () {
const link = window.location.origin + window.location.pathname + '#' + heading.id;
RX.copyText(link).then(function () {
RX.addClass(btn, RX_SEO_UI.classes.copied);
btn.textContent = '✓';
setTimeout(function () {
RX.removeClass(btn, RX_SEO_UI.classes.copied);
btn.textContent = '#';
}, 1400);
});
});
heading.appendChild(btn);
});
},
/**
* ======================================================
* BACK TO TOP
* ======================================================
*/
setupBackToTop() {
if (!RX_SEO_UI.options.enableBackToTop) {
return;
}
let button = RX.qs(RX_SEO_UI.selectors.backToTop);
if (!button) {
button = RX.create('button', 'rx-back-to-top', '↑');
button.type = 'button';
button.setAttribute('aria-label', 'Back to top');
button.setAttribute('data-rx-back-to-top', '');
document.body.appendChild(button);
}
const toggleButton = RX.throttle(function () {
RX.toggleClass(button, RX_SEO_UI.classes.visible, window.pageYOffset > 600);
}, RX_SEO_UI.options.throttleDelay);
button.addEventListener('click', function () {
window.scrollTo({
top: 0,
behavior: RX.prefersReducedMotion() ? 'auto' : 'smooth'
});
});
window.addEventListener('scroll', toggleButton, { passive: true });
toggleButton();
},
/**
* ======================================================
* PRINT
* ======================================================
*/
setupPrintButtons() {
if (!RX_SEO_UI.options.enablePrint) {
return;
}
const printButtons = RX.qsa(RX_SEO_UI.selectors.printButton);
printButtons.forEach(function (button) {
if (RX.hasClass(button, RX_SEO_UI.classes.enhanced)) {
return;
}
button.addEventListener('click', function () {
window.print();
});
RX.addClass(button, RX_SEO_UI.classes.enhanced);
});
},
/**
* ======================================================
* CONTENT SEARCH INSIDE ARTICLE
* ======================================================
*/
setupContentSearch() {
if (!RX_SEO_UI.options.enableContentSearch) {
return;
}
const input = RX.qs(RX_SEO_UI.selectors.searchInput);
if (!input) {
return;
}
const searchableBlocks = RX.qsa('p, li, h2, h3, h4, blockquote', this.article);
const clearMarks = function () {
RX.qsa('mark.rx-search-mark').forEach(function (mark) {
const parent = mark.parentNode;
parent.replaceChild(document.createTextNode(mark.textContent), mark);
parent.normalize();
});
};
const markText = function (node, query) {
const text = node.textContent;
const index = text.toLowerCase().indexOf(query.toLowerCase());
if (index === -1 || !query) {
return false;
}
const before = document.createTextNode(text.slice(0, index));
const mark = RX.create('mark', 'rx-search-mark', text.slice(index, index + query.length));
const after = document.createTextNode(text.slice(index + query.length));
node.textContent = '';
node.appendChild(before);
node.appendChild(mark);
node.appendChild(after);
return true;
};
const runSearch = RX.debounce(function () {
const query = input.value.trim();
clearMarks();
searchableBlocks.forEach(function (block) {
RX.removeClass(block, 'rx-search-hit');
});
if (query.length < 2) {
return;
}
let firstHit = null;
searchableBlocks.forEach(function (block) {
if (block.children.length > 0 && !/^(H2|H3|H4|P|LI|BLOCKQUOTE)$/.test(block.tagName)) {
return;
}
const found = markText(block, query);
if (found) {
RX.addClass(block, 'rx-search-hit');
if (!firstHit) {
firstHit = block;
}
}
});
if (firstHit) {
firstHit.scrollIntoView({
behavior: RX.prefersReducedMotion() ? 'auto' : 'smooth',
block: 'center'
});
}
}, 220);
input.addEventListener('input', runSearch);
},
/**
* ======================================================
* EXTERNAL LINK SEO + SECURITY ENHANCEMENT
* ======================================================
*/
enhanceExternalLinks() {
if (!RX_SEO_UI.options.enableExternalLinkSafety) {
return;
}
const links = RX.qsa(RX_SEO_UI.selectors.externalLinks, this.article);
links.forEach(function (link) {
const url = RX.safeUrl(link.href);
if (!url || url.hostname === window.location.hostname) {
return;
}
RX.addClass(link, RX_SEO_UI.classes.externalLink);
if (!link.target) {
link.target = '_blank';
}
const relParts = new Set((link.rel || '').split(/\s+/).filter(Boolean));
relParts.add('noopener');
relParts.add('noreferrer');
if (link.getAttribute('data-rx-sponsored') === 'true') {
relParts.add('sponsored');
}
if (link.getAttribute('data-rx-ugc') === 'true') {
relParts.add('ugc');
}
link.rel = Array.from(relParts).join(' ');
if (!link.getAttribute('aria-label')) {
link.setAttribute('aria-label', RX.getText(link) + ' opens in a new tab');
}
});
},
enhanceInternalLinks() {
const links = RX.qsa(RX_SEO_UI.selectors.internalLinks, this.article);
links.forEach(function (link) {
const url = RX.safeUrl(link.href);
if (!url) {
return;
}
if (url.hostname === window.location.hostname) {
RX.addClass(link, RX_SEO_UI.classes.internalLink);
}
});
},
/**
* ======================================================
* RESPONSIVE TABLE WRAPPER
* ======================================================
*/
wrapTables() {
if (!RX_SEO_UI.options.enableTableWrap) {
return;
}
const tables = RX.qsa(RX_SEO_UI.selectors.tables);
tables.forEach(function (table) {
if (table.parentElement && RX.hasClass(table.parentElement, RX_SEO_UI.classes.tableWrap)) {
return;
}
const wrapper = RX.create('div', RX_SEO_UI.classes.tableWrap);
wrapper.setAttribute('role', 'region');
wrapper.setAttribute('aria-label', 'Scrollable table');
wrapper.setAttribute('tabindex', '0');
table.parentNode.insertBefore(wrapper, table);
wrapper.appendChild(table);
RX.addClass(table, RX_SEO_UI.classes.enhanced);
});
},
/**
* ======================================================
* IMAGE ENHANCEMENT
* ======================================================
*/
enhanceImages() {
if (!RX_SEO_UI.options.enableImageEnhance) {
return;
}
const images = RX.qsa(RX_SEO_UI.selectors.images);
images.forEach(function (img) {
if (!img.hasAttribute('loading')) {
img.setAttribute('loading', 'lazy');
}
if (!img.hasAttribute('decoding')) {
img.setAttribute('decoding', 'async');
}
if (!img.alt || img.alt.trim() === '') {
img.setAttribute('alt', '');
}
RX.addClass(img, RX_SEO_UI.classes.enhanced);
if (
img.parentElement &&
img.parentElement.tagName !== 'FIGURE' &&
!RX.hasClass(img.parentElement, RX_SEO_UI.classes.imageWrap)
) {
const wrapper = RX.create('span', RX_SEO_UI.classes.imageWrap);
img.parentNode.insertBefore(wrapper, img);
wrapper.appendChild(img);
}
});
},
/**
* ======================================================
* VIDEO / IFRAME ENHANCEMENT
* ======================================================
*/
enhanceVideos() {
if (!RX_SEO_UI.options.enableVideoLazyFrame) {
return;
}
const videos = RX.qsa(RX_SEO_UI.selectors.videos, this.article);
videos.forEach(function (video) {
if (video.tagName.toLowerCase() === 'iframe') {
if (!video.hasAttribute('loading')) {
video.setAttribute('loading', 'lazy');
}
if (!video.hasAttribute('title')) {
video.setAttribute('title', 'Embedded video');
}
if (!video.parentElement.classList.contains('rx-video-wrap')) {
const wrapper = RX.create('div', 'rx-video-wrap');
video.parentNode.insertBefore(wrapper, video);
wrapper.appendChild(video);
}
}
if (video.tagName.toLowerCase() === 'video') {
video.setAttribute('preload', video.getAttribute('preload') || 'metadata');
}
RX.addClass(video, RX_SEO_UI.classes.enhanced);
});
},
/**
* ======================================================
* GENERIC ACCORDION
* ======================================================
*/
setupAccordionBlocks() {
const accordions = RX.qsa(RX_SEO_UI.selectors.accordions);
accordions.forEach(function (accordion, accordionIndex) {
if (RX.hasClass(accordion, RX_SEO_UI.classes.enhanced)) {
return;
}
const triggers = RX.qsa('[data-rx-accordion-trigger]', accordion);
triggers.forEach(function (trigger, index) {
const panelSelector = trigger.getAttribute('data-rx-accordion-trigger');
const panel = panelSelector
? RX.qs(panelSelector, accordion)
: trigger.nextElementSibling;
if (!panel) {
return;
}
const triggerId = trigger.id || 'rx-accordion-trigger-' + accordionIndex + '-' + index;
const panelId = panel.id || 'rx-accordion-panel-' + accordionIndex + '-' + index;
trigger.id = triggerId;
panel.id = panelId;
trigger.setAttribute('role', 'button');
trigger.setAttribute('tabindex', '0');
trigger.setAttribute('aria-controls', panelId);
trigger.setAttribute('aria-expanded', RX.hasClass(panel, RX_SEO_UI.classes.open) ? 'true' : 'false');
panel.setAttribute('role', 'region');
panel.setAttribute('aria-labelledby', triggerId);
if (!RX.hasClass(panel, RX_SEO_UI.classes.open)) {
panel.hidden = true;
}
const togglePanel = function () {
const isOpen = trigger.getAttribute('aria-expanded') === 'true';
trigger.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
panel.hidden = isOpen;
RX.toggleClass(panel, RX_SEO_UI.classes.open, !isOpen);
};
trigger.addEventListener('click', togglePanel);
trigger.addEventListener('keydown', function (event) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
togglePanel();
}
});
});
RX.addClass(accordion, RX_SEO_UI.classes.enhanced);
});
},
/**
* ======================================================
* GLOSSARY TERMS
* ======================================================
*/
setupGlossaryTerms() {
const terms = RX.qsa(RX_SEO_UI.selectors.glossaryTerm, this.article);
terms.forEach(function (term, index) {
if (RX.hasClass(term, RX_SEO_UI.classes.enhanced)) {
return;
}
const definition = term.getAttribute('data-rx-definition');
if (!definition) {
return;
}
const tooltipId = 'rx-glossary-tooltip-' + index;
const tooltip = RX.create('span', 'rx-glossary-tooltip', definition);
tooltip.id = tooltipId;
tooltip.setAttribute('role', 'tooltip');
tooltip.hidden = true;
term.setAttribute('tabindex', '0');
term.setAttribute('aria-describedby', tooltipId);
term.appendChild(tooltip);
const show = function () {
tooltip.hidden = false;
RX.addClass(term, RX_SEO_UI.classes.open);
};
const hide = function () {
tooltip.hidden = true;
RX.removeClass(term, RX_SEO_UI.classes.open);
};
term.addEventListener('mouseenter', show);
term.addEventListener('focus', show);
term.addEventListener('mouseleave', hide);
term.addEventListener('blur', hide);
RX.addClass(term, RX_SEO_UI.classes.enhanced);
});
},
/**
* ======================================================
* LIGHT SCHEMA HINTS
* This does not replace PHP schema.
* It only adds data attributes/classes for front-end consistency.
* Real JSON-LD should be generated from WordPress/PHP.
* ======================================================
*/
setupSchemaHints() {
if (!RX_SEO_UI.options.enableSchemaHints) {
return;
}
const faqContainers = RX.qsa(RX_SEO_UI.selectors.faqContainer);
faqContainers.forEach(function (container) {
container.setAttribute('data-schema-hint', 'FAQPage');
});
const breadcrumbs = RX.qsa(RX_SEO_UI.selectors.breadcrumb);
breadcrumbs.forEach(function (breadcrumb) {
breadcrumb.setAttribute('data-schema-hint', 'BreadcrumbList');
});
if (this.article) {
this.article.setAttribute('data-schema-hint', 'Article');
}
},
/**
* ======================================================
* KEYBOARD UX
* ======================================================
*/
setupKeyboardUX() {
document.addEventListener('keydown', function (event) {
if (event.key === 'Escape') {
RX.qsa('.rx-glossary-tooltip').forEach(function (tooltip) {
tooltip.hidden = true;
});
RX.qsa('.rx-glossary-term, [data-rx-glossary-term]').forEach(function (term) {
RX.removeClass(term, RX_SEO_UI.classes.open);
});
}
});
},
/**
* ======================================================
* CONTENT MUTATION OBSERVER
* Useful when article content is loaded later by AJAX.
* ======================================================
*/
observeContentChanges() {
if (!window.MutationObserver || !this.article) {
return;
}
const observer = new MutationObserver(
RX.debounce(() => {
this.prepareHeadings();
this.setupSmoothJumpLinks();
this.enhanceExternalLinks();
this.enhanceInternalLinks();
this.wrapTables();
this.enhanceImages();
this.enhanceVideos();
this.setupFaqAccordion();
this.setupAccordionBlocks();
this.setupGlossaryTerms();
}, 300)
);
observer.observe(this.article, {
childList: true,
subtree: true
});
}
};
/**
* ==========================================================
* SAFE DOM READY
* ==========================================================
*/
function domReady(callback) {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', callback);
} else {
callback();
}
}
domReady(function () {
RxSeoUI.init();
});
/**
* ==========================================================
* GLOBAL SAFE EXPORT
* ==========================================================
*/
window.RxSeoUI = RxSeoUI;
})();
Add this CSS also in your SEO/UI CSS file, for example:
/assets/css/components/seo-ui.css
.rx-reading-progress {
position: fixed;
top: 0;
left: 0;
z-index: 99999;
width: 100%;
height: 3px;
background: transparent;
pointer-events: none;
}
.rx-progress-bar {
display: block;
width: 0;
height: 100%;
background: currentColor;
transition: width 0.1s linear;
}
.rx-toc {
border: 1px solid currentColor;
border-radius: 12px;
padding: 16px;
margin: 24px 0;
}
.rx-toc-title {
font-weight: 700;
margin-bottom: 10px;
}
.rx-toc-list {
margin: 0;
padding-left: 20px;
}
.rx-toc-item {
margin: 6px 0;
}
.rx-toc-level-3 {
margin-left: 18px;
font-size: 0.95em;
}
.rx-toc-link {
text-decoration: none;
}
.rx-toc-link.is-current {
font-weight: 700;
text-decoration: underline;
}
.rx-heading-copy {
margin-left: 8px;
border: 0;
background: transparent;
cursor: pointer;
font-size: 0.8em;
opacity: 0.45;
}
.rx-heading-copy:hover,
.rx-heading-copy:focus,
.rx-heading-copy.is-copied {
opacity: 1;
}
.rx-table-wrap {
overflow-x: auto;
max-width: 100%;
margin: 20px 0;
}
.rx-table-wrap table {
width: 100%;
min-width: 620px;
}
.rx-image-wrap {
display: inline-block;
max-width: 100%;
}
.rx-image-wrap img {
max-width: 100%;
height: auto;
}
.rx-video-wrap {
position: relative;
overflow: hidden;
width: 100%;
aspect-ratio: 16 / 9;
margin: 24px 0;
}
.rx-video-wrap iframe,
.rx-video-wrap video {
width: 100%;
height: 100%;
border: 0;
}
.rx-faq-question,
[data-rx-accordion-trigger] {
cursor: pointer;
}
.rx-faq-answer[hidden],
[data-rx-accordion] [hidden] {
display: none;
}
.rx-back-to-top {
position: fixed;
right: 18px;
bottom: 18px;
z-index: 9999;
width: 44px;
height: 44px;
border-radius: 999px;
border: 1px solid currentColor;
opacity: 0;
visibility: hidden;
cursor: pointer;
transition: opacity 0.2s ease, visibility 0.2s ease, transform 0.2s ease;
}
.rx-back-to-top.is-visible {
opacity: 1;
visibility: visible;
}
.rx-search-mark {
padding: 0 2px;
}
.rx-glossary-term,
[data-rx-glossary-term] {
position: relative;
cursor: help;
border-bottom: 1px dotted currentColor;
}
.rx-glossary-tooltip {
position: absolute;
left: 0;
bottom: 100%;
z-index: 20;
width: max-content;
max-width: 260px;
padding: 8px 10px;
border-radius: 8px;
background: #111;
color: #fff;
font-size: 13px;
line-height: 1.4;
}
.rx-external-link::after {
content: "↗";
margin-left: 3px;
font-size: 0.8em;
}
@media print {
.rx-reading-progress,
.rx-back-to-top,
.rx-heading-copy,
.rx-share-box,
[data-rx-share] {
display: none !important;
}
.rx-table-wrap {
overflow: visible;
}
}
Example HTML usage inside your WordPress template:
<div class="rx-read-time" data-rx-read-time></div>
<div class="rx-toc" data-rx-toc data-rx-toc-title="Article Contents"></div>
<article class="rx-article entry-content">
<h2>What is High Blood Pressure?</h2>
<p>Content here...</p>
<h2>Symptoms</h2>
<p>Content here...</p>
<h2>FAQs</h2>
<div class="rx-faq" data-rx-faq>
<div class="rx-faq-item">
<div class="rx-faq-question">Is high blood pressure dangerous?</div>
<div class="rx-faq-answer">
<p>Yes, untreated high blood pressure can increase health risks.</p>
</div>
</div>
</div>
</article>
<button data-rx-copy-link="current">Copy link</button>
<button data-rx-print>Print</button>
Best loading style in WordPress:
wp_enqueue_script(
'rx-chunk-049-seo-ui',
get_template_directory_uri() . '/assets/static-js/static-chunks-js/chunk-049-seo-ui.js',
array(),
wp_get_theme()->get('Version'),
true
);
For advanced performance, load it only on single posts/pages:
if ( is_single() || is_page() ) {
wp_enqueue_script(
'rx-chunk-049-seo-ui',
get_template_directory_uri() . '/assets/static-js/static-chunks-js/chunk-049-seo-ui.js',
array(),
wp_get_theme()->get('Version'),
true
);
}
My best advice: keep this file for front-end SEO UI, and keep real SEO schema, meta title, canonical, Open Graph, Twitter cards, and JSON-LD inside PHP files like:
/inc/seo/schema.php
/inc/seo/meta-tags.php
/inc/seo/open-graph.php
/inc/seo/breadcrumb-schema.php