Skip to content
Merged
20 changes: 20 additions & 0 deletions apps/web/src/components/common/Button/Button.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import styled from "@emotion/styled";

export const Button = styled.button<{ $width: string | number }>`
width: ${({ $width }) =>
typeof $width === "number" ? `${$width}px` : $width};
height: 40px;
padding: 0 16px;
border: 0;
border-radius: 10px;
background: #676767;
color: #fdfcfa;
font-size: 14px;
font-weight: 500;
line-height: 20px;
cursor: pointer;

&:hover {
background: #5d5d5d;
}
`;
20 changes: 20 additions & 0 deletions apps/web/src/components/common/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { ButtonHTMLAttributes, PropsWithChildren } from "react";
import * as S from "./Button.styled";

interface ButtonProps
extends PropsWithChildren, ButtonHTMLAttributes<HTMLButtonElement> {
width?: string | number;
}

export function Button({
children,
width = "100%",
type = "button",
...buttonProps
}: ButtonProps) {
return (
<S.Button type={type} $width={width} {...buttonProps}>
{children}
</S.Button>
);
}
1 change: 1 addition & 0 deletions apps/web/src/components/common/Button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Button } from "./Button";
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import styled from "@emotion/styled";

export const Label = styled.label`
display: inline-flex;
align-items: center;
gap: 3.5px;
color: #6b6b6b;
font-size: 14px;
font-weight: 500;
line-height: 20px;
cursor: pointer;
`;

export const Checkbox = styled.input`
width: 13px;
height: 13px;
margin: 0;
accent-color: #676767;
`;
19 changes: 19 additions & 0 deletions apps/web/src/components/common/CheckboxField/CheckboxField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { InputHTMLAttributes } from "react";
import * as S from "./CheckboxField.styled";

interface CheckboxFieldProps extends InputHTMLAttributes<HTMLInputElement> {
label: string;
}

export function CheckboxField({
label,
type = "checkbox",
...inputProps
}: CheckboxFieldProps) {
return (
<S.Label>
<S.Checkbox type={type} {...inputProps} />
<span>{label}</span>
</S.Label>
);
}
1 change: 1 addition & 0 deletions apps/web/src/components/common/CheckboxField/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { CheckboxField } from "./CheckboxField";
35 changes: 35 additions & 0 deletions apps/web/src/components/common/InputField/InputField.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import styled from "@emotion/styled";

export const Field = styled.div`
display: flex;
flex-direction: column;
gap: 8px;
`;

export const Label = styled.label`
color: #1a1a1a;
font-size: 14px;
font-weight: 500;
line-height: 14px;
`;

export const Input = styled.input`
width: 100%;
height: 36px;
padding: 0 14px;
border: 1px solid transparent;
border-radius: 10px;
background: #c6c6c6;
color: #1a1a1a;
font-size: 14px;

&::placeholder {
color: rgba(26, 26, 26, 0.48);
}

&:focus {
outline: none;
border-color: rgba(26, 26, 26, 0.25);
background: #d1d1d1;
}
`;
21 changes: 21 additions & 0 deletions apps/web/src/components/common/InputField/InputField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { InputHTMLAttributes } from "react";
import * as S from "./InputField.styled";

interface InputFieldProps extends InputHTMLAttributes<HTMLInputElement> {
id: string;
label: string;
}

export function InputField({
id,
label,
type = "text",
...inputProps
}: InputFieldProps) {
return (
<S.Field>
<S.Label htmlFor={id}>{label}</S.Label>
<S.Input id={id} type={type} {...inputProps} />
</S.Field>
);
}
1 change: 1 addition & 0 deletions apps/web/src/components/common/InputField/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { InputField } from "./InputField";
13 changes: 13 additions & 0 deletions apps/web/src/components/common/TextButton/TextButton.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import styled from "@emotion/styled";

export const TextButton = styled.a<{ $fontWeight: 400 | 500 }>`
color: #000;
font-size: 14px;
font-weight: ${({ $fontWeight }) => $fontWeight};
line-height: 20px;
text-decoration: none;

&:hover {
text-decoration: underline;
}
`;
19 changes: 19 additions & 0 deletions apps/web/src/components/common/TextButton/TextButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { AnchorHTMLAttributes, PropsWithChildren } from "react";
import * as S from "./TextButton.styled";

interface TextButtonProps
extends PropsWithChildren, AnchorHTMLAttributes<HTMLAnchorElement> {
fontWeight?: 400 | 500;
}

export function TextButton({
children,
fontWeight = 500,
...anchorProps
}: TextButtonProps) {
return (
<S.TextButton $fontWeight={fontWeight} {...anchorProps}>
{children}
</S.TextButton>
);
}
1 change: 1 addition & 0 deletions apps/web/src/components/common/TextButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { TextButton } from "./TextButton";
4 changes: 4 additions & 0 deletions apps/web/src/components/common/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export * from "./Button";
export * from "./CheckboxField";
export * from "./InputField";
export * from "./Layout";
export * from "./TextButton";
75 changes: 74 additions & 1 deletion apps/web/src/pages/LoginPage/LoginPage.styled.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,78 @@
import styled from "@emotion/styled";

export const Page = styled.div`
padding: 2rem;
position: relative;
min-height: calc(100vh - 65px);
overflow: hidden;
background: ${({ theme }) => theme.gray[100]};
`;

export const SplitBackground = styled.div`
position: absolute;
inset: 0;
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
`;

export const LeftBackground = styled.div`
background: #efefef;
`;

export const RightBackground = styled.div`
background: #767676;
`;

export const Content = styled.div`
position: relative;
z-index: 1;
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
align-items: center;
width: 100%;
min-height: calc(100vh - 140px);
padding: 80px 0;
`;
Comment thread
wonhj12 marked this conversation as resolved.

export const FormSection = styled.section`
width: 100%;
max-width: 448px;
padding-top: 8px;
justify-self: center;
`;

export const WelcomeTitle = styled.h1`
margin: 0;
color: #1a1a1a;
font-size: 30px;
font-weight: 700;
line-height: 36px;
`;

export const WelcomeDescription = styled.p`
margin: 8px 0 0;
color: #6b6b6b;
font-size: 16px;
line-height: 24px;
`;

export const Form = styled.form`
display: flex;
flex-direction: column;
gap: 24px;
margin-top: 32px;
`;

export const OptionsRow = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
`;

export const SignupText = styled.p`
margin: 28px 0 0;
color: #6b6b6b;
font-size: 14px;
line-height: 20px;
text-align: center;
`;
46 changes: 44 additions & 2 deletions apps/web/src/pages/LoginPage/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,52 @@
import {
Button,
CheckboxField,
InputField,
TextButton,
} from "@/components/common";
import * as S from "./LoginPage.styled";

export function LoginPage() {
return (
<S.Page>
<h1>로그인</h1>
<p>계정에 로그인합니다.</p>
<S.SplitBackground aria-hidden="true">
<S.LeftBackground />
<S.RightBackground />
</S.SplitBackground>

<S.Content>
<S.FormSection>
<S.WelcomeTitle>다시 만나서 반가워요</S.WelcomeTitle>
<S.WelcomeDescription>
정원에 물을 주러 오셨군요!
</S.WelcomeDescription>

<S.Form>
<InputField id="email" name="email" type="email" label="이메일" />

<InputField
id="password"
name="password"
type="password"
label="비밀번호"
/>

<S.OptionsRow>
<CheckboxField label="로그인 상태 유지" name="remember" />

<TextButton href="#" fontWeight={400}>
비밀번호 찾기
</TextButton>
</S.OptionsRow>
Comment thread
wonhj12 marked this conversation as resolved.

<Button type="submit">로그인</Button>
</S.Form>
Comment thread
wonhj12 marked this conversation as resolved.

<S.SignupText>
아직 정원이 없으신가요? <TextButton href="#">회원가입</TextButton>
</S.SignupText>
</S.FormSection>
</S.Content>
</S.Page>
);
}
6 changes: 5 additions & 1 deletion apps/web/tsconfig.app.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"extends": "@jandi-fe/typescript-config/react-vite-app",
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo"
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src"]
}
10 changes: 10 additions & 0 deletions apps/web/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import path from "node:path";
import { fileURLToPath } from "node:url";
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
Comment thread
wonhj12 marked this conversation as resolved.
},
});
3 changes: 1 addition & 2 deletions packages/ui/src/GlobalStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ const globalStyles = css`

body {
margin: 0;
display: flex;
place-items: center;
width: 100%;
min-width: 320px;
min-height: 100vh;
}
Expand Down