Back to index

State Transition Ledger

Table
Generates documents
State Transition Ledger

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

state-transition-ledger-everything.md

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

Prompt

1.5 KB
Build an HTML document with "State Transition Ledger". This layout runs, so there is no composition to rebuild — what it needs is content.

## What this layout does
State Transition Ledger 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? — 出典・時点・母集団や仮定
- `states` — array (0, 1, 2) — 状態の一覧
- `initial` — string — 初期状態
- `transitions` — array (from, event, guard, to, action) — 遷移元・イベント・条件・遷移先・副作用

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 state-transition-ledger <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/state-transition-ledger/source/template.tsx
- https://design-vault.pages.dev/designs/state-transition-ledger/source/schema.ts
- https://design-vault.pages.dev/designs/state-transition-ledger/source/sample-data.json
- https://design-vault.pages.dev/designs/state-transition-ledger/sample.html

Files

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

template.tsx

6.9 KB

/designs/state-transition-ledger/source/template.tsx

Show contents
import type { ReactNode } from "react";
import type { Props } from "./schema";
function Ledger({heads,rows,caption}:{heads:string[];rows:ReactNode[][];caption?:string}) { return <table className="ledger">{caption && <caption>{caption}</caption>}<thead><tr>{heads.map((h,i)=><th key={i} scope="col">{h}</th>)}</tr></thead><tbody>{rows.map((r,i)=><tr key={i}>{r.map((v,j)=><td key={j} data-label={heads[j]}>{v}</td>)}</tr>)}</tbody></table>; }
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) {

return <section className="asset asset-state-transition-ledger"><style>{CSS}</style><header><p className="eyebrow">STATE TRANSITION LEDGER</p><h1>{d.title}</h1>{d.subtitle && <p className="subtitle">{d.subtitle}</p>}</header>
<div className="flex"><span className="pill focus">初期状態:{d.initial}</span><span className="small muted">{d.states.length}状態 / {d.transitions.length}遷移</span></div><Ledger heads={["遷移元","イベント","成立条件","遷移先","実行すること"]} rows={d.transitions.map(t=>[t.from,t.event,t.guard,t.to,t.action])}/><Notes><h3>状態の一覧</h3><p>{d.states.join(" / ")}</p><p className="rule">記録されていない遷移は、この表では許可されたとは判断できません。</p></Notes>
<footer>イベントと条件を分離する。同じイベントでも条件が違えば別行にする。</footer></section>;
}

schema.ts

1.0 KB

/designs/state-transition-ledger/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("出典・時点・母集団や仮定"),
  states:z.array(short).min(3).max(8).describe("状態の一覧"),initial:short.describe("初期状態"),
  transitions:z.array(z.object({from:short,event:short,guard:text,to:short,action:text}).strict()).min(2).max(10).describe("遷移元・イベント・条件・遷移先・副作用"),
}).strict().refine(d=>unique(d.states)&&d.states.includes(d.initial)&&d.transitions.every(t=>d.states.includes(t.from)&&d.states.includes(t.to))&&unique(d.transitions.map(t=>JSON.stringify([t.from,t.event,t.guard]))),"参照先の状態・初期状態・遷移条件を整合させる");
export type Props = z.infer<typeof schema>;

meta.yaml

1.4 KB

/designs/state-transition-ledger/source/meta.yaml

Show contents
id: state-transition-ledger
name:
  ja: 状態遷移の条件台帳
  en: State Transition Ledger
purpose:
  ja: 状態・イベント・遷移条件・次状態を列で揃え、抜けを確認する。
  en: State Transition Ledger with explicit source values and reading conditions.
status: published
still: ./still.png
category: specification
catalogOrder: 670
jobs:
  - trace-flow
useCases:
  - process
layoutKinds:
  - table
tags:
  - state-transition-ledger
  - specification
  - offline
  - responsive
whenToUse:
  - ja: 状態・イベント・遷移条件・次状態を列で揃え、抜けを確認する。
    en: Use to communicate state transition ledger.
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:
  - process-flow
  - decision-tree
dataSchema:
  title: string — 結論を伝える見出し
  subtitle: string? — 出典・時点・母集団や仮定
  states: array (0, 1, 2) — 状態の一覧
  initial: string — 初期状態
  transitions: array (from, event, guard, to, action) — 遷移元・イベント・条件・遷移先・副作用
sources: []

sample-data.json

1.1 KB

/designs/state-transition-ledger/source/sample-data.json

Show contents
{
  "title": "差戻し後の再申請条件を明示する",
  "subtitle": "架空サンプル|レイアウトの使用例",
  "states": [
    "下書き",
    "受付",
    "確認中",
    "差戻し",
    "完了"
  ],
  "initial": "下書き",
  "transitions": [
    {
      "from": "下書き",
      "event": "送信",
      "guard": "必須項目を充足",
      "to": "受付",
      "action": "受付番号を発行"
    },
    {
      "from": "受付",
      "event": "確認開始",
      "guard": "担当者を割当",
      "to": "確認中",
      "action": "開始時刻を記録"
    },
    {
      "from": "確認中",
      "event": "不備検出",
      "guard": "確認項目に不足",
      "to": "差戻し",
      "action": "理由を通知"
    },
    {
      "from": "差戻し",
      "event": "再送信",
      "guard": "指摘事項を修正",
      "to": "受付",
      "action": "版番号を増加"
    },
    {
      "from": "確認中",
      "event": "確認完了",
      "guard": "全条件を充足",
      "to": "完了",
      "action": "完了通知を送信"
    }
  ]
}