DroidGlass: あらゆるプラットフォームのためのリキッドガラス
リキッドガラスは、Appleのマテリアルで、その下にあるものを屈折させます。リムの近くでは、背景が厚いレンズのように中心に向かって引き寄せられ、端に柔らかな虹色が現れ、リムに沿ってヘアラインのハイライトが走り、タッチするとガラスが膨らみ、指に向かって伸び、バネのように戻ります。通常のグラスモーフィズムは、白っぽい霞の下で背景をぼかすだけであり、それが安っぽくマットに見える理由です。
センスの主なルール:ガラスは透明であること。 背後にあるものが読めないのであれば、それはiOS 7のすりガラスであり、リキッドガラスではありません。テキストがガラスの上に置かれている場合にのみ、ぼかしとサーフェスを追加します。
限界についてはユーザーに正直に伝えてください。本物のマテリアルはiOS、iPadOS、macOS 26以降にのみ存在し、それ以外はすべて模倣です。AndroidではBackdropが完全な模倣を提供します(レンズにはAndroid 13が必要)。Webでは、屈折はChromium(Chrome、Edge、Samsung Internet、Android WebView、Electron)でのみ機能します。Safari、Firefox、およびiOS上のすべてのブラウザは、曲がりのない、同じハイライト、シャドウ、スプリングを備えたライトブラーを取得します。
ワークフロー
- プラットフォームを特定します。
- iOS/macOS 26上のSwiftUI、UIKit、AppKit、WidgetKit:システムAPI。
references/apple.mdを参照してください。何も模倣しないでください。 - Jetpack ComposeまたはCompose Multiplatform:
references/android-compose.mdを参照してください。シェーダーを最初から作成せず、ライブラリを使用してください。 - Web、Electron、Android WebView、Capacitor、Cordova、Windows/Android上のTauri:
assets/内のエンジン。references/web.mdを参照してください。WebViewとElectronでは屈折は常に機能します。 - React Native、Flutter:システムの同等物はありません。React Nativeの場合は、WebViewレイヤーまたはBackdrop上のネイティブモジュールを提案します。Flutterの場合は、フォールバックとして
BackdropFilter、およびreferences/math-and-motion.mdの数式を使用したFragmentProgramを提案します。現在のパッケージを自分で確認してください。このスキルはそれらをリストしません。
- iOS/macOS 26上のSwiftUI、UIKit、AppKit、WidgetKit:システムAPI。
- コントロールレイヤーの1〜3つの要素を選択します: タブバー、フローティングボタン、トップバー、シート、メニュー、タッチ中のトグルまたはスライダーのサム。コンテンツをガラスにしないでください。
- 以下の表からレシピを選択します。 パラメータを発明しないでください。値はKyantのカタログからのものであり、画面上で確認されています。
- ガラスの下の背景を確認します。 ガラスは、写真、色、チャート、スクロール中のテキストなどのリッチなコンテンツの上でのみ表示されます。無地の白いページの上では、透明なガラスはほとんど見えなくなります。背後に色を配置するか、薄暗いレイヤーを追加するか、またはそこからガラスを削除します。
- 適切なリファレンスからビルドし、 最後のチェックリストを使用して自分の目で確認します。 静止時、押下時、ドラッグ時のスクリーンショットなしで引き渡さないでください。
Appleのルール
ヒューマンインターフェースガイドライン、WWDC25「Meet Liquid Glass」、および一般的なコミュニティスキルから。これらのいずれかに違反すると、偽物だとすぐにバレます。
- コンテンツの上に浮かぶナビゲーションとコントロールレイヤーのみ。 リスト、行、カード、ページの背景はガラスにしません。例外:トグルとスライダーのサムは、タッチ中にガラスになります。
- ガラスの上にガラスはありません。 ガラスの上の要素は、塗りつぶしと不透明度によって区別されます。例外(Backdropより):ガラスシート上のスライダーサムはシート(
exportedBackdrop)を屈折させます。2つのマットなプレートではなく、1つのレンズが別のレンズの上に重なります。 - 控えめに使用します。 最も重要なコントロールのみ。
- ガラスは無色です。 色と明るさは背景から来ます。明るい背景には明るく、暗い背景には暗く、ハイライトは背景によって色づけられます。それ自体の白や灰色のサーフェスはありません。
- 2つのモードがあり、混在させないでください。 クリア(ボタン、サム、拡大鏡、ボタンとグループ化されたタブバー):ブラー0〜4、サーフェスなし。レギュラー(長いテキストを含むシート、ダイアログ):ブラー8〜16。テキストなしでは読み取れない場合にのみ、40〜60%のサーフェスを追加します。
- プライマリアクションのみを着色します。
- 形状。 カプセル:半径は高さの半分に等しい。同心円の角:内側の半径は、コンテナの半径からパディングを引いたものに等しい。
- 大きいほど厚くなります: 大きな要素は、より広い湾曲したリムとより深い屈折を取得します。
- コンテンツをガラスの下に半分だけ残したままにしないでください。
- 動きの中の液体。 押すと、ガラスは数ピクセル成長し、減衰しながら指の方に傾きます。サムは1.5倍に膨らみ、速度に応じてドロップ状に伸びます。すべてはスプリング上で動作し、線形のトランジションはありません。
レシピ(Kyantのカタログからの値)
| 要素 | エフェクト | ハイライトとシャドウ | タッチ |
|---|---|---|---|
| ボタン、48 dpカプセル | バイブランシー、ブラー2、レンズ(12、24) | デフォルト、デフォルトシャドウ | グロー。高さ4dp成長。オフセット min × tanh(0.05 × offset / min)。指に向かって最大4dp伸びる |
| 着色されたボタン | 同じ + ティント | 同じ | 同じ |
| サーフェス付きボタン | 同じ + ホワイト30% | 同じ | 同じ |
| 64×28トラック上のトグルサム40×24 | 静止時:ホワイト100%、ブラー8。押下時p:ブラー8(1−p)、レンズ(5p、10p、分散)、ホワイト (1−p) | アンビエント、幅 /1.5、アルファp。シャドウ半径4、5%。インナーシャドウ半径4p、アルファp | 1.5倍にスケール。トラックがレンズを通して見え、0.75に縮小 |
| 6 dpトラック上のスライダーサム40×24 | トグルと同様、レンズ(10p、14p) | トグルと同様 | 1.5倍にスケール。トラックをタップするとそのポイントにジャンプ |
| タブバー64 dp | バイブランシー、ブラー8、レンズ(24、24)、サーフェス #FAFAFA 40%(ダーク #121212 40%) | デフォルト | 押すとパネル全体が幅16dp拡大し(ラベルも連動)、指の後を最大4dp追従 |
隠し行コピー56 dp(tabsBackdrop) | アクセントカラー、alpha(0)の同じパネル:バイブランシー、ブラー8、レンズ(24p、24p)、パネルサーフェス | アルファpのデフォルト、InteractiveHighlight | 押下時にアイコンとラベルが×1.2(LocalLiquidBottomTabScaleはこの行のみに設定) |
| タブバーサム56 dp | 静止時:グレー10%。押下時:背景と行コピーの上のレンズ(10p、14p、分散)(rememberCombinedBackdrop(backdrop, tabsBackdrop)) | デフォルト、シャドウとインナーシャドウ8p、アルファp | 78/56にスケール。ドロップ内に「バーの中のバー」:アクセントカラーで自身のアイテムが拡大表示され、隣接アイテムがリムに表示 |
| ダイアログ、半径48 | 明るさ0.2(ライト)、彩度1.5、ブラー16(ダーク8)、レンズ(24、48、深度) | プレーン | 背景を #29293A 23%(ダーク #121212 56%)に減光 |
| コントロールセンターパネル | バイブランシー、レンズ(24、48、深度) | デフォルト、角度はデバイスの傾きから、フォールオフ2 | 出現:48 dpのオフセットとアルファ |
| 拡大鏡128×96 | レンズ(8、24、深度、分散)、背景1.5倍ズーム | インナーシャドウ16 | ドラッグ |
スプリング、ジェスチャー、およびすべてのプラットフォームに共通する数学は、references/math-and-motion.mdにあります。
WebとAndroid WebViewを1分で
assets/から3つのファイルを変更せずにプロジェクトにコピーし、オプションで設定します。エンジンを書き換えないでください。
<link rel="stylesheet" href="liquid-glass.css">
<button id="buy">Buy</button>
<div id="wifi" aria-label="Wi-Fi"></div>
<div id="volume" style="width: 280px"></div>
<nav id="tabs" style="position: fixed; left: 16px; right: 16px; bottom: 16px"></nav>
<script src="liquid-glass.js"></script>
<script src="liquid-ui.js"></script>
<script>
LiquidUI.button(document.getElementById('buy'), { tint: '#0088FF', onClick: () => {} });
LiquidUI.toggle(document.getElementById('wifi'), { checked: true, onChange: (on) => {} });
LiquidUI.slider(document.getElementById('volume'), { value: 0.4, onInput: (v) => {}, label: 'Volume' });
const tabs = LiquidUI.tabs(document.getElementById('tabs'), {
items: [{ label: 'Home', icon: '<svg>…</svg>' }, { label: 'Search', icon: '<svg>…</svg>' }],
index: 0, onChange: (i) => {},
});
// マージ:バーとその隣のボタンが1つのドロップに溶け合う。隣接する伸縮ボタンは自動的にこれを行う
// LiquidUI.blob([tabs.panel, fab], { blur: 3.5 }); LiquidUI.autoMerge(document.body); 画面変更後は LiquidUI.sweep()
// 独自のマークアップのためのベアガラス
const g = LiquidGlass.attach(document.getElementById('panel'), { radius: 28, blur: 8, refractionHeight: 24, refractionAmount: 24, surface: 'rgb(250 250 250 / .4)' });
// g.set({ lens: 0..1, blur, surface, highlightAlpha, angle, shadowAlpha, innerShadow, press: { p, x, y }, tx, ty, sx, sy, sleep })
</script>
| ファイル | 説明 |
|---|---|
assets/liquid-glass.js | ガラスエンジン:屈折マップ、分散、ハイライト、シャドウ、マージンググループ(LiquidGlass) |
assets/liquid-glass.css | ベーススタイル、アクセシビリティフォールバック、コンポーネントスタイル |
assets/liquid-ui.js | スプリング、ジェスチャー、コンポーネント:ボタン、トグル、スライダー、タブ、拡大鏡、blob、autoMerge(LiquidUI) |
assets/demo.html | すべてのコンポーネントを1ページに表示。正しい見た目を確認するために開く |
何かを変更したり、アプリに組み込んだりする前に、references/web.mdを読んでください。そこには、ほとんどの実装を壊すChromiumの事実(backdrop-root、position: fixed、touch-action、bodyの背景)とすべてのオプションがリストされています。見た目がおかしい場合はreferences/troubleshooting.mdを、カクつく場合はreferences/performance.mdを参照してください。
Webで絶対に間違えてはいけない5つのこと:
- 背景は実際の要素でなければなりません。
bodyに設定されてルートに伝播する背景は、backdrop-filterから認識されません。 - ガラスの祖先に対して
opacity、filter、mask、clip-pathをアニメーションさせないでください(例:画面のフェードイン)。これはbackdrop-rootになり、ガラスは背景を失い暗くなります。画面はtransformで移動し、上からベールレイヤーで表示します。 - ガラス要素に独自の
transformを設定しないでください。ベースオフセットは--lg-baseに書き込んでください。 - 伸縮ボタンには
touch-action: noneが必要です(デフォルト)。pan-yを使用すると、ブラウザがドラッグを横取りし、pointercancelを送信します。 - Android WebViewでは、長押しとハプティクスを無効にします:
setOnLongClickListener(v -> true)およびsetHapticFeedbackEnabled(false)。
アクセシビリティと可読性
- Webエンジンは、
prefers-reduced-transparency: reduce(ガラスオフ、ほぼ不透明なサーフェス。ChromeとEdge 119+、SafariとFirefoxは未対応)、prefers-contrast: more(ソリッドサーフェスと境界線)、prefers-reduced-motion(スプリングが即時になる)を尊重します。 - コンポーネントはロールとキーボードサポートを取得します:button(Enter、Space)、
switch(Enter、Space)、slider(矢印)、tablist(左右矢印)。aria-labelまたはaria-labelledbyでラベルを付けます。 - Androidでは、Kyantのカタログにあるように、
semantics { role = Role.Switch }とラベルを設定します。古いAndroidでは、onDrawSurfaceの色がエフェクトなしで読み取れることを確認します。 - パネルの下に表示される可能性のある最も明るく、最も込み入ったコンテンツに対するテキストのコントラストを確認します。本文テキストでは4.5:1。失敗する場合は、ブラーとサーフェスを追加するか、コンテンツを移動します。テキストシャドウでは修正できません。
禁止事項
- ボタンに半透明の白いサーフェスに
blur(10px)を追加すること。それはすりガラスであり、リキッドガラスではありません。デンシティは、テキストを含むタブバー、シート、ダイアログでのみ適切です。 - ガラスのコンテンツカードとガラスのページ背景。
- 大きなパネルでの分散:コストが高く、派手です。
- ヘアラインハイライトの代わりに太い白い境界線。
- すべてのボタンに同じティント。
- プレスとドラッグにスプリングの代わりに線形の
transitionを使用すること。 - Web上でガラスの
widthとheightをアニメーションさせること。マップは毎フレーム再構築されます。スケールをアニメーションさせてください。 - 複数のステップで読み取られる結果の下で、重いフィルタステップ(ブラー、ディスプレイスメント、コンボリューション、モルフォロジー)を使用すること。Chromeはブランチごとにそれらを一度計算するため、スマートフォンがフリーズします。
- 「見た目のために」独自のサーフェスでガラスをペイントすること。テキストを含む高密度パネルのみがサーフェスを取得します。
- Backdropで十分な場合に、独自のComposeシェーダーを作成すること。
- Appleプラットフォーム外でSF SymbolsとAppleロゴを使用すること。ライセンスで禁止されています。オープンなアイコンセットを使用してください。
- WebまたはAndroidの結果を「公式のLiquid Glass」と呼ぶこと。それはそのスタイルの模倣です。
引き渡す前のチェックリスト
倍率2のスクリーンショットを撮り、自分で確認します。
- グリッドまたはテキスト上の静止時: 透明なガラスを通して線が見え、リムで中心に向かって引き寄せられ、ハイライトは細い。白の上では、ガラスはリムのシェーディングとシャドウによってページから分離され、暗い背景では、明るい側の明るい線によって分離される。
- 押下時: ボタンが成長し、指に向かって傾く。サムは膨らんで透明になり、トラックが透けて見え、虹色はソフトで、色のついた穴はない。
- ドラッグ時: サムは速度に応じて伸び、到着後にのみしぼむ。タブバーは傾いて戻る。端のタブと画面の下端近くでのタブバードロップ:内部に自身のアイテム、リムに隣接アイテム。黒い領域、バンド、ミラーリングされたピースはない。
- プル時: ボタンはラバーバンドのように指に追従し、跳ね返る。隣接するボタンに向かって引っ張られたバーはそれに溶け込み、近づくにつれてブリッジが成長し、継ぎ目や二重のリムはない。静止時および単純な押下時は離れたまま。通常のタップは機能し、遠くに引っ張られて離されたボタンは発火しない。
- 写真の上と白の上: ガラスの下の色は豊かで、テキストは読み取り可能。
- ダークテーマ。
- フォールバック: エンジンを偽装し(Safariユーザーエージェントと空の
navigator.userAgentData)、要素が穴にならず、タブバーサムの下のアイコンがぼやけていないことを確認。 - リサイズと回転: マップが再構築され、リムに階段状の跡がない。
- 画面遷移: 画面が表示されている間に連続したフレームをキャプチャ。ガラスボタンが静止時より暗くなることはなく、マージレイヤーはアニメーション後も正確にボタンの下に留まる。
- コンソールエラーがなく、 ハングがない。連続タップ後もページは素早く応答する。1つのアニメーションループ、無関係な
requestAnimationFrameはない。 - スマートフォン画面に3つ以上の大きなガラス要素がない、ガラスの上にガラスがない、ガラスのコンテンツがない。
何かが失敗した場合は、最初に修正してからユーザーに表示します。
ソース
- Kyant0/AndroidLiquidGlass、Backdropライブラリとカタログ(Apache-2.0):レンズ、分散、ハイライトとシャドウの数式、コンポーネントのレシピ、スプリングとジェスチャーの定数。Webエンジンはこのスキームの移植版です。
- Apple:ヒューマンインターフェースガイドライン「Materials」、「Adopting Liquid Glass」、WWDC25「Meet Liquid Glass」。
- kube.io、「Liquid Glass in the Browser」:
backdrop-filter内のfeDisplacementMapによる屈折。 - iyinchao/liquid-glass-studio(MIT):
sminによる形状のマージ、スネル屈折、高品質の無色ガラス。