Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
Loading