diff --git a/apps/timo-web/app/[locale]/onboarding/_components/.gitkeep b/apps/timo-web/app/[locale]/onboarding/_components/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/apps/timo-web/app/[locale]/onboarding/_components/OnboardingButton.tsx b/apps/timo-web/app/[locale]/onboarding/_components/OnboardingButton.tsx
new file mode 100644
index 00000000..7f4251ab
--- /dev/null
+++ b/apps/timo-web/app/[locale]/onboarding/_components/OnboardingButton.tsx
@@ -0,0 +1,71 @@
+import {
+ ChevronLeftIcon,
+ ChevronSmallRightIcon,
+ ChevronSmallRightWhiteIcon,
+} from "@repo/timo-design-system/icons";
+import { cn } from "@repo/timo-design-system/utils";
+
+export interface OnboardingButtonProps {
+ variant: "next" | "prev" | "start";
+ label: string;
+ isActive?: boolean;
+ disabled?: boolean;
+ onClick?: () => void;
+}
+
+export const OnboardingButton = ({
+ variant,
+ label,
+ isActive = false,
+ disabled = false,
+ onClick,
+}: OnboardingButtonProps) => {
+ if (variant === "prev") {
+ return (
+
+ );
+ }
+
+ if (variant === "start") {
+ return (
+
+ );
+ }
+
+ const isDisabled = disabled || !isActive;
+
+ return (
+
+ );
+};
diff --git a/apps/timo-web/app/[locale]/onboarding/_components/OnboardingGoogleButton.tsx b/apps/timo-web/app/[locale]/onboarding/_components/OnboardingGoogleButton.tsx
new file mode 100644
index 00000000..c898324b
--- /dev/null
+++ b/apps/timo-web/app/[locale]/onboarding/_components/OnboardingGoogleButton.tsx
@@ -0,0 +1,38 @@
+import googleLogo from "@repo/timo-design-system/assets/images/google-logo.svg";
+import { cn } from "@repo/timo-design-system/utils";
+import Image from "next/image";
+
+type OnboardingGoogleButtonVariant = "login" | "connectCalendar";
+
+interface OnboardingGoogleButtonProps {
+ variant: OnboardingGoogleButtonVariant;
+ label: string;
+ isSelected?: boolean;
+ onClick?: () => void;
+}
+
+export const OnboardingGoogleButton = ({
+ label,
+ isSelected = false,
+ onClick,
+}: OnboardingGoogleButtonProps) => {
+ return (
+
+ );
+};
diff --git a/apps/timo-web/app/[locale]/onboarding/_components/OnboardingStepButton.tsx b/apps/timo-web/app/[locale]/onboarding/_components/OnboardingStepButton.tsx
new file mode 100644
index 00000000..d2be88f1
--- /dev/null
+++ b/apps/timo-web/app/[locale]/onboarding/_components/OnboardingStepButton.tsx
@@ -0,0 +1,32 @@
+import { cn } from "@repo/timo-design-system/utils";
+
+interface OnboardingStepButtonProps {
+ step: 1 | 2 | 3 | 4;
+}
+
+const STEPS = [1, 2, 3, 4] as const;
+
+export const OnboardingStepButton = ({ step }: OnboardingStepButtonProps) => {
+ return (
+
+ {STEPS.map((s) => (
+ -
+
+ {s}
+
+
+ ))}
+
+ );
+};
diff --git a/apps/timo-web/app/[locale]/onboarding/_components/OnboardingTimeDropdown.tsx b/apps/timo-web/app/[locale]/onboarding/_components/OnboardingTimeDropdown.tsx
new file mode 100644
index 00000000..196a2507
--- /dev/null
+++ b/apps/timo-web/app/[locale]/onboarding/_components/OnboardingTimeDropdown.tsx
@@ -0,0 +1,64 @@
+import {
+ ChevronDownGrayIcon,
+ ChevronUpGrayIcon,
+} from "@repo/timo-design-system/icons";
+import { Dropdown } from "@repo/timo-design-system/ui";
+import { cn } from "@repo/timo-design-system/utils";
+
+import { getAmPm } from "@/utils/get-am-pm";
+
+const TIME_OPTIONS = Array.from(
+ { length: 25 },
+ (_, i) => `${String(i).padStart(2, "0")}:00`,
+);
+
+interface OnboardingTimeDropdownProps {
+ value: string;
+ placeholder?: string;
+ onChange: (time: string) => void;
+}
+
+export const OnboardingTimeDropdown = ({
+ value,
+ placeholder,
+ onChange,
+}: OnboardingTimeDropdownProps) => {
+ return (
+
+
+ {value ? (
+
+ {value}
+
+ {getAmPm(value)}
+
+
+ ) : (
+
+ {placeholder}
+
+ )}
+
+
+
+
+
+
+ {TIME_OPTIONS.map((time) => (
+ onChange(time)}
+ className={cn(
+ "typo-headline-b-16 flex w-full justify-between rounded-none",
+ value === time ? "text-timo-blue-300" : "text-timo-gray-700",
+ )}
+ >
+ {time}
+ {getAmPm(time)}
+
+ ))}
+
+
+
+ );
+};
diff --git a/apps/timo-web/app/[locale]/onboarding/_containers/.gitkeep b/apps/timo-web/app/[locale]/onboarding/_containers/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/apps/timo-web/app/[locale]/onboarding/_containers/OnboardingButtonContainer.tsx b/apps/timo-web/app/[locale]/onboarding/_containers/OnboardingButtonContainer.tsx
new file mode 100644
index 00000000..368e18c1
--- /dev/null
+++ b/apps/timo-web/app/[locale]/onboarding/_containers/OnboardingButtonContainer.tsx
@@ -0,0 +1,31 @@
+"use client";
+
+import { useTranslations } from "next-intl";
+
+import { OnboardingButton } from "@/app/[locale]/onboarding/_components/OnboardingButton";
+
+interface OnboardingButtonContainerProps {
+ variant: "next" | "prev" | "start";
+ isActive?: boolean;
+ disabled?: boolean;
+ onClick?: () => void;
+}
+
+export const OnboardingButtonContainer = ({
+ variant,
+ isActive,
+ disabled,
+ onClick,
+}: OnboardingButtonContainerProps) => {
+ const t = useTranslations("Onboarding");
+
+ return (
+
+ );
+};
diff --git a/apps/timo-web/app/[locale]/onboarding/_containers/OnboardingGoogleButtonContainer.tsx b/apps/timo-web/app/[locale]/onboarding/_containers/OnboardingGoogleButtonContainer.tsx
new file mode 100644
index 00000000..821d5cb6
--- /dev/null
+++ b/apps/timo-web/app/[locale]/onboarding/_containers/OnboardingGoogleButtonContainer.tsx
@@ -0,0 +1,28 @@
+"use client";
+
+import { useTranslations } from "next-intl";
+
+import { OnboardingGoogleButton } from "@/app/[locale]/onboarding/_components/OnboardingGoogleButton";
+
+interface OnboardingGoogleButtonContainerProps {
+ variant: "login" | "connectCalendar";
+ isSelected?: boolean;
+ onClick?: () => void;
+}
+
+export const OnboardingGoogleButtonContainer = ({
+ variant,
+ isSelected,
+ onClick,
+}: OnboardingGoogleButtonContainerProps) => {
+ const t = useTranslations("Onboarding");
+
+ return (
+
+ );
+};
diff --git a/apps/timo-web/app/[locale]/onboarding/_containers/OnboardingTimeContainer.tsx b/apps/timo-web/app/[locale]/onboarding/_containers/OnboardingTimeContainer.tsx
new file mode 100644
index 00000000..3bf7f761
--- /dev/null
+++ b/apps/timo-web/app/[locale]/onboarding/_containers/OnboardingTimeContainer.tsx
@@ -0,0 +1,17 @@
+"use client";
+
+import { useState } from "react";
+
+import { OnboardingTimeDropdown } from "@/app/[locale]/onboarding/_components/OnboardingTimeDropdown";
+
+export const OnboardingTimeContainer = () => {
+ const [time, setTime] = useState("");
+
+ return (
+
+ );
+};
diff --git a/apps/timo-web/messages/en.json b/apps/timo-web/messages/en.json
index 649a0486..342501de 100644
--- a/apps/timo-web/messages/en.json
+++ b/apps/timo-web/messages/en.json
@@ -40,5 +40,16 @@
"tagLimit": "You can create up to 8 tags, and to add more, please delete existing tags.",
"todoLimitLine1": "You can add up to 20 incomplete to-dos.",
"todoLimitLine2": "To add a new to-do, please complete an existing one."
+ },
+ "Onboarding": {
+ "button": {
+ "next": "Next",
+ "prev": "Previous",
+ "start": "Get Started"
+ },
+ "onboardingGoogleButton": {
+ "login": "Continue with your Google account",
+ "connectCalendar": "Connect Google Calendar"
+ }
}
}
diff --git a/apps/timo-web/messages/ko.json b/apps/timo-web/messages/ko.json
index cf175e99..ee4fa36c 100644
--- a/apps/timo-web/messages/ko.json
+++ b/apps/timo-web/messages/ko.json
@@ -40,5 +40,16 @@
"tagLimit": "태그는 최대 8개까지 만들 수 있으며, 추가하려면 기존 태그를 삭제해 주세요.",
"todoLimitLine1": "완료되지 않은 투두는 최대 20개까지 추가할 수 있어요.",
"todoLimitLine2": "새로운 투두를 추가하려면 기존 투두를 완료해주세요."
+ },
+ "Onboarding": {
+ "button": {
+ "next": "다음",
+ "prev": "이전",
+ "start": "시작하기"
+ },
+ "onboardingGoogleButton": {
+ "login": "Google 계정으로 계속하기",
+ "connectCalendar": "Google 캘린더 연결하기"
+ }
}
}
diff --git a/apps/timo-web/utils/get-am-pm.ts b/apps/timo-web/utils/get-am-pm.ts
new file mode 100644
index 00000000..ccae4dbc
--- /dev/null
+++ b/apps/timo-web/utils/get-am-pm.ts
@@ -0,0 +1,13 @@
+const HOURS_IN_DAY = 24;
+
+/**
+ * "HH:MM" 형식의 시간 문자열에서 오전/오후를 판별합니다.
+ * 24시("24:00")는 다음날 자정(오전)으로 취급합니다.
+ *
+ * @param time - "HH:MM" 형식의 시간 문자열
+ * @returns "AM" 또는 "PM"
+ */
+export const getAmPm = (time: string): "AM" | "PM" => {
+ const hour = parseInt(time.split(":")[0] ?? "0", 10);
+ return hour >= 12 && hour < HOURS_IN_DAY ? "PM" : "AM";
+};
diff --git a/packages/tailwind-config/scrollbar.css b/packages/tailwind-config/scrollbar.css
index 94bd10b5..1b07c7fd 100644
--- a/packages/tailwind-config/scrollbar.css
+++ b/packages/tailwind-config/scrollbar.css
@@ -4,8 +4,8 @@
}
*::-webkit-scrollbar {
- width: 6px;
- height: 6px;
+ width: 8px;
+ height: 8px;
}
*::-webkit-scrollbar-track {
diff --git a/packages/timo-design-system/src/assets/images/google-logo.svg b/packages/timo-design-system/src/assets/images/google-logo.svg
new file mode 100644
index 00000000..8869c6c0
--- /dev/null
+++ b/packages/timo-design-system/src/assets/images/google-logo.svg
@@ -0,0 +1,9 @@
+
diff --git a/packages/timo-design-system/src/icons/source/chevron-down-gray.svg b/packages/timo-design-system/src/icons/source/chevron-down-gray.svg
new file mode 100644
index 00000000..21f4c88f
--- /dev/null
+++ b/packages/timo-design-system/src/icons/source/chevron-down-gray.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/timo-design-system/src/icons/source/chevron-small-down.svg b/packages/timo-design-system/src/icons/source/chevron-small-down.svg
new file mode 100644
index 00000000..2b76ab00
--- /dev/null
+++ b/packages/timo-design-system/src/icons/source/chevron-small-down.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/timo-design-system/src/icons/source/chevron-small-left.svg b/packages/timo-design-system/src/icons/source/chevron-small-left.svg
new file mode 100644
index 00000000..7ef41ae7
--- /dev/null
+++ b/packages/timo-design-system/src/icons/source/chevron-small-left.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/timo-design-system/src/icons/source/chevron-small-right-white.svg b/packages/timo-design-system/src/icons/source/chevron-small-right-white.svg
new file mode 100644
index 00000000..b2ddad4b
--- /dev/null
+++ b/packages/timo-design-system/src/icons/source/chevron-small-right-white.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/timo-design-system/src/icons/source/chevron-small-right.svg b/packages/timo-design-system/src/icons/source/chevron-small-right.svg
new file mode 100644
index 00000000..6354043b
--- /dev/null
+++ b/packages/timo-design-system/src/icons/source/chevron-small-right.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/timo-design-system/src/icons/source/chevron-small-up.svg b/packages/timo-design-system/src/icons/source/chevron-small-up.svg
new file mode 100644
index 00000000..6f46161e
--- /dev/null
+++ b/packages/timo-design-system/src/icons/source/chevron-small-up.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/timo-design-system/src/icons/source/chevron-up-gray.svg b/packages/timo-design-system/src/icons/source/chevron-up-gray.svg
new file mode 100644
index 00000000..6a574c1d
--- /dev/null
+++ b/packages/timo-design-system/src/icons/source/chevron-up-gray.svg
@@ -0,0 +1,3 @@
+