// 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フォーマッターは大きなファイルを処理できますか?
はい、大きなファイルも処理可能です。ただし非常に大きなファイルの場合、パフォーマンスはブラウザやシステムリソースによって異なる場合があります。
フォーマットとミニファイの違いは何ですか?
フォーマットはインデントやスペースを適切に使ってコードの読みやすさを向上させ、ミニファイは不要な文字(空白、コメントなど)を削除してファイルサイズを削減しますが、どちらも機能は変わりません。