UIの磨き上げ (UI Polish)
洗練されたUIは、小さなディテールの積み重ねによって生まれます。このスキルは、どのディテールが重要で、どのような値を持つべきかのリファレンスです。
レビューする際は、インターフェースの速度を落として確認してください。10%の速度で違和感を感じる部分は、フルスピードでは微妙に間違っているものです。
プロジェクトのコンポーネントライブラリ、トークン、密度を尊重し、以下のルールで正確なインタラクションが指定されている場合を除き、プロジェクトのモーション言語に合わせてください。
以下のすべてのduration、カーブ、スケール、ブラーは具体的な値であり、近似値の範囲ではありません。cubic-bezier(0.2, 0, 0, 1) は cubic-bezier(0.4, 0, 0.2, 1) ではなく、0.96 は 0.95 ではありません。記載された値をそのまま使用してください。
テキストの折り返し、フォントレンダリング、タブリック数字、テキスト間隔は better-typography に属します。ヒットエリア、フォーカス、キーボードサポート、ARIA、動きの抑制は better-accessibility に属します。グループ化、セクション間隔、ブレークポイント、空間的なRTLは better-layout に属します。
同心円状の角丸 (Concentric border radius)
外側の半径 = 内側の半径 + パディング。ネストされた要素で半径が一致しないことは、インターフェースがアンバランスに見える最も一般的な原因です。半径、影、アウトラインのレシピは surfaces.md にあります。
幾何学的配置よりも光学的配置 (Optical over geometric alignment)
幾何学的な中央揃えが不自然に見える場合は、光学的に配置します。アイコン付きボタン、再生トライアングル、非対称アイコンはすべて手動で微調整が必要です。
奥行き表現には影、構造表現には境界線 (Shadows for elevation, borders for structure)
奥行きを作るためだけに境界線が存在する場合は、透明な box-shadow の値を重ねて使用します。区切り線、セパレーター、選択状態やフォーカス状態など、構造や状態を伝える境界線はそのまま維持します。
中断可能なアニメーション (Interruptible animations)
インタラクティブな状態変更にはCSSトランジションを使用します。これらはアニメーションの途中で中断できるためです。キーフレームは、一度だけ実行される段階的なシーケンスのために予約します。
エンターアニメーションの分割とスサ (Split and stagger enter animations)
シーケンスが階層を伝えるような、頻度の低い段階的なエントランスでは、コンテンツを意味的なチャンクに分割し、約100msずつずらして表示します。1つのコンテナ全体をアニメーションさせるよりも、同じコストでより良い効果が得られます。高頻度のインタラクションではスサ (stagger) を使用しません。enter-exit.md を参照してください。
控えめな終了アニメーション (Subtle exit animations)
全体の高さではなく、小さな固定の translateY を使用します。終了アニメーションは開始アニメーションよりもソフトであるべきです。両方向に ease-out を使用します。
文脈に応じたアイコンアニメーション (Contextual icon animations)
アイコンは表示/非表示を切り替えるのではなく、opacity、scale、blur でアニメーションします。以下の正確な値を使用します:スケール 0.25 から 1、不透明度 0 から 1、ブラー 4px から 0px。
モーションライブラリ(package.json に motion または framer-motion)がある場合は、そのパッケージのインポートパス、または両方が存在する場合は近くのインポートに合わせます。transition: { type: "spring", duration: 0.3, bounce: 0 } を使用します。バウンスは常に 0 です。
モーションライブラリがない場合は、両方のアイコンをDOMに保持し(一方は絶対配置)、cubic-bezier(0.2, 0, 0, 1) でクロスフェードします。これにより、依存関係なしでエンターとエグジットが実現できます。両方のレシピは icon-transitions.md にあります。
画像のアウトライン (Image outlines)
画像には、一貫した奥行き感を持たせるために、低不透明度の 1px アウトラインを設定します。ライトモードでは純粋な黒 (oklch(0 0 0 / 0.1))、ダークモードでは純粋な白 (oklch(1 0 0 / 0.1)) を使用します。スレートやジンクのような黒に近い色や、色味のあるニュートラルは決して使用しません。色味のあるアウトラインは下地の表面の色を拾い、画像の端に汚れがあるように見えます。
押下時のスケール (Scale on press)
クリック時の scale(0.96) は、ボタンに触覚的なフィードバックを与えます。常に 0.96 を使用します。0.95 未満だと大げさに感じられます。モーションが邪魔になる場所でこれをオフにする static プロパティを追加します。CSS、Tailwind、Motionのレシピ を参照してください。
ページロード時のアニメーションをスキップ (Skip animation on page load)
AnimatePresence で initial={false} を使用して、最初のレンダリングでエンターアニメーションが実行されないようにします。意図的なページエントランスアニメーションがそのまま残ることを確認してください。
テーマ切り替え時のトランジションを抑制 (Suppress transitions on theme switch)
テーマ切り替えでは、ほぼすべての要素の色、背景、境界線、影が一度に変更されます。これらのプロパティに対するすべてのトランジションが同時に発火し、切り替えがスナップされずにスミアのように見えます。*,*::before,*::after{transition:none !important} を注入し、リフローを強制し、次のフレームでそれを削除します。レシピ を参照してください。
変更されるものだけをトランジション (Transition only what changes)
常に正確なプロパティを指定します:transition-property: scale, opacity。Tailwindの transition-transform は transform, translate, scale, rotate をカバーします。
will-change は控えめに使用 (Use will-change sparingly)
GPUがコンポジットできる transform、opacity、filter にのみ使用します。will-change: all は絶対に使用しません。最初のフレームでスタッターが見られた場合にのみ追加し、事前には追加しません。performance.md を参照してください。
アイコンのストロークをテキストの太さに合わせる (Match icon stroke to text weight)
テキストの隣にあるアイコンは、そのテキストの光学的な重みを引き継ぎます:通常(400)のテキストの隣では 1.5px のストローク、セミボールド(600)の隣では 2px のストローク。アイコンセットごとに1つのストローク太さ、サーフェスごとに1つのアイコンライブラリを使用します。サイズ設定とRTL反転は icons.md にあります。
1つのSVGを状態ごとに色替え (One SVG, recolored per state)
アイコンは currentColor を使用し、ホバー、選択、無効状態を別々のアセットからではなく、CSSの色と不透明度から取得します。アウトラインがデフォルトのバリアントで、塗りつぶしがアクティブ状態を示します。
モーションの抑制 (Motion restraint)
高頻度のインタラクションには、即時フィードバック、または不透明度と色に対して150ms以下のトランジションを与えます。そこでカスタムアニメーションを使用すると、トリガーごとに注意コストがかかります。
アニメーションによる状態変更には、常に静的な手がかり(色、アイコン、ラベル)も必要です。モーションは決して唯一のフィードバックチャネルではありません。
終了前の確認 (Before you finish)
| 間違い | 修正方法 |
|---|---|
| アイコンが中央に見えない | パディングで光学的に微調整するか、SVG自体を修正する |
| 段階的なエントランスまたは終了がぎこちない | 頻度の低いエントランスはスサ (stagger) する。終了は控えめにする |
| テーマ切り替えでページ全体がクロスフェードする | 切り替え中はトランジションを無効にし、リフローを強制し、次のフレームで復元する |
要素に transition: all が指定されている | 正確なプロパティを指定する |
| 最初のフレームでアニメーションがスタッターする | will-change: transform を追加する(控えめに) |
| 太字テキストの隣に極細のアイコンがある | ストロークの太さをテキストの太さに合わせる |
報告 (Reporting)
重大度 (Severity). HIGH は、インタラクションを壊す、モーションを使用不能にする、またはアニメーションの実行中にしか状態変化が見えなくなるもの。MEDIUM は、サーフェス、アイコン、モーションに目に見える不整合があるもの。LOW は、局所的な磨き上げ(ポリッシュ)の問題。
検証 (Verification). ブラウザなしの場合:コンポーネントが定義するすべての状態(ホバー、フォーカス、アクティブ、ローディング、空)およびコードから読み取れるモーションのdurationとeasingを確認します。ブラウザありの場合:各状態を遷移させ、ブラウザのアニメーションパネルでモーションを10%の速度で再生します。実行できなかったチェックはすべて Not verified として報告します。
形式 (Format). 発見事項は、違反する原則ごとにグループ化し、重大度順に並べ、根本原因ごとに1行で、それが現れるすべての場所をリストアップします:
| 重大度 | 場所 | 修正前 | 修正後 | 理由 |
|---|
場所 は path/to/file:line です。理由 には原則とユーザーへの影響を記載します。
HIGH が残っている場合は Block で終了し、それ以外の場合は Approve で終了し、残りの項目はテーブルに作業として残します。検査していない範囲に対して Approve を決して行わないでください。報告する事項がない場合は、「No actionable UI-polish findings」と記載し、検証状況を報告します。