1+ <!doctype html>
2+ < html lang ="ja ">
3+ < head prefix ="og: https://ogp.me/ns# ">
4+ < meta charset ="UTF-8 " />
5+ < title > CSSのリファクタリングを手軽にしてみた | PanKUN Blog</ title >
6+ < meta name ="viewport " content ="width=device-width, initial-scale=1 " />
7+ < meta name ="description " content ="個人開発のWebサイトで溜まりがちな「使われていないCSS」をNode.jsスクリプトでサクッと検出して整理しました。 " />
8+
9+ <!-- Canonical -->
10+ < link rel ="canonical " href ="https://breadmotion.github.io/WebSite/blog/blog_00014.html " />
11+
12+ <!-- JSON-LD -->
13+ < script type ="application/ld+json ">
14+ {
15+ "@context" : "https://schema.org" ,
16+ "@type" : "BlogPosting" ,
17+ "mainEntityOfPage" : {
18+ "@type" : "WebPage" ,
19+ "@id" : "https://breadmotion.github.io/WebSite/blog/blog_00014.html"
20+ } ,
21+ "headline" : "CSSのリファクタリングを手軽にしてみた" ,
22+ "description" : "個人開発のWebサイトで溜まりがちな「使われていないCSS」をNode.jsスクリプトでサクッと検出して整理しました。" ,
23+ "image" : [
24+ "assets/img/ogp.png"
25+ ] ,
26+ "datePublished" : "2024-06-27T00:00:00.000Z" ,
27+ "dateModified" : "2024-06-27T00:00:00.000Z" ,
28+ "author" : {
29+ "@type" : "Person" ,
30+ "name" : "PanKUN" ,
31+ "url" : "https://breadmotion.github.io/WebSite"
32+ } ,
33+ "publisher" : {
34+ "@type" : "Organization" ,
35+ "name" : "PanKUN" ,
36+ "logo" : {
37+ "@type" : "ImageObject" ,
38+ "url" : "https://breadmotion.github.io/WebSite/assets/img/favicon-192.png"
39+ }
40+ }
41+ }
42+ </ script >
43+
44+ < meta property ="og:title " content ="CSSのリファクタリングを手軽にしてみた | PanKUN Blog " />
45+ < meta property ="og:description " content ="個人開発のWebサイトで溜まりがちな「使われていないCSS」をNode.jsスクリプトでサクッと検出して整理しました。 " />
46+ < meta property ="og:type " content ="article " />
47+ < meta property ="og:url " content ="https://breadmotion.github.io/WebSite/blog/blog_00014.html " />
48+ < meta property ="og:image " content ="assets/img/ogp.png " />
49+ < meta property ="og:site_name " content ="PanKUN " />
50+ < meta property ="og:email " content ="pankun.dev@gmail.com " />
51+
52+ < meta name ="twitter:card " content ="summary_large_image " />
53+ < meta name ="twitter:title " content ="CSSのリファクタリングを手軽にしてみた " />
54+ < meta name ="twitter:description " content ="個人開発のWebサイトで溜まりがちな「使われていないCSS」をNode.jsスクリプトでサクッと検出して整理しました。 " />
55+ < meta name ="twitter:image " content ="assets/img/ogp.png " />
56+
57+ < link rel ="stylesheet " href ="../assets/css/base.css " />
58+ < link rel ="stylesheet " href ="../assets/css/layout.css " />
59+ < link rel ="stylesheet " href ="../assets/css/blog.css " />
60+ </ head >
61+ < body data-page ="blog ">
62+ < div class ="page-shell ">
63+ < main class ="main-container ">
64+ < article class ="post-detail reveal-on-scroll ">
65+ < header class ="post-detail__header ">
66+ < p class ="post-detail__meta "> Thu Jun 27 2024 09:00:00 GMT+0900 (日本標準時) / 開発ログ</ p >
67+ < h1 class ="post-detail__title "> CSSのリファクタリングを手軽にしてみた</ h1 >
68+ < p class ="post-detail__description "> 個人開発のWebサイトで溜まりがちな「使われていないCSS」をNode.jsスクリプトでサクッと検出して整理しました。</ p >
69+ < p class ="post-detail__tags "> < a class ="tag " href ="../blog.html?tag=css "> css</ a > < a class ="tag " href ="../blog.html?tag=refactoring "> refactoring</ a > < a class ="tag " href ="../blog.html?tag=nodejs "> nodejs</ a > < a class ="tag " href ="../blog.html?tag=automation "> automation</ a > </ p >
70+ </ header >
71+
72+ < section class ="post-detail__body markdown-body ">
73+ < p > こんにちは!パン君です。</ p >
74+ < p > 機能追加やデザイン変更を繰り返しているうちにCSSファイルの中に**「これ、どこで使ってるんだっけ?」**というクラスが増えてきました。</ p >
75+ < ul >
76+ < li > デザイン案Aで書いたけど、結局B案にしたから使わなくなったボタンのクラス</ li >
77+ < li > 共通化しようとして作ったけど、結局固有クラスを使ったから放置されたスタイル</ li >
78+ </ ul >
79+ < p > などなど。< br > 放置しても動くのですが、ファイルサイズも無駄ですし何より精神衛生上よろしくありません。</ p >
80+ < p > そこで、今回はこれらをリファクタリングすることにしました。</ p >
81+ < hr >
82+ < h2 > 手動で探すのは無理</ h2 >
83+ < p > とはいえ、CSSファイルに書かれているクラス名を一つずつ検索して「使用箇所ゼロ」を確認するのは苦行です。
84+ 世の中には < code > PurgeCSS</ code > などの便利なツールもありますが、ビルドプロセスに組み込むのが少し大変だったり設定が複雑だったりします。</ p >
85+ < p > 今回は**「もっと手軽に、ざっくりでいいから不要なものを消したい」**というモチベーションだったので、Node.jsで簡単なスクリプトを書いて検出することにしました。</ p >
86+ < h2 > 自作スクリプトの方針</ h2 >
87+ < ol >
88+ < li > < code > assets/css/</ code > フォルダ内のCSSファイルを全部読み込む。</ li >
89+ < li > 正規表現でクラス名(< code > .classname</ code > )とID名(< code > #idname</ code > )を抽出する。</ li >
90+ < li > プロジェクト内の全ファイル(HTML, JS, MDなど)をスキャンする。</ li >
91+ < li > 「抽出したクラス名が、どのファイルにも一度も登場しない」場合、それを< strong > 未使用候補</ strong > として出力する。</ li >
92+ </ ol >
93+ < p > これだけです。厳密な構文解析(AST)などはせず、単純な文字列検索(< code > includes</ code > )で判定します。
94+ その分、「クラス名と偶然同じ単語が文章中にあったら検知漏れする」という弱点はありますが、**「使っていないものを安全に削除する」**という目的には十分です。</ p >
95+ < pre > < code class ="language-javascript "> // tools/find-unused-css.js (抜粋)
96+
97+ const cssContent = fs.readFileSync(cssFile, 'utf8');
98+ const selectors = extractSelectors(cssContent); // 正規表現で抽出
99+
100+ selectors.forEach(sel => {
101+ let isUsed = false;
102+ for (const content of contents) {
103+ // 単純な文字列検索
104+ if (content.includes(sel.name)) {
105+ isUsed = true;
106+ break;
107+ }
108+ }
109+
110+ if (!isUsed) {
111+ console.log(`[UNUSED] ${sel.name}`);
112+ }
113+ });
114+ </ code > </ pre >
115+ < h2 > 実行結果</ h2 >
116+ < p > スクリプトを実行してみると、いくつかの未使用クラスが見つかりました。</ p >
117+ < pre > < code > Checking assets\css\layout.css...
118+ [UNUSED] .section--soft
119+
120+ Checking assets\css\top.css...
121+ [UNUSED] .btn-outline
122+ </ code > </ pre >
123+ < p > 確認してみると、
124+ < code > section--soft</ code > は初期デザイン案で作ったカードのバリエーション、
125+ < code > btn-outline</ code > は枠線のみのボタンで、現在は塗りつぶしボタンしか使っていませんでした。</ p >
126+ < p > これらは完全に不要なので迷わず削除!
127+ CSSファイルが少しスリムになりました。</ p >
128+ < h2 > まとめ</ h2 >
129+ < p > リファクタリングというと大掛かりな作業をイメージしがちですが、
130+ < strong > 「気になったら小さなスクリプトを書いてサクッと調べる」</ strong >
131+ というアプローチなら、日常的にコードをきれいに保てそうです。</ p >
132+ < p > 自分の手で作ったツールで自分のサイトを改善していくのは、エンジニアならではの楽しみですね。</ p >
133+
134+ </ section >
135+ </ article >
136+ </ main >
137+ </ div >
138+
139+ < script src ="../assets/js/layout.js " defer > </ script >
140+ < script src ="../assets/js/ui.js "> </ script >
141+
142+ < canvas id ="menuAnimationCanvas "> </ canvas >
143+ < script src ="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js "> </ script >
144+ < script src ="../assets/js/particles.js "> </ script >
145+ </ body >
146+ </ html >
0 commit comments