Material Symbols他、アイコンフォント形式のアイコンをFigmaで扱う方法
タイミーのプロダクトデザイナーの横田です。
サービスのUIアイコンの選定は重要でありながら、Figma等でアイコンをどのように扱えるかが影響します。今回は小ネタとして、アイコンフォントをFigmaで利用可能なsvgとして出力し、Figma Componentとして扱うTipsです。
UIアイコンを取り巻く悩みとしては、以下がよく聞かれます。
- 特にGoogleのMaterial Symbolsは、ウェイトやスタイル(Fill, Optical Sizeなど)を細かく調整できる点が非常に魅力的です。
- しかし、Material Symbolは個別のスタイルのFigmaを配布していません
- Figma Communityで配布されているファイルは、特定のウェイトに対応していなかったり、使いたいグリフ(アイコン)が足りなかったりすることがあります。
- Iconifyのようなプラグインは非常に便利ですが、Figmaコンポーネントとして一元管理しにくかったり、意図せず使ったフォントが実装と乖離したりと、悩ましい点もあります。
この課題は、これまでいくつかの会社で直面してきました。
そのたびに思い出して実行してきたのが今回の手法です。フォントファイルからFigmaで快適に扱えるアイコンコンポーネントを半自動で生成する手法をご紹介します。
全体の流れ
手順は大きく3ステップです。フォントをダウンロードし、PythonでSVGに分解、最後にFigmaでコンポーネント化します。
- フォントの準備:Variable Fontから特定のスタイルを切り出す
- 自動化の核心:Pythonで全グリフをSVGに変換する
- 仕上げとベストプラクティス:Figmaでコンポーネントライブラリを構築する
1 | フォントの準備
まず、GitHubからMaterial Symbolsフォントをダウンロードします。手に入るのは可変ウェイトなどに対応したVariable Fontです。
これをwoff2形式からttf形式へ各種ツールで変換してください。
このままではFigmaで扱いにくいため、特定のスタイルを持つフォントファイルを切り出します。ここで活躍するのがfonttools
というライブラリです。このfonttools
を2段階で活用するのが、今回の手法のポイントです。
まず1段階目として、ターミナルで以下のコマンドを実行します。ここでは例として、Weight=700(Bold)、Fill=1(塗りつぶし)のフォントを生成しています。
fonttools varLib.instancer MaterialSymbols.ttf wght=700 FILL=1 -o material-symbols-bold-filled.ttf
これで、特定のスタイルを持つ material-symbols-bold-filled.ttf
が手に入りました。
2 | Pythonで全グリフをSVGに変換する
次に、このttfファイルから全てのアイコン(グリフ)を個別のSVGファイルとして抽出します。ここが自動化の肝です。
以下のPythonスクリプトを実行すると、svg_glyphs
というディレクトリに、全てのグリフがSVGとして書き出されます。※ファイル名等は適宜変更してください。
from fontTools.ttLib import TTFont
from fontTools.pens.boundsPen import BoundsPen
from fontTools.pens.transformPen import TransformPen
from fontTools.pens.svgPathPen import SVGPathPen
import os
# TTFファイルを読み込む
font = TTFont('material-symbols-bold-filled.ttf')
# SVGファイルを保存するディレクトリを作成
output_dir = 'svg_glyphs'
os.makedirs(output_dir, exist_ok=True)
# フォント内の各グリフをSVGファイルに変換
for glyphName in font.getGlyphOrder():
if not glyphName.startswith('.'): # .notdefのようなシステムグリフをスキップ
glyph = font['glyf'][glyphName]
# グリフのバウンディングボックスを計算
bounds_pen = BoundsPen(font.getGlyphSet())
glyph.draw(bounds_pen, font['glyf'])
bbox = bounds_pen.bounds
if bbox is None:
print(f"Skipping glyph '{glyphName}' due to None bounding box")
continue
# グリフのパスを取得し、Y軸を反転
path_pen = SVGPathPen(font.getGlyphSet())
# フォント空間はY軸が上向きなので、SVG標準に合わせて反転させる
transform_pen = TransformPen(path_pen, (1, 0, 0, -1, 0, font['head'].yMax))
glyph.draw(transform_pen, font['glyf'])
# SVGファイルの内容を生成
# viewBoxはフォントのユニットサイズ(em square)に合わせる
svg_content = f'''<svg xmlns="<http://www.w3.org/2000/svg>" viewBox="0 0 {font['head'].unitsPerEm} {font['head'].unitsPerEm}" fill="currentColor">
<path d="{path_pen.getCommands()}"/>
</svg>
'''
# SVGファイルを保存
with open(os.path.join(output_dir, f'{glyphName}.svg'), 'w') as f:
f.write(svg_content)
print("SVG generation complete.")
スクリプトを実行すると、checkbox.svg
やhome.svg
といったファイル名でSVGが大量に生成されます。Material Symbolsの場合は3000以上あります。
3 | Figmaでコンポーネントを構築する
最後のステップはFigmaでの作業です。ただコンポーネント化するだけでなく、管理しやすい構造にすることが重要です。
-
SVGを一括インポート
生成されたSVGファイルを全て選択し、Figmaのキャンバスにドラッグ&ドロップします。 -
コンポーネント化
インポートした全てのベクターを選択した状態で、Batch Create Components
のようなプラグインを実行し、一括で個別のコンポーネントに変換します。 -
Variantsとして統合
作成された全コンポーネントを選択し、右サイドバーの「Combine as variants」をクリックします。これにより、3000以上のバリアントを持つ巨大なコンポーネントセットが完成します。これをMaterial Symbols
のように命名しましょう。
グリフ管理とインスタンスを分離する
ここで一つ、重要なプラクティスを紹介します。先ほど作成した巨大なコンポーネントセットを直接デザインに使うのではなく、「グリフ(形状)を管理するコンポーネント」と「サイズや色を規定する表示用コンポーネント」を分離することを推奨します。
-
グリフ管理用コンポーネント (
Material Symbols
)- 3000以上のアイコン形状を持つ、バリアントの塊。
- このコンポーネントの役割は「形状の管理」のみに特化させます。
-
表示用コンポーネント (
Icon / S
,Icon / M
など)- 実際にデザインで使用するコンポーネントです。
- このコンポーネントの中に、グリフ管理用コンポーネントのインスタンスを配置します。
- フレームサイズを24pxや32pxなどにバリエーションをもたせ、色のスタイルを適用します。
この構成にすることで、以下のようなメリットが生まれます。
- メンテナンス性の向上: アイコンの色やサイズ体系を変更したくなった場合、表示用コンポーネントを修正するだけで済みます。3000以上のバリアントを一つひとつ修正する必要はありません。
- 利用者の混乱を防ぐ: デザイナーは表示用コンポーネントを使うだけでよく、巨大なグリフ管理コンポーネントを直接触る必要がなくなります。
おわりに
fontToolsを使うアイデアは、昔英字のVariable Fontsをつくったときに得たものでした。
アイコンの整備は社内で一度整理してそれきりなので、いろんな会社で自分がこっそり15分で整備してきた。お悩みの方への解決につながればよいなと思います。
Discussion