JNTZN

CSS 포매터

// DOM 요소 가져오기 const cssInput = document.getElementById(‘cssInput’); const output = document.getElementById(‘output’); const formatBtn = document.getElementById(‘formatBtn’); const copyBtn = document.getElementById(‘copyBtn’); const downloadBtn = document.getElementById(‘downloadBtn’); const fileInput = document.getElementById(‘fileInput’); const toast = document.getElementById(‘toast’); // 모든 옵션 요소 가져오기 const options = { sortProperties: document.getElementById(‘sortProperties’), removeComments: document.getElementById(‘removeComments’), alignColons: document.getElementById(‘alignColons’), compressOutput: document.getElementById(‘compressOutput’), vendorPrefix: document.getElementById(‘vendorPrefix’), removeEmptyRules: document.getElementById(‘removeEmptyRules’), convertRGBToHex: document.getElementById(‘convertRGBToHex’), removeLastSemicolon: document.getElementById(‘removeLastSemicolon’), indentSize: document.getElementById(‘indentSize’) }; // 토스트 알림을 표시하는 함수 function showToast(message, isError = false) { toast.textContent = message; toast.classList.toggle(‘error’, isError); toast.classList.add(‘show’); setTimeout(() => toast.classList.remove(‘show’), 3000); } // 파티클 효과를 생성하는 함수 function createParticles(x, y) { const particleCount = 15; for (let i = 0; i < particleCount; i++) { const particle = document.createElement('div'); particle.classList.add('particle'); const size = Math.random() * 8 + 4; particle.style.width = `${size}px`; particle.style.height = `${size}px`; particle.style.left = `${x}px`; particle.style.top = `${y}px`; const angle = Math.random() * Math.PI * 2; const distance = Math.random() * 100; document.body.appendChild(particle); const animation = particle.animate( [ { transform: 'translate(0, 0)', opacity: 1 }, { transform: `translate(${Math.cos(angle) * distance}px, ${Math.sin(angle) * distance}px)`, opacity: 0 } ], { duration: 800, easing: 'ease-out' } ); animation.onfinish = () => { particle.remove(); }; } } // RGB를 HEX 색상으로 변환하는 함수 function rgbToHex(rgb) { const match = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); if (!match) return rgb; const r = parseInt(match[1]); const g = parseInt(match[2]); const b = parseInt(match[3]); const toHex = (n) => { const hex = n.toString(16); return hex.length === 1 ? ‘0’ + hex : hex; }; return `#${toHex(r)}${toHex(g)}${toHex(b)}`; } // CSS를 포맷하는 함수 function formatCSS(css) { // 옵션이 체크되어 있을 경우 주석 제거 if (options.removeComments.checked) { css = css.replace(/\/\*[\s\S]*?\*\//g, ”); } // 규칙으로 분할 let rules = css.split(‘}’) .filter(rule => rule.trim()) .map(rule => { let [selector, properties] = rule.split(‘{‘); if (!properties) return ”; // 선택자 정리 selector = selector.trim(); // 옵션이 체크되어 있을 경우 빈 규칙 건너뛰기 if (options.removeEmptyRules.checked && !properties.trim()) { return ”; } // 속성을 배열로 분할 let propsArray = properties .split(‘;’) .filter(prop => prop.trim()) .map(prop => prop.trim()); // 벤더 접두사 정렬 if (options.vendorPrefix.checked) { propsArray.sort((a, b) => { const aPrefix = a.match(/^-\w+-/) || [”]; const bPrefix = b.match(/^-\w+-/) || [”]; return aPrefix[0].localeCompare(bPrefix[0]); }); } // 옵션이 체크되어 있을 경우 속성 정렬 if (options.sortProperties.checked) { propsArray.sort((a, b) => { const aProp = a.split(‘:’)[0].trim(); const bProp = b.split(‘:’)[0].trim(); return aProp.localeCompare(bProp); }); } // 옵션이 체크되어 있을 경우 RGB를 HEX로 변환 if (options.convertRGBToHex.checked) { propsArray = propsArray.map(prop => { const [name, value] = prop.split(‘:’); if (value && value.includes(‘rgb(‘)) { return `${name}: ${rgbToHex(value.trim())}`; } return prop; }); } // 콜론 정렬을 위한 속성 이름의 최대 길이 찾기 let maxPropLength = 0; if (options.alignColons.checked) { propsArray.forEach(prop => { const propName = prop.split(‘:’)[0]; maxPropLength = Math.max(maxPropLength, propName.trim().length); }); } const indent = ‘ ‘.repeat(parseInt(options.indentSize.value)); // 속성 포맷팅 let formattedProps = propsArray.map((prop, index) => { let [propName, propValue] = prop.split(‘:’); propName = propName.trim(); propValue = propValue ? propValue.trim() : ”; if (options.alignColons.checked) { const padding = ‘ ‘.repeat(maxPropLength – propName.length); return `${indent}${propName}${padding}: ${propValue}`; } else { return `${indent}${propName}: ${propValue}`; } }); // 옵션이 체크되어 있을 경우 마지막 세미콜론 제거 if (options.removeLastSemicolon.checked && formattedProps.length > 0) { formattedProps[formattedProps.length – 1] = formattedProps[formattedProps.length – 1].replace(‘;’, ”); } // 속성 결합 let result = formattedProps.join(‘;\n’); if (!options.removeLastSemicolon.checked) { result += ‘;’; } // 선택자와 속성 결합 if (options.compressOutput.checked) { return `${selector}{${formattedProps.join(‘;’)}}`; } else { return `${selector} {\n${result}\n}`; } }) .filter(rule => rule); // 빈 규칙 제거 return options.compressOutput.checked ? rules.join(”) : rules.join(‘\n\n’); } // 파일 가져오기 핸들러 fileInput.addEventListener(‘change’, (e) => { const file = e.target.files[0]; if (file && file.type === ‘text/css’) { const reader = new FileReader(); reader.onload = (event) => { cssInput.value = event.target.result; showToast(‘CSS 파일을 성공적으로 가져왔습니다’); }; reader.onerror = () => { showToast(‘파일 읽기 오류가 발생했습니다’, true); }; reader.readAsText(file); } else { showToast(‘유효한 CSS 파일을 선택해주세요’, true); } }); // 포맷 버튼 핸들러 formatBtn.addEventListener(‘click’, (e) => { const css = cssInput.value.trim(); if (!css) { showToast(‘CSS 코드를 입력해주세요’, true); return; } try { const formattedCSS = formatCSS(css); output.value = formattedCSS; copyBtn.disabled = !formattedCSS; downloadBtn.disabled = !formattedCSS; const rect = e.target.getBoundingClientRect(); createParticles(e.clientX, rect.top); showToast(‘CSS가 성공적으로 포맷되었습니다’); } catch (error) { showToast(‘CSS 포맷 중 오류가 발생했습니다’, true); console.error(error); } }); // 복사 버튼 핸들러 copyBtn.addEventListener(‘click’, async () => { try { await navigator.clipboard.writeText(output.value); showToast(‘CSS가 클립보드에 복사되었습니다’); const originalText = copyBtn.textContent; copyBtn.textContent = “복사 완료!”; setTimeout(() => { copyBtn.textContent = originalText; }, 2000); } catch (error) { showToast(‘클립보드에 복사하는 중 오류가 발생했습니다’, true); } }); // 다운로드 버튼 핸들러 downloadBtn.addEventListener(‘click’, () => { try { const blob = new Blob([output.value], { type: ‘text/css’ }); const url = URL.createObjectURL(blob); const a = document.createElement(‘a’); a.href = url; a.download = ‘formatted.css’; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showToast(‘CSS 파일이 다운로드되었습니다’); } catch (error) { showToast(‘파일 다운로드 중 오류가 발생했습니다’, true); } }); // 입력 변경 핸들러 cssInput.addEventListener(‘input’, () => { copyBtn.disabled = true; downloadBtn.disabled = true; }); // 초기화 copyBtn.disabled = true; downloadBtn.disabled = true;

CSS 포매터란 무엇인가요?

CSS 포매터는 적절한 들여쓰기, 간격, 줄 바꿈을 추가하여 CSS 코드를 정리하고 보기 좋게 만드는 도구입니다. CSS 파일의 가독성과 일관성을 향상시킵니다.

왜 CSS 포매터를 사용해야 하나요?

CSS 포매터를 사용하면 CSS 코드를 읽고 디버깅하기 쉬워지고, 팀이나 프로젝트 전체에서 일관된 형식을 유지할 수 있으며, 문법 오류나 중복 코드를 쉽게 발견하고, 특히 큰 CSS 파일의 유지 보수성을 높여줍니다.

CSS 포매터는 무료인가요?

네, 저희 CSS 포매터는 무료로 사용할 수 있습니다.

CSS 포매터는 어떻게 작동하나요?

CSS 포매터는 CSS 코드를 선택자, 속성, 값과 같은 구조적 요소로 파싱한 후 일관된 들여쓰기, 줄 바꿈, 간격으로 코드를 재구성하여 기능은 그대로 유지한 채 포맷된 CSS를 출력합니다.

포맷 규칙을 사용자 지정할 수 있나요?

네, CSS 압축 및 축소, 속성 정렬, 콜론 정렬, 주석 제거, 들여쓰기 스타일 등 다양한 포맷 옵션과 규칙을 선택할 수 있습니다.

CSS 포매터가 코드 기능에 영향을 주나요?

아니요, 저희 CSS 포매터는 코드의 외형만 변경하며 동작에는 영향을 주지 않습니다. CSS는 유효하고 정상적으로 작동합니다.

CSS 포매터가 큰 파일도 처리할 수 있나요?

네, 저희 CSS 포매터는 큰 파일도 처리할 수 있습니다. 다만 아주 큰 파일의 경우 성능은 브라우저와 시스템 리소스에 따라 달라질 수 있습니다.

포맷팅과 미니파잉의 차이는 무엇인가요?

포맷팅은 적절한 들여쓰기와 간격으로 코드를 조직하여 가독성을 높이는 반면, 미니파잉은 불필요한 문자(공백, 주석 등)를 제거해 파일 크기를 줄이지만 기능에는 영향을 주지 않습니다.