File size: 34,862 Bytes
178bf4e 58584b2 178bf4e 6c51c66 178bf4e 58584b2 178bf4e 58584b2 178bf4e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | <!doctype html><html lang="en"><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>BEM Class Auditor and SCSS Skeleton Extractor for HTML</title>
<meta name="description" content="16 BEM naming rules read the HTML file you have open, and 22 markup and SCSS skeletons write the stylesheet shell — the class names get fixed before the pull request comes back for them.">
<style>
:root{color-scheme:light dark}
body{margin:0;font:16px/1.6 system-ui,-apple-system,"Segoe UI",sans-serif;
background:#fbfbfa;color:#16161d}
@media(prefers-color-scheme:dark){body{background:#14141a;color:#e8e8ea}}
.wrap{max-width:880px;margin:0 auto;padding:28px 20px 64px}
h1{font-size:1.7rem;line-height:1.25;margin:.2em 0 .3em}
p.sub{margin:0 0 1.4em;opacity:.8}
textarea{width:100%;min-height:230px;font:13px/1.5 ui-monospace,Menlo,Consolas,monospace;
padding:12px;border:1px solid #c9c9d1;border-radius:10px;background:transparent;color:inherit;
box-sizing:border-box}
button{font:600 15px/1 inherit;padding:12px 22px;border:0;border-radius:10px;
background:#1d4ed8;color:#fff;cursor:pointer;margin:12px 0}
button:hover{background:#1a43bd}
table{border-collapse:collapse;width:100%;margin-top:14px;font-size:14px}
th,td{border-bottom:1px solid #d8d8e0;padding:8px 6px;text-align:left;vertical-align:top}
th{font-weight:600;opacity:.75}
td.n{width:64px;font:13px ui-monospace,monospace;opacity:.7}
.ok{padding:14px;border-radius:10px;background:#e8f5ec;color:#14532d;margin-top:14px}
@media(prefers-color-scheme:dark){.ok{background:#15321f;color:#bbf7d0}}
.next{margin-top:30px;padding:18px;border:1px solid #d8d8e0;border-radius:12px}
.next h2{font-size:1.05rem;margin:0 0 .5em}
.next ul{margin:.3em 0 1em 1.1em;padding:0}
.next .buy a{display:inline-block;font:600 15px/1 inherit;padding:12px 22px;border-radius:10px;
background:#1d4ed8;color:#fff;text-decoration:none}
.next .buy a:hover{background:#1a43bd}
.next .fine{margin:.7em 0 0;font-size:13px;opacity:.7}
</style>
<div class="wrap">
<h1>BEM Class Auditor and SCSS Skeleton Extractor for HTML</h1>
<p class="sub">16 BEM naming rules read the HTML file you have open, and 22 markup and SCSS skeletons write the stylesheet shell — the class names get fixed before the pull request comes back for them.</p>
<textarea id="src" placeholder="Paste the file here"></textarea>
<button id="go">Check</button>
<div id="out"></div>
<div class="next"><h2>The same 16 rules, across the whole repository</h2><ul><li><b>Every HTML file in the repository</b>, not only the one on screen</li><li><b>A findings file you attach to the pull request</b> — CSV, JSON or HTML</li></ul><p class="buy"><a href="https://getreadystack.com/api/buy/cl/polar_cl_KpshZaXMI3nFFxkyC13chl0cdxza8558hpEYP2ERbwy">Unlock the repository-wide audit — $39 once</a></p><p class="fine">Checking the file you have open is free, against all 16 rules, and always will be. </p></div>
</div>
<script>
var RULES=[{"pattern": "class=\"[^\"]*\\b[a-z][a-z0-9]*[A-Z]", "flags": "m", "message": "camelCase class name. BEM names are lower kebab-case: cardTitle becomes card-title."}, {"pattern": "class=\"[^\"]*\\b[a-z0-9-]+_(?!_)[a-z0-9-]+", "flags": "m", "message": "The element separator is exactly two underscores: card_title becomes card__title."}, {"pattern": "\\b[a-z0-9-]+__[a-z0-9-]+__[a-z0-9-]+", "flags": "m", "message": "BEM has no element of an element. card__body__title becomes card__body-title, or a block of its own."}, {"pattern": "\\b[a-z0-9-]+--[a-z0-9-]+__", "flags": "m", "message": "The modifier comes last. card--wide__title becomes card__title with card__title--wide."}, {"pattern": "class=\"\\s*[a-z0-9-]+--[a-z0-9-]+\\s*\"", "flags": "m", "message": "A modifier stands alone here. It must ride next to its base class: class=\"card card--wide\"."}, {"pattern": "CLASS=", "flags": "m", "message": "Attribute name in capitals. The extractor reads a lower-case class= attribute.", "fix": "class="}, {"pattern": "class=\"\\s+", "flags": "m", "message": "Space directly after the opening quote. It becomes an empty selector in the extracted stylesheet.", "fix": "class=\""}, {"pattern": "\\bjs_", "flags": "m", "message": "Scripting hook written with an underscore. Use a hyphen: js_toggle becomes js-toggle.", "fix": "js-"}, {"pattern": "class=\"[^\"]*\\b([a-z0-9_-]{3,})\\b[^\"]*\\b\\1\\b", "flags": "m", "message": "The same class appears twice in one attribute, so the extracted stylesheet would carry the rule twice."}, {"pattern": "class=\"[^\"]*\\b(?:red|blue|green|big|small|left|right|center|bold)\\b", "flags": "i", "message": "Presentational class name. Name the role, not the paint: card__title--red becomes card__title--alert."}, {"pattern": "class=\"[^\"]*\\b(?:test|tmp|temp|foo|bar|div1|box1|new|old|copy)\\b", "flags": "i", "message": "Placeholder class name still in the markup. It will land in the stylesheet as a real selector."}, {"pattern": "class=\"[^\"]*(?:\\{\\{|\\{%|<\\?|\\$\\{)", "flags": "m", "message": "Class name built by a template expression. The extractor cannot resolve it; write the static block part as a class of its own."}, {"pattern": "class=\"[^\"]*(?:\\s+[^\\s\"]+){7}\\s*\"", "flags": "m", "message": "Eight or more classes on one element. The block interface is being replaced by loose utilities."}, {"pattern": "\\b[a-z0-9]+(?:-[a-z0-9]+){4,}(?:__|--|\\s|\")", "flags": "m", "message": "Five or more words in one BEM name. Split the block; the name is carrying the whole page path."}, {"pattern": "<[a-z][a-z0-9-]*[^>]*\\sstyle=\"", "flags": "i", "message": "Inline style on this element. That declaration never reaches the extracted stylesheet, so the block ends up styled in two places."}, {"pattern": "class='[^']*'", "flags": "m", "message": "Class attribute in single quotes. The extractor reads double-quoted attributes."}];
function scan(t){var L=String(t).split(/\r?\n/),h=[],i,r,re;
for(i=0;i<L.length;i++){for(var k=0;k<RULES.length;k++){r=RULES[k];
try{re=new RegExp(r.pattern,r.flags||'')}catch(e){continue}
if(re.test(L[i]))h.push({line:i+1,msg:r.message})}}
return h}
document.getElementById('go').onclick=function(){
var t=document.getElementById('src').value,o=document.getElementById('out');
if(!t.trim()){o.innerHTML='';return}
var h=scan(t);
if(!h.length){o.innerHTML='<div class="ok">No naming problems found. Every class in this file follows the 16 BEM rules.</div>';return}
var s='<table><tr><th>Line</th><th>Finding</th></tr>';
for(var i=0;i<h.length;i++){s+='<tr><td class="n">'+h[i].line+'</td><td>'+
String(h[i].msg).replace(/&/g,'&').replace(/</g,'<')+'</td></tr>'}
o.innerHTML=s+'</table>'};
</script>
</html>
<section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://marketplace.visualstudio.com/items?itemName=ReadyStack.bem-class-extractor-html" rel="noopener" data-yj-install-door="mp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'bem-class-extractor-html',src:'mp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">VS Code Marketplace</a><a href="https://www.npmjs.com/package/@readystack/bem-class-extractor-html" rel="noopener" data-yj-install-door="npm" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'bem-class-extractor-html',src:'npm',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">npm</a><a href="https://www.npmjs.com/package/@readystack/bem-class-extractor-html" rel="noopener" data-yj-install-door="mcp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'bem-class-extractor-html',src:'mcp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">MCP server (npx)</a><a href="https://hub.docker.com/r/getreadystack/bem-class-extractor-html" rel="noopener" data-yj-install-door="docker" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'bem-class-extractor-html',src:'docker',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Docker Hub</a><pre style="margin:.5rem 0 0;padding:.6rem .8rem;background:#f3f5f8;border-radius:8px;font-size:.9rem;overflow:auto">ext install ReadyStack.bem-class-extractor-html
npx @readystack/bem-class-extractor-html <file>
npx @readystack/bem-class-extractor-html --mcp</pre><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://getreadystack.com/api/buy/team" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section>
<section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get the full rule table as a PDF</b><div style="opacity:.78;margin:.35rem 0 .8rem">All 16 rules BEM Class Auditor for HTML and SCSS checks, each with its source and date, on printable pages you can hand to your team or auditor. We email it now, and once more only if a rule changes. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Email me the PDF</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">Sent. It arrives within the hour - check spam too.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"bem-class-extractor-html",lang:"en",source:"rule_table"+((b.closest&&b.closest("section")&&b.closest("section").getAttribute("data-yj-moved"))?"_at_result":"")})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script><script>(function(){var e=document.getElementById("yjae"),s=e&&e.closest?e.closest("section"):null,o=document.getElementById("out");if(!s||!o)return;var d=false;function mv(ev){if(d)return;if(ev&&ev.target&&s.contains(ev.target))return;d=true;var b=o;while(b.parentNode&&b.parentNode!==document.body&&b.parentNode.tagName!=="MAIN"&&b.parentNode.tagName!=="ARTICLE")b=b.parentNode;if(!b.parentNode||b===s||b.contains(s))return;try{b.parentNode.insertBefore(s,b.nextSibling);s.setAttribute("data-yj-moved","1");}catch(_){}}document.addEventListener("input",mv,true);document.addEventListener("change",mv,true);})();</script></section>
<section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the complete version <span class="yj-price">$39</span></h2><p style="margin:.25rem 0 1rem;color:#41505f">This page is the working piece. The full pack has everything below.</p><p style="margin:0 0 1rem;color:#41505f">16 BEM naming rules read the HTML file you have open and 22 markup and SCSS skeletons write the stylesheet shell, so the class names get fixed before the pull request comes back for them.</p><p style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">$73 an hour, the 2026 average freelance front-end developer rate (arc.dev, contractrates.fyi); a naming pass over a themed repo is at least one of those hours, and US mid-level contract rates run $89-$140.</p><a href="https://getreadystack.com/api/buy/cl/polar_cl_KpshZaXMI3nFFxkyC13chl0cdxza8558hpEYP2ERbwy" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $39</a></section>
<!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&","<":"<",">":">",'"':"""}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div>
<script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script>
<script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script>
<section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><p style="font:400 13px/1.5 system-ui,sans-serif;color:#6b7680;margin:0 0 14px"><time datetime="2026-09-08">Updated 2026-09-08</time> · ReadyStack</p><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","dateModified":"2026-09-08","author":{"@type":"Organization","name":"ReadyStack"}}</script><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Worked example</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 16px;color:#5b6570">Real numbers from this tool, line by line.</p><div class="yj-article"><p><img src="https://getreadystack.com/img/promo/sku9085_result_card.jpg" alt="Nothing in your toolchain is watching your class names" loading="lazy" style="max-width:100%;border-radius:6px"></p>
<pre><code>card.html
1: camelCase class name. BEM names are lower kebab-case: cardTitle becomes card-title.
2: The element separator is exactly two underscores: card_title becomes card__title.
3: BEM has no element of an element. card__body__title becomes card__body-title, or a block of its own.
4: The modifier comes last. card--wide__title becomes card__title with card__title--wide.
5: The element separator is exactly two underscores: card_title becomes card__title.
5: Scripting hook written with an underscore. Use a hyphen: js_toggle becomes js-toggle.
6: Placeholder class name still in the markup. It will land in the stylesheet as a real selector.
7: Five or more words in one BEM name. Split the block; the name is carrying the whole page path.
—— 8 ——</code></pre>
<p>That is the output panel, one command after opening the file: eight findings on seven lines, each with its line number and the rule it broke. Line 5 is listed twice because that one class breaks two rules at once. Here is the entire file it read, nine lines of it:</p>
<pre><code><section class="cardList">
<article class="card_item">
<h3 class="card__body__title">Wireless keyboard</h3>
<p class="card--wide__text">Low-profile keys.</p>
<a class="js_open" href="/products/keyboard">See details</a>
<span class="tmp-badge">New</span>
<div class="site-header-card-list-item__meta">In stock</div>
</article>
</section></code></pre>
<p>Nothing in there is exotic. It is one component, written quickly, by someone who knows the convention perfectly well. That is the point: BEM drift is not caused by ignorance, it is caused by nothing watching.</p>
<p>Because nothing is. Stylelint reads your stylesheet. Prettier reads your formatting. ESLint does not open HTML at all. None of them ever look at the class attribute in your markup, which is exactly where a BEM name is born. The name is wrong for the whole life of the branch, and the first thing that notices is a human being, in review, late.</p>
<p>That is the expensive part. Naming comments are the cheapest thing a reviewer can find and the most costly round to repeat, because the author has to go back, rename in the markup, rename again in the stylesheet, and push. And it gets worse with time: once those class names have been extracted into SCSS, the rename is no longer one file, it is two, and every partial that referenced the old block.</p>
<p>The BEM Class Auditor moves that round earlier, into the editor. It holds 16 naming rules and runs them over the HTML file you have open. Among them: camelCase names, single-underscore elements, an element of an element, a modifier written before the element it modifies, a modifier standing without its base class, presentational names like <code>--red</code> that describe paint instead of role, placeholder names like <code>tmp</code> and <code>foo</code> that survived into the commit, class names built by a template expression the extractor cannot resolve, eight or more utilities crowding out the block interface, and inline styles that will never reach the extracted stylesheet at all.</p>
<p>It also ships 22 skeletons: 11 markup blocks and the 11 matching SCSS shells for card, nav, modal, form, table, tabs, accordion, alert, breadcrumb, pagination and hero. Insert one at the cursor and the new block starts out correctly named, which is always cheaper than correcting it afterwards.</p>
<p>Auditing the file you have open is free, against all 16 rules, with no key and nothing held back. So is auditing just the lines you selected, and so is inserting all 22 skeletons. What a licence adds is scope and a take-away: the same 16 rules across every HTML file in the repository rather than the one on screen, and a findings file in CSV, JSON or HTML that you attach to the pull request instead of pasting a list into a comment. A freelance front-end developer averaged $73 an hour in 2026, and the naming pass is the round that repeats. The licence is $39 once.</p></div><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:30px 0 10px;color:#1b1f24">15 seconds — what it actually does</h2><video controls muted playsinline preload="none" poster="/img/promo/sku9085_result_card.jpg" style="width:100%;max-width:360px;border-radius:10px;display:block;margin:0 auto"><source src="/media/sku9085_short.mp4" type="video/mp4"></video><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does the BEM Class Auditor actually do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads the class attributes in your HTML and checks them against 16 BEM naming rules, then lists each problem in the output panel with a line number and the corrected name. It also ships 22 skeletons, 11 markup blocks and their 11 matching SCSS shells, that you insert at the cursor so a new block starts out correctly named.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is this for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Front-end contractors and agency developers who hand a server-rendered theme to a client's in-house team, and anyone on a codebase whose style guide says BEM. It suits WordPress, Shopify, Django and Rails templates, where the markup is HTML rather than JSX and no framework enforces the naming for you.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why can't I just use Stylelint or a chatbot?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Stylelint reads your stylesheet, not the class attribute inside your HTML, so a name that never reaches CSS is never checked. A chatbot will rewrite one snippet you paste, but it cannot walk four hundred template files, and it cannot give you line numbers inside your own repository. This runs offline, on the files you have open.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what needs a licence?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Auditing the file you have open is free, against all 16 rules, with no key and nothing held back, as is inserting all 22 skeletons. A licence adds scope and a take-away: the same 16 rules across every HTML file in the repository, and a findings file in CSV, JSON or HTML that you can attach to the pull request.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What would this cost me otherwise?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">A freelance front-end developer averaged $73 an hour in 2026, and US mid-level contract rates run $89 to $140. A naming pass over a themed repository is at least one of those hours, and it is the review round that gets repeated. The licence is $39 once, not per month.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does the BEM Class Auditor actually do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads the class attributes in your HTML and checks them against 16 BEM naming rules, then lists each problem in the output panel with a line number and the corrected name. It also ships 22 skeletons, 11 markup blocks and their 11 matching SCSS shells, that you insert at the cursor so a new block starts out correctly named."}}, {"@type": "Question", "name": "Who is this for?", "acceptedAnswer": {"@type": "Answer", "text": "Front-end contractors and agency developers who hand a server-rendered theme to a client's in-house team, and anyone on a codebase whose style guide says BEM. It suits WordPress, Shopify, Django and Rails templates, where the markup is HTML rather than JSX and no framework enforces the naming for you."}}, {"@type": "Question", "name": "Why can't I just use Stylelint or a chatbot?", "acceptedAnswer": {"@type": "Answer", "text": "Stylelint reads your stylesheet, not the class attribute inside your HTML, so a name that never reaches CSS is never checked. A chatbot will rewrite one snippet you paste, but it cannot walk four hundred template files, and it cannot give you line numbers inside your own repository. This runs offline, on the files you have open."}}, {"@type": "Question", "name": "What is free and what needs a licence?", "acceptedAnswer": {"@type": "Answer", "text": "Auditing the file you have open is free, against all 16 rules, with no key and nothing held back, as is inserting all 22 skeletons. A licence adds scope and a take-away: the same 16 rules across every HTML file in the repository, and a findings file in CSV, JSON or HTML that you can attach to the pull request."}}, {"@type": "Question", "name": "What would this cost me otherwise?", "acceptedAnswer": {"@type": "Answer", "text": "A freelance front-end developer averaged $73 an hour in 2026, and US mid-level contract rates run $89 to $140. A naming pass over a themed repository is at least one of those hours, and it is the review round that gets repeated. The licence is $39 once, not per month."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:9085,slug:"bem-class-extractor-html",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/devicetree-config-language-pack"><span class="yj-rel-t">DeviceTree Config Audit: 23 dtc-Style Rules, 50 Snippets</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/firestore-rules-guard"><span class="yj-rel-t">Firestore Rules Guard</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/jewelry-shopify-theme"><span class="yj-rel-t">Jewelry Shopify Theme - Ring Size Guide | Hallmark Wording, Made-to-Order and </span><span class="yj-rel-p">$180</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/wp-plugin-review-gate"><span class="yj-rel-t">WP Plugin Review Gate</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/agency-website-template"><span class="yj-rel-t">Agency Website Template — 7-Page Digital Marketing Agency Site (Plain HTML, No</span><span class="yj-rel-p">$49</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/bakery-shopify-theme"><span class="yj-rel-t">Bakery Shopify Theme</span><span class="yj-rel-p">$180</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section> |