生成AIを使ってFigmaを実装コードへ変換する
タイトル通り。
トライしましょう。
まず適当に作ってみる画面の対象にTrelloを選ぶ。
適当な画面を用意。
これをこのFigmaの拡張機能で取り込む。
Figmaへimport。
するとこんな感じ。
viewportでも色々分けて出してくれる。
これでデザインができるところまでをシミュレートできた、とする。
で、これをコードに変えていく。
Figmaのこの拡張機能でHTMLへ変換。
こんな感じ。
これをClaudeにコピペ。
まず「explain this code.」で理解してもらって、以下でコード生成。
Change this code into a form that can be implemented in React.js. In doing so, the component design should be split and generated according to Atomic design.
Generate it with tsx.
生成結果
I'll convert the components to TypeScript (TSX) with proper type definitions and interfaces.Trello Board with TypeScript and Atomic DesignClick to open codeI've converted all components to TypeScript with proper type definitions. Key improvements include:
Type Definitions:
Added interfaces for List and Card
Proper typing for all props
Type safety for event handlers
Enhanced Components:
Added proper typing for all props
Included optional descriptions and labels for cards
Added proper event handling types
Type Safety:
Strict typing for all component props
Proper event handler typing
Interface-based type checking
Improved Structure:
Separate types directory
Better organization of interfaces
Proper export/import of types
To use this TypeScript version:
Set up TypeScript in your React project:
bashCopynpm install typescript @types/react @types/react-dom
Configure tsconfig.json:
jsonCopy{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"]
}
Install required dependencies:
bashCopynpm install lucide-react tailwindcss @types/node
Key features of this TypeScript version:
Type Safety:
All components are properly typed
Event handlers have proper TypeScript definitions
Props interfaces are clearly defined
Enhanced Components:
Better type checking for props
Improved error catching at compile time
Better IDE support with TypeScript
Maintainability:
Easier to maintain with type checking
Better code organization
Clear interface definitions
Would you like me to:
Add more TypeScript features like generics?
Implement drag-and-drop functionality?
Add more complex state management?
Add API integration types?
// types/index.ts
import { LucideIcon } from 'lucide-react';
export interface List {
id: number;
title: string;
cards: Card[];
}
export interface Card {
id: number;
title: string;
description?: string;
labels?: string[];
}
// atoms/IconButton.tsx
import React from 'react';
import { LucideIcon } from 'lucide-react';
interface IconButtonProps {
icon: LucideIcon;
label: string;
className?: string;
onClick?: () => void;
}
const IconButton: React.FC<IconButtonProps> = ({
icon: Icon,
label,
className = '',
onClick
}) => {
return (
<button
onClick={onClick}
className={`inline-flex items-center justify-center p-2 rounded hover:bg-gray-200/10 ${className}`}
aria-label={label}
>
<Icon className="w-5 h-5" />
</button>
);
};
export default IconButton;
// atoms/Button.tsx
import React, { ButtonHTMLAttributes } from 'react';
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'default' | 'primary' | 'secondary';
className?: string;
}
const Button: React.FC<ButtonProps> = ({
children,
variant = 'default',
className = '',
...props
}) => {
const variants = {
default: 'bg-transparent text-white hover:bg-white/10',
primary: 'bg-white/20 text-white hover:bg-white/30',
secondary: 'bg-gray-100 text-gray-800 hover:bg-gray-200'
};
return (
<button
className={`px-3 py-1.5 rounded flex items-center ${variants[variant]} ${className}`}
{...props}
>
{children}
</button>
);
};
export default Button;
// atoms/Input.tsx
import React, { InputHTMLAttributes } from 'react';
import { LucideIcon } from 'lucide-react';
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
icon?: LucideIcon;
className?: string;
}
const Input: React.FC<InputProps> = ({
icon: Icon,
className = '',
...props
}) => {
return (
<div className="relative">
{Icon && (
<Icon className="absolute left-2 top-1/2 transform -translate-y-1/2 w-4 h-4 text-white/60" />
)}
<input
className={`h-8 bg-white/20 rounded px-8 text-white placeholder-white/60 ${className}`}
{...props}
/>
</div>
);
};
export default Input;
// molecules/NavItem.tsx
import React from 'react';
import { ChevronDown } from 'lucide-react';
interface NavItemProps {
label: string;
onClick?: () => void;
}
const NavItem: React.FC<NavItemProps> = ({ label, onClick }) => {
return (
<button
onClick={onClick}
className="flex items-center px-3 text-white hover:bg-white/10 rounded"
>
{label}
<ChevronDown className="ml-2 w-4 h-4" />
</button>
);
};
export default NavItem;
// molecules/TaskCard.tsx
import React from 'react';
import { Card } from '../types';
interface TaskCardProps {
card: Card;
onClick?: () => void;
}
const TaskCard: React.FC<TaskCardProps> = ({ card, onClick }) => {
return (
<div
onClick={onClick}
className="bg-white rounded-lg p-3 mb-2 shadow-sm hover:bg-gray-50 cursor-pointer"
>
<p className="text-gray-800">{card.title}</p>
{card.description && (
<p className="text-gray-500 text-sm mt-1">{card.description}</p>
)}
{card.labels && card.labels.length > 0 && (
<div className="flex gap-1 mt-2">
{card.labels.map((label, index) => (
<span
key={index}
className="px-2 py-0.5 bg-blue-100 text-blue-800 text-xs rounded"
>
{label}
</span>
))}
</div>
)}
</div>
);
};
export default TaskCard;
// molecules/AddCardButton.tsx
import React from 'react';
import { Plus } from 'lucide-react';
interface AddCardButtonProps {
onClick: () => void;
}
const AddCardButton: React.FC<AddCardButtonProps> = ({ onClick }) => {
return (
<button
onClick={onClick}
className="flex items-center w-full p-2 text-gray-600 hover:bg-gray-200 rounded"
>
<Plus className="w-4 h-4 mr-2" />
カードを追加
</button>
);
};
export default AddCardButton;
// organisms/Navigation.tsx
import React from 'react';
import { Menu, Search, Settings } from 'lucide-react';
import IconButton from '../atoms/IconButton';
import Input from '../atoms/Input';
import NavItem from '../molecules/NavItem';
const Navigation: React.FC = () => {
return (
<nav className="h-12 bg-gray-900 border-b border-white/20 backdrop-blur">
<div className="flex items-center h-full px-2">
<IconButton icon={Menu} label="Menu" />
<img src="/api/placeholder/75/32" alt="Logo" className="h-8 ml-8" />
<div className="flex items-center space-x-4 ml-4">
<NavItem label="ワークスペース" />
<NavItem label="最近" />
<NavItem label="スター付き" />
</div>
<div className="ml-auto flex items-center space-x-4">
<Input
icon={Search}
placeholder="検索"
className="w-48"
/>
<IconButton icon={Settings} label="Settings" />
</div>
</div>
</nav>
);
};
export default Navigation;
// organisms/BoardHeader.tsx
import React from 'react';
import { Share2 } from 'lucide-react';
import Button from '../atoms/Button';
interface BoardHeaderProps {
title: string;
onShare?: () => void;
}
const BoardHeader: React.FC<BoardHeaderProps> = ({ title, onShare }) => {
return (
<div className="bg-black/25 backdrop-blur-sm p-3">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<h1 className="text-white font-bold">{title}</h1>
<Button variant="secondary">ボード</Button>
<Button onClick={onShare}>
<Share2 className="w-4 h-4 mr-2" />
共有する
</Button>
</div>
</div>
</div>
);
};
export default BoardHeader;
// organisms/TaskList.tsx
import React from 'react';
import { MoreHorizontal } from 'lucide-react';
import { List } from '../types';
import TaskCard from '../molecules/TaskCard';
import AddCardButton from '../molecules/AddCardButton';
import IconButton from '../atoms/IconButton';
interface TaskListProps {
list: List;
onAddCard: (listId: number) => void;
onCardClick: (listId: number, cardId: number) => void;
}
const TaskList: React.FC<TaskListProps> = ({
list,
onAddCard,
onCardClick
}) => {
return (
<div className="w-72 bg-gray-100 rounded-xl p-2">
<div className="flex items-center justify-between p-2">
<h2 className="font-semibold text-gray-800">{list.title}</h2>
<IconButton icon={MoreHorizontal} label="More options" />
</div>
{list.cards.map((card) => (
<TaskCard
key={card.id}
card={card}
onClick={() => onCardClick(list.id, card.id)}
/>
))}
<AddCardButton onClick={() => onAddCard(list.id)} />
</div>
);
};
export default TaskList;
// templates/BoardTemplate.tsx
import React from 'react';
import { Plus } from 'lucide-react';
import { List } from '../types';
import Navigation from '../organisms/Navigation';
import BoardHeader from '../organisms/BoardHeader';
import TaskList from '../organisms/TaskList';
import Button from '../atoms/Button';
interface BoardTemplateProps {
lists: List[];
onAddCard: (listId: number) => void;
onAddList: () => void;
onCardClick: (listId: number, cardId: number) => void;
onShare?: () => void;
}
const BoardTemplate: React.FC<BoardTemplateProps> = ({
lists,
onAddCard,
onAddList,
onCardClick,
onShare
}) => {
return (
<div className="w-full min-h-screen bg-slate-800">
<Navigation />
<BoardHeader title="TODO" onShare={onShare} />
<div className="flex p-4 space-x-4 overflow-x-auto">
{lists.map(list => (
<TaskList
key={list.id}
list={list}
onAddCard={onAddCard}
onCardClick={onCardClick}
/>
))}
<Button
variant="primary"
className="h-10 whitespace-nowrap"
onClick={onAddList}
>
<Plus className="w-4 h-4 mr-2" />
もう1つリストを追加
</Button>
</div>
</div>
);
};
export default BoardTemplate;
// pages/Board.tsx
import React, { useState } from 'react';
import { List, Card } from '../types';
import BoardTemplate from '../templates/BoardTemplate';
const initialLists: List[] = [
{
id: 1,
title: 'TODO',
cards: [{ id: 1, title: 'タスクタイトル' }]
},
{
id: 2,
title: 'Doing',
cards: [{ id: 2, title: 'タスクタイトル' }]
},
{
id: 3,
title: 'Done!',
cards: [{ id: 3, title: 'タスクタイトル' }]
}
];
const Board: React.FC = () => {
const [lists, setLists] = useState<List[]>(initialLists);
const handleAddCard = (listId: number) => {
setLists(lists.map(list => {
if (list.id === listId) {
const newCard: Card = {
id: Math.max(...list.cards.map(c => c.id)) + 1,
title: 'New Task'
};
return {
...list,
cards: [...list.cards, newCard]
};
}
return list;
}));
};
const handleAddList = () => {
const newList: List = {
id: Math.max(...lists.map(l => l.id)) + 1,
title: `List ${lists.length + 1}`,
cards: []
};
setLists([...lists, newList]);
};
const handleCardClick = (listId: number, cardId: number) => {
// Implement card detail modal/drawer logic here
console.log(`Clicked card ${cardId} in list ${listId}`);
};
const handleShare = () => {
// Implement share logic here
console.log('Share button clicked');
};
return (
<BoardTemplate
lists={lists}
onAddCard={handleAddCard}
onAddList={handleAddList}
onCardClick={handleCardClick}
onShare={handleShare}
/>
);
};
export default Board;
Tailwindやらとりあえず動く状態にしたReactプロジェクトにもろもろファイル作成&コピペ。
でできたのがこれ。
なかなか悪くないですね。ここから実装するのも別にできそう。
でも同様にやってみた。
まぁまぁ似たような感じだが、1回目は失敗した。
あんまし安定しない感はある。
ここの記事のカスタム指示を入れると安定し?かも。
次。
レスポンシブデザインに対応できるかを試す。
まず題材となるサイトを適当にv0で作成。
生成されたコード。
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { ShoppingCart, Search, Menu } from "lucide-react"
export default function Component() {
const shoes = [
{ id: 1, name: "クラシックレザーシューズ", price: 12000, image: "/placeholder.svg?height=200&width=200" },
{ id: 2, name: "スポーツランニングシューズ", price: 8500, image: "/placeholder.svg?height=200&width=200" },
{ id: 3, name: "カジュアルスニーカー", price: 6800, image: "/placeholder.svg?height=200&width=200" },
{ id: 4, name: "ハイヒールパンプス", price: 9800, image: "/placeholder.svg?height=200&width=200" },
]
return (
<div className="flex flex-col min-h-screen">
<header className="sticky top-0 z-10 bg-white border-b">
<div className="container mx-auto px-4 py-4 flex items-center justify-between">
<div className="flex items-center">
<Button variant="ghost" size="icon" className="mr-2 md:hidden">
<Menu className="h-6 w-6" />
<span className="sr-only">メニュー</span>
</Button>
<h1 className="text-2xl font-bold">靴のECサイト</h1>
</div>
<nav className="hidden md:flex space-x-4">
<Button variant="ghost">メンズ</Button>
<Button variant="ghost">レディース</Button>
<Button variant="ghost">キッズ</Button>
<Button variant="ghost">セール</Button>
</nav>
<div className="flex items-center space-x-4">
<div className="relative hidden md:block">
<Search className="absolute left-2 top-1/2 transform -translate-y-1/2 text-gray-400" />
<Input className="pl-8" placeholder="検索..." />
</div>
<Button variant="ghost" size="icon">
<ShoppingCart className="h-6 w-6" />
<span className="sr-only">カート</span>
</Button>
</div>
</div>
</header>
<main className="flex-grow container mx-auto px-4 py-8">
<h2 className="text-2xl font-semibold mb-6">人気の商品</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{shoes.map((shoe) => (
<Card key={shoe.id}>
<CardHeader>
<CardTitle>{shoe.name}</CardTitle>
</CardHeader>
<CardContent>
<img src={shoe.image} alt={shoe.name} className="w-full h-48 object-cover mb-4" />
<p className="text-xl font-bold">¥{shoe.price.toLocaleString()}</p>
</CardContent>
<CardFooter>
<Button className="w-full">カートに追加</Button>
</CardFooter>
</Card>
))}
</div>
</main>
<footer className="bg-gray-100 mt-8">
<div className="container mx-auto px-4 py-8">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<div>
<h3 className="text-lg font-semibold mb-4">カスタマーサービス</h3>
<ul className="space-y-2">
<li><a href="#" className="hover:underline">お問い合わせ</a></li>
<li><a href="#" className="hover:underline">返品・交換について</a></li>
<li><a href="#" className="hover:underline">サイズガイド</a></li>
</ul>
</div>
<div>
<h3 className="text-lg font-semibold mb-4">会社情報</h3>
<ul className="space-y-2">
<li><a href="#" className="hover:underline">会社概要</a></li>
<li><a href="#" className="hover:underline">採用情報</a></li>
<li><a href="#" className="hover:underline">プライバシーポリシー</a></li>
</ul>
</div>
<div>
<h3 className="text-lg font-semibold mb-4">ソーシャルメディア</h3>
<ul className="space-y-2">
<li><a href="#" className="hover:underline">Facebook</a></li>
<li><a href="#" className="hover:underline">Twitter</a></li>
<li><a href="#" className="hover:underline">Instagram</a></li>
</ul>
</div>
</div>
<div className="mt-8 text-center text-gray-500">
<p>© 2024 靴のECサイト. All rights reserved.</p>
</div>
</div>
</footer>
</div>
)
}
同様にFigmaに取り込んで
Figmaからコード生成する。
レスポンシブ対応用にwidthが1440pxと390pxのファイルを作る。
<div className="w-[390px] h-[780px] bg-neutral-50 flex-col justify-end items-center inline-flex">
<div className="self-stretch h-[2469px] bg-white flex-col justify-start items-start inline-flex">
<div className="h-[2469px] relative">
<div className="w-[390px] h-[1784px] px-4 py-8 left-0 top-[73px] absolute flex-col justify-start items-start gap-6 inline-flex">
<div className="text-zinc-950 text-2xl font-semibold font-['Inter'] leading-loose">人気の商品</div>
<div className="self-stretch h-[398px] p-[25px] bg-white rounded-lg shadow border border-zinc-200 flex-col justify-start items-start gap-6 flex">
<div className="text-zinc-950 text-2xl font-semibold font-['Inter'] leading-normal">クラシックレザーシューズ</div>
<div className="self-stretch h-48 flex-col justify-start items-start flex">
<div className="w-[308px] h-48 justify-center items-center inline-flex">
<div className="w-[308px] h-[308px] relative flex-col justify-start items-start flex">
<div className="opacity-50 w-[100.91px] h-[100.76px] relative">
<div className="opacity-50 w-[70.13px] h-[70.13px] left-[15.35px] top-[15.21px] absolute">
</div>
<div className="w-[9.93px] h-[9.93px] left-[45.45px] top-[45.31px] absolute">
</div>
</div>
</div>
</div>
</div>
<div className="text-zinc-950 text-xl font-bold font-['Inter'] leading-7">¥12,000</div>
<div className="self-stretch h-10 px-28 py-2.5 bg-zinc-900 rounded-md justify-center items-center inline-flex">
<div className="text-center text-neutral-50 text-sm font-medium font-['Inter'] leading-tight">カートに追加</div>
</div>
</div>
<div className="self-stretch h-[398px] p-[25px] bg-white rounded-lg shadow border border-zinc-200 flex-col justify-start items-start gap-6 flex">
<div className="text-zinc-950 text-2xl font-semibold font-['Inter'] leading-normal">スポーツランニングシューズ</div>
<div className="self-stretch h-48 flex-col justify-start items-start flex">
<div className="w-[308px] h-48 justify-center items-center inline-flex">
<div className="w-[308px] h-[308px] relative flex-col justify-start items-start flex">
<div className="opacity-50 w-[100.91px] h-[100.76px] relative">
<div className="opacity-50 w-[70.13px] h-[70.13px] left-[15.35px] top-[15.21px] absolute">
</div>
<div className="w-[9.93px] h-[9.93px] left-[45.45px] top-[45.31px] absolute">
</div>
</div>
</div>
</div>
</div>
<div className="text-zinc-950 text-xl font-bold font-['Inter'] leading-7">¥8,500</div>
<div className="self-stretch h-10 px-28 py-2.5 bg-zinc-900 rounded-md justify-center items-center inline-flex">
<div className="text-center text-neutral-50 text-sm font-medium font-['Inter'] leading-tight">カートに追加</div>
</div>
</div>
<div className="self-stretch h-[398px] p-[25px] bg-white rounded-lg shadow border border-zinc-200 flex-col justify-start items-start gap-6 flex">
<div className="text-zinc-950 text-2xl font-semibold font-['Inter'] leading-normal">カジュアルスニーカー</div>
<div className="self-stretch h-48 flex-col justify-start items-start flex">
<div className="w-[308px] h-48 justify-center items-center inline-flex">
<div className="w-[308px] h-[308px] relative flex-col justify-start items-start flex">
<div className="opacity-50 w-[100.91px] h-[100.76px] relative">
<div className="opacity-50 w-[70.13px] h-[70.13px] left-[15.35px] top-[15.21px] absolute">
</div>
<div className="w-[9.93px] h-[9.93px] left-[45.45px] top-[45.31px] absolute">
</div>
</div>
</div>
</div>
</div>
<div className="text-zinc-950 text-xl font-bold font-['Inter'] leading-7">¥6,800</div>
<div className="self-stretch h-10 px-28 py-2.5 bg-zinc-900 rounded-md justify-center items-center inline-flex">
<div className="text-center text-neutral-50 text-sm font-medium font-['Inter'] leading-tight">カートに追加</div>
</div>
</div>
<div className="self-stretch h-[398px] p-[25px] bg-white rounded-lg shadow border border-zinc-200 flex-col justify-start items-start gap-6 flex">
<div className="text-zinc-950 text-2xl font-semibold font-['Inter'] leading-normal">ハイヒールパンプス</div>
<div className="self-stretch h-48 flex-col justify-start items-start flex">
<div className="w-[308px] h-48 justify-center items-center inline-flex">
<div className="w-[308px] h-[308px] relative flex-col justify-start items-start flex">
<div className="opacity-50 w-[100.91px] h-[100.76px] relative">
<div className="opacity-50 w-[70.13px] h-[70.13px] left-[15.35px] top-[15.21px] absolute">
</div>
<div className="w-[9.93px] h-[9.93px] left-[45.45px] top-[45.31px] absolute">
</div>
</div>
</div>
</div>
</div>
<div className="text-zinc-950 text-xl font-bold font-['Inter'] leading-7">¥9,800</div>
<div className="self-stretch h-10 px-28 py-2.5 bg-zinc-900 rounded-md justify-center items-center inline-flex">
<div className="text-center text-neutral-50 text-sm font-medium font-['Inter'] leading-tight">カートに追加</div>
</div>
</div>
</div>
<div className="w-[390px] h-[580px] left-0 top-[1889px] absolute bg-gray-100">
<div className="w-[161.66px] h-7 left-[16px] top-[32px] absolute text-zinc-950 text-lg font-semibold font-['Inter'] leading-7">カスタマーサービス</div>
<div className="h-[106px] py-[3px] left-[16px] top-[76px] absolute flex-col justify-start items-start gap-3.5 inline-flex">
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">お問い合わせ</div>
</div>
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">返品・交換について</div>
</div>
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">サイズガイド</div>
</div>
</div>
<div className="w-[72.20px] h-7 left-[16px] top-[196px] absolute text-zinc-950 text-lg font-semibold font-['Inter'] leading-7">会社情報</div>
<div className="h-[106px] py-[3px] left-[16px] top-[240px] absolute flex-col justify-start items-start gap-3.5 inline-flex">
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">会社概要</div>
</div>
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">採用情報</div>
</div>
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">プライバシーポリシー</div>
</div>
</div>
<div className="w-40 h-7 left-[16px] top-[360px] absolute text-zinc-950 text-lg font-semibold font-['Inter'] leading-7">ソーシャルメディア</div>
<div className="h-[106px] py-[3px] left-[16px] top-[404px] absolute flex-col justify-start items-start gap-3.5 inline-flex">
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">Facebook</div>
</div>
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">Twitter</div>
</div>
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">Instagram</div>
</div>
</div>
<div className="w-[307.55px] h-6 left-[41.32px] top-[524px] absolute text-center text-gray-500 text-base font-normal font-['Inter'] leading-normal">© 2024 靴のECサイト. All rights reserved.</div>
</div>
<div className="w-[390px] h-[73px] left-0 top-0 absolute bg-white border-b border-zinc-200">
<div className="w-10 h-10 p-3 left-[16px] top-[16px] absolute rounded-md justify-center items-center inline-flex">
<div className="w-4 h-4 relative" />
</div>
<div className="w-[152.68px] h-8 left-[64px] top-[20px] absolute text-zinc-950 text-2xl font-bold font-['Inter'] leading-loose">靴のECサイト</div>
<div className="w-10 h-10 p-3 left-[334px] top-[16px] absolute rounded-md justify-center items-center inline-flex">
<div className="w-4 h-4 relative" />
</div>
</div>
</div>
</div>
</div>
<div className="w-[1440px] h-[900px] bg-white flex-col justify-start items-start inline-flex">
<div className="h-[900px] relative">
<div className="w-[1400px] h-[543px] left-[20px] top-[73px] absolute">
<div className="w-[120.20px] h-8 left-[16px] top-[32px] absolute text-zinc-950 text-2xl font-semibold font-['Inter'] leading-loose">人気の商品</div>
<div className="w-[324px] h-[422px] left-[16px] top-[88px] absolute bg-white rounded-lg shadow border border-zinc-200">
<div className="h-12 left-[25px] top-[25px] absolute flex-col justify-start items-start inline-flex">
<div className="text-zinc-950 text-2xl font-semibold font-['Inter'] leading-normal">クラシックレザーシュー<br/>ズ</div>
</div>
<div className="w-[274px] h-[200px] pt-2 left-[25px] top-[89px] absolute flex-col justify-start items-start inline-flex">
<div className="w-[274px] h-48 justify-center items-center inline-flex">
<div className="w-[274px] h-[274px] relative flex-col justify-start items-start flex">
<div className="opacity-50 w-[89.77px] h-[89.64px] relative">
<div className="opacity-50 w-[62.39px] h-[62.39px] left-[13.66px] top-[13.53px] absolute">
</div>
<div className="w-[8.83px] h-[8.83px] left-[40.44px] top-[40.30px] absolute">
</div>
</div>
</div>
</div>
</div>
<div className="w-[82.31px] h-7 left-[25px] top-[305px] absolute text-zinc-950 text-xl font-bold font-['Inter'] leading-7">¥12,000</div>
<div className="w-[274px] h-10 px-[95px] py-2.5 left-[25px] top-[357px] absolute bg-zinc-900 rounded-md justify-center items-center inline-flex">
<div className="text-center text-neutral-50 text-sm font-medium font-['Inter'] leading-tight">カートに追加</div>
</div>
</div>
<div className="w-[324px] h-[422px] left-[364px] top-[88px] absolute bg-white rounded-lg shadow border border-zinc-200">
<div className="h-12 left-[25px] top-[25px] absolute flex-col justify-start items-start inline-flex">
<div className="text-zinc-950 text-2xl font-semibold font-['Inter'] leading-normal">スポーツランニングシュ<br/>ーズ</div>
</div>
<div className="w-[274px] h-[200px] pt-2 left-[25px] top-[89px] absolute flex-col justify-start items-start inline-flex">
<div className="w-[274px] h-48 justify-center items-center inline-flex">
<div className="w-[274px] h-[274px] relative flex-col justify-start items-start flex">
<div className="opacity-50 w-[89.77px] h-[89.64px] relative">
<div className="opacity-50 w-[62.39px] h-[62.39px] left-[13.66px] top-[13.53px] absolute">
</div>
<div className="w-[8.83px] h-[8.83px] left-[40.44px] top-[40.30px] absolute">
</div>
</div>
</div>
</div>
</div>
<div className="w-[73.53px] h-7 left-[25px] top-[305px] absolute text-zinc-950 text-xl font-bold font-['Inter'] leading-7">¥8,500</div>
<div className="w-[274px] h-10 px-[95px] py-2.5 left-[25px] top-[357px] absolute bg-zinc-900 rounded-md justify-center items-center inline-flex">
<div className="text-center text-neutral-50 text-sm font-medium font-['Inter'] leading-tight">カートに追加</div>
</div>
</div>
<div className="w-[324px] h-[422px] px-[25px] pt-[25px] pb-[49px] left-[712px] top-[88px] absolute bg-white rounded-lg shadow border border-zinc-200 flex-col justify-start items-start gap-6 inline-flex">
<div className="text-zinc-950 text-2xl font-semibold font-['Inter'] leading-normal">カジュアルスニーカー</div>
<div className="self-stretch h-48 flex-col justify-start items-start flex">
<div className="w-[274px] h-48 justify-center items-center inline-flex">
<div className="w-[274px] h-[274px] relative flex-col justify-start items-start flex">
<div className="opacity-50 w-[89.77px] h-[89.64px] relative">
<div className="opacity-50 w-[62.39px] h-[62.39px] left-[13.66px] top-[13.53px] absolute">
</div>
<div className="w-[8.83px] h-[8.83px] left-[40.44px] top-[40.30px] absolute">
</div>
</div>
</div>
</div>
</div>
<div className="text-zinc-950 text-xl font-bold font-['Inter'] leading-7">¥6,800</div>
<div className="self-stretch h-10 px-[95px] py-2.5 bg-zinc-900 rounded-md justify-center items-center inline-flex">
<div className="text-center text-neutral-50 text-sm font-medium font-['Inter'] leading-tight">カートに追加</div>
</div>
</div>
<div className="w-[324px] h-[422px] px-[25px] pt-[25px] pb-[49px] left-[1060px] top-[88px] absolute bg-white rounded-lg shadow border border-zinc-200 flex-col justify-start items-start gap-6 inline-flex">
<div className="text-zinc-950 text-2xl font-semibold font-['Inter'] leading-normal">ハイヒールパンプス</div>
<div className="self-stretch h-48 flex-col justify-start items-start flex">
<div className="w-[274px] h-48 justify-center items-center inline-flex">
<div className="w-[274px] h-[274px] relative flex-col justify-start items-start flex">
<div className="opacity-50 w-[89.77px] h-[89.64px] relative">
<div className="opacity-50 w-[62.39px] h-[62.39px] left-[13.66px] top-[13.53px] absolute">
</div>
<div className="w-[8.83px] h-[8.83px] left-[40.44px] top-[40.30px] absolute">
</div>
</div>
</div>
</div>
</div>
<div className="text-zinc-950 text-xl font-bold font-['Inter'] leading-7">¥9,800</div>
<div className="self-stretch h-10 px-[95px] py-2.5 bg-zinc-900 rounded-md justify-center items-center inline-flex">
<div className="text-center text-neutral-50 text-sm font-medium font-['Inter'] leading-tight">カートに追加</div>
</div>
</div>
</div>
<div className="h-[252px] px-5 left-0 top-[648px] absolute bg-gray-100 flex-col justify-start items-center inline-flex">
<div className="w-[1400px] h-[252px] relative">
<div className="w-[161.66px] h-7 left-[16px] top-[32px] absolute text-zinc-950 text-lg font-semibold font-['Inter'] leading-7">カスタマーサービス</div>
<div className="h-[106px] py-[3px] left-[16px] top-[76px] absolute flex-col justify-start items-start gap-3.5 inline-flex">
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">お問い合わせ</div>
</div>
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">返品・交換について</div>
</div>
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">サイズガイド</div>
</div>
</div>
<div className="w-[72.20px] h-7 left-[482.66px] top-[32px] absolute text-zinc-950 text-lg font-semibold font-['Inter'] leading-7">会社情報</div>
<div className="h-[106px] py-[3px] left-[482.66px] top-[76px] absolute flex-col justify-start items-start gap-3.5 inline-flex">
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">会社概要</div>
</div>
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">採用情報</div>
</div>
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">プライバシーポリシー</div>
</div>
</div>
<div className="w-40 h-7 left-[949.33px] top-[32px] absolute text-zinc-950 text-lg font-semibold font-['Inter'] leading-7">ソーシャルメディア</div>
<div className="h-[106px] py-[3px] left-[949.33px] top-[76px] absolute flex-col justify-start items-start gap-3.5 inline-flex">
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">Facebook</div>
</div>
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">Twitter</div>
</div>
<div className="flex-col justify-start items-start flex">
<div className="text-zinc-950 text-base font-normal font-['Inter'] leading-normal">Instagram</div>
</div>
</div>
<div className="w-[307.55px] h-6 left-[546.32px] top-[196px] absolute text-center text-gray-500 text-base font-normal font-['Inter'] leading-normal">© 2024 靴のECサイト. All rights reserved.</div>
</div>
</div>
<div className="h-[73px] px-5 pb-px left-0 top-0 absolute bg-white border-b border-zinc-200 flex-col justify-start items-center inline-flex">
<div className="w-[1400px] h-[72px] relative">
<div className="w-[152.68px] h-8 left-[16px] top-[20px] absolute text-zinc-950 text-2xl font-bold font-['Inter'] leading-loose">靴のECサイト</div>
<div className="left-[457.11px] top-[16px] absolute justify-start items-start gap-4 inline-flex">
<div className="px-4 py-2.5 rounded-md justify-center items-center flex">
<div className="text-center text-zinc-950 text-sm font-medium font-['Inter'] leading-tight">メンズ</div>
</div>
<div className="px-4 py-2.5 rounded-md justify-center items-center flex">
<div className="text-center text-zinc-950 text-sm font-medium font-['Inter'] leading-tight">レディース</div>
</div>
<div className="px-4 py-2.5 rounded-md justify-center items-center flex">
<div className="text-center text-zinc-950 text-sm font-medium font-['Inter'] leading-tight">キッズ</div>
</div>
<div className="px-4 py-2.5 rounded-md justify-center items-center flex">
<div className="text-center text-zinc-950 text-sm font-medium font-['Inter'] leading-tight">セール</div>
</div>
</div>
<div className="w-10 h-10 p-3 left-[1344px] top-[16px] absolute rounded-md justify-center items-center inline-flex">
<div className="w-4 h-4 relative" />
</div>
<div className="w-[215px] h-10 left-[1113px] top-[16px] absolute bg-white rounded-md border border-zinc-200">
<div className="pr-[132.30px] pt-[3px] pb-0.5 left-[33px] top-[8.75px] absolute flex-col justify-start items-start inline-flex">
<div className="text-zinc-500 text-[13.02px] font-normal font-['Inter']">検索...</div>
</div>
<div className="w-[169px] h-[17.50px] left-[33px] top-[11.25px] absolute" />
</div>
<div className="w-6 h-6 left-[1121px] top-[24px] absolute" />
</div>
</div>
</div>
</div>
面倒なのでCursorのChatで作ってもらう。
以下のプロンプトを流す。
そのうち、Contextに390.htmlと1440.htmlを渡しておく。
@390px.html @1440px.html
- The two attachments represent the appearance of the web service when the width is 1440px and 390px respectively.
- Change this code into a form that can be implemented in React.js.
-In doing so, the component design should be split and generated according to Atomic design.
- Please support responsive design for 1440px and 390px.
- Generate it with tsx.
なかなかいい感じに出力される。
Apply連打して、適当にimportしまくる。
HeaderとFooterのコンポーネントが何故か無視されるが、それも @Layout.tsx make Header and Footer.
これでなんとかした。
で、できたのがこちら。
フッターのデザインがなんかおかしいがまぁまぁな感じ。
おそらくレスポンシブ対応させたことで適当にjustifyとかしていると思われる。
SP版はこんな感じ。ちゃんとなってる。
リポジトリ↓