Skip to content

refactor(button,card): apply new semantic design tokens#288

Open
osohyun0224 wants to merge 6 commits into
devfrom
feature/token-apply-osohyun0224
Open

refactor(button,card): apply new semantic design tokens#288
osohyun0224 wants to merge 6 commits into
devfrom
feature/token-apply-osohyun0224

Conversation

@osohyun0224

@osohyun0224 osohyun0224 commented Jul 14, 2026

Copy link
Copy Markdown
Member

Changes

Sprint 1 새 semantic 토큰(vars.color.*, vars.spacing.component.*, vars.radius.component.*)을 담당 컴포넌트 Button, Card에 적용했습니다.

Button (packages/button/src/Button.css.ts)

  • 하드코딩 컬러(#FF7C27 / gradient / #FE4E07 / #000) → vars.color.accent.{default,hover,subtle} + foreground.onAccent
  • 기존 color.gray500/600 disabled → vars.color.background.muted + vars.color.foreground.subtle (foreground.muted는 "읽기용 텍스트 금지" 명시로 배제)
  • focus-visible outline → vars.color.border.focus
  • 사이즈별 padding/borderRadius를 vars.spacing.component.* / vars.radius.component.md|lg로 통일
  • fill :activeopacity: 0.9 추가하여 hover와 시각적 계층 구분

Card (packages/card/src/Card.css.ts)

  • 기존 color.gray50/100/200, color.cyan300vars.color.background.{base,subtle}, vars.color.border.{default,strong}
  • outline border는 accent.default 대신 border.strong 사용 (인터랙티브 컬러와 구분선 의미 분리)
  • 하드코딩 borderRadius: 12pxvars.radius.component.lg (8px), padding: 20pxvars.spacing.component.lg (16px) — 토큰 스펙(카드 모서리/카드 내부 패딩) 준수

공통

  • border shorthand는 CSS custom property가 포함될 때 vanilla-extract가 잘못 파싱하는 이슈가 있어 borderWidth/Style/Color로 분해

테스트 (packages/card/src/Card.test.tsx)

  • 기존은 하드코딩된 hex 값을 toHaveStyle로 검증 — semantic 토큰 도입 후 stylesheet에 var(--side-...) 참조만 남아 happy-dom의 getComputedStyle이 값을 반환하지 않음
  • document.styleSheets를 순회해 매칭되는 rule의 cssText에서 var(--side-...) 참조를 확인하는 헬퍼(ruleForClass / rulesForElement)로 재작성
  • selector 매칭은 comma-split some 방식으로 compound selector 대비

Checklist

  • pnpm --filter @sipe-team/button test — 18 tests passed
  • pnpm --filter @sipe-team/card test — 16 tests passed
  • pnpm --filter @sipe-team/button typecheck / pnpm --filter @sipe-team/card typecheck 통과
  • pnpm --filter @sipe-team/button build / pnpm --filter @sipe-team/card build — ESM/CJS/DTS 성공
  • pnpm --filter @sipe-team/button lint / pnpm --filter @sipe-team/card lint — no issues
  • 아키텍트 재검증 (STANDARD tier) APPROVED

Additional Discussion Points

  • accent.* 시리즈에 active/pressed 단계 토큰이 없어 fill :active:hover와 동일 토큰을 재사용합니다. 임시로 opacity로 press 피드백을 추가했지만, 장기적으로 토큰 시스템에 accent.pressed 추가를 제안드립니다.
  • vanilla-extract의 border shorthand + CSS custom property 조합 파싱 이슈는 다른 컴포넌트에도 영향이 있을 수 있으니 마이그레이션 시 참고 부탁드립니다.

Summary by CodeRabbit

  • New Features

    • Accordion now supports single-item mode, controlled state, and value change callbacks.
    • Buttons support fill variants, four sizes, and left/right icons with improved focus and disabled states.
    • Tooltips now support controlled visibility, listener controls, improved positioning, keyboard behavior, and accessibility links.
    • Theme handling now supports light and dark modes.
    • Added generated design-token outputs and token-name exports.
  • Bug Fixes

    • CSS imports are now preserved during bundling across component packages.
  • Documentation

    • Added guidance for semantic dark-mode tokens and updated contribution instructions.

osohyun0224 and others added 2 commits July 14, 2026 18:16
- Replace hardcoded colors (#FF7C27, gradient, #FE4E07, #000) with
  vars.color.accent.{default,hover,subtle} and foreground.onAccent
- Replace primitive color.gray500/600 for disabled with
  vars.color.background.muted / vars.color.foreground.subtle
  (foreground.muted is reserved for non-readable text per token spec)
- Use vars.color.border.focus for focus-visible outline
- Standardize padding/borderRadius to vars.spacing.component.* and
  vars.radius.component.md/lg across all sizes
- Split border shorthand into borderWidth/Style/Color to work around
  vanilla-extract shorthand parsing bug with CSS custom properties
- Add opacity 0.9 to fill :active for hover/press visual hierarchy

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Replace primitive color.gray50/100/200 and color.cyan300 with
  vars.color.background.{base,subtle}, vars.color.border.{default,strong}
- outline variant border uses border.strong instead of accent.default
  to avoid conflating interactive color with divider semantics
- Replace hardcoded borderRadius 12px with vars.radius.component.lg (8px)
  and padding 20px with vars.spacing.component.lg (16px) per token spec
  ("카드 모서리" / "카드 내부 패딩")
- Split border shorthand into individual props to work around
  vanilla-extract CSS custom property shorthand parsing bug
- Rewrite tests to inspect stylesheet cssText for var(--side-...) refs
  since happy-dom does not resolve CSS variables via getComputedStyle;
  helper matches selectorText via comma-split for compound selectors

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5a22c16

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@sipe-team/button Patch
@sipe-team/card Patch
@sipe-team/side Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@osohyun0224
osohyun0224 changed the base branch from main to dev July 14, 2026 09:46
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • main
  • release/v1

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 03ab7f27-e683-4c45-b885-597b284f6d1e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

The PR modernizes repository tooling and package publishing, adds package-policy and token-generation automation, updates theme contracts, and expands Accordion, Button, Card, Flex, and Tooltip behavior with corresponding stories and tests.

Changes

Repository governance and automation

Layer / File(s) Summary
Developer tooling and contribution rules
.claude/*, .github/*, .husky/*, AGENTS.md, CLAUDE.md, package.json
Branch conventions, commit rules, CI workflows, Storybook setup, lint scripts, and agent guidance are updated.
Package consistency validation
package-policy*, scripts/checkPackageConsistency.ts, packages/*/package.json
Package schemas, allowlists, scripts, publishing metadata, CSS side-effect declarations, and consistency reporting are added or revised.
Token generation pipeline
tokens/tokens.json, packages/tokens/config.js, .github/workflows/tokens.yaml
Primitive and semantic tokens are defined, generated into CSS and TypeScript artifacts, compared in pull requests, and synchronized on token pushes.
Component generator updates
scripts/createComponent.ts
Template renaming now applies ordered camelCase, PascalCase, kebab-case, and template substitutions to paths and file contents.

Component and theme behavior

Layer / File(s) Summary
Accordion, Button, Card, and Flex updates
packages/accordion/src/*, packages/button/src/*, packages/card/src/*, packages/flex/src/*
Accordion selection modes, Button icons and sizes, Card ghost styling, and Flex shared-option testing are implemented with updated stories and tests.
Tooltip interaction API
packages/tooltip/src/*
Tooltip controlled state, listener controls, positioning, accessibility linkage, animation, and interaction coverage are expanded.
Theme contract and provider
packages/theme/src/*, packages/tokens/src/theme/*
Theme handling changes to light/dark modes, data attributes, nested semantic contracts, CSS-variable mapping, and deprecated legacy token exports are added.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: g-hoon, kybee, 3o14, minji0214, noahluftyang

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: Button and Card were refactored to use new semantic design tokens.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/token-apply-osohyun0224

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 24

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yaml:
- Around line 18-25: Update the Lint, Test, Type check, and Build steps to pass
github.base_ref through each step’s environment block, then reference that
environment variable in the pnpm --filter expressions instead of directly
interpolating the GitHub context in run commands. Preserve the existing filters
and --if-present behavior.

In @.github/workflows/consistency.yaml:
- Around line 1-6: Define explicit read-only workflow permissions in the
top-level configuration of the “Package consistency” workflow, granting only the
repository contents access required by its jobs. Ensure no broader default token
permissions remain enabled.

In @.github/workflows/tokens.yaml:
- Line 96: Update the stefanzweifel/git-auto-commit-action step to reference a
specific verified commit SHA instead of the mutable `@v5` tag, preserving the
existing action configuration and workflow behavior.
- Line 43: Update the checkout commands in the workflow to pass github.base_ref
and github.event.pull_request.head.sha through step-level env variables, then
reference those variables in the shell commands. Apply this to both checkout
invocations while preserving the existing origin prefix and tokens path.
- Around line 18-25: Add a restrictive top-level permissions block in the
workflow setting the default to none, while preserving each job’s existing
explicit permissions. Add concise rationale comments beside validate’s contents:
write and the corresponding pull-request job’s pull-requests: write entries
describing why those write permissions are required.
- Around line 27-30: Update both actions/checkout@v4 steps in the tokens
workflow, including the checkout in the sync job, to set persist-credentials to
false alongside the existing checkout options. Preserve the current fetch-depth
and ref behavior.
- Line 27: Pin every referenced third-party action in the workflow—both
actions/checkout entries and jdx/mise-action—to immutable commit SHAs instead of
mutable version tags, preserving their current action versions and
configuration.

In @.husky/pre-push:
- Around line 4-6: Update the SPECIAL_REGEX definition to match exactly main and
any branch beginning with dev/, while no longer treating only the exact dev
branch as special. Preserve the existing main-branch exemption and anchor the
pattern so unrelated branch names remain subject to naming rules.

In `@AGENTS.md`:
- Around line 47-48: Insert a blank line between the “Variant enum 패턴 (값 객체 + 타입
유니온):” text and the following TypeScript fenced code block in AGENTS.md,
preserving the block contents.

In `@package.json`:
- Line 15: Update the serve:storybook script to invoke the project-installed
http-server, and add http-server at a pinned version under devDependencies so
local and CI runs use a reproducible dependency instead of unpinned npx
resolution.

In `@packages/accordion/src/Accordion.tsx`:
- Around line 58-68: Memoize the AccordionRoot context value used by
AccordionRootContext.Provider. Wrap onItemToggle in useCallback with its
relevant dependencies, then create the provider value with useMemo so consumers
receive a stable object when type and activeValue are unchanged.

In `@packages/accordion/src/context/AccordionRootContext.tsx`:
- Around line 11-13: Update useAccordionRootContext to check the
useContext(AccordionRootContext) result and throw a clear error when no provider
value is present; otherwise return the context unchanged.

In `@packages/button/src/Button.stories.tsx`:
- Around line 193-210: Add a theme showcase story alongside AllVariantsAndSizes
that wraps Button examples with ThemeProvider and renders the available theme
variants (1st through 4th), while preserving the existing variant and size
coverage.

In `@packages/button/src/Button.test.tsx`:
- Around line 86-112: Remove the duplicated test cases for the Button default
and submit types, retaining one test for each behavior in the Button test suite.
Keep the existing assertions that verify type="button" when omitted and
type="submit" when explicitly provided.

In `@packages/button/src/Button.tsx`:
- Around line 53-55: Mark the decorative icon wrapper spans in the Button render
output as aria-hidden="true" for both leftIcon and rightIcon, while leaving the
Slottable children and existing styling unchanged.

In `@packages/card/src/Card.test.tsx`:
- Line 29: Translate all Korean test descriptions in Card.test.tsx to the
specified English descriptions, including the cases covering children rendering,
default centering, variant defaults, variant-specific tokens, padding, and
radius. Change only the test names and preserve their assertions and test
behavior.

In `@packages/theme/package.json`:
- Around line 26-34: Update the package manifest’s dependency declarations by
adding react and react-dom to a peerDependencies block, while retaining their
devDependencies entries for local development and testing. Use the existing
catalog:react version references and keep the dependency names aligned across
both sections.

In `@packages/tokens/config.js`:
- Around line 113-136: Extract the duplicated grouping and TypeScript
union-emission logic from the primitive and semantic formatters into a shared
emitTokenNamesDts helper. Have the typescript/token-names-dts hook and
typescript/semantic-token-names-dts hook call it with their respective type-name
prefix, union type name, and semantic path filter, preserving the existing
generated output and filtering behavior.
- Around line 268-298: The fallback branch that writes semantic-dark.css must
also generate semantic.d.ts and semantic.d.cts stubs before the token-names
barrel is written. Update the else branch alongside its CSS fallback so the
existing index.d.ts re-exports of ./semantic and SemanticToken references
resolve successfully, using declarations consistent with the generated semantic
type module.

In `@packages/tokens/src/colors/colors.ts`:
- Around line 180-186: Update the text color in the theme5th definition so it
contrasts with the black background; keep background as `#000000` and use the
established light text color convention, such as white, for theme5th.text.

In `@packages/tooltip/src/Tooltip.css.ts`:
- Around line 39-125: Refactor the placement variants in the recipe to use the
relevant placement enum values instead of plain string keys, and introduce a
small helper or generator for the shared selectors['&::after'] structure. Pass
each placement’s unique offset, transform, border width, and border color values
through that helper, reusing the shared tooltip background-color variable rather
than duplicating it across all eight variants.
- Around line 9-38: Update the tooltip recipe’s base styles to import and use
the semantic `@sipe-team/tokens` vars for backgroundColor, color, padding,
borderRadius, fontSize, boxShadow, and zIndex instead of raw literals or
fallback custom properties. Match the token usage established by the migrated
Button and Card styles so Tooltip responds to ThemeProvider theme variants.

In `@packages/tooltip/src/Tooltip.tsx`:
- Around line 105-114: Update the trigger element props near wrapperRef so
aria-describedby preserves and merges any consumer-provided value from rest with
tooltipId when isVisible, rather than overriding it. Keep aria-describedby
undefined only when neither the user value nor the visible tooltip provides an
identifier.

In `@scripts/checkPackageConsistency.ts`:
- Line 12: Align the catalog dependency rule name across the violation flow:
update the reported violation near the isAllowlisted call in the
direct-dependency check to use the canonical name already used by
rulesRequiringFieldPath and allowlistEntrySchema, so isAllowlisted matches
allowlist entries consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: eae8caf8-d53f-4435-8de1-ae8fbec9e698

📥 Commits

Reviewing files that changed from the base of the PR and between 0089e03 and 30bf405.

⛔ Files ignored due to path filters (24)
  • .changeset/accordion-single-mode.md is excluded by !.changeset/**
  • .changeset/add-card-ghost-variant.md is excluded by !.changeset/**
  • .changeset/beige-cows-know.md is excluded by !.changeset/**
  • .changeset/button-v2-redesign.md is excluded by !.changeset/**
  • .changeset/chip-normalize-package-json.md is excluded by !.changeset/**
  • .changeset/package-json-divergence-cleanup.md is excluded by !.changeset/**
  • .changeset/provider-wonju.md is excluded by !.changeset/**
  • .changeset/semantic-tokens-foundation.md is excluded by !.changeset/**
  • .changeset/spicy-wasps-clap.md is excluded by !.changeset/**
  • .changeset/wet-pants-train.md is excluded by !.changeset/**
  • .templates/component/.storybook/main.ts is excluded by !.templates/**
  • .templates/component/.storybook/preview.ts is excluded by !.templates/**
  • .templates/component/global.d.ts is excluded by !.templates/**, !**/*.d.ts
  • .templates/component/package.json is excluded by !.templates/**
  • .templates/component/src/Component.stories.tsx is excluded by !.templates/**
  • .templates/component/src/Component.tsx is excluded by !.templates/**
  • .templates/component/src/Template.css.ts is excluded by !.templates/**
  • .templates/component/src/Template.stories.tsx is excluded by !.templates/**
  • .templates/component/src/Template.test.tsx is excluded by !.templates/**
  • .templates/component/src/Template.tsx is excluded by !.templates/**
  • .templates/component/src/index.ts is excluded by !.templates/**
  • .templates/component/tsup.config.ts is excluded by !.templates/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !**/pnpm-lock.yaml
  • types/index.d.ts is excluded by !**/*.d.ts
📒 Files selected for processing (147)
  • .claude/settings.json
  • .github/CONTRIBUTING.md
  • .github/workflows/ci.yaml
  • .github/workflows/consistency.yaml
  • .github/workflows/release.yaml
  • .github/workflows/tokens.yaml
  • .gitignore
  • .husky/pre-push
  • .mise.toml
  • .storybook/main.ts
  • .storybook/manager.ts
  • .vscode/extensions.json
  • AGENTS.md
  • CLAUDE.md
  • LICENSE
  • README.md
  • commitlint.config.ts
  • docs/introduction.mdx
  • docs/tokens.mdx
  • docs/tokens/semantic-proposal.md
  • package-policy.json
  • package-policy.schema.json
  • package.json
  • packages/accordion/.storybook/main.ts
  • packages/accordion/.storybook/preview.ts
  • packages/accordion/CHANGELOG.md
  • packages/accordion/package.json
  • packages/accordion/src/Accordion.stories.tsx
  • packages/accordion/src/Accordion.test.tsx
  • packages/accordion/src/Accordion.tsx
  • packages/accordion/src/context/AccordionItemContext.tsx
  • packages/accordion/src/context/AccordionRootContext.tsx
  • packages/avatar/.storybook/main.ts
  • packages/avatar/.storybook/preview.ts
  • packages/avatar/CHANGELOG.md
  • packages/avatar/package.json
  • packages/avatar/vite.config.ts
  • packages/badge/.storybook/main.ts
  • packages/badge/.storybook/preview.ts
  • packages/badge/CHANGELOG.md
  • packages/badge/package.json
  • packages/button/.storybook/main.ts
  • packages/button/.storybook/preview.ts
  • packages/button/CHANGELOG.md
  • packages/button/package.json
  • packages/button/src/Button.css.ts
  • packages/button/src/Button.stories.tsx
  • packages/button/src/Button.test.tsx
  • packages/button/src/Button.tsx
  • packages/card/.storybook/main.ts
  • packages/card/.storybook/preview.ts
  • packages/card/CHANGELOG.md
  • packages/card/package.json
  • packages/card/src/Card.css.ts
  • packages/card/src/Card.stories.tsx
  • packages/card/src/Card.test.tsx
  • packages/checkbox/.storybook/main.ts
  • packages/checkbox/.storybook/preview.ts
  • packages/checkbox/CHANGELOG.md
  • packages/checkbox/package.json
  • packages/chip/package.json
  • packages/divider/.storybook/main.ts
  • packages/divider/.storybook/preview.ts
  • packages/divider/CHANGELOG.md
  • packages/divider/package.json
  • packages/flex/.storybook/main.ts
  • packages/flex/.storybook/preview.ts
  • packages/flex/CHANGELOG.md
  • packages/flex/package.json
  • packages/flex/src/Flex.stories.tsx
  • packages/flex/src/Flex.test.tsx
  • packages/grid/.storybook/main.ts
  • packages/grid/.storybook/preview.ts
  • packages/grid/CHANGELOG.md
  • packages/grid/package.json
  • packages/icon/.storybook/main.ts
  • packages/icon/.storybook/preview.ts
  • packages/icon/package.json
  • packages/input/.storybook/main.ts
  • packages/input/.storybook/preview.ts
  • packages/input/CHANGELOG.md
  • packages/input/package.json
  • packages/plugin-figma-codegen/manifest.json
  • packages/plugin-figma-codegen/package.json
  • packages/plugin-figma-codegen/src/index.ts
  • packages/plugin-figma-codegen/tsconfig.json
  • packages/plugin-figma-codegen/tsup.config.ts
  • packages/radio/.storybook/main.ts
  • packages/radio/.storybook/preview.ts
  • packages/radio/CHANGELOG.md
  • packages/radio/package.json
  • packages/reset/CHANGELOG.md
  • packages/reset/package.json
  • packages/side/CHANGELOG.md
  • packages/side/package.json
  • packages/skeleton/.storybook/main.ts
  • packages/skeleton/.storybook/preview.ts
  • packages/skeleton/CHANGELOG.md
  • packages/skeleton/package.json
  • packages/switch/.storybook/main.ts
  • packages/switch/.storybook/preview.ts
  • packages/switch/CHANGELOG.md
  • packages/switch/package.json
  • packages/theme/.storybook/main.ts
  • packages/theme/.storybook/preview.ts
  • packages/theme/package.json
  • packages/theme/src/ThemeProvider.stories.tsx
  • packages/theme/src/ThemeProvider.test.tsx
  • packages/theme/src/ThemeProvider.tsx
  • packages/tokens/.storybook/main.ts
  • packages/tokens/.storybook/preview.ts
  • packages/tokens/config.js
  • packages/tokens/package.json
  • packages/tokens/src/colors/colors.ts
  • packages/tokens/src/colors/index.ts
  • packages/tokens/src/effects/border.ts
  • packages/tokens/src/effects/opacity.ts
  • packages/tokens/src/effects/shadows.ts
  • packages/tokens/src/effects/zIndex.ts
  • packages/tokens/src/layout/breakpoints.ts
  • packages/tokens/src/layout/grid.ts
  • packages/tokens/src/layout/responsiveStyle.ts
  • packages/tokens/src/layout/spacing.ts
  • packages/tokens/src/theme/contract.css.ts
  • packages/tokens/src/theme/themes.css.ts
  • packages/tokens/src/theme/utils.test.ts
  • packages/tokens/src/theme/utils.ts
  • packages/tokens/tsconfig.json
  • packages/tooltip/.storybook/main.ts
  • packages/tooltip/.storybook/preview.ts
  • packages/tooltip/CHANGELOG.md
  • packages/tooltip/package.json
  • packages/tooltip/src/Tooltip.css.ts
  • packages/tooltip/src/Tooltip.stories.tsx
  • packages/tooltip/src/Tooltip.test.tsx
  • packages/tooltip/src/Tooltip.tsx
  • packages/tooltip/src/hooks/useTooltip/useTooltip.ts
  • packages/tooltip/src/hooks/useTooltip/useTooltip.tsx
  • packages/typography/.storybook/main.ts
  • packages/typography/.storybook/preview.ts
  • packages/typography/CHANGELOG.md
  • packages/typography/package.json
  • pnpm-workspace.yaml
  • scripts/checkPackageConsistency.ts
  • scripts/createComponent.ts
  • tokens/tokens.json
  • tsconfig.json
💤 Files with no reviewable changes (44)
  • packages/checkbox/.storybook/preview.ts
  • packages/checkbox/.storybook/main.ts
  • packages/card/.storybook/preview.ts
  • packages/divider/.storybook/main.ts
  • packages/divider/.storybook/preview.ts
  • packages/flex/.storybook/preview.ts
  • packages/tokens/.storybook/preview.ts
  • packages/tokens/.storybook/main.ts
  • packages/avatar/vite.config.ts
  • packages/skeleton/.storybook/main.ts
  • packages/tooltip/.storybook/preview.ts
  • packages/plugin-figma-codegen/tsup.config.ts
  • packages/input/.storybook/main.ts
  • packages/icon/.storybook/main.ts
  • packages/plugin-figma-codegen/manifest.json
  • packages/theme/.storybook/main.ts
  • packages/button/.storybook/main.ts
  • packages/typography/.storybook/preview.ts
  • packages/theme/.storybook/preview.ts
  • packages/radio/.storybook/preview.ts
  • packages/icon/.storybook/preview.ts
  • packages/skeleton/.storybook/preview.ts
  • packages/plugin-figma-codegen/tsconfig.json
  • packages/avatar/.storybook/main.ts
  • packages/plugin-figma-codegen/src/index.ts
  • packages/switch/.storybook/preview.ts
  • packages/badge/.storybook/preview.ts
  • packages/card/.storybook/main.ts
  • packages/accordion/.storybook/preview.ts
  • packages/input/.storybook/preview.ts
  • packages/grid/.storybook/preview.ts
  • packages/accordion/.storybook/main.ts
  • packages/tooltip/.storybook/main.ts
  • packages/tooltip/src/hooks/useTooltip/useTooltip.tsx
  • packages/flex/.storybook/main.ts
  • packages/plugin-figma-codegen/package.json
  • packages/button/.storybook/preview.ts
  • packages/switch/.storybook/main.ts
  • packages/badge/.storybook/main.ts
  • packages/avatar/.storybook/preview.ts
  • packages/radio/.storybook/main.ts
  • packages/theme/src/ThemeProvider.stories.tsx
  • packages/typography/.storybook/main.ts
  • packages/grid/.storybook/main.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

🛑 Comments failed to post (24)
.github/workflows/ci.yaml (1)

18-25: 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Mitigate command injection vulnerability in CI scripts.

Directly interpolating ${{ github.base_ref }} into the inline run scripts exposes the workflow to command injection if a maliciously crafted branch name containing shell metacharacters is used. To mitigate this risk, pass the GitHub context variable securely via an environment variable block.

🔒️ Proposed fix to securely pass the variable
     - name: Lint
-      run: pnpm --filter="...[origin/${{ github.base_ref }}]" --if-present lint
+      env:
+        BASE_REF: ${{ github.base_ref }}
+      run: pnpm --filter="...[origin/$BASE_REF]" --if-present lint
     - name: Test
-      run: pnpm --filter="...[origin/${{ github.base_ref }}]" --if-present test
+      env:
+        BASE_REF: ${{ github.base_ref }}
+      run: pnpm --filter="...[origin/$BASE_REF]" --if-present test
     - name: Type check
-      run: pnpm --filter="...[origin/${{ github.base_ref }}]" --if-present typecheck
+      env:
+        BASE_REF: ${{ github.base_ref }}
+      run: pnpm --filter="...[origin/$BASE_REF]" --if-present typecheck
     - name: Build
-      run: pnpm --filter="...[origin/${{ github.base_ref }}]" --if-present build
+      env:
+        BASE_REF: ${{ github.base_ref }}
+      run: pnpm --filter="...[origin/$BASE_REF]" --if-present build
📝 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.

    - name: Lint
      env:
        BASE_REF: ${{ github.base_ref }}
      run: pnpm --filter="...[origin/$BASE_REF]" --if-present lint
    - name: Test
      env:
        BASE_REF: ${{ github.base_ref }}
      run: pnpm --filter="...[origin/$BASE_REF]" --if-present test
    - name: Type check
      env:
        BASE_REF: ${{ github.base_ref }}
      run: pnpm --filter="...[origin/$BASE_REF]" --if-present typecheck
    - name: Build
      env:
        BASE_REF: ${{ github.base_ref }}
      run: pnpm --filter="...[origin/$BASE_REF]" --if-present build
🧰 Tools
🪛 zizmor (1.26.1)

[error] 19-19: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[error] 21-21: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[error] 23-23: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[error] 25-25: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yaml around lines 18 - 25, Update the Lint, Test, Type
check, and Build steps to pass github.base_ref through each step’s environment
block, then reference that environment variable in the pnpm --filter expressions
instead of directly interpolating the GitHub context in run commands. Preserve
the existing filters and --if-present behavior.

Source: Linters/SAST tools

.github/workflows/consistency.yaml (1)

1-6: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Define explicit workflow permissions.

The workflow currently relies on default token permissions, which can be overly broad. It is a security best practice to explicitly define the minimum required permissions (e.g., read-only access to repository contents) to reduce the attack surface.

🔒️ Proposed fix to restrict permissions
 name: Package consistency
 
 on:
   pull_request:
 
+permissions:
+  contents: read
+
 jobs:
   consistency:
📝 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.

name: Package consistency

on:
  pull_request:

permissions:
  contents: read

jobs:
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 1-28: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block

(excessive-permissions)


[warning] 3-4: insufficient job-level concurrency limits (concurrency-limits): workflow is missing concurrency setting

(concurrency-limits)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/consistency.yaml around lines 1 - 6, Define explicit
read-only workflow permissions in the top-level configuration of the “Package
consistency” workflow, granting only the repository contents access required by
its jobs. Ensure no broader default token permissions remain enabled.

Source: Linters/SAST tools

.github/workflows/tokens.yaml (5)

18-25: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Consider a restrictive top-level permissions: block plus rationale comments.

Both jobs already scope permissions narrowly, but there's no top-level permissions: {} default, so any future job added without an explicit block would inherit the (potentially broader) repository default. Static analysis also suggests a short comment on contents: write / pull-requests: write explaining why each is needed.

🔐 Proposed fix
+permissions: {}
+
 jobs:
   validate:
     name: Build & validate
     runs-on: ubuntu-latest
     permissions:
+      contents: write # required to commit transformed tokens back via git-auto-commit-action

Also applies to: 102-109

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 24-24: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment

(undocumented-permissions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/tokens.yaml around lines 18 - 25, Add a restrictive
top-level permissions block in the workflow setting the default to none, while
preserving each job’s existing explicit permissions. Add concise rationale
comments beside validate’s contents: write and the corresponding pull-request
job’s pull-requests: write entries describing why those write permissions are
required.

Source: Linters/SAST tools


27-27: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Pin third-party/marketplace actions to a commit SHA.

actions/checkout@v4, jdx/mise-action@v2, and the second actions/checkout@v4 are referenced by mutable tag, not a pinned commit hash. Static analysis flags this as a blanket-policy violation (supply-chain hardening: tags can be moved to point at malicious code).

🔒 Proposed fix: pin actions to a commit SHA
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@v4 # v4.x.x
+        # pin to a specific commit SHA, e.g.:
+        # uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683

Apply the same pinning to jdx/mise-action@v2 (line 32) and the second actions/checkout@v4 (line 112).

Also applies to: 32-32, 59-59, 112-112

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 27-30: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 27-27: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/tokens.yaml at line 27, Pin every referenced third-party
action in the workflow—both actions/checkout entries and jdx/mise-action—to
immutable commit SHAs instead of mutable version tags, preserving their current
action versions and configuration.

Source: Linters/SAST tools


27-30: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Add persist-credentials: false to checkout steps.

Both actions/checkout@v4 steps persist the GITHUB_TOKEN in the local git config by default, making it available to any subsequent step (including third-party actions run later in the job). Since neither checkout step needs to push using the persisted credential (the sync job authenticates separately via GH_TOKEN, and git-auto-commit-action supplies its own token handling), disabling persistence limits the credential's exposure window.

🔒 Proposed fix
       - uses: actions/checkout@v4
         with:
           fetch-depth: 0
           ref: ${{ github.event.pull_request.head.sha || github.sha }}
+          persist-credentials: false

Apply the same to the sync job's checkout (lines 112-115).

Also applies to: 112-115

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 27-30: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 27-27: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/tokens.yaml around lines 27 - 30, Update both
actions/checkout@v4 steps in the tokens workflow, including the checkout in the
sync job, to set persist-credentials to false alongside the existing checkout
options. Preserve the current fetch-depth and ref behavior.

Source: Linters/SAST tools


43-43: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Avoid interpolating github.* context values directly into run: shell scripts.

git checkout origin/${{ github.base_ref }} -- tokens (line 43) and git checkout ${{ github.event.pull_request.head.sha }} -- tokens (line 50) splice GitHub Actions context values directly into the shell script text before it runs — a template-injection vector flagged by zizmor. head.sha is a fixed-format commit SHA (lower risk), but base_ref is a branch name and git ref names permit several shell metacharacters, so the safer pattern is to pass these through env: and reference them as $VAR so the shell treats them as data, not code.

🛡️ Proposed fix: use env vars instead of inline template expansion
       - name: Build base tokens
         id: base-build
         if: github.event_name == 'pull_request'
         continue-on-error: true
+        env:
+          BASE_REF: ${{ github.base_ref }}
         run: |
-          git checkout origin/${{ github.base_ref }} -- tokens
+          git checkout "origin/$BASE_REF" -- tokens
           pnpm --filter `@sipe-team/tokens` build:tokens
           mkdir -p /tmp/base-css
           cp packages/tokens/dist/css/*.css /tmp/base-css/

       - name: Restore head tokens
         if: github.event_name == 'pull_request'
-        run: git checkout ${{ github.event.pull_request.head.sha }} -- tokens
+        env:
+          HEAD_SHA: ${{ github.event.pull_request.head.sha }}
+        run: git checkout "$HEAD_SHA" -- tokens

Also applies to: 50-50

🧰 Tools
🪛 zizmor (1.26.1)

[error] 43-43: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/tokens.yaml at line 43, Update the checkout commands in
the workflow to pass github.base_ref and github.event.pull_request.head.sha
through step-level env variables, then reference those variables in the shell
commands. Apply this to both checkout invocations while preserving the existing
origin prefix and tokens path.

Source: Linters/SAST tools


96-96: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Pin stefanzweifel/git-auto-commit-action@v5 to a commit SHA.

Same unpinned-action concern as the other actions in this workflow, flagged separately since it also writes to the repository (higher-impact if the action were compromised).

🧰 Tools
🪛 zizmor (1.26.1)

[error] 96-96: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[info] 96-96: action functionality is already included by the runner (superfluous-actions): use git add, git commit, and git push in a script step

(superfluous-actions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/tokens.yaml at line 96, Update the
stefanzweifel/git-auto-commit-action step to reference a specific verified
commit SHA instead of the mutable `@v5` tag, preserving the existing action
configuration and workflow behavior.

Source: Linters/SAST tools

.husky/pre-push (1)

4-6: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update SPECIAL_REGEX to support dev/* branches.

The current regex only matches exactly main and dev. It will incorrectly block pushes for any sub-branches under dev/. Based on learnings, special branches main and dev/* are exempt from this rule.

🛠️ Proposed fix
 # Special branches that skip naming rules
-SPECIAL_REGEX='^(main|dev)$'
+SPECIAL_REGEX='^(main|dev(/.*)?)$'
📝 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.

# Special branches that skip naming rules
SPECIAL_REGEX='^(main|dev(/.*)?)$'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.husky/pre-push around lines 4 - 6, Update the SPECIAL_REGEX definition to
match exactly main and any branch beginning with dev/, while no longer treating
only the exact dev branch as special. Preserve the existing main-branch
exemption and anchor the pattern so unrelated branch names remain subject to
naming rules.

Source: Learnings

AGENTS.md (1)

47-48: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a blank line before the fenced code block (MD031).

The ```ts fence at line 48 immediately follows the preceding text line with no blank line separator.

📝 Proposed fix
 Variant enum 패턴 (값 객체 + 타입 유니온):
+
 ```ts
 export const ButtonSize = { sm: 'sm', lg: 'lg' } as const;
 export type ButtonSize = (typeof ButtonSize)[keyof typeof ButtonSize];
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.23.0)</summary>

[warning] 48-48: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @AGENTS.md around lines 47 - 48, Insert a blank line between the “Variant
enum 패턴 (값 객체 + 타입 유니온):” text and the following TypeScript fenced code block in
AGENTS.md, preserving the block contents.


</details>

<!-- fingerprinting:phantom:medusa:beignet -->

<!-- cr-indicator-types:potential_issue -->

<!-- cr-comment:v1:6797cf0b1eea2629335d6c7f -->

_Source: Linters/SAST tools_

<!-- This is an auto-generated comment by CodeRabbit -->

</blockquote></details>
<details>
<summary>package.json (1)</summary><blockquote>

15-15: _📐 Maintainability & Code Quality_ | _🔵 Trivial_ | _⚡ Quick win_

**Pin `http-server` as a devDependency instead of using unpinned `npx`.**

`npx http-server` resolves to whatever is latest at run time, which can silently change behavior/output between runs and isn't reproducible in CI.



<details>
<summary>♻️ Proposed fix</summary>

```diff
-    "serve:storybook": "npx http-server storybook-static -p 6006",
+    "serve:storybook": "http-server storybook-static -p 6006",

Add http-server with a pinned version under devDependencies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 15, Update the serve:storybook script to invoke the
project-installed http-server, and add http-server at a pinned version under
devDependencies so local and CI runs use a reproducible dependency instead of
unpinned npx resolution.
packages/accordion/src/Accordion.tsx (1)

58-68: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Memoize the root context value.

<AccordionRootContext.Provider value={{ type, activeValue, onItemToggle }}> creates a new object (and onItemToggle isn't wrapped in useCallback) on every render of AccordionRoot, forcing every consuming AccordionItem to re-render regardless of whether anything relevant changed. ThemeProvider.tsx in this same PR already uses useMemo for its context value — follow the same pattern here for consistency and to avoid the re-render cascade.

♻️ Proposed fix
-  const onItemToggle = (itemValue: string) => {
+  const onItemToggle = useCallback((itemValue: string) => {
     const newValue = activeValue === itemValue ? null : itemValue;
     if (value === undefined) {
       setInternalValue(newValue);
     }
     onValueChange?.(newValue);
-  };
+  }, [activeValue, value, onValueChange]);
+
+  const contextValue = useMemo(() => ({ type, activeValue, onItemToggle }), [type, activeValue, onItemToggle]);

   const Component = asChild ? Slot : 'div';
   return (
-    <AccordionRootContext.Provider value={{ type, activeValue, onItemToggle }}>
+    <AccordionRootContext.Provider value={contextValue}>
📝 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.

  const onItemToggle = useCallback((itemValue: string) => {
    const newValue = activeValue === itemValue ? null : itemValue;
    if (value === undefined) {
      setInternalValue(newValue);
    }
    onValueChange?.(newValue);
  }, [activeValue, value, onValueChange]);

  const contextValue = useMemo(() => ({ type, activeValue, onItemToggle }), [type, activeValue, onItemToggle]);

  const Component = asChild ? Slot : 'div';
  return (
    <AccordionRootContext.Provider value={contextValue}>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/accordion/src/Accordion.tsx` around lines 58 - 68, Memoize the
AccordionRoot context value used by AccordionRootContext.Provider. Wrap
onItemToggle in useCallback with its relevant dependencies, then create the
provider value with useMemo so consumers receive a stable object when type and
activeValue are unchanged.
packages/accordion/src/context/AccordionRootContext.tsx (1)

11-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a null guard to improve error predictability.

When useAccordionRootContext is called outside of AccordionRootContext.Provider, it evaluates to null. Throwing an explicit error inside the hook helps developers quickly identify missing providers rather than deciphering cryptic downstream destructuring errors.

♻️ Proposed refactor to add a guard
 export const useAccordionRootContext = () => {
-  return useContext(AccordionRootContext);
+  const context = useContext(AccordionRootContext);
+  if (!context) {
+    throw new Error('useAccordionRootContext must be used within an AccordionRoot');
+  }
+  return context;
 };
📝 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.

export const useAccordionRootContext = () => {
  const context = useContext(AccordionRootContext);
  if (!context) {
    throw new Error('useAccordionRootContext must be used within an AccordionRoot');
  }
  return context;
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/accordion/src/context/AccordionRootContext.tsx` around lines 11 -
13, Update useAccordionRootContext to check the useContext(AccordionRootContext)
result and throw a clear error when no provider value is present; otherwise
return the context unchanged.
packages/button/src/Button.stories.tsx (1)

193-210: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding a theme showcase story.

As per path instructions for story files, it is recommended to demonstrate components with different theme variants (1st-4th) and to integrate the ThemeProvider for theme-dependent components. Consider adding a new story that explicitly wraps the button in different available theme configurations to showcase the newly integrated semantic tokens.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/button/src/Button.stories.tsx` around lines 193 - 210, Add a theme
showcase story alongside AllVariantsAndSizes that wraps Button examples with
ThemeProvider and renders the available theme variants (1st through 4th), while
preserving the existing variant and size coverage.

Source: Path instructions

packages/button/src/Button.test.tsx (1)

86-112: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove duplicated tests.

The tests checking for default type="button" and type="submit" are duplicated. Lines 86-94 verify the exact same behavior as lines 96-112. Consider removing the duplicate block to keep the test suite clean and maintainable.

♻️ Proposed fix
 test('type="button" is used as default when type is not provided', () => {
   render(<Button>Test</Button>);
-  expect(screen.getByRole('button')).toHaveAttribute('type', 'button');
-});
-
-test('type="submit" works correctly', () => {
-  render(<Button type="submit">Submit</Button>);
-  expect(screen.getByRole('button')).toHaveAttribute('type', 'submit');
-});
-
-test('type="button" is used as default when type is not provided', () => {
-  render(<Button>Test</Button>);
 
   const button = screen.getByRole('button');
 
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/button/src/Button.test.tsx` around lines 86 - 112, Remove the
duplicated test cases for the Button default and submit types, retaining one
test for each behavior in the Button test suite. Keep the existing assertions
that verify type="button" when omitted and type="submit" when explicitly
provided.
packages/button/src/Button.tsx (1)

53-55: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider marking decorative icon wrappers as aria-hidden.

leftIcon/rightIcon are visual embellishments alongside the text label; without aria-hidden="true" on the wrapping <span>, screen readers may announce icon content redundantly (e.g. if the icon component renders any text/SVG title).

♻️ Proposed fix
-      {leftIcon && <span className={styles.iconWrapper}>{leftIcon}</span>}
+      {leftIcon && (
+        <span className={styles.iconWrapper} aria-hidden="true">
+          {leftIcon}
+        </span>
+      )}
       <Slottable>{children}</Slottable>
-      {rightIcon && <span className={styles.iconWrapper}>{rightIcon}</span>}
+      {rightIcon && (
+        <span className={styles.iconWrapper} aria-hidden="true">
+          {rightIcon}
+        </span>
+      )}
📝 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.

      {leftIcon && (
        <span className={styles.iconWrapper} aria-hidden="true">
          {leftIcon}
        </span>
      )}
      <Slottable>{children}</Slottable>
      {rightIcon && (
        <span className={styles.iconWrapper} aria-hidden="true">
          {rightIcon}
        </span>
      )}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/button/src/Button.tsx` around lines 53 - 55, Mark the decorative
icon wrapper spans in the Button render output as aria-hidden="true" for both
leftIcon and rightIcon, while leaving the Slottable children and existing
styling unchanged.
packages/card/src/Card.test.tsx (1)

29-29: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Translate test descriptions to English.

As per path instructions for test files, Korean test descriptions must be converted to English. Please update the test descriptions across these sections.

📝 Proposed translations
  • test('children으로 넘어간 요소를 반환한다.', ...)test('returns the element passed as children', ...)
  • test('Card는 default로 요소를 중앙정렬한다.', ...)test('Card centers elements by default', ...)
  • test('variant는 default로 filled이며 subtle 배경과 default 보더 semantic 토큰을 참조한다.', ...)test('default variant is filled and references subtle background and default border semantic tokens', ...)
  • test('variant가 outline이면 base 배경과 border.strong semantic 토큰을 참조한다.', ...)test('outline variant references base background and border.strong semantic tokens', ...)
  • test('variant가 filled일 때 subtle 배경 토큰을 사용한다.', ...)test('filled variant uses subtle background token', ...)
  • test('variant가 outline일 때 base 배경 토큰을 사용한다.', ...)test('outline variant uses base background token', ...)
  • test('Card는 semantic 컴포넌트 패딩 토큰을 적용한다.', ...)test('Card applies semantic component padding token', ...)
  • test('Card는 semantic 컴포넌트 반경 토큰을 적용한다.', ...)test('Card applies semantic component radius token', ...)

Also applies to: 39-39, 73-125

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/card/src/Card.test.tsx` at line 29, Translate all Korean test
descriptions in Card.test.tsx to the specified English descriptions, including
the cases covering children rendering, default centering, variant defaults,
variant-specific tokens, padding, and radius. Change only the test names and
preserve their assertions and test behavior.

Source: Path instructions

packages/theme/package.json (1)

26-34: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd package.json packages/theme --exec cat -n {}

Repository: sipe-team/side

Length of output: 1875


🏁 Script executed:

fd package.json packages -x sh -c 'printf "\n### %s\n" "$1"; cat -n "$1" | sed -n "1,120p"' sh {}

Repository: sipe-team/side

Length of output: 43188


Declare react and react-dom as peer dependenciespackages/theme/package.json only lists them in devDependencies; add a peerDependencies block so consumers resolve a single React copy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/theme/package.json` around lines 26 - 34, Update the package
manifest’s dependency declarations by adding react and react-dom to a
peerDependencies block, while retaining their devDependencies entries for local
development and testing. Use the existing catalog:react version references and
keep the dependency names aligned across both sections.

Source: Path instructions

packages/tokens/config.js (2)

113-136: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate dts-generation logic between primitive and semantic formatters.

typescript/token-names-dts and typescript/semantic-token-names-dts implement nearly identical grouping/type-emission logic, differing only in the type-name prefix and an added path filter. Extracting a shared helper would reduce duplication and prevent the two from silently diverging over time.

♻️ Proposed refactor: shared dts-emission helper
function emitTokenNamesDts({ dictionary, typeNamePrefix, unionTypeName, pathFilter }) {
  const groups = new Map();
  for (const token of dictionary.allTokens) {
    if (pathFilter && !pathFilter(token)) continue;
    const category = token.path[0];
    if (!groups.has(category)) groups.set(category, []);
    groups.get(category).push(token.name);
  }
  const lines = ['/** Auto-generated — do not edit directly. */\n'];
  const typeNames = [];
  for (const [category, names] of groups) {
    const typeName = `${typeNamePrefix}${toPascalCase(category)}Token`;
    typeNames.push(typeName);
    lines.push(`export type ${typeName} =`);
    lines.push(`${names.map((n) => `  | '${n}'`).join('\n')};\n`);
  }
  lines.push(`export type ${unionTypeName} =`);
  lines.push(`${typeNames.map((t) => `  | ${t}`).join('\n')};\n`);
  return lines.join('\n');
}

Then each hook becomes a one-liner calling emitTokenNamesDts({ dictionary, typeNamePrefix: '', unionTypeName: 'PrimitiveToken' }) / emitTokenNamesDts({ dictionary, typeNamePrefix: 'Semantic', unionTypeName: 'SemanticToken', pathFilter: (t) => semanticOnlyPaths.has(t.path.join('.')) }).

Also applies to: 209-233

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tokens/config.js` around lines 113 - 136, Extract the duplicated
grouping and TypeScript union-emission logic from the primitive and semantic
formatters into a shared emitTokenNamesDts helper. Have the
typescript/token-names-dts hook and typescript/semantic-token-names-dts hook
call it with their respective type-name prefix, union type name, and semantic
path filter, preserving the existing generated output and filtering behavior.

268-298: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and nearby symbols.
ast-grep outline packages/tokens/config.js --view expanded || true

# Read the relevant section with line numbers.
sed -n '180,330p' packages/tokens/config.js | cat -n

# Find any semantic declaration outputs or references in the package.
rg -n "semantic\.d\.(ts|cts)|export \* from './semantic'|SemanticToken" packages/tokens -S || true

# Show the token-names-related source files mentioned in the comment.
sed -n '1,260p' packages/tokens/config.js | rg -n "token-names-dts|semantic-token-names-dts|format" -n -C 2 || true

Repository: sipe-team/side

Length of output: 8321


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the exact branch around the fallback and barrel generation.
sed -n '240,320p' packages/tokens/config.js | cat -n

# Check whether semantic declaration files are generated anywhere else.
rg -n "semantic\.d\.ts|semantic\.d\.cts|semantic\.js|semantic\.cjs" packages/tokens -S || true

# Look for package export mappings that depend on token-names entrypoints.
rg -n "token-names|semantic" packages/tokens/package.json packages/tokens/* -S || true

Repository: sipe-team/side

Length of output: 6613


Fallback branch should also write semantic type stubs
index.d.ts always re-exports ./semantic and references SemanticToken, so the else branch needs to emit semantic.d.ts/semantic.d.cts too. Add stub declarations there to avoid breaking downstream type resolution.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tokens/config.js` around lines 268 - 298, The fallback branch that
writes semantic-dark.css must also generate semantic.d.ts and semantic.d.cts
stubs before the token-names barrel is written. Update the else branch alongside
its CSS fallback so the existing index.d.ts re-exports of ./semantic and
SemanticToken references resolve successfully, using declarations consistent
with the generated semantic type module.
packages/tokens/src/colors/colors.ts (1)

180-186: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

theme5th.text and theme5th.background are identical (#000000), making text invisible.

Any component using this theme with default text-on-background styling will render unreadable content (zero contrast). This looks like a copy-paste value error; text should likely be a light color (e.g., white) to contrast the black background.

🐛 Proposed fix
 export const theme5th: ThemeColor = {
   primary: '`#FF7C27`',
   secondary: '`#FE4E07`',
   background: '`#000000`',
-  text: '`#000000`',
+  text: '`#FFFFFF`',
   gradient: 'linear-gradient(225deg, `#FF4500` 0%, `#FFB24D` 100%)',
 };
📝 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.

export const theme5th: ThemeColor = {
  primary: '`#FF7C27`',
  secondary: '`#FE4E07`',
  background: '`#000000`',
  text: '`#FFFFFF`',
  gradient: 'linear-gradient(225deg, `#FF4500` 0%, `#FFB24D` 100%)',
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tokens/src/colors/colors.ts` around lines 180 - 186, Update the text
color in the theme5th definition so it contrasts with the black background; keep
background as `#000000` and use the established light text color convention, such
as white, for theme5th.text.
packages/tooltip/src/Tooltip.css.ts (2)

9-38: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Tooltip styles hardcode colors, spacing, radius, shadow and z-index instead of using @sipe-team/tokens.

backgroundColor: 'var(--tooltip-bg-color, #000000)', color: '#ffffff', padding, borderRadius, fontSize, boxShadow, and zIndex: 1000 are all raw literals with no vars import. This PR just migrated Button and Card to semantic tokens for these exact same properties, so Tooltip is now the odd one out and won't respond to ThemeProvider theme switches (dark/light or 1st–4th theme variants) the way Button/Card now do.

🎨 Proposed fix: import and use design tokens
-import { keyframes, style } from '`@vanilla-extract/css`';
+import { keyframes } from '`@vanilla-extract/css`';
 import { recipe } from '`@vanilla-extract/recipes`';
+import { vars } from '`@sipe-team/tokens`';
 ...
   base: {
     position: 'fixed',
-    backgroundColor: 'var(--tooltip-bg-color, `#000000`)',
-    color: '`#ffffff`',
-    padding: '8px 12px',
-    borderRadius: '8px',
-    fontSize: '12px',
+    backgroundColor: `var(--tooltip-bg-color, ${vars.color.background.inverse})`,
+    color: vars.color.foreground.inverse,
+    padding: `${vars.spacing.sm} ${vars.spacing.md}`,
+    borderRadius: vars.radius.md,
+    fontSize: vars.typography.fontSize.xs,
     lineHeight: 1.5,
     whiteSpace: 'normal',
     wordWrap: 'break-word',
     maxWidth: '250px',
-    boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
-    zIndex: 1000,
+    boxShadow: vars.shadow.md,
+    zIndex: vars.zIndex.tooltip,

(Adjust token names to match the actual @sipe-team/tokens contract.)

📝 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.

export const tooltip = recipe({
  base: {
    position: 'fixed',
    backgroundColor: `var(--tooltip-bg-color, ${vars.color.background.inverse})`,
    color: vars.color.foreground.inverse,
    padding: `${vars.spacing.sm} ${vars.spacing.md}`,
    borderRadius: vars.radius.md,
    fontSize: vars.typography.fontSize.xs,
    lineHeight: 1.5,
    whiteSpace: 'normal',
    wordWrap: 'break-word',
    maxWidth: '250px',
    boxShadow: vars.shadow.md,
    zIndex: vars.zIndex.tooltip,
    animation: `${fadeIn} 0.15s ease`,
    '`@media`': {
      '(prefers-reduced-motion: reduce)': {
        animation: 'none',
      },
    },
    selectors: {
      '&::after': {
        content: '""',
        position: 'absolute',
        width: 0,
        height: 0,
        borderStyle: 'solid',
      },
    },
  },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tooltip/src/Tooltip.css.ts` around lines 9 - 38, Update the tooltip
recipe’s base styles to import and use the semantic `@sipe-team/tokens` vars for
backgroundColor, color, padding, borderRadius, fontSize, boxShadow, and zIndex
instead of raw literals or fallback custom properties. Match the token usage
established by the migrated Button and Card styles so Tooltip responds to
ThemeProvider theme variants.

Source: Path instructions


39-125: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Placement variants use non-enum string keys and duplicate near-identical blocks 8 times.

Per the repo's coding guideline for *.css.ts, recipe variant keys should derive from enum values; here they're plain string literals ('top-left', 'top', …). Each of the 8 variants also repeats the same selectors['&::after'] shape, differing only in offset/border values (including the hardcoded var(--tooltip-bg-color, #000000) from the base-styles finding above, repeated 8x) — a good candidate for a small generator/helper to reduce duplication.

As per coding guidelines, "Vanilla Extract의 recipe()로 variant를 처리하고, variant 키에는 enum 값을 사용하세요."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tooltip/src/Tooltip.css.ts` around lines 39 - 125, Refactor the
placement variants in the recipe to use the relevant placement enum values
instead of plain string keys, and introduce a small helper or generator for the
shared selectors['&::after'] structure. Pass each placement’s unique offset,
transform, border width, and border color values through that helper, reusing
the shared tooltip background-color variable rather than duplicating it across
all eight variants.

Source: Coding guidelines

packages/tooltip/src/Tooltip.tsx (1)

105-114: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

aria-describedby overrides a user-supplied value from ...rest.

Since aria-describedby is set after spreading ...rest, any aria-describedby a consumer passes in is silently discarded.

♻️ Proposed fix to merge rather than override
       <Component
         {...rest}
         ref={wrapperRef}
-        aria-describedby={isVisible ? tooltipId : undefined}
+        aria-describedby={
+          isVisible ? [rest['aria-describedby'], tooltipId].filter(Boolean).join(' ') : rest['aria-describedby']
+        }
📝 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.

        {...rest}
        ref={wrapperRef}
        aria-describedby={
          isVisible ? [rest['aria-describedby'], tooltipId].filter(Boolean).join(' ') : rest['aria-describedby']
        }
        className={clsx(asChild ? undefined : styles.button, className)}
        onMouseEnter={composeHandlers(onMouseEnter, triggerHandlers.onMouseEnter)}
        onMouseLeave={composeHandlers(onMouseLeave, triggerHandlers.onMouseLeave)}
        onFocus={composeHandlers(onFocus, triggerHandlers.onFocus)}
        onBlur={composeHandlers(onBlur, triggerHandlers.onBlur)}
        onMouseDown={composeHandlers(onMouseDown, triggerHandlers.onMouseDown)}
        onMouseUp={composeHandlers(onMouseUp, triggerHandlers.onMouseUp)}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/tooltip/src/Tooltip.tsx` around lines 105 - 114, Update the trigger
element props near wrapperRef so aria-describedby preserves and merges any
consumer-provided value from rest with tooltipId when isVisible, rather than
overriding it. Keep aria-describedby undefined only when neither the user value
nor the visible tooltip provides an identifier.
scripts/checkPackageConsistency.ts (1)

12-12: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Allowlist rule-name mismatch breaks catalog-dependency exemptions.

isAllowlisted is invoked with rule 'directDependency' (Line 205), but the violation actually pushed uses rule 'catalogDependency' (Line 209). Since isAllowlisted matches on exact entry.rule string, an allowlist entry copied from the reported violation (rule: 'catalogDependency') will never match, and the exemption silently never takes effect — the violation keeps reappearing regardless of the allowlist entry. The rulesRequiringFieldPath set (Line 12) and the allowlistEntrySchema.superRefine validation (Lines 21-36) are also keyed on 'directDependency', reinforcing that this was meant to be the canonical name, while the displayed/output rule diverges.

Align the two rule names (recommend using 'directDependency' consistently, or updating the isAllowlisted call/rulesRequiringFieldPath entry to 'catalogDependency') so allowlisting actually works for this rule.

🐛 Proposed fix to align rule names
-const rulesRequiringFieldPath = new Set(['extraScript', 'directDependency']);
+const rulesRequiringFieldPath = new Set(['extraScript', 'catalogDependency']);
       if (required && spec !== required) {
-        if (!isAllowlisted(policy.allowlist, name, 'directDependency', `${bucket}.${depName}`)) {
+        if (!isAllowlisted(policy.allowlist, name, 'catalogDependency', `${bucket}.${depName}`)) {
           violations.push({

Also applies to: 202-215

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/checkPackageConsistency.ts` at line 12, Align the catalog dependency
rule name across the violation flow: update the reported violation near the
isAllowlisted call in the direct-dependency check to use the canonical name
already used by rulesRequiringFieldPath and allowlistEntrySchema, so
isAllowlisted matches allowlist entries consistently.

Align with the team convention that test descriptions be written in English.
Test bodies and coverage are unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.30769% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
packages/card/src/Card.test.tsx 92.00% 4 Missing ⚠️
Files with missing lines Coverage Δ
packages/button/src/Button.css.ts 100.00% <100.00%> (ø)
packages/card/src/Card.css.ts 100.00% <100.00%> (ø)
packages/card/src/Card.test.tsx 96.19% <92.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Yeom-JinHo Yeom-JinHo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

changeset 추가해주세요~

osohyun0224 and others added 3 commits July 18, 2026 16:12
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per-package changeset keeps each CHANGELOG scoped to its own package
description instead of mixing button and card notes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@G-hoon

G-hoon commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator
스크린샷 2026-07-18 오후 5 07 37

텍스트 색이랑 버튼 색이랑 좀 대비율좀 높여야 할 것 같아요.
접근성 검사하면 바로 잡힐 것 같은 느낌입니다.

웹접근성 최소대비차:
https://brunch.co.kr/@remain/10

padding: `0 ${vars.spacing.component.sm}`,
borderRadius: '6px',
padding: `0 ${vars.spacing.component.md}`,
borderRadius: vars.radius.component.md,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

이거 lg 사이즈의 padding 값이랑 borderRadius 값은 의도하신거라 보면 될까요...?

':active': {
borderColor: vars.color.accent.hover,
color: vars.color.accent.hover,
},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

이거 내용 PR 설명에도 넣어주심 좋을 것 같아요.

':active': {
background: buttonRed,
backgroundColor: vars.color.accent.hover,
opacity: 0.9,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

음....
차라리, opacity 값을 넣는 것보단,
accent.pressed 값을 대비하여, 임시로 backgroundColor 값을 하드코딩하는 건 어떨까요?

(c.c @Yeom-JinHo , @3o14 )


test('applies semantic component spacing token as padding', () => {
render(<Card>Card</Card>);
expect(rulesForElement(screen.getByText('Card'))).toContain('var(--side-spacing-component-lg)');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

음?
이러면 실제 값 검증이 되나요?

임시 테스트 코드라면 주석이라도 달면 좋을 것 같습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants