35
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

shadcn/ui → LiftKit 移行記 ── 「なぜか心地よいUI」の数学的正体を、コードで全部暴く

35
Last updated at Posted at 2026-02-19

unnamed-1.jpg

本記事は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-4rounded-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つに集約される:

  1. 数式: フォントサイズを基準に で padding・borderRadius・spacing が連動
  2. 光学補正: opticIconShiftopticalCorrection でサブピクセル精度の視覚調整
  3. マテリアル統合: glassmorphismなどが material prop 1行で黄金比スケールで適用

まだ "Extremely Early Access" のプロジェクトだが、その分日本語のQiita記事はほぼ皆無。次世代のUI設計思想にいち早く触れたい方は、ぜひ導入してみてほしい。

参考リンク


この記事を書いた人✏️@YushiYamamoto
株式会社プロドウガ CEO / AIアーキテクト
Next.js / TypeScript / n8nを活用した自律型アーキテクチャ設計を専門としています。
日々の自動化の検証結果や、ビジネス側の視点(ROI等)に関するより深い考察は、以下の公式サイトおよびnoteで発信しています。

35
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?