diff --git a/docs/component/breadcrumb-specification.md b/docs/component/breadcrumb-specification.md new file mode 100644 index 000000000..9ed6684ef --- /dev/null +++ b/docs/component/breadcrumb-specification.md @@ -0,0 +1,155 @@ +# Breadcrumb コンポーネント仕様書 + +## 目次 + +1. [概要](#概要) +2. [インポート](#インポート) +3. [基本的な使用方法](#基本的な使用方法) +4. [Props](#props) + - [必須プロパティ](#必須プロパティ) + - [オプションプロパティ](#オプションプロパティ) + - [継承プロパティ](#継承プロパティ) +5. [コンポジション(子コンポーネント)](#コンポジション子コンポーネント) +6. [状態とスタイル](#状態とスタイル) + - [状態に応じたスタイル](#状態に応じたスタイル) + - [その他のスタイル仕様](#その他のスタイル仕様) +7. [使用例](#使用例) + - [基本的な使用例](#基本的な使用例) + - [現在位置を明示する例](#現在位置を明示する例) +8. [アクセシビリティ](#アクセシビリティ) +9. [技術的な詳細](#技術的な詳細) +10. [注意事項](#注意事項) +11. [スタイルのカスタマイズ](#スタイルのカスタマイズ) +12. [更新履歴](#更新履歴) + +--- + +## 概要 + +Breadcrumbコンポーネントは、現在のページがサイト階層のどこに位置するかを示し、上位階層へ戻るための経路を提示するナビゲーションコンポーネントである。`Breadcrumb.Item` を子要素として並べ、スラッシュ区切りのリストを生成する。 + +## インポート + +```typescript +import { Breadcrumb } from '@zenkigen-inc/component-ui'; +``` + +## 基本的な使用方法 + +```typescript +import { Breadcrumb } from '@zenkigen-inc/component-ui'; + +const items = [ + { key: 'home', label: 'ホーム', href: '/' }, + { key: 'project', label: '案件一覧', href: '/projects' }, + { key: 'detail', label: '詳細' }, +]; + + + {items.map((item) => ( + + {item.href != null ? {item.label} : item.label} + + ))} +; +``` + +## Props + +### 必須プロパティ + +| プロパティ | 型 | 説明 | +| ---------- | ----------- | ----------------------------------------------------------------- | +| `children` | `ReactNode` | パンくずとして並べる要素。通常は複数の `Breadcrumb.Item` を渡す。 | + +### オプションプロパティ + +本コンポーネント固有のオプションプロパティは存在しない。 + +### 継承プロパティ + +ネイティブ要素の属性を受け取らない(`Breadcrumb` は `nav` に固定の `aria-label="breadcrumb"` を付与する)。 + +## コンポジション(子コンポーネント) + +`Breadcrumb.Item` はパンくずの各階層を表現する子コンポーネントである。 + +| コンポーネント | 必須/オプション | プロパティ | 型 | デフォルト値 | 説明 | +| ----------------- | --------------- | ---------- | ----------- | ------------ | ------------------------------------------------------------------------------------------------- | +| `Breadcrumb.Item` | 必須 | `children` | `ReactNode` | `undefined` | 1階層分の内容。リンクまたはテキストを渡す。最後の階層はリンク無しで現在位置を示すのが推奨である。 | + +## 状態とスタイル + +### 状態に応じたスタイル + +- リンクが含まれる場合、リンクは `text-interactive02` で表示され、ホバー時に下線が付く。 +- 非リンクのテキストは `text-text01` のまま表示される。 + +### その他のスタイル仕様 + +- 文字スタイル: `typography-label14regular` +- レイアウト: `flex` + `flex-wrap` で横方向に並べ、`gap-2` でアイテム間の間隔を確保する。 +- セパレーター: `after:content-['/']` によるスラッシュを各アイテム末尾に表示し、最後のアイテムでは `last:after:content-none` により非表示とする。 +- 折り返し: `whitespace-nowrap` で各アイテム内の折り返しを抑制しつつ、リスト全体は折り返し可能である。 + +## 使用例 + +### 基本的な使用例 + +```typescript + + + ホーム + + + 案件一覧 + + 詳細 + +``` + +### 現在位置を明示する例 + +```typescript + + + ホーム + + + ユーザー + + + プロフィール + + +``` + +## アクセシビリティ + +- `nav` 要素に `aria-label="breadcrumb"` を付与し、ランドマークとして識別できる。 +- リスト構造(`ul` / `li`)で階層を示す。 +- 現在位置を示す最後の要素は、リンクではなくテキストで表示するか、`aria-current="page"` を付与することが推奨である。 +- セパレーターはCSS擬似要素で描画するため、支援技術に読み上げられず、ナビゲーションの意味を妨げない。 + +## 技術的な詳細 + +- `Breadcrumb.Item` を `Breadcrumb.Item = BreadcrumbItem` として公開し、`Breadcrumb` の名前空間配下で利用できるようにしている。 +- セパレーターは `after:content-['/']` と `last:after:content-none` を組み合わせて実装している。 +- リンク色とホバー時の下線は Tailwind クラス `[&_a]:text-interactive02` および `[&_a]:hover:underline` により適用される。 + +## 注意事項 + +1. 子要素は `Breadcrumb.Item` を用いて構築することを推奨する(任意の要素も受け付けるが、スタイルの統一性が損なわれる)。 +2. 現在地の階層はリンクにしないか、`aria-current="page"` を付与して支援技術に伝えること。 +3. セパレーターは固定で `/` のみであり、カスタマイズ用のプロパティは存在しない。 +4. コンポーネント外からクラス名やスタイルを直接渡すAPIは提供していない。スタイルを変更する場合はコンポーネント自体を拡張する。 + +## スタイルのカスタマイズ + +このコンポーネントは Tailwind CSS のユーティリティクラスを使用しており、`@zenkigen-inc/component-config` で定義されたデザイントークンに依存している。カスタマイズする場合は、当該設定を参照すること。 + +## 更新履歴 + +| 日付 | 内容 | 担当者 | +| -------------------- | -------- | ------ | +| 2025-12-03 08:41 JST | 新規作成 | - | diff --git a/packages/component-ui/src/breadcrumb/Docs.mdx b/packages/component-ui/src/breadcrumb/Docs.mdx index 31e8d1a0a..53118239d 100644 --- a/packages/component-ui/src/breadcrumb/Docs.mdx +++ b/packages/component-ui/src/breadcrumb/Docs.mdx @@ -1,17 +1,33 @@ -import { Canvas, Meta, ArgTypes, Story, Controls, Source } from '@storybook/addon-docs/blocks'; +import { Canvas, Markdown, Meta, Story, Source } from '@storybook/addon-docs/blocks'; import BreadcrumbStories, { Base } from './breadcrumb.stories'; +import spec from '../../../../docs/component/breadcrumb-specification.md?raw'; # Breadcrumb +
+ 詳細仕様書 +
+

※ 目次のアンカーリンクは使用できません。

+ {spec} +
+
+ +### Code Example + +- `Breadcrumb.Item` を子要素として並べる。 +- 最終階層はリンクではなくテキスト、もしくは `aria-current="page"` を付与した要素で現在位置を示す。 + + + ## 概要 -ユーザーが現在のページまでの階層を理解し、親の階層へ戻るのをナビゲートするコンポーネントです。 +ユーザーが現在のページまでの階層を理解し、親の階層へ戻るのをナビゲートするコンポーネントである。 ## 設計の原則(Do) diff --git a/packages/component-ui/src/breadcrumb/breadcrumb-item.tsx b/packages/component-ui/src/breadcrumb/breadcrumb-item.tsx index d605d7c9d..2daff1ca9 100644 --- a/packages/component-ui/src/breadcrumb/breadcrumb-item.tsx +++ b/packages/component-ui/src/breadcrumb/breadcrumb-item.tsx @@ -1,6 +1,11 @@ -import type { PropsWithChildren } from 'react'; +import type { ReactNode } from 'react'; -export const BreadcrumbItem = ({ children }: PropsWithChildren) => { +type BreadcrumbItemProps = { + /** パンくずの1階層分の内容。リンクまたはテキストを渡す。 */ + children: ReactNode; +}; + +export const BreadcrumbItem = ({ children }: BreadcrumbItemProps) => { return (
  • {children} diff --git a/packages/component-ui/src/breadcrumb/breadcrumb.tsx b/packages/component-ui/src/breadcrumb/breadcrumb.tsx index dfedb8ad2..8c11ae7e6 100644 --- a/packages/component-ui/src/breadcrumb/breadcrumb.tsx +++ b/packages/component-ui/src/breadcrumb/breadcrumb.tsx @@ -1,8 +1,13 @@ -import type { PropsWithChildren } from 'react'; +import type { ReactNode } from 'react'; import { BreadcrumbItem } from './breadcrumb-item'; -export function Breadcrumb({ children }: PropsWithChildren) { +type BreadcrumbProps = { + /** パンくずとして表示する要素。通常は複数の Breadcrumb.Item を渡す。 */ + children: ReactNode; +}; + +export function Breadcrumb({ children }: BreadcrumbProps) { return (