Caroa UI

アプリ別テーマ

角丸・シャドウ・フォントをアプリごとにカスタマイズする公式手順

概要

色(--caroa-*)はCSS変数として最初から上書き可能でしたが、角丸・シャドウ・フォントはコンポーネントにrounded-md / shadow-sm / font-monoのようなTailwindクラスが直書きされており、--caroa-*を上書きしても反映されませんでした。

Tailwind CSS v4の@theme inlineを使ってPresetの変数間接化を導入しています。Tailwindがユーティリティ生成に使う--radius-* / --shadow-* / --font-*が、caroa独自の--caroa-radius-* / --caroa-shadow-* / --caroa-font-*を間接参照するようになっているため、アプリ側は--caroa-*を上書きするだけでrounded-* / shadow-* / font-*ユーティリティ全体の見た目を変更できます。

デフォルト値はTailwind v4の組み込みデフォルトと数値上一致させてあるため、何も上書きしないアプリの見た目は一切変化しません(非破壊)。


テーマの作り方

アプリ側でtheme.cssを1枚作り、:root--caroa-*を上書きします。

/* theme.css(アプリ側で新規作成) */
:root {
  /* ブランドカラー */
  --caroa-primary: 210 90% 50%;
 
  /* 角丸: --caroa-radius だけ上書きすればsm/md/lgが連動して変わる */
  --caroa-radius: 0.25rem;
 
  /* フォント: next/fontで読み込んだCSS変数を渡す */
  --caroa-font-sans: var(--font-geist-sans);
  --caroa-font-mono: var(--font-geist-mono);
}
/* globals.css */
@import "tailwindcss";
@import "@caroainc/ui-components/tokens";
@import "@caroainc/ui-components/animations";
@import "./theme.css"; /* tokensの後にimportする(後勝ちで上書きされる) */

--caroa-radiusを上書きすると--caroa-radius-sm / -md / -lgcalc()で連動して変わります(shadcn方式)。個別に段階を変えたい場合だけ--caroa-radius-sm等をピンポイントで上書きしてください。


ライブプレビュー

同じButton / Card / Badgeを、左は素のまま、右は--caroa-radiusをローカルに上書きして並べています(このページ内だけの局所上書き。CSS変数はDOMツリーを継承するため、上書きしたdivの内側にだけ効きます)。

Loading...

角丸だけをライブプレビューにしているのは意図的です。理由は次のセクションを参照してください。


シャドウ・フォントの上書き(このドキュメントサイトではライブプレビューできません)

上の角丸プレビューは、このドキュメントサイト自身のtailwind.config.tsborderRadius.lg/md/sm--caroa-radiusからcalc()で計算するよう明示的に設定しているため、実際に画面上で変化します。

一方、シャドウ(--caroa-shadow-*)とフォント(--caroa-font-*)は、コンポーネントライブラリ側のPreset(src/tailwind-preset.ts、Tailwind CSS v4の@theme inline経由)ではshadow-* / font-*ユーティリティが変数を間接参照するようになっていますが、このドキュメントサイト自体はTailwind CSS v3で構築されており@theme inlineが使えず、サイト側のtailwind.config.tsにもboxShadow / fontFamily--caroa-*変数マッピングを個別に足していません。そのため、ここで--caroa-shadow-sm--caroa-font-monoを上書きしてもshadow-sm / font-monoクラスの見た目は変わりません(旧バージョンのこのページには変化しないライブプレビューが置かれていました。誤解を招くため削除しました)。

Tailwind CSS v4 + @caroainc/ui-components/tokensを使う実際のアプリでは、--caroa-radiusと同じ要領でそのまま反映されます。

/* theme.css(Tailwind v4アプリ側) */
:root {
  --caroa-shadow-sm: 0 0 0 1px hsl(var(--caroa-border));
  --caroa-font-mono: 'Courier New', monospace;
}
<Card className="shadow-sm">...</Card>
<p className="font-mono">...</p>

変数化されている範囲

カテゴリ変数デフォルト値上書きで効くクラス
色(セマンティック)--caroa-primary--caroa-*全般カラーシステム参照bg-primary等の全セマンティックカラークラス
角丸--caroa-radius / --caroa-radius-sm / -md / -lg0.5rem / 0.25rem / 0.375rem / 0.5remrounded-sm / rounded-md / rounded-lgrounded-l-md等の方向指定も含む)
角丸(フル)--caroa-radius-full9999px効かない。下記「変数化しない範囲」を参照
シャドウ--caroa-shadow-xs / -sm / -md / -lgTailwind v4組み込みデフォルトと同一shadow-xs / shadow-sm / shadow-md / shadow-lg
フォント--caroa-font-sans / --caroa-font-monoTailwind v4組み込みデフォルトのシステムフォントスタックfont-sans / font-mono、および無指定時の既定フォント

変数化しない範囲(意図的に対象外)

対象理由
rounded-fullTailwind v4がborder-radius: calc(infinity * 1px)にハードコードしており、CSS変数の上書きでは反映されません。アバターやピル形状など「完全な円」を保つべき要素はブランドが変わっても円のままにするのが望ましいため、意図的にテーマ対象から外しています。--caroa-radius-fullトークン自体は用意していますが、これはカスタムCSSやインラインスタイル用の参照値であり、rounded-fullユーティリティクラスは動かせません
rounded-xl / rounded-2xl(一部コンポーネントの装飾的な角丸)Lockedルール(角丸はrounded-lg以下)の例外として個別採用されている値。テーマ変数の対象外で、Tailwind組み込みの固定値のままです
余白(padding/gap/spacing)Baseの統一感を保つための構造的なルール。プロジェクトごとに変えるとコンポーネント間の整合が崩れるため対象外です
タイポグラフィ階層(type scale・weight system)4 Pillars(静寂性・優しさ・普遍性・ワクワク)を支える構造であり、Locked。フォント「ファミリー」だけがCustomizableです
レイアウト構造(コンポーネントの合成パターン、DOM構造)UIライブラリとしての一貫性の根幹。個別上書きにはコンポーネントの再実装が必要です

詳細な思想・Locked/Customizable区分はdesign-system/CUSTOMIZATION.mdを参照してください。


変数で届かない微調整の逃げ道

上記の変数で表現しきれない、コンポーネント単位のピンポイントな上書きが必要な場合は、data-caroa-ui / data-slotによるセレクタ上書きを使います(詳細は使い方ガイドの「data属性によるコンポーネント識別」を参照)。

/* 例: Select のトリガーだけ角丸を変える */
[data-caroa-ui][data-slot='select-trigger'] {
  border-radius: 9999px;
}

CSS変数のグローバル上書きより詳細度が高く、個別コンポーネントだけに効くため、Base全体のトーンを崩さずに例外対応できます。ただし多用するとBaseとの乖離が大きくなるため、まずは--caroa-*変数での対応を優先し、個別セレクタは最後の手段としてください。


アプリ側の運用

  1. プロジェクトのDESIGN.mdに、theme.cssで上書きしている変数と理由を明記する(「上書きしない = Baseに従う」の原則どおり、書かれていない項目はBase準拠とみなされます)
  2. rounded-xl等、Lockedルールから意図的に逸脱する装飾パターンを使う場合は、アプリ側のdesign-check(lint)の例外リストに追記する(caroaship・thecast-portalの確立パターンに倣う)
  3. Baseの更新(pnpm update @caroainc/ui-components)後は、theme.cssの上書きが新しいデフォルト値と矛盾していないか確認する

useAutoThemeフックとの関係

ランタイムでテーマを動的に切り替えたい場合はカラーシステムにあるuseAutoThemeフックも使えます。borderRadius引数は内部で--caroa-radiusを書き換えるため、本ページの--caroa-radius-sm / -md / -lgの連動計算とそのまま組み合わせられます(互換性あり・非破壊)。