// Minimal CSS syntax highlighter for the docs (shared).
// It converts plain-text CSS inside `.css-view` into highlighted HTML using
// the existing demo.css token classes: .kwd .str .num .punc .com .attr .val .fun
(function () {
function escapeHtml(s) {
return String(s)
.replace(/&/g, '&')
.replace(//g, '>');
}
function highlightCSS(input) {
let s = escapeHtml(input);
// Comments
s = s.replace(/\/\*[\s\S]*?\*\//g, (m) => `${m}`);
// Strings
s = s.replace(/("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*')/g, (m) => `${m}`);
// @rules
s = s.replace(/(^|\s)(@[\w-]+)/g, (m, p1, p2) => `${p1}${p2}`);
// Hex colors
s = s.replace(/(#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8}))/g, (m) => `${m}`);
// Numbers with units (very rough, but good enough visually)
s = s.replace(/(\b\d+(?:\.\d+)?)(%|px|rem|em|vh|vw|vmin|vmax|deg|turn|s|ms)?\b/g, (m, n, unit) => {
return `${n}${unit || ''}`;
});
// Property names: "prop: value"
s = s.replace(/(^|\n)(\s*)([a-zA-Z_-][\w-]*)(\s*):/g, (m, p1, ws, prop, ws2) => {
return `${p1}${ws}${prop}${ws2}:`;
});
// Highlight braces / punctuation
s = s.replace(/([{}();,])/g, (m) => `${m}`);
// Selectors (line before "{"), skip @rules
s = s.replace(/(^|\n)([^\n{]+?)(\s*)(\{<\/span>)/g, (m, p1, sel, ws, brace) => {
const trimmed = sel.trimStart();
if (trimmed.startsWith('@')) return `${p1}${sel}${ws}${brace}`;
return `${p1}${sel}${ws}${brace}`;
});
// Values: after ":" until ";" or "}" (best-effort, runs after punctuation)
s = s.replace(/(:<\/span>)([^;\n}]+)(?=(;|\}))/g, (m, colon, val) => {
// avoid re-highlighting spans
if (val.includes('${val}`;
});
return s;
}
function enhance() {
const blocks = document.querySelectorAll('.css-view');
blocks.forEach((el) => {
try {
if (el.dataset.highlighted === '1') return;
const text = el.textContent || '';
// If already contains spans, assume it's already highlighted.
if (el.innerHTML.includes(' {
const tab = e.target && e.target.closest ? e.target.closest('.tab') : null;
if (!tab) return;
const label = (tab.textContent || '').trim().toLowerCase();
if (label === 'css') {
// Defer so the tab switch can toggle DOM/classes first
setTimeout(enhance, 0);
}
},
true
);
// Observe DOM changes to catch dynamically added `.css-view` blocks.
try {
const mo = new MutationObserver(() => {
// Cheap debounce via microtask
Promise.resolve().then(enhance);
});
mo.observe(document.documentElement, {
subtree: true,
childList: true,
characterData: true
});
} catch (_) {}
})();