イベントのデザイン面は4つある(サイト CSS 変数 / ビジュアル EventBrand / セッションカード CardTheme / メール Brand)。Worldview はその上流の単一真実源 — 1ファイル書き換えれば4面が変わる。 feature flag は不要(全ページ共通の基盤層)。
カバー範囲を先に正しく: worldview が決めるのはトークン(色・書体・角丸・質感・透かし)。 ページの構成・レイアウト・モーションはトークンでは決まらない — そこは参照実装(starter の page/component)とイベント側コードの責務。「世界観の指示 = トークン4面が一気」であり、 「任意の凝った LP 表現が自動生成される」ではない。
1. 世界観を1ファイルで宣言する
apps/<app>/src/worldview.ts:
import { defineWorldview } from "@event/design";
export const worldview = defineWorldview({
name: "<app>",
palette: {
// hex 推奨(#rgb / #rrggbb)— alpha 派生・混色・明度判定が効く
bg: "#f1f3ef", // ページ地色。暗色を置けば「ダークな世界」全体が導出される
surface: "#fafbf9", // カード / パネル面
ink: "#1a211d", // 文字色(bg に対して十分なコントラスト)
accent: "#0b6ea8", // 主張色(1点豪華)
accentAlt: "#e0653c",
},
typography: {
display: '"Shippori Mincho B1", serif', // 見出し — 世界観の半分は書体で決まる
body: '"Noto Sans JP", sans-serif',
mono: 'ui-monospace, monospace',
},
mood: {
radius: "soft", // sharp | soft | round
visualsScheme: "dark", // カード/書き出し画像の舞台(dark 既定 = 紙/SNS の定石)
watermarkGlyph: "S", // ビジュアルの巨大 italic 透かし(空で無し)
// lockScheme: true, // OS ダーク追従を止めてライト固定
},
// logoUrl: "/logo.svg", // メール header / ビジュアル横組ロゴ
});
世界観ブリーフ → パレットの翻訳ガイド: 「シネマティックで静かな高級感」→ bg を暗色 (#0c0e12 系)・ink を生成り (#f2f0ea 系)・accent は彩度低めの1色・display をセリフ体に。 「祭りのような熱」→ bg 明色・accent 高彩度・radius "round"・watermark に象徴グリフ。 bg に暗色を置くと isDark 判定で dark:null(常にその世界)・舞台・plate 色まで自動で反転する。
2. 面1: サイト本体(design.ts を派生に切り替え)
apps/<app>/src/design.ts:
import { defineDesign, worldviewToDesignInput, type EventDesign } from "@event/design";
import { worldview } from "@/worldview";
export const eventDesign: EventDesign = defineDesign(worldviewToDesignInput(worldview));
(layout.tsx の <style>{tokensToCssVars(eventDesign)}</style> 注入は add-design 済み前提。 未了なら add-design の手順3を先に。)派生則: ink2/ink3/line/accentSoft は ink・accent の alpha から、--radius-* は mood.radius から、dark は明色世界なら accent を 25% 明るく/ 暗色世界なら null(すり替えない)。
3. 面2: ビジュアル(EventBrand を派生に切り替え)
apps/<app>/src/lib/visuals.ts の visualsBrand を差し替え:
import { resolveBrand, type EventBrand } from "@event/visuals";
import { worldviewToBrand } from "@event/design";
import { worldview } from "@/worldview";
export const visualsBrand: EventBrand = resolveBrand(worldviewToBrand(worldview));
visualsScheme="dark" のとき、明色世界でも舞台は mix(ink, #000, 0.55) のダークに置かれ、 文字は surface(明色)が使われる。fgDim/fgFaint/hair は fg の alpha 派生。
4. 面3: セッションカード(CardTheme 登録)
apps/<app>/src/lib/cardThemes.ts:
import type { CardTheme } from "@event/session-card";
import { worldviewToCardTheme } from "@event/design";
import { worldview } from "@/worldview";
export const CARD_THEMES: Record<string, CardTheme> = {
[worldview.name]: worldviewToCardTheme(worldview),
};
カードスタジオ(SessionCardDesigner)に themes={CARD_THEMES} を渡す (starter は app/admin/card/[id]/CardStudio.tsx)。テーマ切替 UI に世界観テーマが現れ、 選ぶと --card-bg / --card-fg / --card-accent / plate / 書体が世界観になる。 未指定の変数は resolveCardTheme が既定でマージ(部分定義で安全)。
5. 面4: メール(Brand を派生に・env 優先)
apps/<app>/src/lib/mailer.ts の brand を差し替え:
import { worldviewToMailBrand } from "@event/design";
import { worldview } from "@/worldview";
const wvBrand = worldviewToMailBrand(worldview);
const brand: Brand = {
...wvBrand,
name: process.env.MAIL_BRAND_NAME ?? wvBrand.name,
color: process.env.MAIL_BRAND_COLOR ?? wvBrand.color,
};
設計判断: 暗色世界ではメール shell はライト既定に委ね accent / ロゴだけ持ち込む (メールクライアントの到達性・可読性を世界観の完全再現より優先)。明色世界なら ink/background/surface もパレットから写す。
6. 検証
yarn workspace <app> typecheck # 派生出力と各面の実型の構造互換が証明される
yarn workspace <app> dev
worldview.ts の accent を1色変えて確認:
- サイト全ページのアクセント(リンク/ボタン/nav)が変わる
/admin/visualsの告知ビジュアルのアクセント・透かしが変わる/admin/card/<id>でテーマに「<app>」が現れ、選ぶとカードが世界観になる/admin/mailのプレビューの eyebrow / 見出し色が変わる
完了条件: 1ファイルの変更が4面すべてに反映される。