PNGからSVGへ次期NGWのアイコンを刷新する
次期バージョン(Linux)の、ずっとPNGだったアイコンを全点SVGへ。青×橙のフラットなベクターに一新する——ただの置き換えのはずが、AIと一日やり取りするうちに、DB移行バッチとアイコン設計ツールという実運用の道具が2つ残った、その記録。
きっかけ — 見た目で、差をつけたい
NGW の次期バージョンは、これまでとは違う Linux サーバの上で動かしている。中身の刷新を進めるうちに、見た目でも差をつけたくなった。手はじめに選んだのがアイコンだ。
NGW のアイコンは、ずっと PNG だった。悪くはない。ただ、高解像度のディスプレイで見ると輪郭が少し甘い。色を一括で変えることもできない。この機会に全点を SVG に置き換え、デザインも一新する。方針はひとつ、青(#1976d2)+橙(#e8920c)のフラットなベクター。この2色でシステム全体の表情を揃える。
そもそも、PNGとSVGは何が違うのか
地味だが、ここが今回の肝だ。先に整理しておく。
- PNG=ラスター(画素の集合)。 細かな点の集まりで絵を作る。拡大するとギザギザに崩れる。サイズも色も画像に焼き付いていて、あとから変えるには画像エディタで開き直すしかない。
- SVG=ベクター(図形の数式)。 「ここに半径10の円」「この座標を結ぶ線」という図形の指示をテキストで書いたものだ。何倍に拡大しても劣化しない。色・サイズ・線の太さは、テキストを書き換えれば後から変えられる。コードで生成できるし、一括で変換もできる。
SVG を選んだ狙いは、この性質がそのまま効くからだ。どの解像度でもくっきり。青×橙への色替えが一括でできる。そして130枚を超えるアイコンを、人の手ではなくコードで処理できる。 この「コードで扱える」という一点が、話を思わぬ方向へ転がしていく。
まず「壊さずに」移す
いきなり全部を差し替えると、必ずどこかで事故る。参照が切れてアイコンが出ない画面があちこちに出る。
段取りはこうした。まず現行のアイコン一式をまるごとバックアップ。次に、各 PNG を base64 で埋め込んだだけの SVG——中身は元の画像そのままで、拡張子だけ .svg にした替え玉——を機械的に生成する。見た目は完全に同じだ。これで「全部 SVG」という土台を先に作り、そのうえで1枚ずつ本物のベクターに差し替えていく。対象は全部で 138枚。
AIに「手描きのSVG」でデザインさせる
ここからが本番だ。base64 の替え玉を、Claude に1枚ずつ手描きのベクター SVG として起こしてもらう。原画を見せ、「青×橙のフラット、共通の線幅と角丸」というブリーフを渡し、系統ごとに並べて並列で生成させる。
上はアプリバーに並ぶアイコン群。どれも白いハロー(フチ)をまとっていて、青でも橙でも、どんな背景色の上でも沈まず映えるようにしてある。
「市・町・村」のような文字だけのアイコンは、あえて画像化しない。<text> 要素を使った本物の SVGにした。フォントには依存するが、そのぶん拡大しても一切劣化しない。
写真的な自治体ロゴなど8点は、ベクター化すると質が落ちる。ここは無理をしない。画像を内包したSVG(拡張子は .svg だが中身はラスター)のまま温存した。全点をきれいに割り切るより、割り切らない部分を残すほうが、結果として全体が締まる。
「揃っている」を、作り込む
ばらばらに作ると統一感が出ない。ここが一番時間をかけたところだ。
カレンダー系は27点ある。これを共通のカレンダー基盤+右下のバッジという構造に固定した。土台は全アイコンで完全に同じ。バッジの中身だけを差し替える。 電車、飛行機、祭りの提灯、会議の人影。シンボルは違っても、遠目には同じ系列に見える。
人物の絵も、最初は「頭と胴が離れて宙に浮く」ちぐはぐなものが多かった。これを全部、頭と胴を繋げた一体のシルエットに統一した。さらに、アイコンの意味そのものも再設計していく。人事は履歴書、自治サポは「自治会と役所を繋ぐサポーター関連」、休暇はコーヒーブレイク、簿冊の引継・廃棄は「書庫へ引き継ぎ廃棄する」——絵を見ただけで機能が伝わるように寄せていく。
コードとDBの .png → .svg(ここでツール①が生まれた)
デザインが揃っても、まだ半分だ。システムのあちこちに xxx.png という参照の文字列が残っている。これを .svg に直さないとアイコンが表示されない。
- コードの静的な参照は248箇所/88ファイル。 ここは一括で置換。PDF(TCPDF)はそもそも該当アイコンを描いていなかったので無風だった。
- 厄介なのはデータベースだ。アイコンのパスは、10個ほどのテーブルのアイコン列に
./icon/xxx.pngという値で保存されている。これを SQL で機械的に.svgへ。そう考えて、手が止まった。手で流すのは怖い。 うっかり余計な行まで書き換えたら目も当てられない。
一括変換バッチをひとつ作った。事前に何件が対象かを数えて見せる。トランザクションで囲み、途中でひとつでも失敗したら全部ロールバックする。実行中は、どの行を書き換えたかがログで流れる。135件をまとめてコミットして完了した。
このバッチには、地味だが嬉しい効き目がある。移行データのDBを取り込んでアイコンが404になっても、これを一度流せば全部揃う。 アイコン刷新のためだけに作ったのに、運用で使える道具になった。ここまで来たので PNG は全廃、SVG 一択にした(PDF出力で使う名刺の背景1枚だけ、ラスターを残した)。
微調整の沼(ここでツール②が生まれた)
揃えたつもりでも、実際に画面へ並べると気になる。このアイコンだけ白フチが太い。この絵、あと少し大きくしたい。線がちょっと重い。
これを言葉で AI に伝えて直してもらう。「もう少し細く」「いや戻して」「あと2割だけ大きく」。この往復を何十回もやって、私はようやく気づいたのである。これは言葉でやる作業ではない。 スライダーでやる作業だ。
もうひとつ道具を作った。NGWIconMaker、アイコン設計ツールである。
![]()
肝は、形と数値を分けて持つという設計だ。造形そのものはベースSVGを直接いじる。拡大率・位置・フチの太さといった数値のパラメータは、ツールのスライダーで詰める。保存を押すと、サーバがベースSVG+パラメータから各アイコンを一括で再生成する。
念を入れて、この変換ロジックを PHP・JavaScript・Python の3つで同一に保った。ブラウザのプレビュー(JS)と、サーバの保存処理(PHP)と、バッチ処理(Python)で、同じ入力からは必ず同じ絵が出る。以降は「数値はツール、形は指示」と役割を分けた。微調整が一気に速くなった。
ハマりどころ
一日の作業で踏んだ落とし穴も並べておく。
- Affinity の書き出しが 417×417 になる。 ベクターエディタで 300DPI 設定のまま書き出すと、
viewBoxが本来の 100 から 417(=100×300/72)に化けていた。72DPI で書き出して解決。ツール側も<svgの記述を起点に座標を読むよう頑丈にした。 - PHP のコメントの中に書いた
?>で、関数が丸ごと壊れた。 説明のつもりでコメントに<?xml … ?>と書いたら、その?>が PHP そのものを終了させた。古典中の古典である。久しぶりに踏んだ。 - 決裁のアイコンだけ反映されない。 メニューが参照していたのは
ngwtWorkflowNotice.svg。私が熱心に直していたのは、同じ意味の別ファイルWorkflowNotice.svgだった。ただのファイル名のズレ。三十分ほど溶けた。 - 「更新されない」の大半はブラウザキャッシュ。 サーバのファイルはとっくに新しい。スーパーリロードで一発。毎回引っかかる。学習しない。
できたもの — アイコンのはずが、道具が2つ
始めたときの目的は、ただのアイコンの PNG → SVG 化だった。終わってみると、手元にはアイコンだけでなく、実運用で使える道具が2つ残っていた。DBの値を安全に一括変換するバッチと、アイコンを数値で詰める設計ツールである。
振り返って一番よかったのは、素材(ベースSVG)とパラメータ(数値)を分けて持ったことだ。この2つが分かれていると、人間も AI も後から触りやすい。形を直したいならベースSVGへ、微調整なら数値へ。どちらを触っても、再生成すれば結果が揃う。SVG がコードで扱える図形だったから、この分離が素直に成り立った。
AIとの協働作業での学び
面白かったのは、AI に任せる部分と、仕組みで縛る部分を分けるとうまくいく、ということだ。
130枚を超えるアイコンを AI に手描きさせると、一枚ごとに微妙にばらつく。線が太い。人物が浮いている。これを一枚ずつ口で直すのは不毛だ。だから揃えるべきところは構造で固定した。 カレンダーは共通基盤。微調整はスライダー。変換ロジックは3言語で同一。AI には形の発想を、仕組みには揃える責任を割り振る。AI の速さと、機械の再現性の、いいとこ取りになる。
ないものは造る。揃わないものは仕組みで揃える。アイコンを一新するだけのつもりが、副産物のほうが長く役に立つ道具として残った。
……ちなみに、この一日でいちばん時間を食ったのは、アイコンのデザインでも DB の変換でもなく、直しても直しても変わらない決裁アイコンをにらんでいた三十分である。ファイル名が違っていた。道具は2つ増えたが、私の注意力は増えていない。