Skip to content

feat: 마비노기 테마 팔레트 추가 #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@ next-env.d.ts

# editor
.cursor

# development principles
DEVELOPMENT_PRINCIPLES.md
GIT_COMMIT_CONVENTION.md
97 changes: 97 additions & 0 deletions src/app/(main)/color-palette/page.tsx
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클래스 기반 컬러 설정 외의 컬러 설정 방법을 소개하고 있는데 모든 방법을 소개할 필요는 없고 오히려 데모 페이지에서 프로젝트에서 사용하지 않는 방법까지 보여주면 이후 프로젝트에 참여한 팀원이 헛깔릴 것 같습니다.
현재는 클래스기반 스타일링을 사용하고 있으니 해당 방법만 남기는것이 어떨까 싶습니다.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제없으면 머지하기 전에 resolve해주세요

Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { themeColors, colorCategories } from '@/lib/theme';

export default function ColorPalettePage() {
return (
<div className="container mx-auto p-6">
<h1 className="text-3xl font-bold mb-8">프로젝트 테마 색상</h1>

<div className="space-y-12">
{/* 기본 색상 */}
<section>
<h2 className="text-2xl font-semibold mb-6">기본 색상</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
{Object.entries(colorCategories.neutral).map(([name, color]) => (
<div key={name} className="text-center">
<div
className="w-20 h-20 rounded-lg border border-gray-300 shadow-sm mx-auto mb-3"
style={{ backgroundColor: color }}
/>
<p className="font-medium">{name}</p>
<p className="text-sm text-gray-600">{color}</p>
</div>
))}
</div>
</section>

{/* 주요 색상 */}
<section>
<h2 className="text-2xl font-semibold mb-6">주요 색상</h2>
<div className="grid grid-cols-2 md:grid-cols-3 gap-6">
{Object.entries(colorCategories.primary).map(([name, color]) => (
<div key={name} className="text-center">
<div
className="w-20 h-20 rounded-lg border border-gray-300 shadow-sm mx-auto mb-3"
style={{ backgroundColor: color }}
/>
<p className="font-medium">{name}</p>
<p className="text-sm text-gray-600">{color}</p>
</div>
))}
</div>
</section>

{/* 확장 색상 */}
<section>
<h2 className="text-2xl font-semibold mb-6">확장 색상</h2>
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-6">
{Object.entries(colorCategories.accent).map(([name, color]) => (
<div key={name} className="text-center">
<div
className="w-20 h-20 rounded-lg border border-gray-300 shadow-sm mx-auto mb-3"
style={{ backgroundColor: color }}
/>
<p className="font-medium">{name}</p>
<p className="text-sm text-gray-600">{color}</p>
</div>
))}
</div>
</section>

{/* 사용 방법 */}
<section className="bg-gray-50 p-6 rounded-lg">
<h2 className="text-2xl font-semibold mb-4">사용 방법</h2>
<div className="space-y-4">
<div>
<h3 className="text-lg font-medium mb-2">CSS 클래스</h3>
<div className="bg-white p-3 rounded border">
<code className="text-sm">
&lt;div className=&quot;text-theme-blue bg-theme-red&quot;&gt;
</code>
</div>
</div>

<div>
<h3 className="text-lg font-medium mb-2">CSS 변수</h3>
<div className="bg-white p-3 rounded border">
<code className="text-sm">
color: var(--color-blue);<br />
background-color: var(--color-red);
</code>
</div>
</div>

<div>
<h3 className="text-lg font-medium mb-2">TypeScript</h3>
<div className="bg-white p-3 rounded border">
<code className="text-sm">
import {'{'} themeColors {'}'} from &apos;@/lib/theme&apos;;<br />
const buttonColor = themeColors.blue;
</code>
</div>
</div>
</div>
</section>
</div>
</div>
);
}
65 changes: 65 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,21 @@
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--color-white: #FFFFFF;
--color-black: #000000;
--color-blue: #0000C0;
--color-red: #C00000;
--color-yellow: #FFFF00;
--color-light-gray: #D3D3D3;
--color-dark-gray: #282828;
--color-cobalt-blue: #3678F1;
--color-bright-red: #CF1414;
--color-banana-yellow: #FFE062;
--color-ivory-ice: #DEBA86;
--color-hydran-pink: #FFBFCA;
--color-choco-brown: #4E2E28;
--color-delegate-green: #7DDCC4;
--color-purple: #800080;
}

.dark {
Expand Down Expand Up @@ -138,3 +153,53 @@
-ms-overflow-style: none;
}
}

/* 다크 모드 지원 (선택사항) */
@media (prefers-color-scheme: dark) {
:root {
/* 다크 모드에서 색상 조정이 필요한 경우 여기에 추가 */
}
}

/* 기본 스타일 */
body {
color: var(--color-black);
background: var(--color-white);
}

/* 유틸리티 클래스 */
.text-theme-blue { color: var(--color-blue); }
.text-theme-red { color: var(--color-red); }
.text-theme-yellow { color: var(--color-yellow); }
.text-theme-cobalt-blue { color: var(--color-cobalt-blue); }
.text-theme-bright-red { color: var(--color-bright-red); }
.text-theme-banana-yellow { color: var(--color-banana-yellow); }
.text-theme-ivory-ice { color: var(--color-ivory-ice); }
.text-theme-hydran-pink { color: var(--color-hydran-pink); }
.text-theme-choco-brown { color: var(--color-choco-brown); }
.text-theme-delegate-green { color: var(--color-delegate-green); }
.text-theme-purple { color: var(--color-purple); }

.bg-theme-blue { background-color: var(--color-blue); }
.bg-theme-red { background-color: var(--color-red); }
.bg-theme-yellow { background-color: var(--color-yellow); }
.bg-theme-cobalt-blue { background-color: var(--color-cobalt-blue); }
.bg-theme-bright-red { background-color: var(--color-bright-red); }
.bg-theme-banana-yellow { background-color: var(--color-banana-yellow); }
.bg-theme-ivory-ice { background-color: var(--color-ivory-ice); }
.bg-theme-hydran-pink { background-color: var(--color-hydran-pink); }
.bg-theme-choco-brown { background-color: var(--color-choco-brown); }
.bg-theme-delegate-green { background-color: var(--color-delegate-green); }
.bg-theme-purple { background-color: var(--color-purple); }

.border-theme-blue { border-color: var(--color-blue); }
.border-theme-red { border-color: var(--color-red); }
.border-theme-yellow { border-color: var(--color-yellow); }
.border-theme-cobalt-blue { border-color: var(--color-cobalt-blue); }
.border-theme-bright-red { border-color: var(--color-bright-red); }
.border-theme-banana-yellow { border-color: var(--color-banana-yellow); }
.border-theme-ivory-ice { border-color: var(--color-ivory-ice); }
.border-theme-hydran-pink { border-color: var(--color-hydran-pink); }
.border-theme-choco-brown { border-color: var(--color-choco-brown); }
.border-theme-delegate-green { border-color: var(--color-delegate-green); }
.border-theme-purple { border-color: var(--color-purple); }
Comment on lines +171 to +205
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tailwind를 사용중이라 동일한 색상을 사용중일 경우 이렇게 직접 border, text별로 하나하나 설정할 필요 없이 tailwind.config에서 테마 컬러를 설정해주면 색상별 유틸리티 글래스를 자동으로 생성해줍니다
tailwind.config가 아니라 global.css에서 테마 컬러를 적용하고 싶으면 @theme를 사용할 수 있습니다

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아앗 이 방법을 몰랐습니다. 참고해서 수정하도록 하겠습니다. 혹시 다음 PR에서 수정해도 괜찮을까요?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

브랜치를 급하게 머지해야하거나 충돌하는 부분이 없는데 지금 수정하지 않고 다음 PR에서 수정하려는 이유가 있나요?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

디스코드에서 이야기 진행
dev-ant: 잘 알지 못하는 부분이고 얼마나 걸릴지 몰라 머지 이후 작업하려고 했다.
mental: 얼마 안걸리고 찾아보면 바로 적응 가능하다.
dev-ant: 알겠다 현 요청에 반영 후 머지하겠다.

80 changes: 80 additions & 0 deletions src/lib/theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// 프로젝트 테마 색상 정의
export const themeColors = {
// 기본 색상
white: '#FFFFFF',
black: '#000000',

// 주요 색상
blue: '#0000C0',
red: '#C00000',
yellow: '#FFFF00',

// 그레이 톤
lightGray: '#D3D3D3',
darkGray: '#282828',

// 확장 색상
cobaltBlue: '#3678F1',
brightRed: '#CF1414',
bananaYellow: '#FFE062',
ivoryIce: '#DEBA86',
hydranPink: '#FFBFCA',
chocoBrown: '#4E2E28',
delegateGreen: '#7DDCC4',
purple: '#800080',
Comment on lines +4 to +24
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

global.css에서 이미 정의된 테마를 재정의하고 있는데 이유가 뭔가요?
이후 테마 컬러를 추가하거나 수정할때마다 두 파일 모두 수정해야 하나요?
현재는 SSOT원칙에 위배되는 것으로 보입니다

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theme 색상을 사용할지가 확실하지 않아 우선은 분리해두었습니다. 팔레트 page 만들어두었는데, 색상 한번 리뷰해주시면 이번 PR에서 합치도록 하겠습니다.

Copy link
Collaborator

@mental-disaster mental-disaster Jun 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정 후 resolve 부탁드려요

} as const;

// 색상 타입 정의
export type ThemeColor = keyof typeof themeColors;
export type ThemeColorValue = typeof themeColors[ThemeColor];

// 색상 카테고리별 그룹화
export const colorCategories = {
primary: {
blue: themeColors.blue,
red: themeColors.red,
yellow: themeColors.yellow,
},
neutral: {
white: themeColors.white,
black: themeColors.black,
lightGray: themeColors.lightGray,
darkGray: themeColors.darkGray,
},
accent: {
cobaltBlue: themeColors.cobaltBlue,
brightRed: themeColors.brightRed,
bananaYellow: themeColors.bananaYellow,
ivoryIce: themeColors.ivoryIce,
hydranPink: themeColors.hydranPink,
chocoBrown: themeColors.chocoBrown,
delegateGreen: themeColors.delegateGreen,
purple: themeColors.purple,
},
} as const;

// CSS 변수 이름 생성 함수
export const getCssVariableName = (colorName: ThemeColor): string => {
return `--color-${colorName.replace(/([A-Z])/g, '-$1').toLowerCase()}`;
};

// 색상 값 가져오기 함수
export const getThemeColor = (colorName: ThemeColor): ThemeColorValue => {
return themeColors[colorName];
};

// CSS 변수로 색상 가져오기 함수
export const getCssVariableColor = (colorName: ThemeColor): string => {
return `var(${getCssVariableName(colorName)})`;
};

// 색상 유효성 검사 함수
export const isValidThemeColor = (color: string): color is ThemeColorValue => {
return Object.values(themeColors).includes(color as ThemeColorValue);
};

// 색상 이름으로 색상 찾기 함수
export const findColorByName = (colorValue: string): ThemeColor | null => {
const entry = Object.entries(themeColors).find(([_, value]) => value === colorValue);
return entry ? (entry[0] as ThemeColor) : null;
};