Back to index

Action Register

Table
Generates documents
Action Register

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

action-register-everything.md

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

Prompt

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

## What this layout does
Action Register 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? — 出典・時点・母集団や仮定
- `asOf` — string — 期限判定の基準日
- `actions` — array (id, task, owner, due, status) — 作業ID・内容・責任者・期限・状態

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

Files

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

template.tsx

7.0 KB

/designs/action-register/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) {
const overdue=(a:Props["actions"][number])=>a.status!=="done"&&a.due<d.asOf; const actions=[...d.actions].sort((a,b)=>Number(overdue(b))-Number(overdue(a))||a.due.localeCompare(b.due));const names={open:"未着手",doing:"対応中",done:"完了"};
return <section className="asset asset-action-register"><style>{CSS}</style><header><p className="eyebrow">ACTION REGISTER</p><h1>{d.title}</h1>{d.subtitle && <p className="subtitle">{d.subtitle}</p>}</header>
<div className="flex"><span className="pill">基準日 {d.asOf}</span><strong>期限超過 {actions.filter(overdue).length}件</strong></div><Ledger heads={["ID / 対応","責任者","期限","状態"]} rows={actions.map(a=>[a.id+" / "+a.task,a.owner,a.due,overdue(a)?"期限超過 · "+names[a.status]:names[a.status]])}/><Notes><p>期限当日は超過に含めません。完了作業は過去の期限でも未解決として数えません。</p></Notes>
<footer>現在時刻ではなく入力の基準日で判定し、再生成しても結果を再現できる。</footer></section>;
}

schema.ts

996 B

/designs/action-register/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 date = z.string().regex(/^\d{4}-\d{2}-\d{2}$/).refine(s => Number.isFinite(Date.parse(s)) && new Date(s).toISOString().slice(0,10) === s, "実在する日付を YYYY-MM-DD で指定する");
const unique = (values: string[]) => new Set(values).size === values.length;

export const schema = z.object({
  title: text.describe("結論を伝える見出し"),
  subtitle: text.optional().describe("出典・時点・母集団や仮定"),
  asOf:date.describe("期限判定の基準日"),
  actions:z.array(z.object({id:short,task:text,owner:short,due:date,status:z.enum(["open","doing","done"])}).strict()).min(2).max(10).describe("作業ID・内容・責任者・期限・状態"),
}).strict().refine(d=>unique(d.actions.map(a=>a.id)),"作業IDは一意");
export type Props = z.infer<typeof schema>;

meta.yaml

1.3 KB

/designs/action-register/source/meta.yaml

Show contents
id: action-register
name:
  ja: 期限と責任者のアクション台帳
  en: Action Register
purpose:
  ja: 基準日を固定し、未完了・期限超過・完了の作業を整理する。
  en: Action Register with explicit source values and reading conditions.
status: published
still: ./still.png
category: operations
catalogOrder: 560
jobs:
  - assign-governance
useCases:
  - process
layoutKinds:
  - table
tags:
  - action-register
  - operations
  - offline
  - responsive
whenToUse:
  - ja: 基準日を固定し、未完了・期限超過・完了の作業を整理する。
    en: Use to communicate action register.
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:
  - kanban-wip
  - meeting-agenda-timebox
dataSchema:
  title: string — 結論を伝える見出し
  subtitle: string? — 出典・時点・母集団や仮定
  asOf: string — 期限判定の基準日
  actions: array (id, task, owner, due, status) — 作業ID・内容・責任者・期限・状態
sources: []

sample-data.json

775 B

/designs/action-register/source/sample-data.json

Show contents
{
  "title": "期限超過2件を先に解消する",
  "subtitle": "架空サンプル|レイアウトの使用例",
  "asOf": "2026-09-08",
  "actions": [
    {
      "id": "A1",
      "task": "例外案件を分類する",
      "owner": "業務担当",
      "due": "2026-09-05",
      "status": "doing"
    },
    {
      "id": "A2",
      "task": "API接続仕様を確定",
      "owner": "基盤担当",
      "due": "2026-09-07",
      "status": "open"
    },
    {
      "id": "A3",
      "task": "教育資料を配布",
      "owner": "移行担当",
      "due": "2026-09-12",
      "status": "doing"
    },
    {
      "id": "A4",
      "task": "画面文言を確認",
      "owner": "UX担当",
      "due": "2026-09-04",
      "status": "done"
    }
  ]
}