作品として見る 生成された文書そのもの — 切り替えも開閉もそのまま動きます
作品として見る 生成された文書そのもの — 切り替えも開閉もそのまま動きます
コピーして貼るか、ファイルに保存して渡す。ネットにつながらない環境には「全部入り」を 1 つ渡せば足りる。
このパネルの中身を、URL の先にあるファイルの中身まで含めて 1 つにまとめた Markdown — 大きいのでファイルで渡す(コピーは 32KB まで)
「データアクセス申請・審査キュー」でHTML資料を作ってください。この型は実行できるので、版面を組み直す必要はありません — 必要なのは中身です。 ## この型がすること データアクセス申請を機密区分・最小権限・業務理由・承認者・再審査日・自動終了日とともに処理する。 ## 渡す必要があるもの - 匿名依頼者コード、資産、機密区分、必要な権限、利用目的、資産責任者、審査者、状態、再審査日を記録する。 ## 入力の形 - `title` — string — アクセス審査の要点 - `subtitle` — string? — 申請窓口・ポリシー版・対象期間 - `program` — string — データガバナンスプログラム - `requests` — array (3–12) — 匿名依頼者コード、資産、区分、最小権限、理由、所有者、承認、状態、期限 入力の機械可読な検証は schema.ts が持ちます。足りない入力は推測せず質問してください。 ## 向かない場合 - 役割ごとの標準権限そのものを比較・設計する場合は、権限マトリクスを使う。 ## 作り方 入力を JSON にまとめ、次を実行します。 ```bash node scripts/render.mjs data-access-request-queue <入力.json> --theme ink -o out.html ``` 出力は 1 ファイルで完結し、外部から何も読み込みません。 ## 原本 - https://design-vault.pages.dev/designs/data-access-request-queue/source/template.tsx - https://design-vault.pages.dev/designs/data-access-request-queue/source/schema.ts - https://design-vault.pages.dev/designs/data-access-request-queue/source/sample-data.json - https://design-vault.pages.dev/designs/data-access-request-queue/sample.html
テンプレート・入力スキーマ・サンプルデータ・メタ情報。この文書はこの 4 つから生成されています。 生成された文書
/designs/data-access-request-queue/source/template.tsx
import type { Props } from "./schema";
const CSS = String.raw`
.asset-data-access-request-queue{font-family:var(--font-sans);color:var(--color-primary);background:var(--color-surface);max-width:var(--container-slide);margin:auto;padding:3rem 2.5rem;line-height:1.55;font-size:var(--text-body-sm)}
.asset-data-access-request-queue *{box-sizing:border-box}.asset-data-access-request-queue header{margin-bottom:1.2rem}.asset-data-access-request-queue .eyebrow{font-family:var(--font-mono);font-size:var(--text-label);color:var(--color-tertiary);margin:0 0 .6rem}.asset-data-access-request-queue h1{font-size:var(--text-h1);line-height:1.25;max-width:28em;margin:0}.asset-data-access-request-queue .subtitle{color:var(--color-secondary);margin:.65rem 0 0}.asset-data-access-request-queue .summary{display:flex;gap:1.5rem;flex-wrap:wrap;padding:.7rem 0;border-block:1px solid var(--color-outline);margin-bottom:1rem}.asset-data-access-request-queue .summary p{margin:0;color:var(--color-secondary);font-size:var(--text-label)}.asset-data-access-request-queue .summary strong{font-family:var(--font-mono);font-size:var(--text-h3);color:var(--color-primary);margin-right:.3rem}.asset-data-access-request-queue .request{padding:.85rem 0;border-top:1px solid var(--color-outline)}.asset-data-access-request-queue .request:last-child{border-bottom:1px solid var(--color-outline)}.asset-data-access-request-queue .head{display:flex;gap:.55rem;align-items:center;flex-wrap:wrap}.asset-data-access-request-queue code{font-family:var(--font-mono);font-size:var(--text-label);color:var(--color-tertiary);overflow-wrap:anywhere}.asset-data-access-request-queue .head h2{font-size:var(--text-body-md);margin:0;flex:1}.asset-data-access-request-queue .badge{font-size:var(--text-label);border:1px solid var(--color-outline);padding:.03rem .4rem;color:var(--color-secondary)}.asset-data-access-request-queue .scope{display:flex;gap:.5rem;flex-wrap:wrap;margin-top:.35rem}.asset-data-access-request-queue .purpose{margin:.55rem 0 .2rem;font-weight:600}.asset-data-access-request-queue .justification{margin:.15rem 0;color:var(--color-secondary);overflow-wrap:anywhere}.asset-data-access-request-queue .details{display:grid;grid-template-columns:1fr 1fr;gap:.4rem 1.2rem;margin-top:.55rem;padding:.5rem .65rem;background:var(--color-surface-subtle)}.asset-data-access-request-queue .details span{display:block;font-size:var(--text-label);color:var(--color-secondary)}.asset-data-access-request-queue .details p{margin:0;overflow-wrap:anywhere}.asset-data-access-request-queue .approval{font-family:var(--font-mono);font-size:var(--text-label)}.asset-data-access-request-queue .still-svg{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
@media(max-width:680px){.asset-data-access-request-queue{padding:1.5rem 1rem}.asset-data-access-request-queue h1{font-size:var(--text-h2)}.asset-data-access-request-queue .details{grid-template-columns:minmax(0,1fr);gap:.4rem}.asset-data-access-request-queue .summary{gap:.8rem}}
@media print{.asset-data-access-request-queue{max-width:none;padding:1rem}.asset-data-access-request-queue .request{break-inside:avoid}}
`;
const statusLabel = { submitted: "申請", "owner-review": "所有者確認", "security-review": "セキュリティ審査", approved: "承認済", provisioned: "付与済", denied: "却下", revoked: "取消済" };
const classificationLabel = { public: "公開", internal: "社内", confidential: "機密", restricted: "制限" };
const accessLabel = { read: "閲覧", "limited-write": "限定編集", admin: "管理", export: "抽出" };
function Still({ data }: { data: Props }) {
return <svg className="still-svg" data-layout-still="true" width="1280" height="720" viewBox="0 0 1280 720" aria-hidden="true">
<rect width="1280" height="720" fill="var(--color-surface)" />
<text x="48" y="44" fontSize="14" fontFamily="sans-serif" fill="var(--color-secondary)">{data.program}</text>
<text x="48" y="85" fontSize="26" fontWeight="700" fontFamily="sans-serif" fill="var(--color-on-surface)">{data.title}</text>
<text x="48" y="120" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">申請者コード · データ資産 · 機密区分 · 最小権限 · 審査状態 · 終了日</text>
{data.requests.slice(0, 8).map((request, index) => {
const y = 166 + index * 62;
return <g key={request.id}>
<line x1="48" x2="1232" y1={y - 19} y2={y - 19} stroke="var(--color-outline)" />
<text x="52" y={y + 3} fontSize="11" fontFamily="monospace" fill="var(--color-tertiary)">{request.id}</text>
<text x="150" y={y + 3} fontSize="12" fontWeight="700" fontFamily="sans-serif" fill="var(--color-on-surface)">{request.asset}</text>
<text x="410" y={y + 3} fontSize="11" fontFamily="sans-serif" fill="var(--color-secondary)">{classificationLabel[request.classification]} · {accessLabel[request.access]}</text>
<text x="675" y={y + 3} fontSize="11" fontFamily="sans-serif" fill="var(--color-on-surface)">{request.requesterId} · {request.owner}</text>
<text x="1020" y={y + 3} fontSize="11" fontFamily="sans-serif" fill="var(--color-secondary)">{statusLabel[request.status]} · {request.expiry ?? "期限未設定"}</text>
</g>;
})}
<text x="48" y="704" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">{data.requests.length}申請 · 承認・終了日つきの最小権限付与</text>
</svg>;
}
export default function Template(data: Props) {
const pending = data.requests.filter((request) => ["submitted", "owner-review", "security-review"].includes(request.status)).length;
const provisioned = data.requests.filter((request) => request.status === "provisioned").length;
const restricted = data.requests.filter((request) => request.classification === "restricted").length;
return <section className="asset asset-data-access-request-queue">
<style>{CSS}</style>
<header>
<p className="eyebrow">{data.program} · データアクセス審査</p>
<h1>{data.title}</h1>
{data.subtitle && <p className="subtitle">{data.subtitle}</p>}
</header>
<div className="summary" aria-label="申請状況"><p><strong>{data.requests.length}</strong>申請</p><p><strong>{pending}</strong>審査中</p><p><strong>{provisioned}</strong>付与済</p><p><strong>{restricted}</strong>制限区分</p></div>
{data.requests.map((request) => <article className="request" key={request.id}>
<div className="head"><code>{request.id}</code><h2>{request.asset}</h2><span className="badge" data-status={request.status}>{statusLabel[request.status]}</span></div>
<div className="scope"><span className="badge" data-classification={request.classification}>{classificationLabel[request.classification]} · {request.classification}</span><span className="badge" data-access={request.access}>権限: {accessLabel[request.access]} · {request.access}</span></div>
<p className="purpose">{request.purpose}</p><p className="justification">{request.justification}</p>
<div className="details">
<div><span>依頼者コード・所属</span><p>{request.requesterId} · {request.requesterTeam}</p></div>
<div><span>資産責任者・審査者</span><p>{request.owner} · {request.reviewer}</p></div>
<div><span>再審査日・アクセス終了日</span><p>{request.reviewDate} · {request.expiry ?? "未設定"}</p></div>
<div><span>承認記録</span><p className="approval">{request.approvalRef ?? "審査完了後に記録"}</p></div>
</div>
</article>)}
<Still data={data} />
</section>;
}
/designs/data-access-request-queue/source/schema.ts
import { z } from "zod";
const text = z.string().trim().min(1).max(160);
const short = z.string().trim().min(1).max(48);
const date = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "日付はYYYY-MM-DD形式にすること");
const requestSchema = z.object({
id: short.describe("アクセス依頼の一意な識別子"),
requesterId: short.describe("氏名や連絡先を含めない依頼者コード"),
requesterTeam: short.describe("依頼チーム"),
asset: short.describe("要求するデータ資産またはシステム"),
classification: z.enum(["public", "internal", "confidential", "restricted"]).describe("資産の機密区分"),
access: z.enum(["read", "limited-write", "admin", "export"]).describe("求める最小アクセス範囲"),
purpose: short.describe("業務上の利用目的"),
justification: text.describe("アクセスが必要な理由と対象範囲"),
owner: short.describe("データ資産の責任者"),
reviewer: short.describe("アクセス判断者"),
approvalRef: short.optional().describe("承認記録の参照"),
reviewDate: date.describe("申請内容またはアクセスの再審査日"),
expiry: date.optional().describe("付与アクセスの終了日"),
status: z.enum(["submitted", "owner-review", "security-review", "approved", "provisioned", "denied", "revoked"]).describe("申請から付与・取消までの状態"),
}).strict();
export const schema = z.object({
title: text.describe("アクセス審査の要点"),
subtitle: text.optional().describe("申請窓口・ポリシー版・対象期間"),
program: short.describe("対象データガバナンスプログラム"),
requests: z.array(requestSchema).min(3).max(12).describe("最小範囲・期間限定のデータアクセス申請"),
}).strict().superRefine((data, ctx) => {
const ids = data.requests.map(({ id }) => id);
if (new Set(ids).size !== ids.length) {
ctx.addIssue({ code: "custom", path: ["requests"], message: "申請IDは一意にすること" });
}
data.requests.forEach((request, index) => {
if (["approved", "provisioned", "revoked"].includes(request.status) && (!request.approvalRef || !request.expiry)) {
ctx.addIssue({ code: "custom", path: ["requests", index], message: "承認済み・付与済み申請には承認記録と終了日が必要" });
}
if (request.expiry && request.expiry < request.reviewDate) {
ctx.addIssue({ code: "custom", path: ["requests", index, "expiry"], message: "アクセス終了日は再審査日以後にすること" });
}
});
});
export type Props = z.infer<typeof schema>;
/designs/data-access-request-queue/source/meta.yaml
id: data-access-request-queue
name:
ja: データアクセス申請・審査キュー
en: Data Access Request and Review Queue
purpose:
ja: データアクセス申請を機密区分・最小権限・業務理由・承認者・再審査日・自動終了日とともに処理する。
en: Process data access requests with classification, least privilege, purpose, approver, review date, and automatic expiry.
status: published
still: ./still.png
category: governance
catalogOrder: 865
jobs:
- assign-governance
- prioritize-diagnose
useCases:
- status
- requirements
layoutKinds:
- table
- stack
tags:
- data-access-request
- least-privilege
- approval-workflow
- time-bound-access
- responsive
whenToUse:
- ja: 申請ごとに必要最小限の権限を審査し、期限と承認記録を追跡するとき。
en: Use to review least-privilege access per request and track its expiry and approval evidence.
avoidWhen:
- ja: 役割ごとの標準権限そのものを比較・設計する場合は、権限マトリクスを使う。
en: Use a permission matrix to compare and design baseline permissions by role.
requiredInputs:
- ja: 匿名依頼者コード、資産、機密区分、必要な権限、利用目的、資産責任者、審査者、状態、再審査日を記録する。
en: Record a pseudonymous requester code, asset, classification, requested access, purpose, owner, reviewer, state, and review date.
relatedLayouts:
- permission-matrix
- security-exception-register
dataSchema:
title: string — アクセス審査の要点
subtitle: string? — 申請窓口・ポリシー版・対象期間
program: string — データガバナンスプログラム
requests: array (3–12) — 匿名依頼者コード、資産、区分、最小権限、理由、所有者、承認、状態、期限
sources: []
/designs/data-access-request-queue/source/sample-data.json
{
"title": "機密顧客データの期限付き閲覧を承認し、管理者権限の申請は差し戻す",
"subtitle": "Data Access Governance · Q3 2026 · weekly review queue",
"program": "Northstar Data Stewardship",
"requests": [
{ "id": "DAR-218", "requesterId": "USR-7F21", "requesterTeam": "Risk Analytics", "asset": "customer-risk-view", "classification": "confidential", "access": "read", "purpose": "Quarterly risk review", "justification": "Read-only access for the approved 2026-Q3 portfolio analysis; no row-level export is required.", "owner": "Customer Data Steward", "reviewer": "Risk Data Approver", "approvalRef": "GOV-APR-218", "reviewDate": "2026-10-01", "expiry": "2026-12-31", "status": "provisioned" },
{ "id": "DAR-221", "requesterId": "USR-90AC", "requesterTeam": "Product Insights", "asset": "checkout-events", "classification": "internal", "access": "read", "purpose": "Funnel investigation", "justification": "Compare retry behavior for the current checkout experiment; aggregate access is sufficient.", "owner": "Commerce Data Owner", "reviewer": "Product Data Steward", "reviewDate": "2026-09-29", "status": "owner-review" },
{ "id": "DAR-226", "requesterId": "USR-4D31", "requesterTeam": "Platform Support", "asset": "identity-admin-console", "classification": "restricted", "access": "admin", "purpose": "Support investigation", "justification": "The submitted scope is broader than the stated support task and has no time-bound incident reference.", "owner": "Identity Platform Owner", "reviewer": "Security Access Board", "reviewDate": "2026-09-27", "status": "denied" },
{ "id": "DAR-229", "requesterId": "USR-25B8", "requesterTeam": "Finance Operations", "asset": "invoice-aging-mart", "classification": "confidential", "access": "export", "purpose": "Month-end close", "justification": "A controlled extract is required to reconcile open invoices; export is limited to the approved finance workspace.", "owner": "Revenue Data Owner", "reviewer": "Finance Data Approver", "reviewDate": "2026-09-30", "status": "submitted" }
]
}