🎨 CSS 格式化

CSS 样式表格式化:规范化选择器缩进、对齐属性、支持 @media/@supports/@keyframes 嵌套。

等待输入...
已复制
const SAMPLE = '/* 按钮样式 */\n.btn{display:inline-block;padding:8px 16px;border-radius:4px;background:#667eea;color:#fff;font-size:14px;cursor:pointer}\n.btn-primary{background:linear-gradient(135deg,#667eea,#764ba2);border:none}\n.btn-primary:hover{background:linear-gradient(135deg,#5568d3,#653a92);transform:translateY(-1px)}\n@media (max-width:600px){.btn{width:100%;padding:12px}.btn-primary{font-size:16px}}\n@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}'; const IND = () => { const v = document.getElementById('indent').value; return v === 'tab' ? '\t' : ' '.repeat(parseInt(v)); }; function tokenizeCss(src){ const tokens = []; let i = 0; while(i < src.length){ const c = src[i]; if(c === '/' && src[i+1] === '*'){ let s = i; i += 2; while(i < src.length && !(src[i] === '*' && src[i+1] === '/')) i++; i += 2; tokens.push({t:'comment', v: src.slice(s, i)}); continue; } if(/\s/.test(c)){ let s = i; while(i < src.length && /\s/.test(src[i])) i++; tokens.push({t:'ws', v: src.slice(s, i)}); continue; } if(c === '{' || c === '}' || c === ';'){ tokens.push({t:'punct', v:c}); i++; continue; } if(c === ':'){ tokens.push({t:'colon', v:c}); i++; continue; } let s = i; while(i < src.length && !/\s/.test(src[i]) && !'{};:/*'.includes(src[i])) i++; if(i > s) tokens.push({t:'word', v: src.slice(s, i)}); else { tokens.push({t:'word', v: c}); i++; } } return tokens; } function formatCss(){ setErr(''); try{ const tokens = tokenizeCss(document.getElementById('input').value); let out = ''; let depth = 0; let atLineStart = true; let afterColon = false; let prevType = null; const pad = () => IND().repeat(depth); let needNewlineBeforeNext = false; for(let k = 0; k < tokens.length; k++){ const tk = tokens[k]; if(tk.t === 'ws') continue; if(tk.t === 'comment'){ if(!atLineStart) out += '\n'; out += pad() + tk.v + '\n'; atLineStart = true; continue; } if(tk.v === '{'){ out += ' {\n'; depth++; atLineStart = true; afterColon = false; continue; } if(tk.v === '}'){ depth = Math.max(0, depth - 1); if(!atLineStart) out += '\n'; out += pad() + '}\n'; atLineStart = true; afterColon = false; if(k + 1 < tokens.length && tokens[k+1].t !== 'ws'){ out += '\n'; } continue; } if(tk.v === ';'){ out += ';\n'; atLineStart = true; afterColon = false; continue; } if(tk.t === 'colon'){ out += ': '; afterColon = true; continue; } if(tk.t === 'word'){ if(atLineStart){ out += pad() + tk.v; atLineStart = false; } else if(afterColon){ out += tk.v; } else { out += ' ' + tk.v; } afterColon = false; } } setOut(out.trim() + '\n'); setErr('✅ 格式化成功'); }catch(e){ setErr('❌ ' + e.message); } } function minifyCss(){ setErr(''); try{ const src = document.getElementById('input').value; let out = src.replace(/\/\*[\s\S]*?\*\//g, ''); out = out.replace(/\s+/g, ' '); out = out.replace(/\s*\{\s*/g, '{'); out = out.replace(/\s*\}\s*/g, '}'); out = out.replace(/\s*;\s*/g, ';'); out = out.replace(/\s*:\s*/g, ':'); out = out.replace(/\s*$/, ''); out = out.replace(/^\s*/, ''); out = out.replace(/\}/g, '}\n'); setOut(out.trim()); setErr('✅ 压缩成功'); }catch(e){ setErr('❌ ' + e.message); } } function doBeautify(){ formatCss(); } function doMinify(){ minifyCss(); } function doValidate(){ setErr(''); try{ const src = document.getElementById('input').value; const braceO = (src.match(/{/g) || []).length; const braceC = (src.match(/}/g) || []).length; const colonCount = (src.match(/:/g) || []).length; const semiCount = (src.match(/;/g) || []).length; const atRuleCount = (src.match(/@[a-z]+/g) || []).length; const stats = '✅ CSS 分析\n' + '总长度: ' + src.length + ' 字符\n' + '行数: ' + src.split('\n').length + '\n' + '规则块 { }: ' + braceO + ' / ' + braceC + '\n' + '声明 : 数量: ' + colonCount + '\n' + '声明 ; 数量: ' + semiCount + '\n' + '@ 规则: ' + atRuleCount; setOut(stats); if(braceO !== braceC) setErr('❌ 花括号不平衡:{ ' + braceO + ' / } ' + braceC); else setErr('✅ 校验通过'); }catch(e){ setErr('❌ ' + e.message); } } function getIndent(){const v=document.getElementById('indent').value;return v==='tab'?'\t':' '.repeat(parseInt(v));} function setOut(s){document.getElementById('output').textContent=s;} function setErr(s){const e=document.getElementById('err');e.textContent=s||'';e.className='err'+(s?'':' ok');} function clearAll(){document.getElementById('input').value='';setOut('等待输入...');setErr('');} function loadSample(){document.getElementById('input').value=SAMPLE;doBeautify();}