From 399e637817e1c07950ae5840f00ff992bfe8ab24 Mon Sep 17 00:00:00 2001 From: dasosann Date: Tue, 27 Jan 2026 17:35:03 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=ED=8F=B0=ED=8A=B8=20=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=EC=A6=88=20=EC=9C=A0=ED=8B=B8=EB=A6=AC=ED=8B=B0=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD=20(text-{size}-{weight}?= =?UTF-8?q?=20->=20typo-{size}-{weight})?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/generate-tokens.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/generate-tokens.js b/scripts/generate-tokens.js index c047395..3e34833 100644 --- a/scripts/generate-tokens.js +++ b/scripts/generate-tokens.js @@ -176,14 +176,14 @@ colorTokens }); const textColorUtilities = Array.from(textColorUtilitiesSet).sort().join("\n"); -// 폰트 사이즈 유틸리티 (text-{size}-{weight}) +// 폰트 사이즈 유틸리티 (typo-{size}-{weight}) const weights = ["400", "500", "600", "700"]; const fontUtilitiesSet = new Set(); fontTokens.forEach((t) => { const size = t.value; if (typeof size === "number") { weights.forEach((weight) => { - fontUtilitiesSet.add(` .text-${size}-${weight} { + fontUtilitiesSet.add(` .typo-${size}-${weight} { font-size: ${size}px; font-weight: ${weight}; line-height: 1.5; From f4caccfc3d937b10e6a315109f7a16ab39f39562 Mon Sep 17 00:00:00 2001 From: dasosann Date: Tue, 27 Jan 2026 17:36:06 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=EB=B2=84=ED=8A=BC=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B8=B0=EC=A1=B4=20=EB=B8=94=EB=9F=AC=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/page.tsx | 3 +- app/tokens.css | 56 +++++++++--------- components/ui/Button.tsx | 125 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 29 deletions(-) create mode 100644 components/ui/Button.tsx diff --git a/app/page.tsx b/app/page.tsx index 718c073..19f56fb 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,9 +1,10 @@ import Blur from "@/components/common/Blur"; +import Button from "@/components/ui/Button"; export default function Home() { return (
- +
); } diff --git a/app/tokens.css b/app/tokens.css index 02dc7fb..753c63a 100644 --- a/app/tokens.css +++ b/app/tokens.css @@ -135,169 +135,169 @@ @layer utilities { /* Font utilities */ - .text-16-400 { + .typo-16-400 { font-size: 16px; font-weight: 400; line-height: 1.5; } - .text-16-500 { + .typo-16-500 { font-size: 16px; font-weight: 500; line-height: 1.5; } - .text-16-600 { + .typo-16-600 { font-size: 16px; font-weight: 600; line-height: 1.5; } - .text-16-700 { + .typo-16-700 { font-size: 16px; font-weight: 700; line-height: 1.5; } - .text-20-400 { + .typo-20-400 { font-size: 20px; font-weight: 400; line-height: 1.5; } - .text-20-500 { + .typo-20-500 { font-size: 20px; font-weight: 500; line-height: 1.5; } - .text-20-600 { + .typo-20-600 { font-size: 20px; font-weight: 600; line-height: 1.5; } - .text-20-700 { + .typo-20-700 { font-size: 20px; font-weight: 700; line-height: 1.5; } - .text-24-400 { + .typo-24-400 { font-size: 24px; font-weight: 400; line-height: 1.5; } - .text-24-500 { + .typo-24-500 { font-size: 24px; font-weight: 500; line-height: 1.5; } - .text-24-600 { + .typo-24-600 { font-size: 24px; font-weight: 600; line-height: 1.5; } - .text-24-700 { + .typo-24-700 { font-size: 24px; font-weight: 700; line-height: 1.5; } - .text-12-400 { + .typo-12-400 { font-size: 12px; font-weight: 400; line-height: 1.5; } - .text-12-500 { + .typo-12-500 { font-size: 12px; font-weight: 500; line-height: 1.5; } - .text-12-600 { + .typo-12-600 { font-size: 12px; font-weight: 600; line-height: 1.5; } - .text-12-700 { + .typo-12-700 { font-size: 12px; font-weight: 700; line-height: 1.5; } - .text-14-400 { + .typo-14-400 { font-size: 14px; font-weight: 400; line-height: 1.5; } - .text-14-500 { + .typo-14-500 { font-size: 14px; font-weight: 500; line-height: 1.5; } - .text-14-600 { + .typo-14-600 { font-size: 14px; font-weight: 600; line-height: 1.5; } - .text-14-700 { + .typo-14-700 { font-size: 14px; font-weight: 700; line-height: 1.5; } - .text-10-400 { + .typo-10-400 { font-size: 10px; font-weight: 400; line-height: 1.5; } - .text-10-500 { + .typo-10-500 { font-size: 10px; font-weight: 500; line-height: 1.5; } - .text-10-600 { + .typo-10-600 { font-size: 10px; font-weight: 600; line-height: 1.5; } - .text-10-700 { + .typo-10-700 { font-size: 10px; font-weight: 700; line-height: 1.5; } - .text-18-400 { + .typo-18-400 { font-size: 18px; font-weight: 400; line-height: 1.5; } - .text-18-500 { + .typo-18-500 { font-size: 18px; font-weight: 500; line-height: 1.5; } - .text-18-600 { + .typo-18-600 { font-size: 18px; font-weight: 600; line-height: 1.5; } - .text-18-700 { + .typo-18-700 { font-size: 18px; font-weight: 700; line-height: 1.5; diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx new file mode 100644 index 0000000..e0a30af --- /dev/null +++ b/components/ui/Button.tsx @@ -0,0 +1,125 @@ +import React from "react"; +import { cn } from "@/lib/utils"; + +interface ButtonProps extends React.ButtonHTMLAttributes { + children: React.ReactNode; + backgroundColor?: string; + width?: string; + height?: string; + fixed?: boolean; + bottom?: string; // px 단위 문자열 (예: "20px", "0px") + sideGap?: string; // px 단위 문자열 (예: "16px") + maxWidth?: string; // fixed일 때 최대 너비 (예: "430px") + textColor?: string; + fontSize?: string; + safeArea?: boolean; // safe-area-inset-bottom 적용 여부 +} + +const Button = React.forwardRef( + ( + { + children, + backgroundColor = "bg-button-primary", + width = "w-full", + height = "h-12", + fixed = false, + bottom = "0px", + sideGap = "16px", + maxWidth = "max-w-[430px]", + textColor = "text-button-primary-text-default", + fontSize = "typo-20-600", + safeArea = true, + disabled = false, + className, + ...props + }, + ref, + ) => { + // Tailwind 클래스인지 확인 (bg-, text- 등으로 시작) + const isTailwindClass = (value: string) => { + return /^(bg-|text-|border-)/.test(value); + }; + + // disabled 상태에 따른 배경색/텍스트색 결정 + const finalBackgroundColor = disabled + ? "bg-button-background-disabled" + : backgroundColor; + const finalTextColor = disabled + ? "text-button-primary-text-disabled" + : textColor; + + // bg-button-primary일 때 border와 shadow 추가 + const isPrimaryButton = finalBackgroundColor === "bg-button-primary"; + + // 색상 값을 className과 style로 분리 + const bgClass = isTailwindClass(finalBackgroundColor) + ? finalBackgroundColor + : undefined; + const bgStyle = !isTailwindClass(finalBackgroundColor) + ? finalBackgroundColor + : undefined; + + const textClass = isTailwindClass(finalTextColor) + ? finalTextColor + : undefined; + const textStyle = !isTailwindClass(finalTextColor) + ? finalTextColor + : undefined; + + // fixed일 때 bottom 계산 (safeArea 적용) + const getBottomValue = () => { + if (!fixed) return undefined; + + if (safeArea) { + return `calc(${bottom} + env(safe-area-inset-bottom))`; + } + + return bottom; + }; + + return ( + + ); + }, +); + +Button.displayName = "Button"; + +export default Button; From b6431f8073cd3147d89815c344cac2aeb860b8c0 Mon Sep 17 00:00:00 2001 From: dasosann Date: Tue, 27 Jan 2026 17:44:15 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=EB=B2=84=ED=8A=BC=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A5=BC=20forwardRef=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=ED=95=98=EC=97=AC=20ref=20=EC=A7=80=EC=9B=90?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/ui/Button.tsx | 193 +++++++++++++++++++-------------------- 1 file changed, 93 insertions(+), 100 deletions(-) diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx index e0a30af..a81a68c 100644 --- a/components/ui/Button.tsx +++ b/components/ui/Button.tsx @@ -13,113 +13,106 @@ interface ButtonProps extends React.ButtonHTMLAttributes { textColor?: string; fontSize?: string; safeArea?: boolean; // safe-area-inset-bottom 적용 여부 + ref?: React.Ref; } -const Button = React.forwardRef( - ( - { - children, - backgroundColor = "bg-button-primary", - width = "w-full", - height = "h-12", - fixed = false, - bottom = "0px", - sideGap = "16px", - maxWidth = "max-w-[430px]", - textColor = "text-button-primary-text-default", - fontSize = "typo-20-600", - safeArea = true, - disabled = false, - className, - ...props - }, - ref, - ) => { - // Tailwind 클래스인지 확인 (bg-, text- 등으로 시작) - const isTailwindClass = (value: string) => { - return /^(bg-|text-|border-)/.test(value); - }; +export default function Button({ + children, + backgroundColor = "bg-button-primary", + width = "w-full", + height = "h-12", + fixed = false, + bottom = "0px", + sideGap = "16px", + maxWidth = "max-w-[430px]", + textColor = "text-button-primary-text-default", + fontSize = "typo-20-600", + safeArea = true, + disabled = false, + className, + ref, + ...props +}: ButtonProps) { + // Tailwind 클래스인지 확인 (bg-, text- 등으로 시작) + const isTailwindClass = (value: string) => { + return /^(bg-|text-|border-)/.test(value); + }; - // disabled 상태에 따른 배경색/텍스트색 결정 - const finalBackgroundColor = disabled - ? "bg-button-background-disabled" - : backgroundColor; - const finalTextColor = disabled - ? "text-button-primary-text-disabled" - : textColor; + // disabled 상태에 따른 배경색/텍스트색 결정 + const finalBackgroundColor = disabled + ? "bg-button-background-disabled" + : backgroundColor; + const finalTextColor = disabled + ? "text-button-primary-text-disabled" + : textColor; - // bg-button-primary일 때 border와 shadow 추가 - const isPrimaryButton = finalBackgroundColor === "bg-button-primary"; + // bg-button-primary일 때 border와 shadow 추가 + const isPrimaryButton = finalBackgroundColor === "bg-button-primary"; - // 색상 값을 className과 style로 분리 - const bgClass = isTailwindClass(finalBackgroundColor) - ? finalBackgroundColor - : undefined; - const bgStyle = !isTailwindClass(finalBackgroundColor) - ? finalBackgroundColor - : undefined; + // 색상 값을 className과 style로 분리 + const bgClass = isTailwindClass(finalBackgroundColor) + ? finalBackgroundColor + : undefined; + const bgStyle = !isTailwindClass(finalBackgroundColor) + ? finalBackgroundColor + : undefined; - const textClass = isTailwindClass(finalTextColor) - ? finalTextColor - : undefined; - const textStyle = !isTailwindClass(finalTextColor) - ? finalTextColor - : undefined; + const textClass = isTailwindClass(finalTextColor) + ? finalTextColor + : undefined; + const textStyle = !isTailwindClass(finalTextColor) + ? finalTextColor + : undefined; - // fixed일 때 bottom 계산 (safeArea 적용) - const getBottomValue = () => { - if (!fixed) return undefined; + // fixed일 때 bottom 계산 (safeArea 적용) + const getBottomValue = () => { + if (!fixed) return undefined; - if (safeArea) { - return `calc(${bottom} + env(safe-area-inset-bottom))`; - } + if (safeArea) { + return `calc(${bottom} + env(safe-area-inset-bottom))`; + } - return bottom; - }; + return bottom; + }; - return ( - - ); - }, -); - -Button.displayName = "Button"; - -export default Button; + return ( + + ); +} From e589f6c10723985c5e7b04829f8e664479a76d98 Mon Sep 17 00:00:00 2001 From: dasosann Date: Tue, 27 Jan 2026 17:46:29 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20=EB=B2=84=ED=8A=BC=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=9D=98=20=EC=97=AC=EB=B0=B1=20?= =?UTF-8?q?=EB=B0=8F=20=EC=B5=9C=EB=8C=80=20=EB=84=88=EB=B9=84=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=EC=9D=84=20=EB=AC=B8=EC=9E=90=EC=97=B4=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=88=AB=EC=9E=90=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/ui/Button.tsx | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx index a81a68c..3be5b44 100644 --- a/components/ui/Button.tsx +++ b/components/ui/Button.tsx @@ -7,9 +7,9 @@ interface ButtonProps extends React.ButtonHTMLAttributes { width?: string; height?: string; fixed?: boolean; - bottom?: string; // px 단위 문자열 (예: "20px", "0px") - sideGap?: string; // px 단위 문자열 (예: "16px") - maxWidth?: string; // fixed일 때 최대 너비 (예: "430px") + bottom?: number; // 하단 여백 (px) + sideGap?: number; // 좌우 여백 (px) + maxWidth?: number; // fixed일 때 최대 너비 (px) textColor?: string; fontSize?: string; safeArea?: boolean; // safe-area-inset-bottom 적용 여부 @@ -22,9 +22,9 @@ export default function Button({ width = "w-full", height = "h-12", fixed = false, - bottom = "0px", - sideGap = "16px", - maxWidth = "max-w-[430px]", + bottom = 0, + sideGap = 16, + maxWidth = 430, textColor = "text-button-primary-text-default", fontSize = "typo-20-600", safeArea = true, @@ -69,10 +69,10 @@ export default function Button({ if (!fixed) return undefined; if (safeArea) { - return `calc(${bottom} + env(safe-area-inset-bottom))`; + return `calc(${bottom}px + env(safe-area-inset-bottom))`; } - return bottom; + return `${bottom}px`; }; return ( @@ -87,7 +87,7 @@ export default function Button({ textClass, fontSize, // fixed 속성에 따른 분기 - fixed ? `fixed z-50 mx-auto ${maxWidth}` : width, + fixed ? "fixed z-50 mx-auto" : width, // disabled 상태에 따른 커서 disabled ? "cursor-not-allowed" : "cursor-pointer", className, @@ -96,8 +96,9 @@ export default function Button({ ...(fixed ? { bottom: getBottomValue(), - left: sideGap, - right: sideGap, + left: `${sideGap}px`, + right: `${sideGap}px`, + maxWidth: `${maxWidth}px`, } : undefined), ...(bgStyle ? { backgroundColor: bgStyle } : undefined), From 9e2fb1bc876fa7341e64a9fa772de95052abd508 Mon Sep 17 00:00:00 2001 From: dasosann Date: Tue, 27 Jan 2026 17:50:37 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20=EB=B2=84=ED=8A=BC=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=97=90=EC=84=9C=20Tailwind=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=ED=99=95=EC=9D=B8=20=EB=B0=8F=20?= =?UTF-8?q?=EC=83=89=EC=83=81=20=EA=B0=92=EC=9D=84=20=EB=B6=84=EB=A6=AC?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/ui/Button.tsx | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx index 3be5b44..a021bef 100644 --- a/components/ui/Button.tsx +++ b/components/ui/Button.tsx @@ -33,11 +33,6 @@ export default function Button({ ref, ...props }: ButtonProps) { - // Tailwind 클래스인지 확인 (bg-, text- 등으로 시작) - const isTailwindClass = (value: string) => { - return /^(bg-|text-|border-)/.test(value); - }; - // disabled 상태에 따른 배경색/텍스트색 결정 const finalBackgroundColor = disabled ? "bg-button-background-disabled" @@ -49,21 +44,6 @@ export default function Button({ // bg-button-primary일 때 border와 shadow 추가 const isPrimaryButton = finalBackgroundColor === "bg-button-primary"; - // 색상 값을 className과 style로 분리 - const bgClass = isTailwindClass(finalBackgroundColor) - ? finalBackgroundColor - : undefined; - const bgStyle = !isTailwindClass(finalBackgroundColor) - ? finalBackgroundColor - : undefined; - - const textClass = isTailwindClass(finalTextColor) - ? finalTextColor - : undefined; - const textStyle = !isTailwindClass(finalTextColor) - ? finalTextColor - : undefined; - // fixed일 때 bottom 계산 (safeArea 적용) const getBottomValue = () => { if (!fixed) return undefined; @@ -82,9 +62,9 @@ export default function Button({ className={cn( // 기본 스타일 "flex items-center justify-center rounded-[12px] transition-colors duration-100", - bgClass, + finalBackgroundColor, height, - textClass, + finalTextColor, fontSize, // fixed 속성에 따른 분기 fixed ? "fixed z-50 mx-auto" : width, @@ -101,8 +81,6 @@ export default function Button({ maxWidth: `${maxWidth}px`, } : undefined), - ...(bgStyle ? { backgroundColor: bgStyle } : undefined), - ...(textStyle ? { color: textStyle } : undefined), ...(isPrimaryButton ? { border: "0.8px solid rgba(255, 255, 255, 0.3)", From bb3ff567540a94da604077064a6d67d4027ab95f Mon Sep 17 00:00:00 2001 From: dasosann Date: Wed, 28 Jan 2026 13:18:00 +0900 Subject: [PATCH 6/6] =?UTF-8?q?feat:=20=EB=B2=84=ED=8A=BC=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=97=90=20type=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/ui/Button.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx index a021bef..d3740d1 100644 --- a/components/ui/Button.tsx +++ b/components/ui/Button.tsx @@ -31,6 +31,7 @@ export default function Button({ disabled = false, className, ref, + type = "button", ...props }: ButtonProps) { // disabled 상태에 따른 배경색/텍스트색 결정 @@ -58,6 +59,7 @@ export default function Button({ return (