|
5 | 5 | const allElements = document.querySelectorAll("*"); |
6 | 6 |
|
7 | 7 | allElements.forEach(el => { |
8 | | - el.classList.forEach(cls => { |
| 8 | + // Use Array.from because classList is a live collection and we'll mutate it |
| 9 | + Array.from(el.classList).forEach(cls => { |
9 | 10 | let prefix = null; |
10 | 11 | let className = cls; |
11 | 12 |
|
12 | | - const matchPrefix = cls.match(/^([a-z]+):(.+)$/); |
| 13 | + // Detect responsive prefix (allow digits like 2xl) |
| 14 | + const matchPrefix = cls.match(/^([a-z0-9]+):(.+)$/i); |
13 | 15 | if (matchPrefix) { prefix = matchPrefix[1]; className = matchPrefix[2]; } |
14 | 16 |
|
| 17 | + // Background color (bracket form) e.g. bg-[#123456] |
15 | 18 | const bgMatch = className.match(/^bg-\[#([0-9A-Fa-f]{3,6})\]$/); |
16 | 19 | if (bgMatch) { |
17 | | - const color = `#${bgMatch[1]}`; |
18 | | - if (!prefix) el.style.backgroundColor = color; |
19 | | - else applyResponsive(el, prefix, "backgroundColor", color); |
| 20 | + applyStyle(el, prefix, "backgroundColor", `#${bgMatch[1]}`); |
| 21 | + return; |
20 | 22 | } |
21 | 23 |
|
| 24 | + // Text color (bracket form) e.g. text-[#123456] |
22 | 25 | const textMatch = className.match(/^text-\[#([0-9A-Fa-f]{3,6})\]$/); |
23 | 26 | if (textMatch) { |
24 | | - const color = `#${textMatch[1]}`; |
25 | | - if (!prefix) el.style.color = color; |
26 | | - else applyResponsive(el, prefix, "color", color); |
| 27 | + applyStyle(el, prefix, "color", `#${textMatch[1]}`); |
| 28 | + return; |
27 | 29 | } |
| 30 | + |
| 31 | + // Padding classes (bracket form) p-[20px], pt-[10px], etc. |
| 32 | + const paddingMatch = className.match(/^p([trbl]?)-\[(.+)\]$/); |
| 33 | + if (paddingMatch) { |
| 34 | + const dir = paddingMatch[1]; |
| 35 | + const value = paddingMatch[2]; |
| 36 | + const prop = getSpacingProperty("padding", dir); |
| 37 | + applyStyle(el, prefix, prop, value); |
| 38 | + return; |
| 39 | + } |
| 40 | + |
| 41 | + // Margin classes (bracket form) m-[10px], mt-[5px], etc. |
| 42 | + const marginMatch = className.match(/^m([trbl]?)-\[(.+)\]$/); |
| 43 | + if (marginMatch) { |
| 44 | + const dir = marginMatch[1]; |
| 45 | + const value = marginMatch[2]; |
| 46 | + const prop = getSpacingProperty("margin", dir); |
| 47 | + applyStyle(el, prefix, prop, value); |
| 48 | + return; |
| 49 | + } |
| 50 | + |
| 51 | + // If there's a prefix and this is a normal utility like text-blue-500 / bg-red-500 etc. |
| 52 | + if (prefix) { |
| 53 | + const width = breakpoints[prefix]; |
| 54 | + if (!width) return; // unknown prefix (skip) |
| 55 | + |
| 56 | + const mql = window.matchMedia(`(min-width: ${width}px)`); |
| 57 | + const update = e => { |
| 58 | + // When matches, ensure the unprefixed utility is present; otherwise remove it. |
| 59 | + if (e.matches) { |
| 60 | + if (!el.classList.contains(className)) el.classList.add(className); |
| 61 | + } else { |
| 62 | + if (el.classList.contains(className)) el.classList.remove(className); |
| 63 | + } |
| 64 | + }; |
| 65 | + // run once to set initial state |
| 66 | + update(mql); |
| 67 | + // listen for changes (modern and fallback) |
| 68 | + if (mql.addEventListener) mql.addEventListener("change", update); |
| 69 | + else if (mql.addListener) mql.addListener(update); |
| 70 | + return; |
| 71 | + } |
| 72 | + |
| 73 | + // No prefix and no bracketed handler -> nothing to do here (regular classes come from CSS) |
28 | 74 | }); |
29 | 75 | }); |
30 | 76 | } |
31 | 77 |
|
32 | | - function applyResponsive(el, prefix, prop, value) { |
33 | | - const width = breakpoints[prefix]; |
34 | | - if (!width) return; |
| 78 | + function getSpacingProperty(type, dir) { |
| 79 | + switch (dir) { |
| 80 | + case "t": return `${type}Top`; |
| 81 | + case "b": return `${type}Bottom`; |
| 82 | + case "l": return `${type}Left`; |
| 83 | + case "r": return `${type}Right`; |
| 84 | + case "": return type; // p-[16px] or m-[16px] |
| 85 | + default: return type; |
| 86 | + } |
| 87 | + } |
35 | 88 |
|
36 | | - const mql = window.matchMedia(`(min-width: ${width}px)`); |
37 | | - const applyStyle = e => { if (e.matches) el.style[prop] = value; else el.style[prop] = ""; }; |
| 89 | + function applyStyle(el, prefix, prop, value) { |
| 90 | + if (!prefix) { |
| 91 | + el.style[prop] = value; |
| 92 | + } else { |
| 93 | + const width = breakpoints[prefix]; |
| 94 | + if (!width) return; |
38 | 95 |
|
39 | | - mql.addEventListener("change", applyStyle); |
40 | | - applyStyle(mql); |
| 96 | + const mql = window.matchMedia(`(min-width: ${width}px)`); |
| 97 | + const update = e => { if (e.matches) el.style[prop] = value; else el.style[prop] = ""; }; |
| 98 | + update(mql); |
| 99 | + if (mql.addEventListener) mql.addEventListener("change", update); |
| 100 | + else if (mql.addListener) mql.addListener(update); |
| 101 | + } |
41 | 102 | } |
42 | 103 |
|
43 | 104 | if (document.readyState === "loading") { |
44 | 105 | document.addEventListener("DOMContentLoaded", applyDynamicClasses); |
45 | | - } else { applyDynamicClasses(); } |
| 106 | + } else { |
| 107 | + applyDynamicClasses(); |
| 108 | + } |
46 | 109 | })(); |
0 commit comments