diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 2b15e55c..3ed097d9 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -6,7 +6,7 @@ env: on: pull_request: - branches: ['main'] + branches: ['main', feat/**] jobs: build: diff --git a/.stylelintrc b/.stylelintrc index 2aa80adc..cd432f6d 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -8,6 +8,7 @@ } ], "rules": { - "selector-max-type": null + "selector-max-type": null, + "no-descending-specificity": null } } diff --git a/src/components/BottomSheet/index.stories.tsx b/src/components/BottomSheet/index.stories.tsx index 76136f02..a10f0980 100644 --- a/src/components/BottomSheet/index.stories.tsx +++ b/src/components/BottomSheet/index.stories.tsx @@ -53,10 +53,13 @@ export const 바텀시트: StoryObj = {

누구에게 보낼까요?

- - diff --git a/src/components/Button/index.stories.tsx b/src/components/Button/index.stories.tsx index 9a92c77f..ec788c83 100644 --- a/src/components/Button/index.stories.tsx +++ b/src/components/Button/index.stories.tsx @@ -1,20 +1,104 @@ import type { Meta, StoryObj } from '@storybook/react'; +import { css } from '@emotion/react'; +import { PencilLine } from '@/assets/icons'; import Button from './'; const meta = { title: 'Components/Button', component: Button, tags: ['autodocs'], - argTypes: {}, + argTypes: { + disabled: { + control: 'boolean', + description: '버튼의 비활성화 여부입니다.', + }, + }, } satisfies Meta; export default meta; type Story = StoryObj; -export const Primary: Story = { +export const Default: Story = { args: { variant: 'primary', - children: '버튼입니다', + size: 'md', + children: 'button', + disabled: false, }, }; + +const styles = { + container: css` + display: flex; + flex-direction: column; + gap: 1rem; + width: 21rem; + `, + semiTransparentContainer: css` + display: flex; + gap: 1rem; + padding: 1rem; + background-color: #fcecd0; + `, +}; + +export const Primary: Story = { + render: () => ( +
+ + +
+ ), +}; + +export const PrimaryOutline: Story = { + render: () => ( +
+ + +
+ ), +}; + +export const Secondary: Story = { + render: () => ( +
+ + +
+ ), +}; + +export const SemiTransparent: Story = { + render: () => ( +
+ + +
+ ), +}; diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 58307b8f..5953f954 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -1,17 +1,19 @@ import React, { forwardRef } from 'react'; -import styles, { type ButtonVariant } from './styles'; +import styles, { type ButtonSize, type ButtonVariant } from './styles'; interface ButtonProps extends React.ComponentProps<'button'> { /** 버튼의 배경 색상과 폰트 색상을 지정합니다. */ variant?: ButtonVariant; + /** 버튼에 들어갈 패딩의 크기를 지정합니다. */ + size?: ButtonSize; /** 버튼의 내용을 지정합니다. */ children?: React.ReactNode; } const Button = forwardRef( - ({ variant = 'primary', children, ...props }, ref) => { + ({ variant = 'primary', size = 'md', children, ...props }, ref) => { return ( - ); diff --git a/src/components/Button/styles.ts b/src/components/Button/styles.ts index ed9aa614..e72b9806 100644 --- a/src/components/Button/styles.ts +++ b/src/components/Button/styles.ts @@ -1,46 +1,113 @@ import { css } from '@emotion/react'; +import textStyles from '@/styles/textStyles'; import COLORS from '@/constants/colors'; -export type ButtonVariant = 'primary' | 'primary-unaccent' | 'white'; +export type ButtonVariant = + | 'primary' + | 'primary-outline' + | 'secondary' + | 'semi-transparent' + | 'semi-transparent-unaccent'; + +export type ButtonSize = 'md' | 'sm'; const styles = { - button: (variant: ButtonVariant) => css` + button: (variant: ButtonVariant, size: ButtonSize) => css` display: flex; flex: 1 0; gap: 0.5rem; justify-content: center; align-items: center; - padding: 0.75rem 1.25rem; border: none; - border-radius: 3.8rem; - font-weight: 500; - font-size: 0.875rem; + border-radius: 6.25rem; cursor: pointer; + transition: all 0.2s ease; + + &:not(:disabled):active { + box-shadow: 0 2px 10px rgb(0 0 0 / 0.2); + transform: scale(0.98); + } + &:disabled { + box-shadow: none; + cursor: not-allowed; + } + + ${textStyles.t3} + ${sizeStyles[size]} ${variantStyles[variant]} `, }; +const sizeStyles = { + sm: css` + padding: 0.75rem 1.25rem; + `, + md: css` + padding: 1rem 1.25rem; + `, +}; + const variantStyles = { primary: css` + background-color: ${COLORS.primary}; + color: white; + box-shadow: 0 8px 16px 0 rgb(13 35 51 / 0.2); + + &:hover { + background-color: ${COLORS.primaryHover}; + } + + &:disabled { + background-color: ${COLORS.primaryDisabled}; + } + `, + 'primary-outline': css` + background-color: white; + color: ${COLORS.primary}; + box-shadow: 0 8px 16px 0 rgb(13 35 51 / 0.2); + outline: 1px solid ${COLORS.primary}; + + &:hover { + outline: 1px solid ${COLORS.primaryHover}; + } + + &:disabled { + background-color: ${COLORS.gray5}; + color: ${COLORS.gray4}; + } + `, + secondary: css` + background-color: white; + color: black; + box-shadow: 0 8px 16px 0 rgb(153 153 153 / 0.2); + + &:hover { + background-color: ${COLORS.gray6}; + } + + &:disabled { + background-color: ${COLORS.gray5}; + color: ${COLORS.gray4}; + } + `, + 'semi-transparent': css` background: radial-gradient( 491.85% 132.88% at 0% 12.5%, rgb(255 255 255 / 0.7) 0%, rgb(255 255 255 / 0.28) 100% ); color: black; + box-shadow: 0 8px 16px 0 rgb(153 153 153 / 0.2); `, - 'primary-unaccent': css` + 'semi-transparent-unaccent': css` background: radial-gradient( 491.85% 132.88% at 0% 12.5%, rgb(255 255 255 / 0.7) 0%, rgb(255 255 255 / 0.28) 100% ); color: ${COLORS.gray3}; - `, - white: css` - background-color: white; - color: black; + box-shadow: 0 8px 16px 0 rgb(153 153 153 / 0.2); `, }; diff --git a/src/components/Navbar/index.stories.tsx b/src/components/Navbar/index.stories.tsx index 91c847a7..ccf0e666 100644 --- a/src/components/Navbar/index.stories.tsx +++ b/src/components/Navbar/index.stories.tsx @@ -22,13 +22,26 @@ const styles = { width: 390px; background-color: #fcecd0; `, + 편지_작성하기: { + iconContainer: css` + flex-grow: 0; + margin-left: 0.75rem; + padding: 0.5rem 0.75rem; + border: none; + border-radius: 0.5rem; + `, + }, }; export const 흘러온_편지: Story = { render: () => ( - - + + ), }; @@ -36,9 +49,11 @@ export const 흘러온_편지: Story = { export const 내게_온_답장: Story = { render: () => ( - - + @@ -54,8 +69,12 @@ export const 사진: Story = { backdrop-filter: blur(2px); `} > - - + + ), }; @@ -63,21 +82,19 @@ export const 사진: Story = { export const 편지_작성하기: Story = { render: () => ( - - + +
- +
), @@ -86,43 +103,14 @@ export const 편지_작성하기: Story = { export const 편지_쓰기: Story = { render: () => ( - ), }; -export const 온보딩: Story = { - render: () => ( - -
- -
-
- ), -}; - export const 로그인: Story = { render: () => ( diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index 066334be..c860f41c 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -2,16 +2,14 @@ import React from 'react'; import styles from './styles'; interface NavbarProps { - /** 클래스 이름입니다. */ - className?: string; /** Navbar에 들어갈 내용입니다. */ children?: React.ReactNode; } /** 화면의 하단에 위치 시킬 컴포넌트입니다. */ -const Navbar = ({ className, children }: NavbarProps) => { +const Navbar = ({ children, ...props }: NavbarProps) => { return ( -