インストール
npm install styled-components
atom
atom/button/PrimaryButton.jsx
import React from 'react';
import styled from "styled-components";
import BaseButton from "./BaseButton.jsx";
const PrimaryButton = (props) => {
const {children} = props
return (
<>
<SButton>{children}</SButton>
</>
);
};
const SButton = styled.button`
background-color: #40514e;
color: #fff;
padding: 6px 24px;
border: none;
outline: none;
border-radius: 9999px;
&:hover {
cursor: pointer;
opacity: 0.8;
}
`
export default PrimaryButton;
ボタン名をpropsで受け取る
AtomicDesign.jsx
import React from 'react';
import PrimaryButton from "./atom/button/PrimaryButton.jsx";
import SecondaryButton from "./atom/button/SecondaryButton.jsx";
import SearchInput from "./molecules/SearchInput.jsx";
const AtomicDesign = () => {
return (
<div>
<p>AtomicDesign</p>
<PrimaryButton>テスト</PrimaryButton>
<SecondaryButton>検索</SecondaryButton>
</div>
);
};
export default AtomicDesign;
ボタン名をpropsで渡す
共通の部分を抜き出す
atom/button/BaseButton.jsx
import styled from "styled-components";
const BaseButton = styled.button`
color: #fff;
padding: 6px 24px;
border: none;
outline: none;
border-radius: 9999px;
&:hover {
cursor: pointer;
opacity: 0.8;
}
`
export default BaseButton;
共通部分
atom/button/PrimaryButton.jsx
import React from 'react';
import styled from "styled-components";
import BaseButton from "./BaseButton.jsx";
const PrimaryButton = (props) => {
const {children} = props
return (
<>
<SButton>{children}</SButton>
</>
);
};
const SButton = styled(BaseButton)`
background-color: #40514e;
`
export default PrimaryButton;
styled(BaseButton)を使い
共通部分を呼び出す
BaseButtonが上書きされる
molecules
molecules/SearchInput.jsx
import React from 'react';
import PrimaryButton from "../atom/button/PrimaryButton.jsx";
import Input from "../atom/input/Input.jsx";
import styled from "styled-components";
const SearchInput = () => {
return (
<SContainer>
<Input placeholder="検索条件を表示" />
<SButtonWrapper>
<PrimaryButton>検索</PrimaryButton>
</SButtonWrapper>
</SContainer>
);
};
const SContainer = styled.div`
display: flex;
align-items: center;
`
const SButtonWrapper = styled.div`
padding-left: 8px;
`
export default SearchInput;
atomを読み込んで組み合わせる
Input.jsx
import React from 'react';
import styled from "styled-components";
const Input = (props) => {
const {placeholder = ""} = props
return (
<>
<SInput type="text" placeholder={placeholder} />
</>
);
};
const SInput = styled.input`
padding: 8px 16px;
border: solid 1px #ddd;
border-radius: 9999px;
outline: none;
`
export default Input;
placeholderでpropsを受け取る
PrimaryButton.jsx
import React from 'react';
import styled from "styled-components";
import BaseButton from "./BaseButton.jsx";
const PrimaryButton = (props) => {
const {children} = props
return (
<>
<SButton>{children}</SButton>
</>
);
};
const SButton = styled(BaseButton)`
background-color: #40514e;
`
export default PrimaryButton;
organisms
上位でもatom(Card)で囲む場合もある? ここはルールがよくわからな
AtomicDesigh.jsx
import React from 'react';
import PrimaryButton from "./atom/button/PrimaryButton.jsx";
import SecondaryButton from "./atom/button/SecondaryButton.jsx";
import SearchInput from "./molecules/SearchInput.jsx";
import UserCard from "./organisms/user/UserCard.jsx";
const AtomicDesign = () => {
const user = {
name: "yukilulu0229",
image: "https://picsum.photos/160",
email: "apple@fluits.com",
tel: "01-2345-6789",
company: {
name: "company web"
},
website: "xxx.com"
}
return (
<div>
<UserCard user={user} />
</div>
);
};
export default AtomicDesign;
user情報を渡す
organisms/user/UserCard.jsx
import React from 'react';
import styled from "styled-components";
import Card from "../../atom/card/Card.jsx";
import UserIconWithName from "../../molecules/user/UserIconWithName.jsx";
const UserCard = (props) => {
const {user} = props
return (
<Card>
<UserIconWithName image={user.image} name={user.name} />
<SDl>
<dt>メール</dt>
<dd>{user.email}</dd>
<dt>tel</dt>
<dd>{user.tel}</dd>
<dt>会社名</dt>
<dd>{user.company.name}</dd>
<dt>web</dt>
<dd>{user.website}</dd>
</SDl>
</Card>
);
};
const SDl = styled.dl`
text-align: left;
margin-bottom: 0;
dt {
float: left;
}
dd {
padding-left: 32px;
padding-bottom: 8px;
overflow-wrap: break-word;
}
`
export default UserCard;
user情報を受け取る
UserIconWithNameにpropsを渡す
Cardの中をchildrenで渡す
molecules/user/UserIconWithName.jsx
import React from 'react';
import styled from "styled-components";
const UserIconWithName = (props) => {
const {image, name} = props
return (
<SContainer>
<SImage src={image} alt="プロフィール" />
<SName>{name}</SName>
</SContainer>
);
};
const SContainer = styled.div`
text-align: center;
`
const SImage = styled.img`
border-radius: 50%;
`
const SName = styled.p`
font-size: 18px;
font-weight: bold;
margin: 0;
color: #303030;
`
export default UserIconWithName;
imageとnameのpropsを受け取る
atom/card/Card.jsx
import React from 'react';
import styled from "styled-components";
const Card = (props) => {
const {children} = props
return (
<SCard>{children}</SCard>
);
};
const SCard = styled.div`
background-color: #d7d1d1;
box-shadow: #ddd 0 0 4px 2px;
border-radius: 8px;
padding: 16px;
`
export default Card;
UserCardからchildrenを受け取る
templates
atomicDesign.jsx
import React from 'react';
import DefaultLayout from "./templates/DefaultLayout.jsx";
const AtomicDesign = () => {
return (
<DefaultLayout>
<p>AtomicDesign</p>
</DefaultLayout>
);
};
export default AtomicDesign;
DefaultLayoutにchildrenを渡す
templates/DefaultLayout.jsx
import React from 'react';
import Footer from "../atoms/layout/Footer.jsx";
import Header from "../atoms/layout/Header.jsx";
import styled from "styled-components";
const DefaultLayout = (props) => {
const {children} = props
return (
<>
<SDefaultLayout>
<Header />
{children}
<Footer />
</SDefaultLayout>
</>
);
};
const SDefaultLayout = styled.div`
min-height: 100vh;
`
export default DefaultLayout;
HeaderとFooterを読み込む
atomicDesignで渡されたchildrenを受け取る
そしてHeaderとFooterにchildrenを入れている
atom/Layout/Header.jsx
import React from 'react';
import {Link} from "react-router-dom";
import styled from "styled-components";
const Header = () => {
return (
<SHeader>
<SLink to="/AtomicDesign">AtomicDesign</SLink>
<SLink to="/AtomicDesign">Users</SLink>
</SHeader>
);
};
const SHeader = styled.header`
background-color: #11999e;
color: #fff;
text-align: center;
padding: 8px 0;
`
const SLink = styled(Link)`
margin: 0 8px;
color: #fff;
`
export default Header;
これがatomsの中でよいのかは疑問が残っている
atoms/layout/footer.jsx
import React from 'react';
import {Link} from "react-router-dom";
import styled from "styled-components";
const Footer = () => {
return (
<SFooter>
<p>© company web</p>
</SFooter>
);
};
const SFooter = styled.footer`
background-color: #11999e;
color: #fff;
text-align: center;
padding: 8px 0;
position: fixed;
bottom: 0;
width: 100%;
`
export default Footer;
これがatomsの中でよいのかは疑問が残っている
pages
今回の話はUsersのほう(Usersは共通の部品がないのでtemplateを使っていない)
router/Router.jsx
import React from 'react';
import { Route, Routes } from "react-router-dom";
import Home from "../components/Home.jsx";
import Page2 from "../components/Page2.jsx";
import Page3 from "../components/Page3.jsx";
import Top from "../components/pages/Top.jsx";
import Users from "../components/pages/Users.jsx";
import DefaultLayout from "../components/templates/DefaultLayout.jsx";
import Page404 from "../Page404.jsx";
import { page2Routes } from "./Page2Routes.jsx";
import { page3Routes } from "./Page3Routes.jsx";
const Router = () => {
return (
<>
<Routes path="/AtomicDesign/*" >
<Route path="/" element={<DefaultLayout />} >
<Route index={true} element={<Top/>} />
</Route>
<Route path="Users/*" element={<Users />} />
</Routes>
</>
);
};
export default Router;
routerの設定でlayoutを選択している
pages/Users.jsx
import React from 'react';
import styled from "styled-components";
import Header from "../atoms/layout/Header.jsx";
import SearchInput from "../molecules/SearchInput.jsx";
import UserCard from "../organisms/user/UserCard.jsx";
const Users = () => {
const users = [...Array(10).keys()].map((value) => {
return {
id: value,
name: `yukilulu0229 + ${value}`,
image: "https://picsum.photos/160",
email: "apple@fluits.com",
tel: "01-2345-6789",
company: {
name: "company web"
},
website: "xxx.com"
}
})
return (
<>
<SContainer>
<Header />
<h2>User Page</h2>
<SearchInput />
<SUserArea>
{users.map((user) => (
<UserCard key={user.id} user={user} />
))}
</SUserArea>
</SContainer>
</>
);
};
const SContainer = styled.div`
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
`
const SUserArea = styled.div`
padding-top: 40px;
width: calc(100% - 48px);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-gap: 20px;
`
export default Users;