登壇者の自己紹介とアジェンダ紹介

mimo氏:こんにちは。これから、「ピクシブのデザインシステム『Charcoal』アイコンライブラリをつくる」のセッションを始めていきます。

まず自己紹介からさせてください。自分は、mimoと申します。2022年に新卒入社して、デザインシステム部と、オンデマンド印刷サービスを開発しているFACTORY部を兼務しています。

趣味的なことを言うと、初音ミクが大好きです。あとはDJをする趣味があって、最近は「RubyKaigi」に絡めたDJイベントをしたりしています。

自分のセッションでは、(スライドを示して)この4つに大きく分けて話していきます。まず、「Charcoalとは?」と題して、Charcoalデザインシステムそのものについて紹介をします。

その次に、「@charcoal-ui/iconsとは?」と題して、CharcoalのWeb実装は、実はパッケージが10個あるモノレポになっているのですが、その中の「icons」というライブラリについて紹介します。

3番目に、「@charcoal-ui/iconsをつくる」と題して、@charcoal-ui/iconsのアーキテクチャや実装の話に触れていきます。そして最後に、アイコンライブラリの一番のミソであるSVGのimportについて内部の実装がどうなっているかを紹介していきます。

「pixiv」「BOOTH」「pixivFACTORY」「VRoid Hub」などで利用されているデザインシステム「Charcoal」

ではまず、ピクシブのデザインシステム、Charcoalをご紹介します。

Charcoalは、ピクシブのデザインシステムです。SNSの「pixiv」をはじめ、「BOOTH」「pixivFACTORY」「VRoid Hub」などのプロダクトで全社的に利用されています。

CharcoalはWeb実装だけでなく、iOSの実装、Androidの実装も存在しており、そのすべてがOSSとして公開されています。また、Charcoalは2020年に「PIXIV DEV MEETUP」でOSS化しますとお披露目されました。このようなカンファレンスに来ていただいている方は、記憶に新しいかと思います。

そこから2年が経ち、当時はタスクフォースとして、部署とはなっていなかったのですが、2022年、Charcoalをメンテナンスする部署として、デザインシステム部というものが新しく立ち上がりました。今、そのデザインシステム部のメンバーが日夜開発を続けています。

デザインシステム部は、基本的にほかのプロダクトと兼務しているメンバーが所属していて、中にはUIデザイナー、Webフロントエンドエンジニア、モバイルアプリエンジニアなどが在籍しています。

それから、部外からもドカドカと、プルリクエストをいただいているので、それをレビューしたりマージしたりリリースしたりというのも自分たちのお仕事です。

今回お話しする、アイコンライブラリが含まれるCharcoalのWeb実装についても詳しく紹介します。先ほども申し上げたのですが、CharcoalのWeb実装が入っている、pixiv/charcoal.gitというリポジトリは、10個のパッケージを持っている、Web実装のモノレポになっています。

例えば、「@charcoal-ui/styled」「@charcoal-ui/tailwind-config」「@charcoal-ui/react」など、それぞれのフレームワークに対応したライブラリが、このモノレポの中には含まれています。今回はその中でも、アイコンライブラリである@charcoal-ui/iconsについてお話をしていきます。

Web Componentsとして実装されている、Charcoalのアイコンライブラリ「@charcoal-ui/icons」

@charcoal-ui/iconsには、アイコンが150種類以上収録されています。使い始めれば、これらのアイコンがいくらでも使いたい放題という感じになっています。

@charcoal-ui/iconsは、Charcoalのアイコンライブラリとして存在していて、Web Componentsとして実装されています。Web Componentsは、再利用できるHTML要素を作る方法として標準化されているもので、特定のフレームワークやライブラリに依存する必要がありません。

その特徴を活かして、@charcoal-ui/iconsは「styled-components」などのフレームワークに依存することなく使えるように作られています。

Charcoalモノレポ内の、ほかのパッケージへの依存もほとんどないので、簡単に使い始めることができます。また、@charcoal-ui/iconsに収録されていないアイコンを使いたい場合にも対応できるよう、SVGファイルを独自アイコンとして登録できるAPIも提供しています。

ピクシブにはさまざまなサービスがあり、それらのロゴや独自機能のアイコンなどがよく作られるので、この機能を使って対応をお願いしています。

@charcoal-ui/iconsは、簡単に使い始められると先ほど言ったのですが、どれぐらい簡単に使えるのか、3行にまとめてきました。

yarn addして、importして、pixiv-iconというタグを書く、これだけで使えるので、みなさんもおうちに帰ったらぜひ使ってみてください。

「@charcoal-ui/icons」のパッケージ構造

続いて、@charcoal-ui/iconsのパッケージ構造も簡単に説明します。@charcoal-ui/iconsをアイコンライブラリたらしめる役者としては、「Figma」のアイコンがまとまっているファイルと、「icons-cli」「icon-files」というパッケージがあります。

デザイナーは、Figmaにアイコンを追加したり編集したりしています。@charcoal-ui/icons-cliは、週1回「GitHub Actions」でFigmaのAPIを使って、Figmaのファイルの中からアイコンのSVGデータをたくさん取ってきて、icon-filesの中にファイルを作っています。

icon-filesはSVGのタグを文字列としてたくさん持っていて、それをexportするだけのJavaScriptのファイルがたくさんあります。

@charcoal-ui/iconsは、icon-filesのSVGタグの文字列がexportされているJavaScriptのファイルをimportしてきて、それをWeb ComponentsのCustom Elementsとして登録する機能を持っているというアーキテクチャになっています。

Figmaのアイコンを取ってくる「@charcoal-ui/icons-cli」

それぞれのパッケージをもう少しだけ詳しく説明させてください。@charcoal-ui/icons-cliは、Figmaのアイコンを取ってきて、ファイルを作って、プルリクエストを作るところまでやっています。FigmaにはAPIがあり、それでファイルの中の特定のレイヤーのものを書き出してSVGを取得しています。

取得したSVGは、Figmaで塗り潰されてしまっているので、fill属性をcurrentcolorというものに置き換えます。これでWebで出した時にCSSで色が変えられるようになります。

SVGの下処理ができたら、そのSVGを文字列としてexportするだけのJavaScriptのファイルを作って、置いています。ファイルを作った結果、icon-files配下に差分があれば、プルリクエストを作ってエンジニアにレビューをしてもらって、マージをしてリリースするというワークフローになっています。

SVGタグを文字列として返す「@charcoal-ui/icon-files」

続いて、@charcoal-ui/icon-filesについてです。@charcoal-ui/icon-filesは、SVGタグを文字列としてexportするJavaScriptのファイルをいっぱい持っているパッケージです。

そのコードはほとんどが「export default '<svg……」うんたらかんたらという、(スライドを示して)こんな感じのコードが1行だけ存在するファイルになっていて、本当にこれだけの単純なパッケージです。

実は2022年にバージョン2にアップデートする時にけっこう仕組みを変えていて、このパッケージはその時に新しく追加したものです。どうしてこういうパッケージを作るに至ったのかは、後ほど説明しますのでお楽しみに。

SVG文字列をCustom Elementsとして登録する「@charcoal-ui/icons」

最後に、@charcoal-ui/iconsです。@charcoal-ui/iconsは、先ほどのicon-filesに依存しています。icon-filesのSVG文字列をCustom Elementsとして登録する機能を持っていて、指定されたSVGファイルを登録する機能も提供しています。

icon-filesにないアイコンを使いたい時には、この指定されたSVGファイルを登録する機能を使ってなんとかしてくださいということになっています。

なぜWeb Componentsなのか? 当時求められた要件

次に、なぜ@charcoal-ui/iconsは、Web Componentsになることを選んだのかについて説明します。

アイコンライブラリというと、例えば「Font Awesome」みたいにアイコンフォントにしたり、「React Icons」みたいにReactコンポーネントで作ってしまえばいいのでは、と思うかもしれません。しかし、iconsに求められていた要件と、紆余曲折あった技術選択があるので、それについて紹介していきます。

あらためて、@charcoal-ui/iconsは、Web Componentsとして作られています。npmパッケージとしてのCharcoalの開発は、2021年頃から始まっていて、その当時としては少々珍しい技術選択だったそうです。

Web Componentsは、ECMAScript 2015で標準化された機能で、2016年から「Chrome」と「Safari」で、2018年から「Firefox」で、2020年からやっと当時の「Edge」で使えるようになったようなので、確かにギリギリな感じがします。

それでもWeb Componentsが最適な手段のように思えたのは、ピクシブとして求められることがあったためでした。アイコンライブラリを作る上で課題になったのは、(スライドを示して)このようなことでした。

一番の課題は、ピクシブの展開するサービスごとに使っているフレームワークが異なること。PHPのテンプレートエンジンの「Smarty」が使われている部分もあるし、Railsプロダクトになれば、ERBやSlimを使っている部分もあります。

Reactが採用されていたとしても、全部がReactというわけでもなく、奥底までたどっていくとSmartyがいたり、管理画面はバリバリ、テンプレートエンジンだったり、Reactだけでなく、ピクシブにはVueを使っているプロダクトもいくつかあります。

これらに対応できないと、そもそも導入が不可能に近くなってしまうため、対応する必要があります。また、スムーズに導入でき、アイコンの更新に手間がかからないこと、SSRにも対応できることも要件に加えました。これらの要件を念頭に検討したものの、採用に至らなかったものが5つほどあるので、それらについて紹介します。

ボツ案その1 Reactでアイコンライブラリを作る

ということで、@charcoal-ui/iconsボツ案と題して、5つほど紹介していきます。ボツになったものが、(スライドを示して)この5つです。けっこうみなさんも使っているものがあると思うのですが、順番に話していきます。

まず、Reactでアイコンライブラリを作ることです。これは、アイコンライブラリを使うためにReactを導入する必要が生まれてしまうためボツとなりました。

「Reactプロダクトだけでも対応できればそれでいいじゃん」と思うかもしれませんが、先ほどもちょっと言ったように、Reactを採用しているプロダクトだとしても、古い画面はSmartyであったり、管理画面はERBを使っていたり、テンプレートエンジンを使っている部分があるので、「これがベストな手法である」とは言えないでしょう。

ボツ案その2 icon font

2番目のボツ案は、icon fontです。これは、Figmaから取ってきたSVGファイルをフォントにするのがちょっと難しそうというか、どうやってやったらいいのかが正直よくわからないというのが欠点になると思います。

この話題を話すにあたって、アイコンファイルをGitHub Actionsでいい感じに作るというのをちょっと調べたのですが、あまり知見がなさそうで、どうやったらいいかが正直よくわかりませんでした。

大変な思いをしてフォントファイルができてHTMLに読ませたとして、CSSでfont familyがもし上書きされたとしたら、結局変な文字が出る。

Font Awesomeなどを使っている人はよく経験があると思いますが、変な文字が出て、「なんだこの文字は?」となったり、うまくアイコンが入らないことが起こるのも考えられるし、スクリーンリーダーの対応やアクセシビリティのことを考えると、それにはすごく弱い方法なのかなと思い、ボツになりました。

ボツ案その3 SVGRのようなアプローチ

次は、3番目のボツ案で、SVGRのような方法です。SVGRは、SVGをimportするとReactコンポーネントになるライブラリです。

SVGファイルをimportするだけなので簡単に見えると思いますが、JSXからSVGをimportすると、inline SVGになって、HTMLがすごく巨大になるおそれがあるのでやめようねという啓蒙がこの頃に行われていて、そうなってしまうとSSR対応が厳しくなりそうなので対応を見送りました。

また、JSXやReactであることが結局求められてしまうために、React前提になることから逃れられないと判断して、これは結局ボツになりました。

ボツ案その4 SVGのuseタグ

4番目のボツ案が、SVGのuseタグです。SVG内でid属性が付いているものを再利用するuseタグというものがあります。それを使ってアイコンをいい感じに出すのはどうかという案なのですが、useタグで呼び出すためにはそのsymbolを1つの巨大なSVGファイルにまとめなくてはならないという不安がありました。

それ以外も、「ファイルを細切れにしたとしても、どうやって読み込ませるねん?」というのがよくわかりませんでした。また、仮に1つの巨大なSVGファイルを作り出して、それを配信するとなれば、ページの読み込みが遅くなることと、アイコンの管理が難しくなることが予想されたため、これはボツとなりました。

ボツ案その5 Lit HTML

最後に、「Lit HTML」の採用です。Lit HTMLは、Web Componentsが作れるライブラリです。

最近のVue 3などだと、Web Componentsとしても使える機能が提供されていたりするのですが、Vueほどコッテリしたライブラリは、この要件に対して必要ではないと感じていたため、Lit HTMLをいったん採用しました。

(スライドを示して)これが、昔のリポジトリを掘り返してきて発見した2年前ぐらいのコミットログなのですが、はっきりと「use lit-html」と書いてありますね。しかし、開発が進んでSSR対応を始めた時に、Lit HTMLのSSR対応のパッケージの実装がちょっと嫌な感じだぞということがわかってきたので、結局これはやめることになります。

Lit HTMLのそもそも何が嫌だったのかというポイントは、内部でDOM APIをたくさん触っていること。SSR対応がそのままだとできませんでした。

エクスペリメンタルなパッケージで、lit-labs/ssrだったかな、そんな感じのやつがあるのですが、これがエクスペリメンタルだったということもありました。

globalThisに対して、DOM APIのようなものをたくさん生やしているので、globalThisに、こんなもいっぱいいろいろなものを生やさなくても実装できるでしょう、と当時は考えていたので、これを採用しないことになりました。

ここの実装がglobalThisじゃなくいい感じになっていたら、もしかしたら最後までLit HTMLで作っていた可能性があります。

(次回へつづく)