Back to index

Process Control Chart

Timeline
Generates documents
Process Control Chart

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

control-chart-everything.md

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

Prompt

1.8 KB
Build an HTML document with "Process Control Chart". This layout runs, so there is no composition to rebuild — what it needs is content.

## What this layout does
Compare sequential measurements with a center line and control limits to identify points outside routine variation.

## What you have to supply
- Order measurements by occurrence and keep method, frequency, and population conditions consistent.
- Supply a center line and upper/lower control limits; keep them distinct from specification limits.

## The shape of the input
- `title` — string — 工程の状態を伝える見出し
- `subtitle` — string? — 対象工程・測定条件・期間
- `unit` — string — 測定値の単位
- `centerLine` — number — 工程平均または管理中心線
- `lowerControlLimit` — number — 下方管理限界
- `upperControlLimit` — number — 上方管理限界
- `points` — array (period, value) — 時系列順の測定点。5〜12点、時点ラベルは6文字以内で一意

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

## When not to use it
- Do not use control limits to judge specification compliance or target attainment.
- Do not use for unrelated categories without a meaningful observation order.

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

```bash
node scripts/render.mjs control-chart <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/control-chart/source/template.tsx
- https://design-vault.pages.dev/designs/control-chart/source/schema.ts
- https://design-vault.pages.dev/designs/control-chart/source/sample-data.json
- https://design-vault.pages.dev/designs/control-chart/sample.html

Files

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

template.tsx

8.6 KB

/designs/control-chart/source/template.tsx

Show contents
import type { Props } from "./schema";

const CSS = String.raw`
.asset{font-family:var(--font-sans);color:var(--color-on-surface);background:var(--color-surface);margin:0;line-height:1.5}
.asset .desktop-chart{display:block;width:1280px;height:720px;max-width:100%;margin:0 auto}
.asset .mobile-chart{display:none}
.asset details{border-top:1px solid var(--color-outline);margin-top:1rem;padding-top:.75rem}
.asset summary{cursor:pointer;min-height:2.75rem;padding:.5rem 0;color:var(--color-secondary)}
.asset ol{list-style:none;padding:0;margin:1rem 0}
.asset li{border-top:1px solid var(--color-outline);padding:.9rem 0;overflow-wrap:anywhere}
.asset .mobile-period{font:600 .8rem var(--font-mono);color:var(--color-secondary)}
.asset .mobile-value{font:700 1.1rem var(--font-mono);margin:.25rem 0}
.asset .mobile-status{font-size:.85rem;color:var(--color-secondary)}
.asset .mobile-status[data-outside="true"]{font-weight:700;color:var(--color-tertiary)}
.asset .limits{display:flex;flex-wrap:wrap;gap:.5rem}
.asset .limit{border:1px solid var(--color-outline);border-radius:999px;padding:.25rem .65rem;font-size:.8rem}
.asset .desktop-notes{max-width:1080px;margin:0 auto;padding:0 1rem}
@media(max-width:640px){.asset .desktop-chart{display:none!important}.asset .mobile-chart{display:block;padding:1.25rem 1rem}.asset .desktop-notes{display:block}.asset h1{font-size:1.5rem;line-height:1.35;overflow-wrap:anywhere}.asset .mobile-header{margin-bottom:1.25rem}.asset .mobile-kicker{font:600 .75rem var(--font-mono);letter-spacing:.08em;color:var(--color-secondary)}}
@media(min-width:641px){.asset .mobile-chart{display:none!important}}
`;

const formatValue = (value: number, unit: string) => `${value.toLocaleString("ja-JP", { maximumFractionDigits: 2 })} ${unit}`;
const wrap = (value: string, max: number) => {
  const chars = [...value];
  const lines: string[] = [];
  for (let i = 0; i < chars.length; i += max) lines.push(chars.slice(i, i + max).join(""));
  return lines.length ? lines : [""];
};

export default function Template(d: Props) {
  const domainMin = Math.min(d.lowerControlLimit, ...d.points.map((point) => point.value));
  const domainMax = Math.max(d.upperControlLimit, ...d.points.map((point) => point.value));
  const padding = (domainMax - domainMin) * 0.12 || 1;
  const min = domainMin - padding;
  const max = domainMax + padding;
  const titleLines = wrap(d.title, 46);
  const subtitleLines = wrap(d.subtitle ?? "", 82);
  const metadataY = 132 + titleLines.length * 31 + subtitleLines.length * 20;
  const plot = { left: 150, right: 1145, top: Math.max(250, metadataY + 45), bottom: 520 };
  const x = (index: number) => plot.left + index * (plot.right - plot.left) / Math.max(1, d.points.length - 1);
  const y = (value: number) => plot.bottom - (value - min) / (max - min) * (plot.bottom - plot.top);
  const outOfControl = d.points.filter((point) => point.value < d.lowerControlLimit || point.value > d.upperControlLimit).length;
  const line = d.points.map((point, index) => `${index === 0 ? "M" : "L"} ${x(index).toFixed(2)} ${y(point.value).toFixed(2)}`).join(" ");
  const guideLines = [
    { label: "上方管理限界", value: d.upperControlLimit, kind: "limit" },
    { label: "中心線", value: d.centerLine, kind: "center" },
    { label: "下方管理限界", value: d.lowerControlLimit, kind: "limit" },
  ];

  return <section className="asset asset-control-chart">
    <style>{CSS}</style>
    <svg className="desktop-chart" data-layout-still="true" width="1280" height="720" viewBox="0 0 1280 720" role="img" aria-labelledby="control-title control-desc">
      <title id="control-title">{d.title}</title>
      <desc id="control-desc">{d.points.length}件の測定値を中心線と管理限界に対して示す工程管理図。管理限界外は{outOfControl}件。</desc>
      <rect x="0" y="0" width="1280" height="720" fill="var(--color-surface)" />
      <text x="72" y="39" fontFamily="sans-serif" fontSize="14" fontWeight="700" letterSpacing="2" fill="var(--color-tertiary)">PROCESS CONTROL</text>
      {titleLines.map((part, index) => <text key={`title-${index}`} x="72" y={82 + index * 31} fontFamily="sans-serif" fontSize="25" fontWeight="700" fill="var(--color-on-surface)">{part}</text>)}
      {subtitleLines.map((part, index) => <text key={`subtitle-${index}`} x="72" y={98 + titleLines.length * 31 + index * 20} fontFamily="sans-serif" fontSize="14" fill="var(--color-secondary)">{part}</text>)}
      <text x="72" y={metadataY} fontFamily="sans-serif" fontSize="13" fill="var(--color-secondary)">対象 {d.points.length}点 · 単位 {d.unit} · 管理限界外 {outOfControl}点</text>
      <text x={plot.left} y={plot.top - 14} fontFamily="sans-serif" fontSize="13" fill="var(--color-secondary)">測定順</text>
      {guideLines.map((guide) => <g key={guide.label}>
        <line x1={plot.left} x2={plot.right} y1={y(guide.value)} y2={y(guide.value)} stroke={guide.kind === "center" ? "var(--color-on-surface)" : "var(--color-tertiary)"} strokeWidth={guide.kind === "center" ? 2 : 1.5} strokeDasharray={guide.kind === "limit" ? "7 6" : undefined} />
        <text x="140" y={y(guide.value) + 5} textAnchor="end" fontFamily="sans-serif" fontSize="13" fill="var(--color-secondary)">{guide.label} {formatValue(guide.value, d.unit)}</text>
      </g>)}
      <line x1={plot.left} x2={plot.right} y1={plot.bottom} y2={plot.bottom} stroke="var(--color-outline)" strokeWidth="1" />
      <path d={line} fill="none" stroke="var(--color-on-surface)" strokeWidth="3" strokeLinejoin="round" strokeLinecap="round" />
      {d.points.map((point, index) => {
        const outside = point.value < d.lowerControlLimit || point.value > d.upperControlLimit;
        return <g key={point.period}>
          <circle cx={x(index)} cy={y(point.value)} r={outside ? 8 : 6} fill={outside ? "var(--color-tertiary)" : "var(--color-on-surface)"} stroke="var(--color-surface)" strokeWidth="2" />
          <text data-point-period={point.period} x={x(index)} y="548" textAnchor="middle" fontFamily="sans-serif" fontSize="12" fill="var(--color-secondary)">{point.period}</text>
          {outside && <text x={x(index)} y={y(point.value) - 13} textAnchor="middle" fontFamily="sans-serif" fontSize="12" fontWeight="700" fill="var(--color-tertiary)">限界外</text>}
        </g>;
      })}
      <line x1="72" x2="1208" y1="592" y2="592" stroke="var(--color-outline)" strokeWidth="1" />
      <line x1="78" x2="110" y1="623" y2="623" stroke="var(--color-tertiary)" strokeWidth="2" strokeDasharray="7 6" />
      <text x="120" y="628" fontFamily="sans-serif" fontSize="13" fill="var(--color-secondary)">上下の管理限界</text>
      <line x1="310" x2="342" y1="623" y2="623" stroke="var(--color-on-surface)" strokeWidth="2" />
      <text x="352" y="628" fontFamily="sans-serif" fontSize="13" fill="var(--color-secondary)">中心線</text>
      <circle cx="500" cy="623" r="6" fill="var(--color-tertiary)" />
      <text x="516" y="628" fontFamily="sans-serif" fontSize="13" fill="var(--color-secondary)">管理限界外の測定値</text>
      <rect x="72" y="656" width="1136" height="1" fill="var(--color-outline)" />
      <text x="72" y="684" fontFamily="sans-serif" fontSize="13" fill="var(--color-secondary)">管理限界は工程の通常変動を示します。規格値の合否や目標値とは区別します。</text>
    </svg>

    <div className="mobile-chart">
      <header className="mobile-header">
        <p className="mobile-kicker">PROCESS CONTROL</p>
        <h1>{d.title}</h1>
        {d.subtitle && <p>{d.subtitle}</p>}
        <p className="limits">
          <span className="limit">上限 {formatValue(d.upperControlLimit, d.unit)}</span>
          <span className="limit">中心 {formatValue(d.centerLine, d.unit)}</span>
          <span className="limit">下限 {formatValue(d.lowerControlLimit, d.unit)}</span>
        </p>
        <p>管理限界外 {outOfControl} / {d.points.length}点</p>
      </header>
      <ol>{d.points.map((point) => {
        const outside = point.value < d.lowerControlLimit || point.value > d.upperControlLimit;
        return <li key={point.period} data-point-period={point.period}>
          <span className="mobile-period">{point.period}</span>
          <p className="mobile-value">{formatValue(point.value, d.unit)}</p>
          <p className="mobile-status" data-outside={outside ? "true" : "false"}>{outside ? "管理限界外・要調査" : "管理限界内"}</p>
        </li>;
      })}</ol>
    </div>
    <details className="desktop-notes"><summary>管理図の読み方</summary><p>管理限界は工程の通常変動から計算します。規格値の合否や目標値とは別に扱い、逸脱点は測定条件と記録を確認します。</p></details>
  </section>;
}

schema.ts

1.1 KB

/designs/control-chart/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(6);
const value = z.number();
const unique = (values: string[]) => new Set(values).size === values.length;

export const schema = z.object({
  title: text.describe("工程の状態について伝える見出し"),
  subtitle: text.optional().describe("対象工程・測定条件・期間"),
  unit: short.describe("測定値の単位"),
  centerLine: value.describe("工程平均または管理中心線"),
  lowerControlLimit: value.describe("下方管理限界"),
  upperControlLimit: value.describe("上方管理限界"),
  points: z.array(z.object({
    period: short.describe("測定順を示す時点ラベル"),
    value: value.describe("各時点の測定値"),
  }).strict()).min(5).max(12).describe("時系列順の測定点"),
}).strict().refine(d =>
  d.lowerControlLimit < d.centerLine && d.centerLine < d.upperControlLimit,
  "下方管理限界 < 中心線 < 上方管理限界であること",
).refine(d => unique(d.points.map(point => point.period)), "時点ラベルは重複しない");

export type Props = z.infer<typeof schema>;

meta.yaml

2.3 KB

/designs/control-chart/source/meta.yaml

Show contents
id: control-chart
name:
  ja: 工程管理図
  en: Process Control Chart
purpose:
  ja: 時系列の測定値を中心線と上下の管理限界に照らし、通常変動から外れた時点を見つける。
  en: Compare sequential measurements with a center line and control limits to identify points outside routine variation.
status: published
still: ./still.png
category: analysis
catalogOrder: 720
jobs:
  - measure-change
  - prioritize-diagnose
useCases:
  - process
  - kpi
layoutKinds:
  - timeline
tags:
  - control-chart
  - statistical-process-control
  - process-quality
  - offline
  - responsive
whenToUse:
  - ja: 製造・運用工程の測定値が、工程固有の通常変動に収まっているかを時系列で判断する。
    en: Use when deciding whether sequential process measurements remain within routine process variation.
  - ja: 上下の管理限界を越えた時点を特定し、調査対象を絞りたい。
    en: Use to identify measurements beyond upper or lower control limits and focus investigation.
avoidWhen:
  - ja: 規格値への適合や目標達成を判定する場合。管理限界は規格限界・目標値ではない。
    en: Do not use control limits to judge specification compliance or target attainment.
  - ja: 時点順がなく、独立したカテゴリ同士だけを比較する場合。
    en: Do not use for unrelated categories without a meaningful observation order.
requiredInputs:
  - ja: 測定値を発生順に並べる。測定方法・頻度・対象条件を揃える。
    en: Order measurements by occurrence and keep method, frequency, and population conditions consistent.
  - ja: 中心線と上下の管理限界を指定する。規格限界と混同しない。
    en: Supply a center line and upper/lower control limits; keep them distinct from specification limits.
relatedLayouts:
  - line-trend
  - bullet-target
dataSchema:
  title: string — 工程の状態を伝える見出し
  subtitle: string? — 対象工程・測定条件・期間
  unit: string — 測定値の単位
  centerLine: number — 工程平均または管理中心線
  lowerControlLimit: number — 下方管理限界
  upperControlLimit: number — 上方管理限界
  points: array (period, value) — 時系列順の測定点。5〜12点、時点ラベルは6文字以内で一意
sources: []

sample-data.json

766 B

/designs/control-chart/source/sample-data.json

Show contents
{
  "title": "充填量は管理限界内で推移、一時的な逸脱を確認",
  "subtitle": "架空サンプル|飲料ラインA・週次の抜取検査",
  "unit": "mL",
  "centerLine": 500,
  "lowerControlLimit": 494,
  "upperControlLimit": 506,
  "points": [
    { "period": "週01", "value": 499 },
    { "period": "週02", "value": 501 },
    { "period": "週03", "value": 498 },
    { "period": "週04", "value": 503 },
    { "period": "週05", "value": 500 },
    { "period": "週06", "value": 502 },
    { "period": "週07", "value": 504 },
    { "period": "週08", "value": 501 },
    { "period": "週09", "value": 499 },
    { "period": "週10", "value": 507 },
    { "period": "週11", "value": 503 },
    { "period": "週12", "value": 500 }
  ]
}