diff --git a/.agents/skills/dads-design-system/SKILL.md b/.agents/skills/dads-design-system/SKILL.md index 52d318da..a546da38 100644 --- a/.agents/skills/dads-design-system/SKILL.md +++ b/.agents/skills/dads-design-system/SKILL.md @@ -17,6 +17,8 @@ description: このプロジェクト(devtools)のフロントエンド UI 1. このSKILL.md(カラー・タイポグラフィ・余白・角の形状の基本仕様) 2. `references/components.md`(コンポーネントの実装パターン) +> 📦 **関連スキル**: 公式 DADS の忠実再現パッケージ(公式トークン値 `#0017c1` 系・React コンポーネント・行政ポータル UI キット同梱)は `dads-design` スキルにある。**プロトタイプ / モック / デザインアセット生成**や**公式値の正確な参照**はそちらを使う。本スキルは devtools 本体(`src/` 配下)向けの適応版(`--color-primary: #1a56db` 等)が正本。 + --- ## 1. カラーシステム(青基調) diff --git a/.agents/skills/dads-design/LICENSE.md b/.agents/skills/dads-design/LICENSE.md new file mode 100644 index 00000000..60fc6fb9 --- /dev/null +++ b/.agents/skills/dads-design/LICENSE.md @@ -0,0 +1,38 @@ +# License / 帰属表示 + +このディレクトリ(`dads-design` スキル)のデザイントークン値・コンポーネント構造・タイプスケール・ +アイコンパスデータ・サンプル画像は、デジタル庁が MIT ライセンスで公開する以下のリポジトリから +転記・派生したものです: + +- https://github.com/digital-go-jp/design-system-example-components-react — Copyright (c) 2025 デジタル庁 +- https://github.com/digital-go-jp/design-tokens — Copyright (c) 2023 デジタル庁 +- https://github.com/digital-go-jp/tailwind-theme-plugin +- https://github.com/digital-go-jp/design-system-example-components-html + +本パッケージはデザインツール用の非公式な再現であり、デジタル庁とは無関係・非公認です +(詳細は `readme.md` 冒頭の免責を参照)。「デジタル庁」のロゴ・ワードマークは政府の公式アイデンティティ +であり、本パッケージのブランドマークはプレースホルダーです。 + +--- + +MIT License + +Copyright (c) 2023-2025 デジタル庁 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.agents/skills/dads-design/SKILL.md b/.agents/skills/dads-design/SKILL.md new file mode 100644 index 00000000..601c44dc --- /dev/null +++ b/.agents/skills/dads-design/SKILL.md @@ -0,0 +1,45 @@ +--- +name: dads-design +description: 'デジタル庁デザインシステム(DADS)v2 の忠実再現パッケージ。公式トークン値(blue-900 #0017c1 / yellow-300 フォーカスリング / Solid Gray 階調)・Noto Sans JP タイプスケール・React コンポーネント 13 種・行政ポータル UI キットを同梱。DADS 準拠のプロトタイプ / モック / スライド / デザインアセットを HTML で生成するとき、または公式 DADS の正確なトークン値・コンポーネント構造を参照したいときに使うこと。※devtools 本体(src/ 配下)の UI 実装規約は dads-design-system スキルと CLAUDE.md §7 が正本(本体のカラー値はこのスキルと異なる)。' +user-invocable: true +--- + +# DADS デザインシステム(忠実再現版) + +まずこのスキル内の `readme.md` を読むこと。コンテンツ原則(公共サービス日本語・必須/任意バッジ・絵文字禁止)、ビジュアル基盤(カラー・タイポグラフィ・余白・エレベーション・フォーカスリング)、アイコン方針が全て記載されている。 + +## 用途の切り分け(重要) + +| 作業対象 | 参照先 | +| -------------------------------------------------------------- | ----------------------------------------------------------------------- | +| 使い捨てプロトタイプ / モック / スライド / デザインアセット生成 | **このスキル**。アセットをコピーして静的 HTML を作成しユーザーに見せる | +| 公式 DADS のトークン値・コンポーネント構造の正確な参照 | **このスキル**(公式リポジトリから転記した忠実値) | +| devtools 本体(`src/` 配下)の UI 実装 | `dads-design-system` スキル + `CLAUDE.md` §7(本体は `--color-primary: #1a56db` 等の独自適応値。このスキルの `#0017c1` をそのまま持ち込まない) | + +ガイダンスなしで起動された場合は、何を作りたいかをユーザーに質問し、デザイナーとして HTML アーティファクトまたはプロダクションコードを出し分けること。 + +## 主要ファイル + +- `styles.css` — 全トークン・タイプクラス・コンポーネントスタイルを `@import` する単一エントリポイント。HTML から相対パスで link するだけで使える +- `tokens/` — colors / typography(`text-std-16N-170` 形式のユーティリティクラス含む)/ spacing(余白・角丸・エレベーション・フォーカスリング)/ fonts(Google Fonts 経由 Noto Sans JP/Mono)/ base(リセット + focus) +- `components/` — React プリミティブ 13 種(Button, Input, Select, Checkbox, Radio, Textarea, Label, NotificationBanner, StatusBadge, Accordion, Breadcrumbs, ChipLabel, Table)。各 `*.prompt.md` に用途と JSX 例、`*.d.ts` に props 契約 +- `lib/dads.jsx` — 全コンポーネントを `window.DADS_952a55` に登録するスタンドアロン版(Babel standalone + CDN React で動作) +- `guidelines/` — カラー / タイポグラフィ / 余白 / ブランドの specimen HTML(styles.css を実 link した見本帳) +- `ui_kits/gov-portal/` — 行政手続きポータルの実働 UI キット(ホーム・手続き一覧・FAQ・3 ステップ申請フロー)。`index.html` をブラウザで開けばそのまま動く +- `assets/` — ブランドマーク(プレースホルダー favicon.svg)とサンプル写真 + +## DADS デザインの絶対条件 + +- **デュアルフォーカスリング**を全インタラクティブ要素に維持: 2px `yellow-300` (#ffd43d) リング + 4px 黒アウトライン(offset 2px)。削除厳禁 +- プライマリアクションは `blue-900` (#0017c1)、リンクは `blue-1000`、押下は `blue-1200` +- 平易で丁寧な日本語(です・ます調)。フィールドには必ず 必須/任意 を明記 +- プロダクト UI に絵文字を使わない(アイコンは inline SVG + `currentColor`) +- 装飾より余白と高コントラスト。hover は色変化 + 下線(色だけに頼らない) +- 本文 16px 以上・行高 170%。フォントは Noto Sans JP / Noto Sans Mono のみ + +## 注意(caveats) + +- ロゴはプレースホルダー(公式デジタル庁ワードマークではない)。実用前に差し替えること +- フォントは Google Fonts から CDN 読み込み。本番では self-host 推奨 +- 値は公式リポジトリ(digital-go-jp/design-tokens の figma/tokens.json 等)からの転記。精度が必要な場合は最新版と照合すること(詳細は readme.md の Sources 参照) +- 出典の MIT ライセンス帰属表示は `LICENSE.md` を参照(Copyright デジタル庁。本パッケージは非公式・非公認の再現) diff --git a/.agents/skills/dads-design/assets/favicon.svg b/.agents/skills/dads-design/assets/favicon.svg new file mode 100644 index 00000000..fec81885 --- /dev/null +++ b/.agents/skills/dads-design/assets/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/.agents/skills/dads-design/assets/img/photo-1.webp b/.agents/skills/dads-design/assets/img/photo-1.webp new file mode 100644 index 00000000..473931fa Binary files /dev/null and b/.agents/skills/dads-design/assets/img/photo-1.webp differ diff --git a/.agents/skills/dads-design/assets/img/photo-2.webp b/.agents/skills/dads-design/assets/img/photo-2.webp new file mode 100644 index 00000000..b832dcb0 Binary files /dev/null and b/.agents/skills/dads-design/assets/img/photo-2.webp differ diff --git a/.agents/skills/dads-design/assets/img/photo-3.webp b/.agents/skills/dads-design/assets/img/photo-3.webp new file mode 100644 index 00000000..ae4cdaee Binary files /dev/null and b/.agents/skills/dads-design/assets/img/photo-3.webp differ diff --git a/.agents/skills/dads-design/assets/img/photo-4.webp b/.agents/skills/dads-design/assets/img/photo-4.webp new file mode 100644 index 00000000..efce07bf Binary files /dev/null and b/.agents/skills/dads-design/assets/img/photo-4.webp differ diff --git a/.agents/skills/dads-design/assets/img/photo-5.webp b/.agents/skills/dads-design/assets/img/photo-5.webp new file mode 100644 index 00000000..233ddff2 Binary files /dev/null and b/.agents/skills/dads-design/assets/img/photo-5.webp differ diff --git a/.agents/skills/dads-design/components/components.css b/.agents/skills/dads-design/components/components.css new file mode 100644 index 00000000..fd063252 --- /dev/null +++ b/.agents/skills/dads-design/components/components.css @@ -0,0 +1,210 @@ +/* ============================================================= + DADS — Component styles + Class-based styling for the reusable primitives. The React + components in /components apply these classes; consumers who + only link styles.css get the same styling for free. + Every interactive element carries the DADS dual focus ring. + ============================================================= */ + +/* ---- shared focus ring helper ---- */ +.dads-focus:focus-visible, +.dads-btn:focus-visible, +.dads-field:focus, +.dads-checkbox:focus-visible, +.dads-radio:focus-visible { + outline: 4px solid var(--color-black); + outline-offset: 2px; + box-shadow: 0 0 0 2px var(--color-focus-yellow); +} + +/* =========================== Button =========================== */ +.dads-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + font-family: var(--font-sans); + font-weight: 700; + line-height: 1; + text-decoration: none; + cursor: pointer; + border: 1px solid transparent; + border-radius: var(--radius-8); + transition: background-color 0.08s ease; + user-select: none; + white-space: nowrap; +} +.dads-btn:focus-visible { border-radius: var(--radius-8); } + +/* sizes */ +.dads-btn--lg { min-width: 136px; min-height: 56px; padding: 12px 16px; font-size: 16px; border-radius: var(--radius-8); } +.dads-btn--md { min-width: 96px; min-height: 48px; padding: 8px 16px; font-size: 16px; border-radius: var(--radius-8); } +.dads-btn--sm { min-width: 80px; min-height: 36px; padding: 2px 12px; font-size: 16px; border-radius: var(--radius-6); } +.dads-btn--xs { min-width: 72px; min-height: 28px; padding: 2px 8px; font-size: 14px; border-radius: var(--radius-4); } + +/* variant · solid-fill (4px double border is a DADS signature) */ +.dads-btn--solid-fill { background: var(--color-blue-900); color: #fff; border: 4px double transparent; } +.dads-btn--solid-fill:hover { background: var(--color-blue-1000); text-decoration: underline; } +.dads-btn--solid-fill:active { background: var(--color-blue-1200); text-decoration: underline; } +.dads-btn--solid-fill[aria-disabled="true"], +.dads-btn--solid-fill:disabled { background: var(--color-solid-gray-300); color: var(--color-solid-gray-50); pointer-events: none; } + +/* variant · outline */ +.dads-btn--outline { background: #fff; color: var(--color-blue-900); border: 1px solid currentColor; } +.dads-btn--outline:hover { background: var(--color-blue-200); color: var(--color-blue-1000); text-decoration: underline; } +.dads-btn--outline:active { background: var(--color-blue-300); color: var(--color-blue-1200); text-decoration: underline; } +.dads-btn--outline[aria-disabled="true"], +.dads-btn--outline:disabled { background: #fff; color: var(--color-solid-gray-300); pointer-events: none; } + +/* variant · text */ +.dads-btn--text { background: transparent; color: var(--color-blue-900); text-decoration: underline; text-underline-offset: 3px; } +.dads-btn--text:hover { background: var(--color-blue-50); color: var(--color-blue-1000); text-decoration-thickness: 3px; } +.dads-btn--text:active { background: var(--color-blue-100); color: var(--color-blue-1200); } +.dads-btn--text[aria-disabled="true"], +.dads-btn--text:disabled { background: transparent; color: var(--color-solid-gray-300); pointer-events: none; } + +/* =========================== Fields =========================== */ +.dads-field { + font-family: var(--font-sans); + font-size: 16px; + line-height: 1; + color: var(--text-body); + background: #fff; + border: 1px solid var(--border-field); + border-radius: var(--radius-8); + padding: 0 16px; + max-width: 100%; +} +.dads-field::placeholder { color: var(--text-placeholder); } +.dads-field:hover:read-write { border-color: var(--color-black); } +.dads-field--lg { height: 56px; } +.dads-field--md { height: 48px; } +.dads-field--sm { height: 40px; } +.dads-field[aria-invalid="true"] { border-color: var(--color-error-1); } +.dads-field[aria-invalid="true"]:hover:read-write { border-color: var(--color-red-1000); } +.dads-field:read-only { border-style: dashed; } +.dads-field[aria-disabled="true"], +.dads-field:disabled { + border-style: solid; border-color: var(--color-solid-gray-300); + background: var(--color-solid-gray-50); color: var(--text-disabled); + pointer-events: none; +} + +/* textarea variant */ +textarea.dads-field { line-height: 1.7; padding: 12px 16px; height: auto; min-height: 120px; resize: vertical; } + +/* select wrapper + chevron */ +.dads-select-wrap { position: relative; display: inline-block; } +.dads-select-wrap select.dads-field { appearance: none; width: 100%; padding-right: 40px; } +.dads-select-wrap .dads-select-chevron { + position: absolute; right: 16px; top: 50%; transform: translateY(-50%); + pointer-events: none; color: var(--color-solid-gray-900); +} + +/* =========================== Checkbox / Radio =========================== */ +.dads-choice { display: inline-flex; align-items: flex-start; gap: 8px; width: fit-content; padding: 8px 0; cursor: pointer; } +.dads-choice__box { display: flex; align-items: center; justify-content: center; flex-shrink: 0; width: 24px; height: 24px; border-radius: 3px; } +.dads-choice:hover .dads-choice__box:not(:has(:focus-visible)) { background: var(--color-solid-gray-420); } +.dads-radio.dads-choice__box, .dads-choice__box.dads-radio { border-radius: var(--radius-full); } +.dads-choice__label { color: var(--text-body); font: 400 16px/1.3 var(--font-sans); padding-top: 1px; } + +.dads-checkbox, .dads-radio { + appearance: none; -webkit-appearance: none; margin: 0; + width: 18px; height: 18px; background: #fff; border: 2px solid var(--border-field); + display: grid; place-content: center; cursor: pointer; +} +.dads-checkbox { border-radius: 2px; } +.dads-radio { border-radius: var(--radius-full); } +.dads-checkbox:hover, .dads-radio:hover { border-color: var(--color-black); } +.dads-checkbox::before { + content: ""; width: 14px; height: 14px; transform: scale(0); transform-origin: center; + background: #fff; clip-path: path('M5.6,11.2L12.65,4.15L11.25,2.75L5.6,8.4L2.75,5.55L1.35,6.95L5.6,11.2Z'); +} +.dads-checkbox:checked { background: var(--color-blue-900); border-color: var(--color-blue-900); } +.dads-checkbox:checked::before { transform: scale(1); } +.dads-checkbox:checked:hover { background: var(--color-blue-1100); border-color: var(--color-blue-1100); } +.dads-radio::before { + content: ""; width: 100%; height: 100%; transform: scale(0); + background: var(--color-blue-900); clip-path: circle(31.25%); +} +.dads-radio:checked { border-color: var(--color-blue-900); } +.dads-radio:checked::before { transform: scale(1); } +.dads-checkbox[aria-invalid="true"], .dads-radio[aria-invalid="true"] { border-color: var(--color-error-1); } +.dads-checkbox[aria-invalid="true"]:checked { background: var(--color-error-1); } +.dads-radio[aria-invalid="true"]:checked::before { background: var(--color-error-1); } +.dads-checkbox:disabled, .dads-radio:disabled { border-color: var(--color-solid-gray-300) !important; background: var(--color-solid-gray-50); pointer-events: none; } +.dads-checkbox:disabled:checked { background: var(--color-solid-gray-300); } + +/* =========================== Chip / Tag =========================== */ +.dads-chip { + display: inline-flex; align-items: center; gap: 4px; min-height: 32px; + border-radius: var(--radius-8); font: 400 16px/1 var(--font-sans); + padding: 4px 8px; overflow-wrap: anywhere; +} +.dads-chip--outline { padding: 3px 7px; border: 1px solid currentColor; } +.dads-chip--filled-outline { padding: 3px 7px; border: 1px solid currentColor; } +.dads-chip--fill { padding: 3px 7px; border: 1px solid transparent; color: #fff; } + +/* =========================== Status badge =========================== */ +.dads-status-badge { + display: inline-block; border-radius: 8px; background: var(--color-solid-gray-536); + padding: 8px; font: 400 16px/1 var(--font-sans); color: #fff; +} + +/* =========================== Link =========================== */ +.dads-link { + color: var(--link); text-decoration: underline; text-underline-offset: 3px; +} +.dads-link:visited { color: var(--color-magenta-900); } +.dads-link:hover { color: var(--color-blue-1000); text-decoration-thickness: 3px; } +.dads-link:active { color: var(--color-orange-800); text-decoration-thickness: 1px; } + +/* =========================== Breadcrumbs =========================== */ +.dads-breadcrumbs ol { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: center; } +.dads-breadcrumbs li { display: inline-flex; align-items: center; font: 400 16px/1 var(--font-sans); color: var(--text-subtle); } +.dads-breadcrumbs li:not([aria-current]) .dads-bc-sep { margin: 0 8px; color: var(--text-subtle); display: inline-flex; } +.dads-breadcrumbs a { color: var(--link); text-decoration: underline; text-underline-offset: 3px; } +.dads-breadcrumbs a:hover { color: var(--color-blue-900); text-decoration-thickness: 3px; } +.dads-breadcrumbs [aria-current="page"] { color: var(--text-body); } + +/* =========================== Accordion =========================== */ +.dads-accordion { border-bottom: 1px solid var(--border-divider); } +.dads-accordion__summary { + list-style: none; cursor: pointer; position: relative; + padding: 12px 8px 12px 44px; font: 700 16px/1.7 var(--font-sans); color: var(--text-heading); +} +.dads-accordion__summary::-webkit-details-marker { display: none; } +.dads-accordion__summary:hover { background: var(--color-solid-gray-50); } +.dads-accordion__icon { + position: absolute; left: 6px; top: 50%; transform: translateY(-50%); + width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center; + border: 1px solid currentColor; border-radius: var(--radius-full); color: var(--color-blue-1000); background: #fff; + transition: transform 0.15s ease; +} +.dads-accordion[open] .dads-accordion__icon { transform: translateY(-50%) rotate(180deg); } +.dads-accordion__content { padding: 8px 16px 24px 44px; font: 400 16px/1.7 var(--font-sans); color: var(--text-body); } + +/* =========================== Notification banner =========================== */ +.dads-banner { + display: grid; grid-template-columns: auto 1fr; gap: 8px 16px; + padding: 16px 24px 24px; border: 3px solid currentColor; border-radius: var(--radius-12); +} +.dads-banner__head { display: flex; align-items: flex-start; gap: 12px; grid-column: 1 / -1; } +.dads-banner__icon { flex-shrink: 0; width: 28px; height: 28px; } +.dads-banner__title { font: 700 18px/1.5 var(--font-sans); color: var(--text-heading); } +.dads-banner__body { grid-column: 1 / -1; color: var(--text-body); font: 400 16px/1.7 var(--font-sans); } +.dads-banner--info1 { color: var(--color-blue-900); } +.dads-banner--info2 { color: var(--color-solid-gray-536); } +.dads-banner--success { color: var(--color-success-2); } +.dads-banner--warning { color: var(--color-warning-yellow-2); } +.dads-banner--error { color: var(--color-error-1); } +/* color-chip style: thin border + thick inset left bar */ +.dads-banner--chip { border-width: 2px; padding-left: 40px; box-shadow: inset 16px 0 0 0 var(--chip-color, currentColor); border-radius: var(--radius-12); } + +/* =========================== Table =========================== */ +.dads-table { border-collapse: collapse; width: 100%; font: 400 16px/1.5 var(--font-sans); color: var(--text-body); } +.dads-table caption { text-align: left; font: 700 18px/1.6 var(--font-sans); color: var(--text-heading); padding-bottom: 12px; } +.dads-table th, .dads-table td { text-align: left; padding: 12px 16px; border-bottom: 1px solid var(--border-divider); vertical-align: top; } +.dads-table thead th { background: var(--color-solid-gray-50); color: var(--text-heading); font-weight: 700; border-bottom: 2px solid var(--color-solid-gray-400); } +.dads-table tbody tr:hover { background: var(--color-blue-50); } +.dads-table th[scope="row"] { font-weight: 700; color: var(--text-heading); } diff --git a/.agents/skills/dads-design/components/data-display/ChipLabel.d.ts b/.agents/skills/dads-design/components/data-display/ChipLabel.d.ts new file mode 100644 index 00000000..3373c3a9 --- /dev/null +++ b/.agents/skills/dads-design/components/data-display/ChipLabel.d.ts @@ -0,0 +1,18 @@ +import type { HTMLAttributes, ReactNode } from 'react'; + +export type ChipVariant = 'text' | 'outline' | 'filled-outline' | 'fill'; +export type ChipColor = + | 'gray' | 'blue' | 'light-blue' | 'cyan' | 'green' | 'lime' + | 'yellow' | 'orange' | 'red' | 'magenta' | 'purple'; + +/** Small categorisation tag / chip. */ +export interface ChipLabelProps extends HTMLAttributes { + /** Visual treatment. @default 'text' */ + variant?: ChipVariant; + /** Accent colour. @default 'gray' */ + color?: ChipColor; + children?: ReactNode; + className?: string; +} + +export function ChipLabel(props: ChipLabelProps): JSX.Element; diff --git a/.agents/skills/dads-design/components/data-display/ChipLabel.jsx b/.agents/skills/dads-design/components/data-display/ChipLabel.jsx new file mode 100644 index 00000000..a97598b1 --- /dev/null +++ b/.agents/skills/dads-design/components/data-display/ChipLabel.jsx @@ -0,0 +1,55 @@ +import React from 'react'; + +// Per-colour shade map lifted from the DADS ChipLabel source. +const MAIN = { + gray: 'var(--color-solid-gray-800)', + blue: 'var(--color-blue-700)', + 'light-blue': 'var(--color-light-blue-800)', + cyan: 'var(--color-cyan-900)', + green: 'var(--color-green-800)', + lime: 'var(--color-lime-900)', + yellow: 'var(--color-yellow-1000)', + orange: 'var(--color-orange-900)', + red: 'var(--color-red-900)', + magenta: 'var(--color-magenta-800)', + purple: 'var(--color-purple-800)', +}; +const FILL = { ...MAIN, gray: 'var(--color-solid-gray-700)' }; +const BG50 = { + gray: 'var(--color-solid-gray-50)', + blue: 'var(--color-blue-50)', + 'light-blue': 'var(--color-light-blue-50)', + cyan: 'var(--color-cyan-50)', + green: 'var(--color-green-50)', + lime: 'var(--color-lime-50)', + yellow: 'var(--color-yellow-50)', + orange: 'var(--color-orange-50)', + red: 'var(--color-red-50)', + magenta: 'var(--color-magenta-50)', + purple: 'var(--color-purple-50)', +}; + +/** + * DADS ChipLabel — a small categorisation tag. + * variant: text | outline | filled-outline | fill. 11 colours. + */ +export function ChipLabel(props) { + const { variant = 'text', color = 'gray', className = '', children, style, ...rest } = props; + + let s = {}; + if (variant === 'text') s = { color: MAIN[color] }; + else if (variant === 'outline') s = { color: MAIN[color], borderColor: MAIN[color] }; + else if (variant === 'filled-outline') + s = { color: MAIN[color], borderColor: MAIN[color], background: BG50[color] }; + else if (variant === 'fill') s = { background: FILL[color] }; + + return ( + + {children} + + ); +} diff --git a/.agents/skills/dads-design/components/data-display/ChipLabel.prompt.md b/.agents/skills/dads-design/components/data-display/ChipLabel.prompt.md new file mode 100644 index 00000000..3fd4e632 --- /dev/null +++ b/.agents/skills/dads-design/components/data-display/ChipLabel.prompt.md @@ -0,0 +1,10 @@ +Chip / tag — categorise or label content. Choose `variant` for emphasis and `color` for meaning. + +```jsx +新着 +受付中 +下書き +``` + +- `variant`: `text` · `outline` · `filled-outline` · `fill`. +- `color`: 11 options (gray, blue, light-blue, cyan, green, lime, yellow, orange, red, magenta, purple). diff --git a/.agents/skills/dads-design/components/data-display/Table.d.ts b/.agents/skills/dads-design/components/data-display/Table.d.ts new file mode 100644 index 00000000..8e736863 --- /dev/null +++ b/.agents/skills/dads-design/components/data-display/Table.d.ts @@ -0,0 +1,16 @@ +import type { TableHTMLAttributes, ReactNode } from 'react'; + +/** Data table with the DADS header + row-hover treatment. */ +export interface TableProps extends TableHTMLAttributes { + /** Optional caption shown above the table. */ + caption?: ReactNode; + /** Column headers (used when `rows` is provided). */ + columns?: ReactNode[]; + /** Row data as a 2-D array (used with `columns`). */ + rows?: ReactNode[][]; + /** Alternatively, compose / yourself. */ + children?: ReactNode; + className?: string; +} + +export function Table(props: TableProps): JSX.Element; diff --git a/.agents/skills/dads-design/components/data-display/Table.jsx b/.agents/skills/dads-design/components/data-display/Table.jsx new file mode 100644 index 00000000..d005cdda --- /dev/null +++ b/.agents/skills/dads-design/components/data-display/Table.jsx @@ -0,0 +1,40 @@ +import React from 'react'; + +/** + * DADS Table — data table with the standard header treatment. + * Pass `columns` and `rows`, or compose children directly. + */ +export function Table(props) { + const { caption, columns, rows, children, className = '', ...rest } = props; + + if (children) { + return ( + + {caption && } + {children} +
{caption}
+ ); + } + + return ( + + {caption && } + + + {columns.map((c, i) => ( + + ))} + + + + {rows.map((row, ri) => ( + + {row.map((cell, ci) => ( + + ))} + + ))} + +
{caption}
{c}
{cell}
+ ); +} diff --git a/.agents/skills/dads-design/components/data-display/Table.prompt.md b/.agents/skills/dads-design/components/data-display/Table.prompt.md new file mode 100644 index 00000000..a0929d3b --- /dev/null +++ b/.agents/skills/dads-design/components/data-display/Table.prompt.md @@ -0,0 +1,14 @@ +Data table — quickest path is the `columns` + `rows` shorthand; rows highlight on hover. + +```jsx + +``` + +- For complex tables (row headers, colspans), pass `/` as children instead. diff --git a/.agents/skills/dads-design/components/feedback/NotificationBanner.d.ts b/.agents/skills/dads-design/components/feedback/NotificationBanner.d.ts new file mode 100644 index 00000000..4cb485d2 --- /dev/null +++ b/.agents/skills/dads-design/components/feedback/NotificationBanner.d.ts @@ -0,0 +1,22 @@ +import type { ReactNode, HTMLAttributes } from 'react'; + +export type NotificationType = 'info1' | 'info2' | 'success' | 'warning' | 'error'; +export type NotificationStyle = 'standard' | 'color-chip'; + +/** + * Page-level notification / alert banner. + * + * @startingPoint section="Feedback" subtitle="Info / success / warning / error message blocks" viewport="700x200" + */ +export interface NotificationBannerProps extends HTMLAttributes { + /** Severity — drives colour and icon. @default 'info1' */ + type?: NotificationType; + /** 'standard' full border, or 'color-chip' inset left bar. @default 'standard' */ + bannerStyle?: NotificationStyle; + /** Bold heading line. */ + title: string; + children?: ReactNode; + className?: string; +} + +export function NotificationBanner(props: NotificationBannerProps): JSX.Element; diff --git a/.agents/skills/dads-design/components/feedback/NotificationBanner.jsx b/.agents/skills/dads-design/components/feedback/NotificationBanner.jsx new file mode 100644 index 00000000..7cd64ae6 --- /dev/null +++ b/.agents/skills/dads-design/components/feedback/NotificationBanner.jsx @@ -0,0 +1,46 @@ +import React from 'react'; + +const ICONS = { + info1: 'M12 2a10 10 0 100 20 10 10 0 000-20zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z', + info2: 'M12 2a10 10 0 100 20 10 10 0 000-20zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z', + success: 'M12 2a10 10 0 100 20 10 10 0 000-20zm-2 15l-5-5 1.4-1.4L10 14.2l7.6-7.6L19 8l-9 9z', + warning: 'M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z', + error: 'M12 2a10 10 0 100 20 10 10 0 000-20zm5 13.6L15.6 17 12 13.4 8.4 17 7 15.6 10.6 12 7 8.4 8.4 7 12 10.6 15.6 7 17 8.4 13.4 12 17 15.6z', +}; + +/** + * DADS NotificationBanner — page-level message block. + * type sets colour + icon; bannerStyle 'standard' is a full border, + * 'color-chip' a thin border with a thick inset left bar. + */ +export function NotificationBanner(props) { + const { + type = 'info1', + bannerStyle = 'standard', + title, + children, + className = '', + ...rest + } = props; + + const classes = [ + 'dads-banner', + `dads-banner--${type}`, + bannerStyle === 'color-chip' ? 'dads-banner--chip' : '', + className, + ] + .filter(Boolean) + .join(' '); + + return ( +
+
+ + {title} +
+ {children &&
{children}
} +
+ ); +} diff --git a/.agents/skills/dads-design/components/feedback/NotificationBanner.prompt.md b/.agents/skills/dads-design/components/feedback/NotificationBanner.prompt.md new file mode 100644 index 00000000..8a20997d --- /dev/null +++ b/.agents/skills/dads-design/components/feedback/NotificationBanner.prompt.md @@ -0,0 +1,11 @@ +Page-level alert — announce status, validation summaries, or system notices. + +```jsx + + 入力内容に誤りがあります。赤色の項目をご確認ください。 + + +``` + +- `type`: `info1` (blue), `info2` (grey), `success` (green), `warning` (yellow), `error` (red). +- `bannerStyle`: `standard` (3px border) or `color-chip` (thin border + thick inset left bar). diff --git a/.agents/skills/dads-design/components/feedback/StatusBadge.d.ts b/.agents/skills/dads-design/components/feedback/StatusBadge.d.ts new file mode 100644 index 00000000..765b7fb0 --- /dev/null +++ b/.agents/skills/dads-design/components/feedback/StatusBadge.d.ts @@ -0,0 +1,13 @@ +import type { HTMLAttributes, ReactNode } from 'react'; + +export type StatusBadgeTone = 'neutral' | 'info' | 'success' | 'warning' | 'error'; + +/** Small inline status pill (e.g. 受付中 / 完了 / エラー). */ +export interface StatusBadgeProps extends HTMLAttributes { + /** Semantic colour. @default 'neutral' */ + tone?: StatusBadgeTone; + children?: ReactNode; + className?: string; +} + +export function StatusBadge(props: StatusBadgeProps): JSX.Element; diff --git a/.agents/skills/dads-design/components/feedback/StatusBadge.jsx b/.agents/skills/dads-design/components/feedback/StatusBadge.jsx new file mode 100644 index 00000000..6ac90b06 --- /dev/null +++ b/.agents/skills/dads-design/components/feedback/StatusBadge.jsx @@ -0,0 +1,27 @@ +import React from 'react'; + +/** + * DADS StatusBadge — small inline status pill. + * Default is neutral grey; pass a `tone` for semantic colour. + */ +export function StatusBadge(props) { + const { tone = 'neutral', className = '', children, style, ...rest } = props; + + const tones = { + neutral: { background: 'var(--color-solid-gray-536)', color: '#fff' }, + info: { background: 'var(--color-blue-900)', color: '#fff' }, + success: { background: 'var(--color-success-2)', color: '#fff' }, + warning: { background: 'var(--color-warning-yellow-2)', color: '#fff' }, + error: { background: 'var(--color-error-1)', color: '#fff' }, + }; + + return ( + + {children} + + ); +} diff --git a/.agents/skills/dads-design/components/feedback/StatusBadge.prompt.md b/.agents/skills/dads-design/components/feedback/StatusBadge.prompt.md new file mode 100644 index 00000000..ba1ce9a0 --- /dev/null +++ b/.agents/skills/dads-design/components/feedback/StatusBadge.prompt.md @@ -0,0 +1,9 @@ +Status pill — a compact filled label for record or process state. + +```jsx +完了 +確認中 +下書き +``` + +- `tone`: `neutral` (default grey) · `info` · `success` · `warning` · `error`. diff --git a/.agents/skills/dads-design/components/forms/Button.d.ts b/.agents/skills/dads-design/components/forms/Button.d.ts new file mode 100644 index 00000000..b664147a --- /dev/null +++ b/.agents/skills/dads-design/components/forms/Button.d.ts @@ -0,0 +1,24 @@ +import type { ButtonHTMLAttributes, ReactNode } from 'react'; + +export type ButtonVariant = 'solid-fill' | 'outline' | 'text'; +export type ButtonSize = 'lg' | 'md' | 'sm' | 'xs'; + +/** + * Primary action button for the Digital Agency Design System. + * + * @startingPoint section="Forms" subtitle="Primary / secondary / tertiary actions in 4 sizes" viewport="700x180" + */ +export interface ButtonProps extends ButtonHTMLAttributes { + /** Visual emphasis. @default 'solid-fill' */ + variant?: ButtonVariant; + /** Control size. @default 'md' */ + size?: ButtonSize; + /** Render as an anchor pointing here instead of a + ); +} diff --git a/.agents/skills/dads-design/components/forms/Button.prompt.md b/.agents/skills/dads-design/components/forms/Button.prompt.md new file mode 100644 index 00000000..622dc091 --- /dev/null +++ b/.agents/skills/dads-design/components/forms/Button.prompt.md @@ -0,0 +1,12 @@ +Primary action button — use for the main call-to-action; pick `variant` by emphasis and `size` by density. + +```jsx + + + +``` + +- `variant`: `solid-fill` (primary, blue-900 fill), `outline` (secondary), `text` (tertiary, underlined). +- `size`: `lg` (56px) · `md` (48px) · `sm` (36px) · `xs` (28px). +- Pass `href` to render an `` styled as a button. `disabled` sets `aria-disabled`. +- Hover adds an underline; the dual yellow/black focus ring is built in. diff --git a/.agents/skills/dads-design/components/forms/Checkbox.d.ts b/.agents/skills/dads-design/components/forms/Checkbox.d.ts new file mode 100644 index 00000000..d368c3a4 --- /dev/null +++ b/.agents/skills/dads-design/components/forms/Checkbox.d.ts @@ -0,0 +1,12 @@ +import type { InputHTMLAttributes, ReactNode } from 'react'; + +/** Checkbox with optional inline label. */ +export interface CheckboxProps extends Omit, 'size' | 'type'> { + /** Label rendered to the right of the box. */ + children?: ReactNode; + isError?: boolean; + disabled?: boolean; + className?: string; +} + +export function Checkbox(props: CheckboxProps): JSX.Element; diff --git a/.agents/skills/dads-design/components/forms/Checkbox.jsx b/.agents/skills/dads-design/components/forms/Checkbox.jsx new file mode 100644 index 00000000..398ed37d --- /dev/null +++ b/.agents/skills/dads-design/components/forms/Checkbox.jsx @@ -0,0 +1,30 @@ +import React from 'react'; + +/** + * DADS Checkbox — accessible checkbox with optional label. + */ +export function Checkbox(props) { + const { children, isError = false, disabled = false, className = '', ...rest } = props; + + const input = ( + + + + ); + + if (!children) return input; + + return ( + + ); +} diff --git a/.agents/skills/dads-design/components/forms/Checkbox.prompt.md b/.agents/skills/dads-design/components/forms/Checkbox.prompt.md new file mode 100644 index 00000000..ab041db7 --- /dev/null +++ b/.agents/skills/dads-design/components/forms/Checkbox.prompt.md @@ -0,0 +1,9 @@ +Checkbox with an optional inline label — the blue-900 check mark uses a clip-path glyph. + +```jsx +利用規約に同意する +必須項目です +``` + +- Pass label text as children; omit children for a bare box. +- `isError` turns the box red; `disabled` greys it out. diff --git a/.agents/skills/dads-design/components/forms/Input.d.ts b/.agents/skills/dads-design/components/forms/Input.d.ts new file mode 100644 index 00000000..4554ce02 --- /dev/null +++ b/.agents/skills/dads-design/components/forms/Input.d.ts @@ -0,0 +1,15 @@ +import type { InputHTMLAttributes } from 'react'; + +export type InputBlockSize = 'lg' | 'md' | 'sm'; + +/** Single-line text field. */ +export interface InputProps extends Omit, 'size'> { + /** Field height. @default 'lg' */ + blockSize?: InputBlockSize; + /** Error state — sets aria-invalid and the error border. */ + isError?: boolean; + disabled?: boolean; + className?: string; +} + +export function Input(props: InputProps): JSX.Element; diff --git a/.agents/skills/dads-design/components/forms/Input.jsx b/.agents/skills/dads-design/components/forms/Input.jsx new file mode 100644 index 00000000..e701fe2f --- /dev/null +++ b/.agents/skills/dads-design/components/forms/Input.jsx @@ -0,0 +1,30 @@ +import React from 'react'; + +/** + * DADS Input — single-line text field. + * Sizes lg/md/sm. Error and disabled states map to the DADS + * field treatment (dashed border when read-only). + */ +export function Input(props) { + const { + blockSize = 'lg', + isError = false, + disabled = false, + className = '', + ...rest + } = props; + + const classes = ['dads-field', `dads-field--${blockSize}`, className] + .filter(Boolean) + .join(' '); + + return ( + + ); +} diff --git a/.agents/skills/dads-design/components/forms/Input.prompt.md b/.agents/skills/dads-design/components/forms/Input.prompt.md new file mode 100644 index 00000000..ce21eb53 --- /dev/null +++ b/.agents/skills/dads-design/components/forms/Input.prompt.md @@ -0,0 +1,9 @@ +Single-line text input — pair with a `