モダンUIコンポーネントの実装パターン
実装ガイドReact、Vue、Angularで共通して使えるUIパターンを解説
コンテンツを視覚的に区切る線要素。セクション間の境界を明確にし、情報の階層構造を表現。
コンテンツの区切りに使用します
次のセクション
Small
Medium
Large
Solid
Dashed
Dotted
ここから新しい内容が始まります
実線、破線、点線、装飾的なデザインから選択可能
水平・垂直方向、テキスト付き、位置調整に対応
意味のある区切りを提供し、アクセシビリティに配慮
プロンプト例:
スクロールに連動するアニメーションディバイダーを作成してください。波形、パーティクル、グラデーション効果、視差効果、インタラクティブな要素を含め、ページの雰囲気を演出する装飾的な区切り線を実装してください。
プロンプト例:
周囲のコンテンツに応じて自動的にスタイルを調整するスマートディバイダーを開発してください。セクションの重要度、コンテンツの種類、ユーザーの読書進捗に基づいて、太さ・色・スタイルを動的に変更してください。
プロンプト例:
ナラティブコンテンツ用の特別なディバイダーシステムを実装してください。章の区切り、時間経過の表現、場面転換のエフェクト、読者の注意を引く装飾要素を含め、読書体験を向上させてください。
import React from 'react';
import type { BaseDividerProps } from '../types';
interface DividerProps extends BaseDividerProps {
className?: string;
children?: React.ReactNode;
}
export const Divider: React.FC<DividerProps> = ({
orientation = 'horizontal',
variant = 'solid',
size = 'md',
color = 'gray',
align = 'center',
className = '',
children,
}) => {
// サイズに基づくスタイル
const getSizeClasses = () => {
const sizes = {
sm: orientation === 'horizontal' ? 'h-px' : 'w-px',
md: orientation === 'horizontal' ? 'h-0.5' : 'w-0.5',
lg: orientation === 'horizontal' ? 'h-1' : 'w-1',
};
return sizes[size as keyof typeof sizes] || sizes.md;
};
// カラーに基づくスタイル
const getColorClasses = () => {
const colors = {
gray: 'bg-gray-300 border-gray-300',
blue: 'bg-blue-500 border-blue-500',
green: 'bg-green-500 border-green-500',
red: 'bg-red-500 border-red-500',
purple: 'bg-purple-500 border-purple-500',
};
return colors[color as keyof typeof colors] || colors.gray;
};
// バリアントに基づくスタイル
const getVariantClasses = () => {
if (variant === 'dashed') {
return orientation === 'horizontal'
? 'border-t-2 border-dashed bg-transparent'
: 'border-l-2 border-dashed bg-transparent';
} else if (variant === 'dotted') {
return orientation === 'horizontal'
? 'border-t-2 border-dotted bg-transparent'
: 'border-l-2 border-dotted bg-transparent';
}
return '';
};
// テキスト位置の調整
const getAlignClasses = () => {
const alignments = {
start: 'justify-start',
center: 'justify-center',
end: 'justify-end',
};
return alignments[align as keyof typeof alignments] || alignments.center;
};
const dividerClasses = `
${getSizeClasses()}
${getColorClasses()}
${getVariantClasses()}
${orientation === 'vertical' ? 'h-full' : 'w-full'}
`.trim();
// テキスト付きディバイダー
if (children) {
return (
<div
className={`
flex items-center
${orientation === 'horizontal' ? 'flex-row' : 'flex-col'}
${getAlignClasses()}
${className}
`}
>
<div
className={`
${dividerClasses}
${orientation === 'horizontal' ? 'flex-1' : 'flex-1'}
`}
/>
<span
className={`
px-4 py-1 text-sm text-gray-600
${orientation === 'horizontal' ? 'mx-4' : 'my-4'}
`}
>
{children}
</span>
<div
className={`
${dividerClasses}
${orientation === 'horizontal' ? 'flex-1' : 'flex-1'}
`}
/>
</div>
);
}
// シンプルなディバイダー
return <div className={`${dividerClasses} ${className}`} />;
};
// 装飾的なディバイダー
export const DecorativeDivider: React.FC<{
type?: 'dots' | 'wave' | 'gradient';
className?: string;
}> = ({ type = 'dots', className = '' }) => {
if (type === 'dots') {
return (
<div className={`flex items-center justify-center py-4 ${className}`}>
<div className="flex space-x-2">
<div className="w-2 h-2 bg-gray-400 rounded-full"></div>
<div className="w-2 h-2 bg-gray-400 rounded-full"></div>
<div className="w-2 h-2 bg-gray-400 rounded-full"></div>
</div>
</div>
);
}
if (type === 'wave') {
return (
<div className={`w-full py-4 ${className}`}>
<svg
className="w-full h-6"
viewBox="0 0 1200 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0 12C150 12 150 0 300 0C450 0 450 12 600 12C750 12 750 0 900 0C1050 0 1050 12 1200 12"
stroke="#D1D5DB"
strokeWidth="2"
/>
</svg>
</div>
);
}
if (type === 'gradient') {
return (
<div className={`w-full h-1 bg-gradient-to-r from-transparent via-gray-300 to-transparent ${className}`} />
);
}
return null;
};
// セクションディバイダー
export const SectionDivider: React.FC<{
title: string;
subtitle?: string;
icon?: React.ReactNode;
className?: string;
}> = ({ title, subtitle, icon, className = '' }) => {
return (
<div className={`relative py-8 ${className}`}>
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-300"></div>
</div>
<div className="relative flex justify-center">
<div className="bg-white px-6 flex items-center">
{icon && <span className="mr-3">{icon}</span>}
<div className="text-center">
<h3 className="text-lg font-semibold text-gray-900">{title}</h3>
{subtitle && <p className="text-sm text-gray-600">{subtitle}</p>}
</div>
</div>
</div>
</div>
);
};
// ディバイダーのデモコンポーネント
export const DividerDemo: React.FC = () => {
return (
<div className="space-y-12">
{/* 基本的なディバイダー */}
<div className="space-y-4">
<h3 className="text-lg font-semibold">基本的なディバイダー</h3>
<div className="space-y-4">
<Divider />
<div className="p-4 bg-gray-50 rounded">
<p className="mb-4">コンテンツの区切りに使用します</p>
<Divider />
<p className="mt-4">次のセクション</p>
</div>
</div>
</div>
{/* サイズバリエーション */}
<div className="space-y-4">
<h3 className="text-lg font-semibold">サイズバリエーション</h3>
<div className="space-y-4">
<div>
<p className="text-sm text-gray-600 mb-2">Small</p>
<Divider size="sm" />
</div>
<div>
<p className="text-sm text-gray-600 mb-2">Medium</p>
<Divider size="md" />
</div>
<div>
<p className="text-sm text-gray-600 mb-2">Large</p>
<Divider size="lg" />
</div>
</div>
</div>
{/* バリアント */}
<div className="space-y-4">
<h3 className="text-lg font-semibold">バリアント</h3>
<div className="space-y-4">
<div>
<p className="text-sm text-gray-600 mb-2">Solid</p>
<Divider variant="solid" />
</div>
<div>
<p className="text-sm text-gray-600 mb-2">Dashed</p>
<Divider variant="dashed" />
</div>
<div>
<p className="text-sm text-gray-600 mb-2">Dotted</p>
<Divider variant="dotted" />
</div>
</div>
</div>
{/* カラー */}
<div className="space-y-4">
<h3 className="text-lg font-semibold">カラーバリエーション</h3>
<div className="space-y-4">
<Divider color="gray" />
<Divider color="blue" />
<Divider color="green" />
<Divider color="red" />
<Divider color="purple" />
</div>
</div>
{/* テキスト付き */}
<div className="space-y-4">
<h3 className="text-lg font-semibold">テキスト付きディバイダー</h3>
<div className="space-y-4">
<Divider>または</Divider>
<Divider align="start">セクション 1</Divider>
<Divider align="center">中央配置</Divider>
<Divider align="end">右寄せ</Divider>
</div>
</div>
{/* 垂直ディバイダー */}
<div className="space-y-4">
<h3 className="text-lg font-semibold">垂直ディバイダー</h3>
<div className="flex items-center h-20 gap-4">
<span>左側</span>
<Divider orientation="vertical" />
<span>右側</span>
<Divider orientation="vertical" variant="dashed" />
<span>破線</span>
</div>
</div>
{/* 装飾的なディバイダー */}
<div className="space-y-4">
<h3 className="text-lg font-semibold">装飾的なディバイダー</h3>
<DecorativeDivider type="dots" />
<DecorativeDivider type="wave" />
<DecorativeDivider type="gradient" />
</div>
{/* セクションディバイダー */}
<div className="space-y-4">
<h3 className="text-lg font-semibold">セクションディバイダー</h3>
<SectionDivider
title="新しいセクション"
subtitle="ここから新しい内容が始まります"
/>
<SectionDivider
title="機能紹介"
icon={
<svg className="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
}
/>
</div>
</div>
);
};
export default Divider;
// 基本的な使用例
import { Divider, DecorativeDivider, SectionDivider } from './divider';
function App() {
return (
<div>
{/* 基本的なディバイダー */}
<Divider />
{/* サイズとカラー */}
<Divider size="lg" color="blue" />
{/* バリアント */}
<Divider variant="dashed" />
<Divider variant="dotted" />
{/* テキスト付き */}
<Divider>または</Divider>
<Divider align="start">セクション 1</Divider>
<Divider align="end">終了</Divider>
{/* 垂直ディバイダー */}
<div className="flex items-center h-20">
<span>左側</span>
<Divider orientation="vertical" />
<span>右側</span>
</div>
{/* 装飾的なディバイダー */}
<DecorativeDivider type="dots" />
<DecorativeDivider type="wave" />
<DecorativeDivider type="gradient" />
{/* セクションディバイダー */}
<SectionDivider
title="新しいセクション"
subtitle="ここから新しい内容が始まります"
/>
{/* アイコン付きセクション */}
<SectionDivider
title="機能紹介"
icon={<StarIcon />}
/>
{/* カスタムスタイル */}
<Divider
className="my-8"
size="md"
color="purple"
/>
</div>
);
}