@bluelamp/brand-kit
BlueLamp テナント branding とカード部品の器。API 型 / 判定の純関数 / React 部品 / CSS を 1 か所から配る。
フロント(YASHIRO yamatovision の admin・portal)とバックエンド(mikoto-monorepo/backend)は
別リポにある。型と見た目の定義を各自で持てば必ずズレるので、本パッケージから配って各自で持たない。
- 仕様の正本: YASHIRO リポ
docs/requirements-yashiro-brand-E.md(型は §6 が正本) - 🚨 原則④「BFF は加工しない=判断は BlueLamp 側に置いたまま」。 本パッケージは判断を配る器であって、受け手が独自の判断を足す場所ではない。
入っているもの
| 面 | 出どころ | 備考 |
|---|---|---|
型 TenantBranding / PublicBranding / TenantCategory | 要件書 §6(正本) | 実装から逆算して増やさない |
sanitize sanitizeBranding ほか | backend/src/bluelamp/lib/brandingSanitize.ts から移設 | ⛔ backend 側に re-export は残していない |
CSS トークン resolveTenantTheme / buildThemeCssVariables ほか | apps/bluelamp-vscode/src/ui/theme/tenantTheme.ts の値が正 | 拡張側の差し替えは本便外 |
カード部品(/cards サブパス) | apps/bluelamp-admin/src/components/promptCards/ から移設(板 #556 便 H1) | 見た目の正本は vscode 側・下記 |
使い方
import {
sanitizeBranding,
resolveTenantTheme,
buildThemeCssVariables,
TENANT_THEME_CSS_PROPERTIES,
type TenantBranding,
} from '@bluelamp/brand-kit';
// 配る側(BlueLamp backend): 公開前に 1 回だけ通す
const publicBranding = sanitizeBranding(tenant.branding);
// 受け手(admin のプレビュー / portal / VS Code webview): そのまま CSS 変数へ
const vars = buildThemeCssVariables(resolveTenantTheme(publicBranding));
for (const key of TENANT_THEME_CSS_PROPERTIES) {
const value = vars[key];
// 今回設定されなかったものは消す(テナント A → B で A の色が残らないようにするため)
if (value) el.style.setProperty(key, value);
else el.style.removeProperty(key);
}
カード部品(@bluelamp/brand-kit/cards)
会員が VSCode で見ているカードカタログそのもの(判定・見た目・D&D)。BlueLamp admin と YASHIRO admin の両方がここから import する(=UI を 1 回だけ書く)。
import {
PromptCardTabs, PromptCardView, useCardDrag,
buildTabs, toCatalogCards, planPlacement, buildPlacementBody,
injectPromptCardStyles, injectSlotsEditorStyles,
type CatalogCard, type PlaceableAssetsResponse,
} from '@bluelamp/brand-kit/cards';
// 見た目の注入は面が 1 回呼ぶ(この順序で)。二重注入は id で弾かれる。
injectPromptCardStyles(); // 会員側の design-system.css + promptCards.css(+器の下に閉じた reset)
injectSlotsEditorStyles(); // 編集モードで足す分
🚨 見た目の単一源はここではない
origin: 'vscode' の CSS の正本は apps/bluelamp-vscode/media/**(会員が実際に見ている面)で、
本パッケージは配布路である。直すときは:
# ① 出典(vscode 側)を直す ② 焼き直す ③ 差分を commit
pnpm --filter "@bluelamp/brand-kit" run bake:css
⛔ src/cards/generated/vscodeCss.ts を手で直さない(単一源が 2 本になる)。
焼き直し忘れと手編集は tests/cards/cssSingleSource.test.ts が捕まえる。
⚠️ この焼き込みは出典が vscode 側に在る間の経過措置であり、恒久の設計ではない(CTO32 承認条件 ②)。
例外は src/cards/slotsEditor.css(編集モードで足す分)。会員側に対応物が無いのでここが正本。
🚨 root からは出していない
カード部品は react を requires するため、入口は /cards サブパスに閉じてある。
root(@bluelamp/brand-kit)は react 非依存のままである——react を持たない消費者
(mikoto-monorepo/backend)が root を require したときに実行時 MODULE_NOT_FOUND にしないため。
2 つの色の正規化を取り違えないこと
| 関数 | 何のため | #abc の扱い | 落ちたとき |
|---|---|---|---|
sanitizeBrandingColors | 保存値を公開して安全にする | #abc のまま(形を変えない) | そのキーだけ undefined |
normalizeHexColor | CSS へ入れる値にする | #aabbcc へ展開 | null(=CSS 変数を設定しない=既定色) |
前者は「保存された生値の器」、後者は「CSS 文脈へ持ち込む値」。片方に寄せると、 編集画面が表示する値と保存値がズレるか、CSS に短縮形が混ざる。両方いる。
落とし方の原則
不正な 1 トークンだけ落として既定へフォールバックし、他のトークンは活かす。 「1 つ不正だから branding 全体を捨てる」は、利用者から見て原因が分からない壊れ方になる。
開発
npm install
npm run tsc # 型検査
npm test # unit(純関数のみ・モック不要)+ CSS 単一源の対照
npm run bake:css # 出典 CSS の焼き直し(出典を直した後に回す)
npm run build # dist/
⚠️ build は tsc だけである。出典 CSS を読みに行かせないこと——本番 backend イメージの
ビルド文脈には .gcloudignore により apps/ が入っておらず、build が出典を読むと
本番イメージのビルドが落ちる(実測 2026-09-02)。焼き込みは bake:css の手番である。
⛔ npm publish は CTO が握る(.github/workflows/publish-npm.yml の承認ゲート経由・不可逆チャネル)。