🐧 採用ページに「すりガラス文字エフェクト」を実装したんだけど、Mac で確認したらフォントがぜんぜん違う!!
デザインでは Montserrat ExtraBold Italic なのに、Mac ではなんか違うゴシック体みたいになってる。
Windows・Android では完璧なのに Mac だけ再現しない、あの感じ。
結論から言うと、CSS の mask-image は SVG をレンダリングする際に隔離されたコンテキストを使うため、ドキュメントの Web フォントが読み込まれないことが原因だった。
解決策として、フォントを SVG に直接埋め込む方法を試したらうまくいったので、その手順をメモしておく!!
↑ backdrop-filter + mask-image のすりガラス文字エフェクト(フォント: Montserrat ExtraBold Italic)
まず結論
フォントをサブセット化して WOFF2 → base64 にし、SVG の @font-face に直接埋め込む。
さらにその SVG 全体を base64 に変換して data:image/svg+xml;base64,... 形式で mask-image に渡す。
① fonttools でフォントをサブセット化
pip install fonttools brotliimport base64, io
from fontTools import ttLib, subset
font_path = "Montserrat-ExtraBoldItalic.ttf"
chars = "CREATE ENERGY" # 使う文字だけサブセット化
ss = subset.Subsetter()
ss.populate(unicodes={ord(c) for c in chars})
font = ttLib.TTFont(font_path)
ss.subset(font)
buf = io.BytesIO()
font.flavor = "woff2" # WOFF2 形式で出力
font.save(buf)
font_b64 = base64.b64encode(buf.getvalue()).decode("ascii")
print(f"サイズ: {len(buf.getvalue())} bytes") # → 約 3,500 bytes② SVG に @font-face を埋め込んで data URI に変換
style = (
"<style>@font-face{"
"font-family:'Montserrat';font-weight:800;font-style:italic;"
f"src:url('data:font/woff2;base64,{font_b64}') format('woff2')"
"}</style>"
)
svg = (
'<svg width="1921" height="272" viewBox="0 0 1921 272"'
' xmlns="http://www.w3.org/2000/svg">'
f"<defs>{style}</defs>"
'<text fill="#fff" font-size="198" font-family="Montserrat"'
' font-weight="800" font-style="italic" letter-spacing=".04em"'
' transform="translate(15 207)">CREATE ENERGY</text>'
"</svg>"
)
# SVG 全体を base64 にして data URI に変換
svg_b64 = base64.b64encode(svg.encode()).decode("ascii")
mask_uri = f'url("data:image/svg+xml;base64,{svg_b64}")'③ SCSS で使う
.blur-text {
/* Python で生成した base64 data URI を変数にセット */
--_mask-svg: url("data:image/svg+xml;base64,AAEC...");
backdrop-filter: blur(50px) brightness(1.2);
mask-image: var(--_mask-svg);
mask-size: contain;
mask-repeat: no-repeat;
mask-position: center;
}そもそも何が起きてたか
実装したエフェクトはこんな仕組み。
mask-imageに SVG テキストを渡して、文字の形でマスクを作る- マスクされた部分に
backdrop-filter: blur(...) brightness(...)を適用 - → 背景画像がすりガラス状にぼけて、文字の形に見える
この時の mask-image の値はデータ URI の SVG で、こんな感じ:
/* ❌ data URI だけど font-family で Web フォントを指定 */
.blur-text {
backdrop-filter: blur(50px);
mask-image: url("data:image/svg+xml,%3Csvg ...%3E%3Ctext font-family='Montserrat'%3ECREATE%3C/text%3E%3C/svg%3E");
}
/* → mask-image のレンダリングは隔離コンテキストなので
ドキュメントの @font-face が読まれない
Mac だとシステムフォントに化けてしまう */Windows や Android では Montserrat がシステムにインストールされてるケースが多いのでたまたまうまくいってた。
でも Mac の場合はシステムフォントに Montserrat がないので、フォールバックフォントでレンダリングされてしまう。
なぜ mask-image の SVG でフォントが効かないのか
mask-image で参照される SVG は、ブラウザが隔離されたレンダリングコンテキストで処理する。
つまりドキュメント側で読み込んだ @font-face がそのコンテキストには届かない。
これは CSS の仕様上の制約で、外部リソース(Web フォント含む)へのアクセスが制限される。
セキュリティのためと考えるとまあ納得できる仕様。
試したけど NG だった方法
| アプローチ | 結果 | 理由 |
|---|---|---|
外部 SVG ファイルに @font-face を書く | ❌ | backdrop-filter が壊れる(外部 SVG では動かない) |
data URI の SVG に font-family 指定 | ❌ | 隔離コンテキストで Web フォントが読まれない |
| SVG をパスデータに変換して書く | △ | パスが正確じゃないと字形が崩れる・手間が大きい |
外部 SVG ファイルに @font-face を書く方法をさらに詳しく説明すると:
/* ❌ 外部 SVG ファイルを参照 → backdrop-filter が壊れる */
.blur-text {
backdrop-filter: blur(50px);
mask-image: url("create-energy.svg");
}
/* ❌ SVG ファイル側に @font-face を書いても同じ */
/* → backdrop-filter が動かない or フォントが無視される */外部 SVG だと backdrop-filter 自体が動作しなくなるブラウザが多い。
フォント問題より先に、エフェクト自体が壊れてしまうので NG。
解決策がなぜ動くのか
data URI の SVG 内に直接 @font-face を書いて、フォントもそこに base64 で埋め込んでしまえば外部リソースへのアクセスが不要になる。
隔離コンテキストでも自己完結したフォント情報が使えるので、どの OS・ブラウザでも同じフォントでレンダリングされる。
ただしフォントファイル全体を埋め込むとサイズが爆発するので、サブセット化が必須。
今回は「CREATE ENERGY」の9種類の文字だけに絞ったら、約 3.5KB に収まった!!
Mac 固有の罠:SVG の <filter> でイタリック体が切れる
フォント埋め込みで Mac のフォント問題は解決したんだけど、今度は「Y の右上が切れてる」問題が発生した。
原因は SVG の <filter> 要素に明示的な座標・サイズを指定していたこと。
<filter x="0" y="0" width="100%" height="100%"> のように範囲を明示すると、フィルタの出力範囲がその矩形にクリップされる。
イタリック体のグリフは右方向に張り出すのに、Mac(CoreText)は Windows より少しだけ広めにグリフをレンダリングする。
その差分でフィルタ領域をはみ出して切れてしまった。
解決策は単純で、mask-image 用の SVG ではフィルタを使う必要がないので <filter> 要素ごと削除した。backdrop-filter は CSS 側(要素に直接)かけるので、SVG 側にはフィルタ不要!!
まとめ
mask-imageの SVG は隔離コンテキストで動く → ドキュメントの Web フォントが使えないbackdrop-filterと一緒に使う場合は data URI 形式が必須(外部 SVG ではbackdrop-filterが壊れる)- 解決策:フォントをサブセット化 → WOFF2 → base64 → SVG の
@font-faceに埋め込み → SVG 全体を base64 で data URI に - fonttools の
brotliも入れないと WOFF2 出力でエラーになる - SVG に
<filter>を書く場合は明示的なクリップ領域に注意(Mac でイタリック体が切れやすい) mask-image用 SVG にbackdrop-filterを持たせる必要はない →<filter>は削除でOK
参考
以上!!
誰かのお役に立てれば嬉しいです🐧
