きっかけ
先日、IEがお亡くなりになるのを期にし、ダークモード対応のSVGを作ろうと思い、ライダーロゴメーカーというのを作る機会がありました。(ロゴメーカーを作りたいのではなく、faviconをやってみたいがためにロゴメーカーを作ったわけです。)
その時、ふと思ったのはこの作業はいずれ『デザイナーとエンジニア(特にフロントエンド)との戦争になるなぁ』と。
だた、それを見過ごすのも『ダサいなぁ』と思いHybrid SVG Makerなるものを作りました。
戦争になると思った理由は以下のような理由です。
- デザイナーがすぐ飛びつきそうな技術
- イラレなどのツールで対象のSVGファイルがダークモードに対応しているのか確認・編集できない
- 確認するのにいちいちOSのモードを変更しなければならない
- 誰かがエディタを開いて編集しなければならない
- 調子に乗って色を使いすぎたりすると素直にエディタ使うのは辛い(でも本来画像はそういう制限を気にしないで作るべき)
- 仮にそこそこエディタが使えるデザイナーが作ったとしても引き継いだデザイナーができるとは限らない
Movable TypeやWordPressなどのCMSが流行するようになる前から**殆どプログラミング作業の発生しないテンプレート(HTML/CSS)の作成はデザイナー領域なのか?エンジニア領域なのか?**という問題はあったと思います。
前述のCMSが流行するのと同時に、CSSはless/sassなどのメタ言語になったりBEMなどのアーキテクチャが生まれ、JSもjQueryからSPA向けのフレームワークになったりちょっとHTMLとCSSが分かってjQyeryをコピペでやってきた人とちゃんと分かる人との乖離が大きくなったように思います。
そうじゃなくても、非エンジニアとエンジニアの衝突はネットの様々なところでネタにされているのはqiitaを読まれている方であれば周知の事実だと思います。
御宅を並べてしまいましたが「色を変更するぐらいのCSSならそっち(デザイナー)で解決してくれよ」というのを自分なりに解決しようとしたツールを開発したわけです。
今回はその開発に至るプロセスとかハマったことを記載します。
尚、文章中にあるHybrid SVGとは私の造語で、1つのsvgファイルでLight mode/Dark modeなどに複数モードに対応したSVGファイルのことを示します。
ペルソナ
エンジニアとは言え、ツールを開発する際UI/UXは気にしなければいけません。
簡単に言うと、
- CUIでよいのかGUIにするのか?
- バイナリ形式で配布するのか?
みたいなところです。
対象者がエンジニアであってもCUI(ターミナル作業)が苦手だったり自分の手元で書いた何かのスクリプト言語のbatが対象の環境下にインストールされているとは限らないわけで、この辺りはかなり慎重に対応しないといけないでしょう。
で、今回は対象者がデザイナーです。一応、ペルソナとしては
- gitのcommit / pushはなんとなくできるが、よく分かってない
- 既に何かのプロジェクトを担当している or 現在はダークモードを検討していないが対応する可能性がある
- Windows / Mac 両方のユーザー
という感じを想定しました。
一括処理にするか? or 1ファイルでの処理にするか?
上記のペルソナにより、私が最初に考えたのはローカルにある「img」ディレクトリや「svg」ディレクトリにある複数のsvgを一括置換です。
GUIであれば、一番上にディレクトリを選択するボタンがあって、それを選択するとディレクトリ以下のSVGから色を抽出して、リストを表示し、その右側に置換する色を入力して、「置換!」みたいなものです。
↑こんな感じのGUIにしようと思っていました。
で、問題になるのが言語とFWですよね。
必須なのはxmlのparserが必須なので使いなればXpathが使えるもの(できればRubyのnokogiriレベルのものがよい)、そしてバイナリを生成できる言語です。
自分的にはCrystal-langで書きたかったんですがgtkやqtを使うのは面倒だなぁと思い、元々クロスコンパイルがあるGoかRustならGUIのFWも充実してそうだと思ってeguiのあるRustを選択しました。
作っている途中で気づく その1
Rustで書いている途中で思いました、「あれ?実際にHybrid SVG使えるのってWebだけで、iOS/Androidは未対応じゃん」と。
つまり、実際はIEが死んだあとの、webの開発のみに必要なわけで、そんなに『一括置換する機会があるか?』と言えば『Noになるのでは?』と。
と、迷いながら実装している間に事件が起こります。
Macbook Proが死ぬ…
とりあえず、Mac miniはあるものの機動性がなくなるし、ということでM1 proのMacbook proを手配しますが、これにより各種プログラム用の環境がなくなったばかりか、CPUがIntelから変わってしまい、『Macでバイナリ生成して2端末でdebugとかしんどいなぁ』というのもダメ押しになり。
もうコンパイルの走る言語より「スクリプト言語で作ったほうがマシだなぁ」と思いphpで書くことにしました。
pure PHPでいくか? ライブラリを使うか?
Ruby+nokogiriが頭にあったので、phpで使えるnokogiriを探しcomposerでinstallしてRustで書いてたものをphpに移植していきます。
ただ、やっぱりRubyのnokogiriと違うんですね、しかもRustのはXPathしか使ってないので、微妙に移植がし辛い…
結局、DOMDocumentを使えばいいじゃんと思い、Xpathだけで実装していたところ…
『これ、やっぱり一括変換としてローカルで使うかもなぁ…』
『だとしたら、Ruby(+Shoes!)でやってもいいかもなぁ…』
と思ったりして同時にRubyでも書いたりしていた時に決定的な事が頭をよぎりました。
作っている途中で気づく その2
『未リリースのアイコンを知らないサーバーにアップロードして変換ってポリシー的にNGだろ』
と。で、タイトルにある「結局JSで実装したんかい!」となるわけです。
いや、自分で思いましたよ『4つの言語で実装』ってなんだろ…って。
しかも、JSすんごい苦手なのです。(PHPもだけど標準関数が多い分まだ…)
forEachで連想配列回してくれないしbreakできないからsome使うとか、sprintfが標準でないとかetc…
ホント辛い。
技術的に面倒だった点
nodeの環境を再構築するのが面倒だったので、完全にpure jsでやったので、cssのparserも自分で実装したこととかなんですが
<path id="hoge" style="fill:#000" />
<path class="foo" fill="#000" />
<path class="bar" style="fill:#000" />
<path fill="#000" />
↑これ全部同じになるの分かります?
<style>
#hoge{fill:#000}
</style>
<path id="hoge" />
<path class="foo" style="fill:#f00" />
<path fill="#000" />
<style>
@media (prefs-color-scheme: dark){
.foo{
fill:#fff;
}
}
</style>
↑みたいにstyleがいろんな所に複数出てくるパターン、しかもinline styleも併用で、既にダークモード対応しているパターンとか、この辺Drawツールによって表記が全く違うのでメッチャ面倒くさかったです。(多分、全パターン網羅できてません)
しかも、このSVGを上記のHTMLに2つ埋め込む場合、idが重複しちゃうのでどちらかが効かなくなります。
この辺も、ビュアー専用に書き換えたりするという手間が発生して…
世の中のHTML/XML/CSS parserのライブラリ作ってる人本当に尊敬します。
まとめ
と言う事で、セキュリティ的なポリシー、言語/FWの剪定などを含め無駄に周り道して時間(1/28から構想して6日程)がかかってしまいましたが無事にリリースできました。
興味ある方は使ってみてください。
え? JSで実装したなら『ローカル版はElectronでいいんじゃね?』って話?