+
{iconName}
diff --git a/src/components/ui/Icon/Icon.tsx b/src/components/ui/Icon/Icon.tsx
index c23ff85..e22f3a4 100644
--- a/src/components/ui/Icon/Icon.tsx
+++ b/src/components/ui/Icon/Icon.tsx
@@ -7,6 +7,10 @@ import PlusIcon from "@/assets/svg/ic-plus.svg?react";
export type IconNameType = "camera" | "close" | "gallery" | "leftArrow" | "paste" | "plus";
+export interface IconProps {
+ name: IconNameType;
+}
+
export const ICONS = {
camera: CameraIcon,
close: CloseIcon,
@@ -17,7 +21,7 @@ export const ICONS = {
};
// 추후 사이즈, 컬러등 추가 가능
-const Icon = ({ name }: { name: IconNameType }) => {
+const Icon = ({ name }: IconProps) => {
const SvgIcon = ICONS[name];
return
;
diff --git a/src/components/ui/IconButton/IconButton.module.scss b/src/components/ui/IconButton/IconButton.module.scss
new file mode 100644
index 0000000..f1f51d8
--- /dev/null
+++ b/src/components/ui/IconButton/IconButton.module.scss
@@ -0,0 +1,42 @@
+@use "@/styles/mixins" as *;
+
+.IconButton {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ &.size-md {
+ gap: 0.375rem;
+ background-color: var(--color-gray400);
+ color: var(--color-white);
+ @include buttonSecondary;
+ }
+
+ &.size-sm {
+ gap: 0.125rem;
+ height: 2.375rem;
+ padding: 0.5rem 0.875rem;
+ background-color: var(--color-text01);
+ color: var(--color-white);
+ border-radius: 0.75rem;
+ @include buttonTertiary;
+ }
+}
+
+.IconButtonStory {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+
+ .Wrapper {
+ display: flex;
+ gap: 1rem;
+ justify-content: center;
+ align-items: center;
+ }
+
+ .Text {
+ font-size: 1.125rem;
+ width: 120px;
+ }
+}
diff --git a/src/components/ui/IconButton/IconButton.stories.tsx b/src/components/ui/IconButton/IconButton.stories.tsx
new file mode 100644
index 0000000..f897bed
--- /dev/null
+++ b/src/components/ui/IconButton/IconButton.stories.tsx
@@ -0,0 +1,50 @@
+import IconButton from "@/components/ui/IconButton/IconButton";
+import styles from "@/components/ui/IconButton/IconButton.module.scss";
+import type { IconButtonProps } from "@/components/ui/IconButton/IconButton.types";
+
+import type { Meta, StoryObj } from "@storybook/react";
+
+const meta: Meta
= {
+ title: "Example/IconButton",
+ component: IconButton,
+ parameters: {
+ layout: "centered",
+ },
+ tags: ["!autodocs"],
+};
+
+export default meta;
+
+export const Primary: StoryObj = {
+ args: {
+ text: "갤러리",
+ iconName: "gallery",
+ size: "md",
+ },
+};
+
+export const IconNameProps: StoryObj = {
+ render: () => (
+
+ ),
+};
+
+export const SizeProps: StoryObj = {
+ render: () => (
+
+ ),
+};
diff --git a/src/components/ui/IconButton/IconButton.tsx b/src/components/ui/IconButton/IconButton.tsx
new file mode 100644
index 0000000..036a372
--- /dev/null
+++ b/src/components/ui/IconButton/IconButton.tsx
@@ -0,0 +1,50 @@
+import React, { useCallback } from "react";
+
+import classNames from "classnames";
+
+import BaseButton from "@/components/ui/BaseButton/BaseButton";
+import Icon from "@/components/ui/Icon/Icon";
+import styles from "@/components/ui/IconButton/IconButton.module.scss";
+import type { IconButtonProps } from "@/components/ui/IconButton/IconButton.types";
+
+const IconButton = React.forwardRef(
+ (
+ {
+ as = BaseButton,
+ className,
+ size = "md",
+ disabled = false,
+ onClick,
+ text,
+ iconName,
+ ...props
+ },
+ ref,
+ ) => {
+ const Comp = as as typeof BaseButton;
+
+ const handleClick = useCallback(
+ (e: React.MouseEvent) => {
+ if (!disabled) {
+ onClick?.(e);
+ }
+ },
+ [onClick, disabled],
+ );
+
+ return (
+
+
+ {text}
+
+ );
+ },
+);
+
+export default IconButton;
diff --git a/src/components/ui/IconButton/IconButton.types.ts b/src/components/ui/IconButton/IconButton.types.ts
new file mode 100644
index 0000000..ba761ac
--- /dev/null
+++ b/src/components/ui/IconButton/IconButton.types.ts
@@ -0,0 +1,11 @@
+import type { ButtonOwnProps } from "@/components/ui/BaseButton/BaseButton.types";
+import type { IconNameType } from "@/components/ui/Icon/Icon";
+
+type IconButtonSize = "md" | "sm";
+
+export interface IconButtonProps
+ extends React.ButtonHTMLAttributes,
+ ButtonOwnProps {
+ size?: IconButtonSize;
+ iconName: IconNameType;
+}
diff --git a/src/main.tsx b/src/main.tsx
index 0f2457d..40b99a9 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -8,6 +8,7 @@ import App from "@/App";
import ReactQueryClientProvider from "@/components/provider/ReactQueryClientProvider";
import "@/styles/reset.scss";
+import "@/styles/global.scss";
createRoot(document.getElementById("root")!).render(
diff --git a/src/styles/_mixins.scss b/src/styles/_mixins.scss
index 21419b3..b06644d 100644
--- a/src/styles/_mixins.scss
+++ b/src/styles/_mixins.scss
@@ -33,17 +33,17 @@
font-weight: var(--font-weight-medium);
}
-@mixin buttonMd {
+@mixin buttonPrimary {
font-size: var(--font-size-default);
font-weight: var(--font-weight-semi-bold);
}
-@mixin buttonSm {
+@mixin buttonSecondary {
font-size: var(--font-size-default);
font-weight: var(--font-weight-medium);
}
-@mixin buttonXs {
+@mixin buttonTertiary {
font-size: var(--font-size-xs);
font-weight: var(--font-weight-medium);
}
diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss
index f714bcd..0b0e75d 100644
--- a/src/styles/_variables.scss
+++ b/src/styles/_variables.scss
@@ -4,13 +4,13 @@
--font-weight-semi-bold: 600;
--font-weight-bold: 700;
- --font-size-xl: 28px;
- --font-size-lg: 24px;
- --font-size-md: 22px;
- --font-size-default: 16px;
- --font-size-sm: 15px;
- --font-size-xs: 14px;
- --font-size-xxs: 13px;
+ --font-size-xl: 1.75rem;
+ --font-size-lg: 1.5rem;
+ --font-size-md: 1.375rem;
+ --font-size-default: 1rem;
+ --font-size-sm: 0.9375rem;
+ --font-size-xs: 0.875rem;
+ --font-size-xxs: 0.8125rem;
--color-white: #fff;
--color-black: #000;
@@ -22,6 +22,7 @@
--color-gray100: #f8f8f8;
--color-gray200: #ebecf0;
--color-gray300: #e1e2e8;
+ --color-gray350: #dcdde3;
--color-gray400: #00000026;
--color-gray500: #00000040;
--color-gray600: #363642;
diff --git a/src/styles/global.scss b/src/styles/global.scss
new file mode 100644
index 0000000..0e78698
--- /dev/null
+++ b/src/styles/global.scss
@@ -0,0 +1,29 @@
+* {
+ font-family: "Pretendard", sans-serif;
+ padding: 0;
+ margin: 0;
+ box-sizing: border-box;
+}
+
+body {
+ max-width: 37.5rem;
+ margin: 0 auto;
+}
+
+a {
+ text-decoration: none;
+ color: inherit;
+}
+
+button {
+ border: none;
+ outline: none;
+ background-color: transparent;
+}
+
+input,
+textarea {
+ border: none;
+ outline: none;
+ background-color: transparent;
+}
diff --git a/src/styles/reset.scss b/src/styles/reset.scss
index a04e60e..2560287 100644
--- a/src/styles/reset.scss
+++ b/src/styles/reset.scss
@@ -128,7 +128,3 @@ table {
border-collapse: collapse;
border-spacing: 0;
}
-
-* {
- font-family: "Pretendard", sans-serif;
-}
diff --git a/tsconfig.json b/tsconfig.json
index ed6beba..0b035c7 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -27,5 +27,5 @@
"plugins": [{ "name": "typescript-plugin-css-modules" }],
"types": ["@testing-library/jest-dom", "vite-plugin-svgr/client", "vite/client"]
},
- "include": ["src", "src/types"]
+ "include": ["src", "src/types", ".storybook/**/*"]
}
diff --git a/vite.config.ts b/vite.config.ts
index efc2bd5..7eafd3f 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -11,15 +11,18 @@ export default defineConfig({
base: "/",
resolve: {
alias: {
- "@": path.resolve(__dirname, "./src"),
+ "@": path.resolve(__dirname, "src"),
},
},
css: {
+ modules: {
+ localsConvention: "camelCase",
+ },
preprocessorOptions: {
scss: {
additionalData: `
- @use "@/styles/_variables.scss" as *;
- @use "@/styles/_mixins.scss" as *;
+ @use "@/styles/_variables.scss";
+ @use "@/styles/_mixins.scss";
`,
},
},