Skip to content
Merged
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
1 change: 1 addition & 0 deletions .biomeignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
**/*.md


38 changes: 15 additions & 23 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
# CodeRabbit 설정 파일
# 모든 브랜치에서 자동 리뷰 활성화
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
language: "ko-KR"
early_access: false

reviews:
# 모든 브랜치에서 리뷰 활성화
profile: "chill"
request_changes_workflow: false
high_level_summary: true
poem: false
review_status: true
# 기본 브랜치가 아닌 브랜치에서도 리뷰 실행
base_branches:
- main
- develop
- "feature/**"
- "fix/**"
- "release/**"

# 리뷰 범위 설정
language:
# TypeScript, JavaScript 파일 리뷰
typescript:
enabled: true
javascript:
collapse_walkthrough: false
auto_review:
enabled: true
drafts: false
base_branches:
- main
- develop

# 리뷰 스타일 설정
review:
# 간결한 리뷰
concise: true
# 코드 개선 제안 포함
suggestions: true
chat:
auto_reply: true
1 change: 1 addition & 0 deletions .github/ACCESSIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,4 @@ CI 환경에서는 접근성 테스트가 자동으로 실행되며, 위반 사




1 change: 1 addition & 0 deletions .github/HUSKY_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,4 @@ TypeScript 타입 오류를 수정한 후 다시 커밋을 시도하세요.




1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,4 @@ disabled 상태에서도 onClick 이벤트가 발생합니다




1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@ Input 컴포넌트를 추가하고 싶습니다. 다음 기능이 필요합니




1 change: 1 addition & 0 deletions .github/RELEASE_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,4 @@ function App() {




8 changes: 8 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { StorybookConfig } from "@storybook/react-vite";
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
import tsconfigPaths from "vite-tsconfig-paths";
import path from "path";
import { fileURLToPath } from "url";

Expand All @@ -21,15 +22,22 @@ const config: StorybookConfig = {
},
async viteFinal(config) {
if (config.plugins) {
config.plugins.push(
tsconfigPaths({
root: path.resolve(dirname, ".."),
})
);
config.plugins.push(vanillaExtractPlugin());
}
// Storybook에서도 alias 사용 가능하도록 설정
// @storybook/* 패키지는 node_modules에서 찾아야 하므로 alias에서 제외
if (config.resolve) {
config.resolve.alias = {
...config.resolve.alias,
"@": path.resolve(dirname, "../src"),
"@/components": path.resolve(dirname, "../src/components"),
"@/tokens": path.resolve(dirname, "../src/tokens"),
"@/icons": path.resolve(dirname, "../src/icons"),
"@/stories": path.resolve(dirname, "../src/stories"),
};
Comment on lines 35 to 42

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check if tsconfig.json exists and examine its paths configuration
if [ -f "tsconfig.json" ]; then
  echo "=== tsconfig.json paths configuration ===" 
  cat tsconfig.json | jq '.compilerOptions.paths // "No paths found"' 2>/dev/null || rg -B 2 -A 10 '"paths"' tsconfig.json
else
  echo "No tsconfig.json found in root"
fi

Repository: Team-NumberOne/daepyro-design-system

Length of output: 152


🏁 Script executed:

# Check main.ts to see if tsconfigPaths plugin is being used
echo "=== Checking .storybook/main.ts for tsconfigPaths usage ==="
cat -n .storybook/main.ts | head -50

Repository: Team-NumberOne/daepyro-design-system

Length of output: 2165


🏁 Script executed:

# Check for any tsconfig files in .storybook directory
echo "=== Checking for tsconfig files in .storybook directory ==="
find .storybook -name "tsconfig*.json" -type f 2>/dev/null

Repository: Team-NumberOne/daepyro-design-system

Length of output: 164


🏁 Script executed:

# Check .storybook/tsconfig.json paths configuration
echo "=== .storybook/tsconfig.json ===" 
cat .storybook/tsconfig.json | jq '.compilerOptions.paths // "No paths found"' 2>/dev/null || cat .storybook/tsconfig.json

Repository: Team-NumberOne/daepyro-design-system

Length of output: 129


Remove redundant alias entries that are already handled by tsconfigPaths.

The root tsconfig.json defines "@/*": ["src/*"], which the tsconfigPaths plugin (line 26) will automatically resolve. The manual alias entries for @/components, @/tokens, @/icons, and @/stories (lines 38-41) duplicate this behavior and can be removed. Only the bare @ alias (line 37) is necessary since it's not covered by the @/* pattern in tsconfig.

🤖 Prompt for AI Agents
In .storybook/main.ts around lines 35 to 42, the alias entries "@/components",
"@/tokens", "@/icons", and "@/stories" are redundant because tsconfigPaths
(configured earlier) already resolves the "@/*" pattern; remove those four
specific alias lines and keep only the bare "@" alias mapping to "../src" while
leaving the spread of config.resolve.alias intact so tsconfigPaths continues to
handle subpaths.

}
Expand Down
9 changes: 9 additions & 0 deletions .storybook/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"jsx": "react-jsx",
"types": ["vite/client", "@testing-library/jest-dom", "@storybook/addon-vitest"]
},
"include": ["../src/**/*", "**/*"]
}

14 changes: 10 additions & 4 deletions .storybook/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,24 @@ export const isTestEnvironment =
/**
* vitest 환경에서 전역 expect를 안전하게 가져옵니다.
* isTestEnvironment가 true일 때만 호출해야 하며, 이 경우 expect는 항상 정의되어 있습니다.
* @testing-library/jest-dom/vitest를 통해 jest-dom matcher가 포함된 expect를 반환합니다.
*
* @returns vitest의 expect 함수
* @returns vitest의 expect 함수 (jest-dom matcher 포함)
* @throws isTestEnvironment가 false일 때 호출하면 에러 발생
*/
export function getExpect(): typeof import("vitest").expect {
export function getExpect() {
if (!isTestEnvironment) {
throw new Error(
"expect는 테스트 환경에서만 사용할 수 있습니다. isTestEnvironment를 확인해주세요."
);
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return (globalThis as { expect: typeof import("vitest").expect }).expect!;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-explicit-any
// @testing-library/jest-dom/vitest가 vitest의 expect 타입을 모듈 확장을 통해 확장하지만,
// 함수 반환 타입에서는 확장된 타입을 직접 참조할 수 없습니다.
// 따라서 타입 단언을 사용하여 jest-dom matcher가 포함된 expect를 반환합니다.
// 실제 런타임에서는 @testing-library/jest-dom/vitest가 이미 로드되어 있으므로
// jest-dom matcher들이 정상적으로 작동합니다.
return (globalThis as unknown as { expect: any }).expect!;
}

/**
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0




1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.




5 changes: 4 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
"linter": {
"enabled": true,
"rules": {
"recommended": true
"recommended": true,
"a11y": {
"noSvgWithoutTitle": "off"
}
Comment on lines +18 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find and examine biome.json
find . -name "biome.json" -type f | head -5

Repository: Team-NumberOne/daepyro-design-system

Length of output: 92


🏁 Script executed:

# List files to understand repo structure
git ls-files | head -30

Repository: Team-NumberOne/daepyro-design-system

Length of output: 827


🏁 Script executed:

# Search for SVG components mentioned (PlusIcon, Button, Switch, FloatingButton)
fd -e tsx -e ts -e jsx -e js | grep -i "plus\|button\|switch" | head -20

Repository: Team-NumberOne/daepyro-design-system

Length of output: 607


🏁 Script executed:

# Examine biome.json around lines 18-21
cat -n ./biome.json

Repository: Team-NumberOne/daepyro-design-system

Length of output: 949


🏁 Script executed:

# Check PlusIcon.tsx for SVG implementation and a11y attributes
cat -n ./src/icons/PlusIcon.tsx

Repository: Team-NumberOne/daepyro-design-system

Length of output: 1296


🏁 Script executed:

# Check if other components contain SVG elements
rg -A 10 "<svg" src/components/ src/icons/

Repository: Team-NumberOne/daepyro-design-system

Length of output: 529


🏁 Script executed:

# Check accessibility guidelines in the repo
cat -n ./.github/ACCESSIBILITY.md

Repository: Team-NumberOne/daepyro-design-system

Length of output: 7940


🏁 Script executed:

# Check Button.tsx for SVG usage
cat -n ./src/components/Button/Button.tsx

Repository: Team-NumberOne/daepyro-design-system

Length of output: 2405


🏁 Script executed:

# Check FloatingButton.tsx for SVG usage
cat -n ./src/components/FloatingButton/FloatingButton.tsx

Repository: Team-NumberOne/daepyro-design-system

Length of output: 2102


🏁 Script executed:

# Check Switch.tsx for SVG usage
cat -n ./src/components/Switch/Switch.tsx

Repository: Team-NumberOne/daepyro-design-system

Length of output: 1510


🏁 Script executed:

# Search for any accessibility checklist or PR comments that might mention accessibility verification
find . -name "*.md" -type f | xargs grep -l "accessibility\|a11y\|checklist" 2>/dev/null | head -10

Repository: Team-NumberOne/daepyro-design-system

Length of output: 208


🏁 Script executed:

# Check CHECKLIST.md
cat -n ./.github/CHECKLIST.md

Repository: Team-NumberOne/daepyro-design-system

Length of output: 4777


🏁 Script executed:

# Check pull_request_template.md for accessibility mentions
cat -n ./.github/pull_request_template.md

Repository: Team-NumberOne/daepyro-design-system

Length of output: 1994


🏁 Script executed:

# Verify: search for <title> elements in SVG files in the repo
rg "<title" src/icons/ src/components/

Repository: Team-NumberOne/daepyro-design-system

Length of output: 62


Reconsider disabling noSvgWithoutTitle globally; use local fixes instead.

The PlusIcon and other SVG components in this PR actually implement proper accessibility through aria-label and aria-hidden attributes (see PlusIcon.tsx lines 28-29, and FloatingButton.tsx line 63). The components are accessible.

However, disabling this linter rule globally contradicts both the PR's accessibility checklist requirements (pull_request_template.md lines 52-56) and the accessibility guidelines in ACCESSIBILITY.md. The issue is that noSvgWithoutTitle specifically checks for <title> child elements in SVGs, not aria-label attributes, even though aria-label is a valid accessibility approach.

Better approaches:

  • Add <title> elements inside SVG elements (e.g., <title>Icon label</title>)
  • Or add inline /* biome-ignore lint/a11y/noSvgWithoutTitle: aria-label provides accessibility */ comments at specific SVG usages
  • Document the rationale if using aria-label exclusively

Global rule disabling weakens linter enforcement across the entire codebase.

🤖 Prompt for AI Agents
In biome.json around lines 18 to 21 the a11y rule "noSvgWithoutTitle" was turned
off globally; instead revert this global disable and restore the rule, then fix
the specific SVGs in the PR by either adding <title> elements inside the SVGs
with appropriate labels or adding inline biome-ignore comments at the exact SVG
usage (/* biome-ignore lint/a11y/noSvgWithoutTitle: aria-label provides
accessibility */) where aria-label is intentionally relied upon, and update
ACCESSIBILITY.md or the PR notes to document the chosen approach so the global
lint enforcement remains intact.

}
},
"javascript": {
Expand Down
1 change: 1 addition & 0 deletions docs/STYLING.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,4 @@ export const buttonLoading = style({




1 change: 1 addition & 0 deletions docs/THEMING.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,4 @@ export const button = style({




1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
"typescript": "^5.9.3",
"vite": "^7.2.4",
"vite-plugin-dts": "^4.5.4",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^4.0.13"
},
"dependencies": {
Expand Down
41 changes: 41 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

101 changes: 96 additions & 5 deletions src/components/Button/Button.css.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,100 @@
import { style } from "@vanilla-extract/css";
import { style, styleVariants } from "@vanilla-extract/css";
import { colors, radius, shadows, textStyles } from "../../tokens";

export const button = style({
padding: "8px 12px",
borderRadius: "9999px",
const baseButton = style({
...textStyles.heading4,
padding: "0px 16px",
borderRadius: radius.base,
border: "none",
cursor: "pointer",
fontWeight: 500,

display: "grid",
gridTemplateColumns: "24px 1fr 24px",
alignItems: "center",
transition: "background-color 0.2s, box-shadow 0.2s",
width: "100%",
height: "48px",
});

// Variant styles
export const buttonVariants = styleVariants({
default: [
baseButton,
{
backgroundColor: colors.grey[50],
color: colors.grey[500],
":hover": {
backgroundColor: colors.grey[100],
},
selectors: {
'&[data-pressed="true"]': {
backgroundColor: colors.grey[100],
boxShadow: shadows["btn-pressed"],
},
"&:disabled": {
backgroundColor: colors.grey[50],
color: colors.grey[300],
cursor: "not-allowed",
},
},
},
],
gray: [
baseButton,
{
backgroundColor: colors.grey[800],
color: colors.base.white,
":hover": {
backgroundColor: colors.grey[700],
},
selectors: {
'&[data-pressed="true"]': {
backgroundColor: colors.grey[900],
boxShadow: shadows["btn-pressed"],
},
"&:disabled": {
backgroundColor: colors.grey[300],
color: colors.grey[100],
cursor: "not-allowed",
},
},
},
],
primary: [
baseButton,
{
backgroundColor: colors.orange[500],
color: colors.base.white,
":hover": {
backgroundColor: colors.orange[600],
},
selectors: {
'&[data-pressed="true"]': {
backgroundColor: colors.orange[600],
boxShadow: shadows["btn-pressed"],
},
"&:disabled": {
backgroundColor: colors.orange[100],
cursor: "not-allowed",
},
},
},
],
Comment on lines +63 to +82

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

primary variant의 disabled 상태에 텍스트 색상이 누락되었습니다.

default와 gray variant는 disabled 상태에서 color 속성을 명시적으로 지정하지만, primary variant는 지정하지 않습니다. 이로 인해 orange[100] 배경에 흰색 텍스트가 표시되어 접근성 대비 기준을 충족하지 못할 수 있습니다.

 "&:disabled": {
   backgroundColor: colors.orange[100],
+  color: colors.orange[300],
   cursor: "not-allowed",
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
primary: [
baseButton,
{
backgroundColor: colors.orange[500],
color: colors.base.white,
":hover": {
backgroundColor: colors.orange[600],
},
selectors: {
'&[data-pressed="true"]': {
backgroundColor: colors.orange[600],
boxShadow: shadows["btn-pressed"],
},
"&:disabled": {
backgroundColor: colors.orange[100],
cursor: "not-allowed",
},
},
},
],
primary: [
baseButton,
{
backgroundColor: colors.orange[500],
color: colors.base.white,
":hover": {
backgroundColor: colors.orange[600],
},
selectors: {
'&[data-pressed="true"]': {
backgroundColor: colors.orange[600],
boxShadow: shadows["btn-pressed"],
},
"&:disabled": {
backgroundColor: colors.orange[100],
color: colors.orange[300],
cursor: "not-allowed",
},
},
},
],
🤖 Prompt for AI Agents
In src/components/Button/Button.css.ts around lines 63-82, the primary variant's
disabled selector omits a text color which can produce poor contrast on the
orange[100] background; update the '&:disabled' (or '&[data-pressed="true"]'
disabled case if present) selector to explicitly set a readable color (use the
same color token used by default/gray variants, e.g. colors.base.gray[400] or
the existing disabled text token) so the button meets contrast/accessibility
requirements.

});

// Default export for backward compatibility
export const button = buttonVariants.default;

// Icon wrapper
export const iconWrapper = style({
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "24px",
});

export const contentWrapper = style({
display: "flex",
alignItems: "center",
justifyContent: "center",
});
Loading