pixiv.incピクシブのデザインシステム「Charcoal」アイコンライブラリをつくる@mi2_okmt
View Slide
Profilemimoフロントエンドエンジニア2022年 新卒入社CTO室デザインシステム部兼 クリエイター事業部 FACTORY部初音ミクが好き DJする趣味がある
しゃべること● Charcoalとは?● charcoal-ui/iconsとは?● charcoal-ui/iconsをつくる● SVGのimportについて
とは?
とは?● pixivをはじめ、様々なプロダクトが利用● web / iOS / Android それぞれに対応した実装がある● 2022年7月からはデザインシステム部が開発してる○ DEV MEETUP(2020) のときはタスクフォースだった○ 部外からもドカドカPRをくれるのでレビューしてるピクシブのデザインシステム「Charcoal」
● 10個のパッケージを持つweb実装のモノレポ● @charcoal-ui/styled, /tailwind-config, /react …などなど● 今回は @charcoal-ui/icons について話しますweb実装 pixiv/charcoalとは?
@charcoal-ui/icons
@charcoal-ui/icons● Web Componentsとして実装○ 他フレームワークへの依存なしで利用可能○ Charcoalの他パッケージへの依存もほとんどない● SVGファイルを独自アイコンとして登録できるCharcoalのアイコンライブラリ
@charcoal-ui/icons1. yarn add @charcoal-ui/icons2. import ‘@charcoal-ui/icons’3. 簡単 3ステップ クイックスタート!
@charcoal-ui/iconsパッケージの構造FigmaSVG stringをexportするJSのパッケージ@charcoal-ui/icon-files.cjsSVGをCustom Elementsにする@charcoal-ui/iconsjsでimport@charcoal-ui/icons-cli
@charcoal-ui/icons● Figma APIからSVGとしてアイコンを取得● fill属性を currentcolor に置き換える● SVGをstringでexportするファイルをつくる● /icon-files に差分があれば commit して PR をつくるFigmaのアイコンを取ってくる icons-cli
@charcoal-ui/icons● icons-cli によって作られる.cjsファイルのパッケージ● export default '● どうしてこうなったのかはあとで話しますSVGタグをstringで返す icon-files
@charcoal-ui/icons● icon-filesに依存 SVG文字列をCustom Elementsにする● 指定されたSVGファイルをアイコンにする○ icon-files にないアイコンを使いたい需要に対応○ PixivIcon.extend()Custom Elementsをつくる icons
どうしてWeb Components?
どうしてWeb Components?● 2021年2月としては珍しい技術選択● ピクシブに求められる要件がいくつかあった@charcoal-ui/iconsはWeb Components
どうしてWeb Components?● プロダクトごとに異なるフレームワーク○ Smarty ERB React Vue …● 導入がスムーズに行えること● SSRできる● 検討したがボツになったものを具体的に紹介しますピクシブでの要件
@charcoal-ui/iconsボツ案
@charcoal-ui/icons ボツ案1. Reactでつくる2. icon font3. SVGRボツ案一覧4. SVGのuseタグ5. Lit HTML
@charcoal-ui/icons ボツ案× Reactでしか使えないピクシブにはVueプロダクトもある (BOOTH pixivのSP版 …など)× ReactプロダクトでもReactだけを使うとは限らない局所的にSmartyやERBをなこともある1. Reactでつくる
@charcoal-ui/icons ボツ案× フォントをつくるのがめんどくさい× font familyの上書きで変な文字が出る× スクリーンリーダーやa11yに弱いタグなどを本来と異なる用途で使うため2. icon font
@charcoal-ui/icons ボツ案SVGR: SVGをimportするとReactコンポーネントになるもの× inline SVGになって、HTMLが巨大になるおそれJSXからのimportでHTMLが巨大になるリスクが指摘されていた× React前提になる3. SVGRのようなアプローチ
@charcoal-ui/icons ボツ案id のついているSVG要素を useタグで呼び出せる× どうやってアイコンファイルを配信するか不明SVGアイコン全部入りファイルを読み込ませる必要がありそう× ロード時間や維持管理が厳しそう4. SVGのuseタグ
@charcoal-ui/icons ボツ案Web Componentsが作れる薄めなライブラリ✓ いったん採用した× SSR対応のとき、Lit HTMLが嫌な実装だったので結局やめた5. Lit HTML
@charcoal-ui/icons ボツ案× 内部でDOM APIをめっちゃさわる× SSRができない nodeでimportするとクラッシュした× lit-labs/ssr があったが嫌な実装だったglobalThisにDOM APIを生やすような感じここがいい感じだったら採用していたかもしれない...5. Lit HTML
そしてWeb Components へ
そして Web Components へ✓ 使うアイコンだけ読み込ませられる✓ 特定のフレームワークに依存させることなく実装できる✓ 無理のない実装方法であるion-icon も Web ComponentsだったSVGファイルをDOMにレンダリング
そして Web Components へ● 当時「Vue 3でWeb Components対応」と言われていた● SSR対応のCustom Elementsの参考に実装● globalThis に頼った箇所がひとつだけ残った○ HTMLElementsがSSRでundefinedになるのを回避させる部分SSRでも動くコードで実装
できあがった@charcoal-ui/icons
できあがった @charcoal-ui/icons👍 dependenciesがたった2つになったDOMPurifyとwarningsのみ👍 最新でも、レガシーでも、CSRでも、SSRでも使えるtailwindもstyledもReactもVueもいらない!👍 社内プロダクトへの導入がスムーズだった良かったこと
できあがった @charcoal-ui/icons🥳< 属人化していて更新しづらくて不幸になってました。ツールでサポートされたので属人性も手間もなくなり、工数削減だけでなく参入もしやすくなりました。喜びの声
🤔 PixivIcon.extends() を使うときはバンドラの設定が必要独自アイコンを使うなら各自でバンドラを設定してね!🤔 a11yが不十分 もし対応不可能なことがあるとマズい🤔 レイアウトシフトが起こりやすいドキュメントにリセットCSSを載せてるだけなのでパッケージに含めたい...残された課題・デメリットできあがった @charcoal-ui/icons
SVGのimport
SVGのimportおさらいFigmaSVG stringをexportするJSのパッケージ@charcoal-ui/icon-files.cjsSVGをCustom Elementsにする@charcoal-ui/iconsjsでimport@charcoal-ui/icons-cli
SVGのimport● 最近はwebpack以外のバンドラが増えた○ vite swc Parcel …● /icons は webpack 環境下でしか動かなかった● 社内から webpack 以外でも動いてほしいとの要望が来たwebpackが必須だった /icons v1.x.x
SVGのimport● 「.jsではないものを相対パスで読み込む」に差が出やすいなぜ webpack でしか動かなかったのかconst { default: filepath } = await import(`../svg/${encodeURIComponent(size)}/${encodeURIComponent(name)}.svg`)● jsに従って文字列を扱えば解決するのでは?
SVGのimport● CIでやることを追加○ icons-cli で SVGを取ってきてSVGファイルをつくる○ + SVGファイル → string を export する .cjs ファイル○ + 保存する場所を /icon-files に変更○ つくったファイルをまとめてcommitv2.x.x から増えた /icon-files
SVGのimport● アイコンの import する処理を分離○ SVG string 用の関数を用意○ 独自アイコン用に dynamic import する処理も残す■ PixivIcon.extends()にバンドラ設定が必要なのはこのため/icons の SVG import 改修
SVGのimport// v1.x.xconst { default: filepath } = await import(`../svg/${encodeURIComponent(size)}/${encodeURIComponent(name)}.svg`)// v2.x.ximport charcoalIconFiles from ‘@charcoal-ui/icon-files’get importIconFile() {return charcoalIconFiles[this._name]}
今後の話
今後の話● webpack以外でも使えることを確認すべき● 依存ライブラリのバージョンアップの影響もうける○ /icons だけは依存ライブラリの影響は少ない○ 他パッケージは peer dependenciesが多い■ /react /tailwind-config /styled …動作環境、依存ライブラリはどんどん変わる
今後の話● /icons は a11y やパフォーマンスの課題を抱えている● Charcoalは品質をあげるステップに入った○ より安定、よりハイパフォーマンスであるべき● Charcoalのぜんぶで頑張っている最中● キリがないのであとでとっ捕まえて聞いてくださいデザイナーやiOSエンジニアも捕えておきます
宣伝● Charcoalのセッションは他にもあります!○ SUB STAGE: cilvia「BOOTHにおけるCharcoal実践活用術」現場で実際に行われているテーマのカスタムなどの話が聞けそうです○ SUB STAGE: ああうえ「Charcoalをモバイルアプリで使う」CharcoalのiOS開発メンバーの発表です● 懇親会パートでmimoはDJをします