Back to index

Options Tradeoff

Three Column
Generates documents
Options Tradeoff

Seen as a design

Take it with you

Copy and paste, or save a file and hand it over. For a machine with no network, one “Everything” file is enough.

Everything

options-tradeoff-everything.md

Everything in this panel, including the contents behind each URL, in one Markdown file

Prompt

1.4 KB
Build an HTML document with "Options Tradeoff". This layout runs, so there is no composition to rebuild — what it needs is content.

## What this layout does
Options Tradeoff with explicit source values and reading conditions.

## What you have to supply
- Provide the source values, labels and decision conditions described by the schema.

## The shape of the input
- `title` — string — 結論を伝える見出し
- `subtitle` — string? — 出典・時点・母集団や仮定
- `options` — array (id, label, benefit, cost, condition) — 候補ID、利点、代償、成立条件
- `recommendedId` — string — 検討上の推奨候補ID
- `rationale` — string — 推奨する理由と判断条件

schema.ts holds the machine-checkable validation. Ask rather than guess when an input is missing.

## When not to use it
- Do not compare values with incompatible units, populations, or assumptions.

## How to build it
Put the input in a JSON file and run:

```bash
node scripts/render.mjs options-tradeoff <input.json> --theme ink -o out.html
```

The output is one self-contained file and fetches nothing at render time.

## The source
- https://design-vault.pages.dev/designs/options-tradeoff/source/template.tsx
- https://design-vault.pages.dev/designs/options-tradeoff/source/schema.ts
- https://design-vault.pages.dev/designs/options-tradeoff/source/sample-data.json
- https://design-vault.pages.dev/designs/options-tradeoff/sample.html

Files

Template, input schema, sample data and metadata. This document was generated from those four. The generated document

template.tsx

6.6 KB

/designs/options-tradeoff/source/template.tsx

Show contents
import type { ReactNode } from "react";
import type { Props } from "./schema";
function Notes({children}:{children:ReactNode}) { return <details><summary>データ・判断条件を確認</summary>{children}</details>; }

const CSS = String.raw`
.asset{font-family:var(--font-sans);color:var(--color-primary);background:var(--color-surface);max-width:var(--container-slide);margin:auto;padding:3rem 2rem;line-height:1.6;font-size:var(--text-body-sm)}
.asset *{box-sizing:border-box}.asset header{margin-bottom:2rem}.asset .eyebrow{font-family:var(--font-mono);font-size:var(--text-label);letter-spacing:.08em;color:var(--color-secondary);margin-bottom:.75rem}.asset h1{font-size:var(--text-h1);font-weight:700;line-height:1.35;letter-spacing:-.025em;max-width:32em}.asset h2{font-size:var(--text-h3);font-weight:600;margin-bottom:.75rem}.asset h3{font-size:var(--text-body-md);font-weight:600}.asset .subtitle,.asset .muted{color:var(--color-secondary)}.asset .subtitle{margin-top:.75rem}.asset .number{font-variant-numeric:tabular-nums;font-family:var(--font-mono)}.asset .hero-number{font-size:var(--text-display);line-height:1.2;font-weight:700;letter-spacing:-.04em}.asset .callout{border-left:.25rem solid var(--color-tertiary);padding:1rem 1.5rem;background:var(--color-surface-subtle);margin-bottom:1.5rem}.asset .grid2{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem}.asset .grid3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1.5rem}.asset .panel{padding:1.5rem;background:var(--color-surface-subtle);border-radius:var(--radius-md);min-width:0}.asset .rule{border-top:1px solid var(--color-outline);padding-top:1rem;margin-top:1rem}.asset .pill{display:inline-block;padding:.25rem .75rem;border:1px solid var(--color-outline);border-radius:var(--radius-full);font-size:var(--text-label)}.asset .focus{background:var(--color-tertiary-container);color:var(--color-on-tertiary-container)}.asset .dark{background:var(--color-primary);color:var(--color-on-primary)}.asset .dark .muted{color:var(--color-on-primary-container)}.asset .row{display:grid;grid-template-columns:12rem minmax(0,1fr) 6rem;gap:1rem;align-items:center;padding:.75rem 0;border-bottom:1px solid var(--color-outline)}.asset .row>*{min-width:0}.asset .track{height:1rem;position:relative;background:var(--color-neutral);border-radius:var(--radius-sm)}.asset .bar{height:100%;background:var(--color-tertiary);border-radius:var(--radius-sm)}.asset .track.tall{height:2rem}.asset .flex{display:flex;gap:1rem;align-items:center;justify-content:space-between;flex-wrap:wrap}.asset .stack>*+*{margin-top:1rem}.asset ul{list-style:disc;padding-left:1.25rem}.asset li+li{margin-top:.5rem}.asset .plot{display:block;width:100%;height:auto;overflow:visible}.asset .plot text{font-family:var(--font-sans);font-size:14px;fill:var(--color-secondary)}.asset .plot .ink{fill:var(--color-primary)}.asset .plot .axis{stroke:var(--color-outline);stroke-width:1}.asset .plot .accent{stroke:var(--color-tertiary);fill:none;stroke-width:3}.asset .legend{display:flex;gap:1.5rem;flex-wrap:wrap;font-size:var(--text-label);margin-bottom:1rem}.asset .legend b{display:inline-block;width:.75rem;height:.75rem;margin-right:.5rem}.asset .ledger{border-collapse:collapse;width:100%;font-variant-numeric:tabular-nums}.asset .ledger caption{text-align:left;font-weight:600;margin:1rem 0}.asset .ledger th{font-weight:600;color:var(--color-secondary);text-align:left}.asset .ledger th,.asset .ledger td{padding:.75rem 1rem;border-bottom:1px solid var(--color-outline);vertical-align:top;overflow-wrap:anywhere}.asset .ledger th:first-child,.asset .ledger td:first-child{padding-left:0}.asset .ledger th:last-child,.asset .ledger td:last-child{padding-right:0}.asset .ledger tbody tr:hover{background:var(--color-surface-subtle)}.asset details{border-top:1px solid var(--color-outline);margin-top:1.5rem;padding-top:1rem}.asset summary{cursor:pointer;color:var(--color-secondary);padding:.5rem 0;min-height:2.75rem}.asset summary:focus-visible{outline:2px solid var(--color-tertiary);outline-offset:.25rem}.asset .small{font-size:var(--text-label)}.asset .mobile-only{display:none}.asset .horizontal-list{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1rem}.asset .board-column{border-top:.25rem solid var(--color-primary);padding-top:1rem;min-width:0}.asset .board-item{padding:1rem 0;border-bottom:1px solid var(--color-outline)}.asset .board-item p{margin-top:.5rem}.asset .statement{font-size:var(--text-h2);line-height:1.5;font-weight:600}.asset .meta-line{margin-top:.75rem;font-size:var(--text-label);color:var(--color-secondary)}.asset pre{white-space:pre-wrap;overflow-wrap:anywhere}.asset footer{margin-top:2rem;font-size:var(--text-label);color:var(--color-secondary)}
@media(max-width:640px){.asset{padding:1.5rem 1rem}.asset h1{font-size:var(--text-h2)}.asset header{margin-bottom:1.5rem}.asset .grid2,.asset .grid3,.asset .horizontal-list{grid-template-columns:1fr}.asset .row{grid-template-columns:minmax(0,1fr) 5rem;gap:.5rem}.asset .row>:first-child{grid-column:1/-1}.asset .desktop-only{display:none!important}.asset .mobile-only{display:block}.asset .panel{padding:1rem}.asset .ledger,.asset .ledger tbody,.asset .ledger tr,.asset .ledger td{display:block}.asset .ledger thead{display:none}.asset .ledger tr{padding:.75rem 0;border-bottom:1px solid var(--color-outline)}.asset .ledger td{display:grid;grid-template-columns:7rem minmax(0,1fr);gap:.75rem;border:0;padding:.25rem 0!important}.asset .ledger td:before{content:attr(data-label);color:var(--color-secondary);font-size:var(--text-label)}.asset .hero-number{font-size:var(--text-h1)}}
@media print{.asset{max-width:none;padding:1rem}.asset details>*{display:block}.asset summary{display:none}}
`;

export default function Template(d: Props) {
const selected=d.options.find(o=>o.id===d.recommendedId)!;
return <section className="asset asset-options-tradeoff"><style>{CSS}</style><header><p className="eyebrow">OPTIONS TRADEOFF</p><h1>{d.title}</h1>{d.subtitle && <p className="subtitle">{d.subtitle}</p>}</header>
<div className="callout"><strong>{selected.label}</strong><p>{d.rationale}</p></div><div className="horizontal-list">{d.options.map(o=><article key={o.id} className={o.id===d.recommendedId?"panel focus":"panel"}><h2>{o.id} · {o.label}</h2><p className="small muted">得られること</p><p>{o.benefit}</p><p className="small muted rule">引き受けること</p><p>{o.cost}</p><p className="small muted rule">成立条件</p><p>{o.condition}</p></article>)}</div><Notes><p>推奨は入力された判断です。評価点の自動計算は行いません。</p></Notes>
<footer>推奨と、その結論が変わる条件を一緒に示す。</footer></section>;
}

schema.ts

915 B

/designs/options-tradeoff/source/schema.ts

Show contents
import { z } from "zod";
const text = z.string().trim().min(1).max(160);
const short = z.string().trim().min(1).max(48);
const value = z.number().finite();
const positive = value.positive();
const unique = (values: string[]) => new Set(values).size === values.length;

export const schema = z.object({
  title: text.describe("結論を伝える見出し"),
  subtitle: text.optional().describe("出典・時点・母集団や仮定"),
  options:z.array(z.object({id:short,label:short,benefit:text,cost:text,condition:text}).strict()).min(2).max(4).describe("候補ID、利点、代償、成立条件"),recommendedId:short.describe("検討上の推奨候補ID"),rationale:text.describe("推奨する理由と判断条件"),
}).strict().refine(d=>unique(d.options.map(o=>o.id))&&d.options.some(o=>o.id===d.recommendedId),"推奨IDは実在する一意の候補を参照");
export type Props = z.infer<typeof schema>;

meta.yaml

1.4 KB

/designs/options-tradeoff/source/meta.yaml

Show contents
id: options-tradeoff
name:
  ja: 選択肢とトレードオフ
  en: Options Tradeoff
purpose:
  ja: 候補ごとの利点・代償・成立条件を並べ、選択の理由を明示する。
  en: Options Tradeoff with explicit source values and reading conditions.
status: published
still: ./still.png
category: decision
catalogOrder: 470
jobs:
  - compare-choose
useCases:
  - comparison
layoutKinds:
  - 3-column
tags:
  - options-tradeoff
  - decision
  - offline
  - responsive
whenToUse:
  - ja: 候補ごとの利点・代償・成立条件を並べ、選択の理由を明示する。
    en: Use to communicate options tradeoff.
avoidWhen:
  - ja: 同じ条件・単位で比較できないデータには使わない
    en: Do not compare values with incompatible units, populations, or assumptions.
requiredInputs:
  - ja: 推奨と、その結論が変わる条件を一緒に示す。
    en: Provide the source values, labels and decision conditions described by the schema.
relatedLayouts:
  - weighted-decision-scorecard
  - decision-record
dataSchema:
  title: string — 結論を伝える見出し
  subtitle: string? — 出典・時点・母集団や仮定
  options: array (id, label, benefit, cost, condition) — 候補ID、利点、代償、成立条件
  recommendedId: string — 検討上の推奨候補ID
  rationale: string — 推奨する理由と判断条件
sources: []

sample-data.json

914 B

/designs/options-tradeoff/source/sample-data.json

Show contents
{
  "title": "標準機能を優先し、拡張を段階導入する",
  "subtitle": "架空サンプル|レイアウトの使用例",
  "options": [
    {
      "id": "A",
      "label": "一括置換",
      "benefit": "システムを一度に統一",
      "cost": "変更範囲と切替負荷が大きい",
      "condition": "全領域の要件確定"
    },
    {
      "id": "B",
      "label": "段階導入",
      "benefit": "学習結果を次段階へ反映",
      "cost": "並行運用の期間が発生",
      "condition": "境界と移行順の合意"
    },
    {
      "id": "C",
      "label": "現行継続",
      "benefit": "短期の変更負荷が少ない",
      "cost": "改善効果の実現が遅れる",
      "condition": "保守余力の確保"
    }
  ],
  "recommendedId": "B",
  "rationale": "初期対象を受付に絞り、運用負荷を確認してから拡大する"
}