本記事はLiftKit入門編の続編です。基本インストールは前回の記事を参照してください。
この記事を書いた動機
先週、同僚のデザイナーにNext.jsアプリのデモを見せたら、こう言われた。
「このUI、なんか違う感じがするんだけど、何使ってるの?」
答えは「LiftKit」。
変更したのはボタン・カード・テキストフィールドだけ。コード的には5行も書いてない。なのにデザイナーが気づいた。
その「違い」の正体は 黄金比 だった。今回はその仕組みをコードレベルで全部解剖する。
shadcn/uiのボタンに「何かが足りない」理由
まずは普通のshadcn/uiボタン(アイコン付き)のコードから。
// shadcn/ui
import { Button } from "@/components/ui/button"
<Button>
<span className="mr-2">🚀</span>
Launch
</Button>
見た目は普通。でも、アイコンを左右に入れると妙に左が重く見える。
なぜか?答えは「アイコンの視覚的重心」にある。アイコンは正方形の枠に収まっているが、中の絵柄は左寄りが多い。数値的に等しいpaddingでも、人間の目には不均等に映ってしまうのだ。
LiftKitが解決する仕組み
LiftKitの Button には opticIconShift という prop がある。
// LiftKit
import { Button } from "@/components/liftkit/button"
<Button
label="Launch"
startIcon="rocket_launch"
opticIconShift={true} // デフォルトでtrue
variant="fill"
size="md"
/>
opticIconShift は、アイコンをサブピクセル単位でわずかに上方にシフトさせる。わずか0.5〜1px程度の差だが、人間の目はこれを「バランスが取れている」と認識する。これは 光学補正(Optical Correction) と呼ばれるタイポグラフィの技法をコンポーネントに自動適用したものだ。
黄金比 がpaddingになる数学
LiftKitの核心は 「1つの scaleFactor からすべての値が決まる」 という設計思想にある。
フォントサイズを基準値 とすると、LiftKitのpadding計算はこうなる:
例えばbody text(16px)の場合:
borderRadiusも同じ比率から導出される:
つまりLiftKitのUIは、フォントサイズ1つから、間隔・角丸・余白がすべて数学的に連動している。Tailwindの p-4 や rounded-md のような固定値ではなく、フォントを変えるとすべての比率が完璧に追従するのだ。
Cardの opticalCorrection を使う
CSSの line-height は上下に余白を生む。padding: 24px のカードでも、最上部のテキストに line-height があると実際の余白は24px以上に見えてしまう。
// shadcn風(上の余白が大きく見える)
<div className="p-6 rounded-lg border">
<h2 className="text-xl font-bold">カードタイトル</h2>
<p>カードの内容...</p>
</div>
LiftKitの Card はこれを opticalCorrection で自動補正する。
// LiftKit
import { Card } from "@/components/liftkit/card"
<Card
scaleFactor="body" // 最大フォントサイズを指定
variant="outline"
material="glass" // glassmorphism対応
isClickable
onClick={() => console.log("clicked")}
>
<h2>カードタイトル</h2>
<p>カードの内容...</p>
</Card>
scaleFactor="body" を指定することで、内部のテキストの line-height を計算に組み込み、視覚的な上下余白を均等にする。数値的な対称より「目に感じる対称」を優先するこの設計が、「なんかいい感じ」の正体だ。
マテリアルシステム ── glassmorphismを1行で
LiftKitは material プロパティでデザイントレンドの質感を適用できる。
// Glassmorphism
<Card material="glass" materialThickness="default">
...
</Card>
// フラットデザイン
<Card material="flat">
...
</Card>
自分で backdrop-filter: blur() と background: rgba() を書く必要がない。material="glass" の1行で、blur・透過・ボーダーが黄金比スケールで適用される。
material プロパティは Button・Card・Navbar など、LiftKitの大半のコンポーネントに共通で使える。
Dynamic Color ── 「テーマ変更」が革命的に楽
shadcn/uiのダークモード対応をこう書いた経験はないだろうか。
/* 従来の方法 */
:root {
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
}
.dark {
--primary: 217.2 32.6% 17.5%;
/* ... 30行続く */
}
LiftKitは ThemeController コンポーネントを開発中の任意のファイルにポン置きするだけで済む。
// app/dev/page.tsx(開発用ページ)
import { ThemeController } from "@/components/liftkit/theme-controller"
export default function DevPage() {
return (
<>
<ThemeController />
{/* 他のコンポーネントをここに置いてリアルタイムプレビュー */}
</>
)
}
これだけでブラウザ上にコントロールパネルが出現し、色の変更がリアルタイムで全コンポーネントに反映される。確定したら ThemeController を外して本番ファイルに反映するだけだ。
Buttonの variant × size × color マトリクス
Buttonは3軸のpropを組み合わせてUIを構成する。
| variant | size | color | 用途 |
|---|---|---|---|
"fill" |
"lg" |
"primary" |
メインCTA |
"outline" |
"md" |
"secondary" |
サブアクション |
"text" |
"sm" |
"error" |
削除・キャンセル |
// CTAボタン
<Button
label="はじめる"
variant="fill"
size="lg"
color="primary"
startIcon="rocket_launch"
/>
// 削除ボタン
<Button
label="削除"
variant="text"
size="sm"
color="error"
startIcon="delete"
/>
実際に使ってみて分かった3つのハマりポイント
1. @import の順序に注意
/* globals.css */
/* ❌ これだと上書きされることがある */
@tailwind base;
@import url('@/lib/css/index.css');
/* ✅ LiftKitのimportを後ろに */
@tailwind base;
@tailwind components;
@tailwind utilities;
@import url('@/lib/css/index.css');
2. React 19で --force が必要
shadcn依存の都合でpeer dependency警告が出るが、機能には影響しない。
# React 19環境での正しいインストール手順
npm run add button
# → 警告が出たら "Use --force" を選択
3. scaleFactor は「内部の最大フォントサイズ」を指定する
// ❌ 間違い:コンテナの想定サイズを指定している
<Card scaleFactor="sm">
<h2>見出し(lg相当)</h2> // ← これが最大なのに
</Card>
// ✅ 正解:内部で一番大きいテキストのサイズを指定する
<Card scaleFactor="lg">
<h2>見出し(lg相当)</h2>
</Card>
shadcn/ui vs LiftKit ── 使い分け判断表
| 観点 | shadcn/ui | LiftKit |
|---|---|---|
| 成熟度 | ✅ プロダクション実績多数 | ⚠️ Extremely Early Access |
| カスタマイズ | ✅ 自由度高い | ✅ CSS変数で柔軟 |
| スペーシング哲学 | 固定値(p-4等) | 黄金比・相対値 |
| アイコン補正 | ❌ 手動 | ✅ opticIconShift で自動 |
| マテリアル | ❌ 別途実装 | ✅ material prop 一発 |
| テーマ変更UX | 設定ファイル編集 |
ThemeController でリアルタイム |
| 日本語情報 | ✅ 豊富 | ⚠️ 少ない(今のうちに書くチャンス!) |
結論: プロダクションのコアUI(複雑なフォームなど)はshadcn/uiを使用し、ビジュアルのポリッシュ(美しさ)が求められるカードやボタン周りにLiftKitを上乗せするハイブリッド運用が現実的だ。
まとめ
LiftKit が生む「なぜか心地よいUI」の正体はこの3つに集約される:
- 数式: フォントサイズを基準に で padding・borderRadius・spacing が連動
-
光学補正:
opticIconShift・opticalCorrectionでサブピクセル精度の視覚調整 -
マテリアル統合: glassmorphismなどが
materialprop 1行で黄金比スケールで適用
まだ "Extremely Early Access" のプロジェクトだが、その分日本語のQiita記事はほぼ皆無。次世代のUI設計思想にいち早く触れたい方は、ぜひ導入してみてほしい。
参考リンク
この記事を書いた人✏️@YushiYamamoto
株式会社プロドウガ CEO / AIアーキテクト
Next.js / TypeScript / n8nを活用した自律型アーキテクチャ設計を専門としています。
日々の自動化の検証結果や、ビジネス側の視点(ROI等)に関するより深い考察は、以下の公式サイトおよびnoteで発信しています。
