import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle, StatusBadge, } from "@godxjp/ui/data-display"; import { Text } from "@godxjp/ui/general"; import { Flex, PageContainer } from "@godxjp/ui/layout"; import { Star } from "lucide-react"; /** * Badge — status / category chip. Composed only from real @godxjp/ui components. * variant is STRUCTURAL (default / secondary / outline); tone is SEMANTIC (ToneProp). * the reference design: semantic mapping is FIXED (success 若竹 / warning 山吹 / info 群青 / * destructive 茜). Labels never wrap (rule #35). */ const variants = [ { variant: "default" as const, label: "既定 Default" }, { variant: "secondary" as const, label: "区分 Category" }, { variant: "outline" as const, label: "補助 Subtle" }, { variant: "dashed" as const, label: "点線 Dashed" }, ]; const shapes = [ { shape: "default" as const, label: "既定 Default" }, { shape: "pill" as const, label: "丸み Pill" }, { shape: "sharp" as const, label: "角 Sharp" }, ]; /** * Colours a person picked, not colours that mean something — a status and an * issue type out of a project's own settings screen, plus the two ends of the * range an administrator can still type. `#488a5a` is here because it is the * worst case for a SOLID chip: near-black and white measure equal on it, both * 4.15:1, under the 4.5 badge-sized text needs. Washed, it clears 8.52. */ const dataColours = [ { color: "#4488c5", label: "処理中 In progress" }, { color: "#ea9a10", label: "未対応 Open" }, { color: "#488a5a", label: "完了 Done" }, { color: "#000000", label: "黒 Black" }, { color: "#ffffff", label: "白 White" }, ]; const tones = [ { tone: "default" as const, label: "既定 Default" }, { tone: "primary" as const, label: "ブランド Brand" }, { tone: "success" as const, label: "承認済 Approved" }, { tone: "warning" as const, label: "保留 Pending" }, { tone: "destructive" as const, label: "却下 Rejected" }, { tone: "info" as const, label: "情報 Info" }, { tone: "muted" as const, label: "控えめ Muted" }, { tone: "neutral" as const, label: "中立 Neutral" }, ]; const statuses = ["active", "draft", "pending", "cancelled", "failed", "scheduled"]; /** * Subscription/billing lifecycle keys. They ship the SAME canonical status→tone→icon * map as every other key, so a billing screen never keeps a page-local colour table. They are * demoed explicitly because their i18n labels were once missing. A missing key renders the raw * `status.trialing` string, and only a rendered demo makes that regression visible. */ const billingStatuses = ["trialing", "past_due", "incomplete", "canceled"]; /** * StatusBadge is the status-first name for the same chip, so it carries the SAME structural * axes. `incomplete` is the one canonical key that resolves to the tone-neutral default, which * is why the four variants stay legible next to each other: a tone would repaint the fill. */ const statusBadgeVariants = [ { variant: "default" as const, note: "既定 Default" }, { variant: "secondary" as const, note: "控えめ Secondary" }, { variant: "outline" as const, note: "枠線 Outline" }, { variant: "dashed" as const, note: "点線 Dashed" }, ]; /** A project status outside the canonical map declares its own tone; the label stays authored. */ const statusBadgeTones = [ { tone: "default" as const, label: "受付 Received" }, { tone: "primary" as const, label: "優先対応 Priority" }, { tone: "success" as const, label: "検収済 Accepted" }, { tone: "warning" as const, label: "修正待ち Awaiting fix" }, { tone: "destructive" as const, label: "検収差戻し Rejected" }, { tone: "info" as const, label: "監査中 Under audit" }, { tone: "muted" as const, label: "対象外 Out of scope" }, { tone: "neutral" as const, label: "下書き Draft" }, ]; export default function Demo() { return ( Structural variants variant is structural emphasis only: default (filled), secondary (muted fill), outline (bordered), dashed (dashed border). It carries no semantic meaning; use tone for that. {variants.map((v) => ( {v.label} ))} Shape shape sets corner radius from the tokens: default (badge radius) / pill (fully rounded) / sharp (square). Independent of variant and tone. {shapes.map((s) => ( {s.label} ))} The record’s own colour color carries DATA: a status an administrator coloured, an issue type, a tag. The chip is washed into the surface rather than filled, because a solid chip has to choose a foreground and no choice is readable for every colour a picker can produce. Retune the wash with --badge-tint-fill / --badge-tint-edge. {dataColours.map((c) => ( {c.label} ))} Semantic tones tone conveys intent, pick by meaning, never aesthetics. success = approved/paid · warning = pending · destructive = rejected · info · muted · neutral = category. {tones.map((t) => ( {t.label} ))} Lifecycle status status="key" auto-maps known lifecycle keys to tone + icon + i18n label. Unknown keys fall back to neutral + a generic icon + the raw key text. {statuses.map((s) => ( ))} Billing lifecycle Subscription keys map through the SAME canonical table · trialing = info · past_due = warning · incomplete = default · canceled = destructive. Every label is translated, so a billing screen never shows a raw status.* key and never keeps its own colour map. {billingStatuses.map((s) => ( ))} {billingStatuses.map((s) => ( ))} StatusBadge · 構造 / 形状 / トーン StatusBadge はステータス主導の別名で、Badge と同じ variant / shape / tone を受け取ります。canonical マップに無い自社ステータスは tone を明示し、色表をアプリ側に持たせません。 variant · status="incomplete"(トーン中立のキー) {statusBadgeVariants.map((v) => ( ))} shape · 既定 / pill / sharp {shapes.map((s) => ( ))} tone · 自社ステータスに意味の色を与える {statusBadgeTones.map((t) => ( {t.label} ))} tabular · 数字を桁で揃える 件数のチップが縦に並ぶときは tabular を。プロポーショナル数字だと 1 が 0 より狭いので、列で見たときに桁がずれてチップ幅も揺れます。既定はオフ。語を載せる チップまで幅の広い数字を払う必要はありません。 既定(比例数字) 11 100 1811 tabular 11 100 1811 Icon overrides icon replaces the status-mapped icon; icon={null} suppresses it entirely while keeping the resolved tone and label. ); }