{"_id":"@bubbly-design-system/icons","name":"@bubbly-design-system/icons","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@bubbly-design-system/icons","version":"0.1.0","description":"Bubbly Design System icon components","type":"module","keywords":["bubbly-design-system","bubbly-lab","design-system","icons"],"homepage":"https://github.com/bubbly-lab/bubbly-design-system","repository":{"type":"git","url":"git+https://github.com/bubbly-lab/bubbly-design-system.git","directory":"packages/icons"},"author":{"name":"@bubbly-lab/dev"},"license":"MIT","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"main":"./dist/index.cjs","module":"./dist/index.mjs","types":"./dist/index.d.mts","sideEffects":false,"publishConfig":{"access":"public"},"scripts":{"build":"tsdown","generate":"tsx scripts/generate.ts","test":"vitest run","check-types":"tsc --noEmit","storybook":"storybook dev -p 6007","build-storybook":"storybook build"},"devDependencies":{"@storybook/react":"^10.3.0","@storybook/react-vite":"^10.3.0","@types/react":"^19.2.14","@types/react-dom":"^19.2.3","@vitejs/plugin-react":"^4.7.0","react":"^19.2.4","react-dom":"^19.2.4","storybook":"^10.3.0","svgo":"^4.0.1","tsx":"^4.21.0","tsdown":"^0.21.4","typescript":"^5.8.3","vite":"^6.4.1","vitest":"^4.1.1"},"peerDependencies":{"react":">=18.0.0"},"gitHead":"c5337c330a6e2ec99d881a3cc9cd0115126b62a8","_id":"@bubbly-design-system/icons@0.1.0","bugs":{"url":"https://github.com/bubbly-lab/bubbly-design-system/issues"},"_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-R8k8Jr3qH+CeBlKAbYTEKmTcXzoootSijfOoylLtGqD7R151Xi0e6NJXWylApsKEllTpW+yKeIlltPaNxnTuug==","shasum":"26ec99953356c3fafff8e0cb84eea545bf9bf462","tarball":"https://registry.npmjs.org/@bubbly-design-system/icons/-/icons-0.1.0.tgz","fileCount":7,"unpackedSize":160400,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC5+0/CBluU4u4V2fV7S9czG67mfRULLALFQynNqccyewIhAPVDGKIbbhwcXb5M/RBk+ny7m3b+LA43dTxz2bTUqYOF"}]},"_npmUser":{"name":"bubbly-lab-bot","email":"admin@rumyscape.com"},"directories":{},"maintainers":[{"name":"yhyem","email":"yoonhyemin0120@gmail.com"},{"name":"bubbly-lab-bot","email":"admin@rumyscape.com"},{"name":"ndaemy","email":"yuyaebean@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/icons_0.1.0_1775115645826_0.7996671435107683"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-02T07:40:45.714Z","0.1.0":"2026-04-02T07:40:45.973Z","modified":"2026-04-02T07:40:46.272Z"},"maintainers":[{"name":"yhyem","email":"yoonhyemin0120@gmail.com"},{"name":"bubbly-lab-bot","email":"admin@rumyscape.com"},{"name":"ndaemy","email":"yuyaebean@gmail.com"}],"description":"Bubbly Design System icon components","homepage":"https://github.com/bubbly-lab/bubbly-design-system","keywords":["bubbly-design-system","bubbly-lab","design-system","icons"],"repository":{"type":"git","url":"git+https://github.com/bubbly-lab/bubbly-design-system.git","directory":"packages/icons"},"author":{"name":"@bubbly-lab/dev"},"bugs":{"url":"https://github.com/bubbly-lab/bubbly-design-system/issues"},"license":"MIT","readme":"# @bubbly-design-system/icons\n\nBubbly Design System의 아이콘 패키지. 39개의 React SVG 컴포넌트를 제공합니다.\n\n- **Tree-shakeable** — 사용한 아이콘만 번들에 포함\n- **`currentColor` 기반** — CSS `color` 속성으로 색상 제어\n- **`filled` prop** — outline/filled 두 가지 variant 전환\n- **접근성** — `title`, `aria-label` 지원, 기본 `aria-hidden`\n- **Server Components 호환** — `'use client'` 지시어 포함\n\n---\n\n## 설치\n\n```bash\npnpm add @bubbly-design-system/icons\n```\n\npeer dependency로 React 18 이상이 필요합니다.\n\n---\n\n## 사용법\n\n### 기본\n\n```tsx\nimport { IconHome, IconSearch } from '@bubbly-design-system/icons';\n\nexport function MyComponent() {\n  return (\n    <div>\n      <IconHome />\n      <IconSearch />\n    </div>\n  );\n}\n```\n\n### 크기 (`size`)\n\n기본값은 `'1em'`으로, 부모 요소의 `font-size`를 따릅니다.\n\n```tsx\n<IconHome size={24} />        // 24px\n<IconHome size=\"1.5rem\" />    // 1.5rem\n<IconHome size=\"2em\" />       // 부모 font-size의 2배\n```\n\n### 색상\n\n`color` CSS 속성을 통해 제어합니다. 아이콘은 `currentColor`를 사용하므로 별도 prop 없이 CSS만으로 색상이 적용됩니다.\n\n```tsx\n// CSS로 제어\n<IconHome style={{ color: '#FF5733' }} />\n<IconHome className=\"text-blue-500\" />  // Tailwind\n\n// SVG 속성 직접 전달도 가능\n<IconHome fill=\"red\" />\n```\n\n### Filled variant (`filled`)\n\noutline/filled 두 가지 variant를 가진 아이콘은 `filled` prop으로 전환합니다. 기본값은 `false` (outline).\n\n```tsx\n<IconHome />              // outline (기본)\n<IconHome filled />       // filled\n<IconHome filled={true} />\n```\n\n`filled` prop이 없는 아이콘(단일 variant)은 prop을 받아도 무시됩니다.\n\n### 접근성\n\n기본적으로 `aria-hidden={true}`가 적용됩니다. 아이콘이 의미를 전달해야 하는 경우 `title` 또는 `aria-label`을 사용하세요.\n\n```tsx\n// 장식용 (기본) — 스크린 리더가 무시\n<IconHome />\n\n// 의미 있는 아이콘 — title로 설명 제공\n<IconHome title=\"홈으로 이동\" />\n\n// aria-label 사용\n<button aria-label=\"홈으로 이동\">\n  <IconHome />\n</button>\n\n// aria-labelledby 사용\n<IconHome aria-labelledby=\"home-label\" />\n```\n\n### ref 전달\n\n모든 아이콘은 `forwardRef`로 구현되어 있어 `SVGSVGElement` ref를 전달할 수 있습니다.\n\n```tsx\nconst ref = useRef<SVGSVGElement>(null);\n<IconHome ref={ref} />\n```\n\n### SVG 속성 전달\n\n`IconProps`는 `React.SVGAttributes<SVGElement>`를 확장하므로 모든 SVG 속성을 그대로 전달할 수 있습니다.\n\n```tsx\n<IconHome\n  className=\"my-icon\"\n  style={{ verticalAlign: 'middle' }}\n  onClick={() => console.log('clicked')}\n/>\n```\n\n---\n\n## 아이콘 목록\n\n총 39개. 이름은 camelCase이며 컴포넌트는 `Icon` 접두사를 붙입니다 (예: `home` → `IconHome`).\n\n### Outline + Filled (9개)\n\n`filled` prop으로 variant 전환 가능.\n\n| 이름 | 컴포넌트 |\n|------|----------|\n| home | `IconHome` |\n| userCircle | `IconUserCircle` |\n| lock | `IconLock` |\n| mail | `IconMail` |\n| location | `IconLocation` |\n| pencil | `IconPencil` |\n| bubble | `IconBubble` |\n| questionCircle | `IconQuestionCircle` |\n| infoCircle | `IconInfoCircle` |\n\n### Outline only (27개)\n\n| | | |\n|--|--|--|\n| `IconPlus` | `IconMinus` | `IconSearch` |\n| `IconClose` | `IconCloseCircle` | `IconCheck` |\n| `IconMenu` | `IconFilter` | `IconKebab` |\n| `IconBullet` | `IconWarningCircle` | `IconReload` |\n| `IconLoading` | `IconLink` | `IconCopy` |\n| `IconExternalLink` | `IconImgNone` | `IconUnlock` |\n| `IconTime` | `IconArrowRight` | `IconArrowLeft` |\n| `IconArrowUp` | `IconArrowDown` | `IconChevronRight` |\n| `IconChevronLeft` | `IconChevronUp` | `IconChevronDown` |\n\n### Filled only (3개)\n\n별점 UI 구성용 아이콘입니다. `IconStar`는 꽉 찬 별, `IconStarHalf`는 좌측 절반만 불투명하고 우측은 50% 투명, `IconStarEmpty`는 전체가 50% 투명합니다.\n\n| 이름 | 컴포넌트 | 시각적 특성 |\n|------|----------|-------------|\n| star | `IconStar` | 불투명 (100%) |\n| starHalf | `IconStarHalf` | 좌측 100%, 우측 50% |\n| starEmpty | `IconStarEmpty` | 전체 50% |\n\n---\n\n## Storybook\n\n모든 아이콘을 시각적으로 확인할 수 있습니다.\n\n```bash\npnpm storybook:icons\n```\n\n<!-- TODO: 배포 후 링크 추가 -->\n\n---\n\n## TypeScript\n\n```ts\nimport type { IconProps } from '@bubbly-design-system/icons';\n```\n\n```ts\ninterface IconProps extends React.SVGAttributes<SVGElement> {\n  children?: never;\n  size?: string | number;   // 기본값: '1em'\n  filled?: boolean;         // 기본값: false\n  title?: string;           // 접근성용 SVG <title>\n}\n```\n\n---\n\n## 기여 가이드\n\n### 아이콘 추가/수정 방법\n\n`src/icons/` 파일을 직접 수정하지 마세요. 아이콘은 파이프라인을 통해 자동 생성됩니다.\n\n**파이프라인 구조:**\n\n```\nSVG 소스 (외부)\n    ↓ scripts/fetch-figma.ts\nsvg/{name}/{variant}.svg          ← 원본 SVG (git 추적)\n    ↓ scripts/optimize-svgs.ts    (SVGO v4)\nsvg/{name}/{variant}.svg          ← 최적화된 SVG (in-place)\n    ↓ scripts/generate-components.ts\nsrc/icons/Icon{Name}.tsx          ← 생성된 컴포넌트 (git 추적)\n    ↓ scripts/generate-exports.ts\nsrc/index.ts                      ← barrel export (git 추적)\n    ↓ scripts/generate-stories.ts\nsrc/stories/Icon{Name}.stories.tsx ← Storybook 스토리 (git 추적)\n```\n\n**전체 파이프라인 실행:**\n\n```bash\n# 루트 .env에 FIGMA_PAT, FIGMA_FILE_KEY 필요\npnpm --filter @bubbly-design-system/icons generate\n```\n\n**개별 스크립트 실행 (packages/icons/ 에서):**\n\n```bash\nnpx tsx scripts/fetch-figma.ts         # SVG 다운로드\nnpx tsx scripts/optimize-svgs.ts      # SVGO 최적화\nnpx tsx scripts/generate-components.ts # React 컴포넌트 생성\nnpx tsx scripts/generate-exports.ts   # barrel index.ts 생성\nnpx tsx scripts/generate-stories.ts   # Storybook 스토리 생성\n```\n\n### 아이콘 추가\n\n1. 디자인 소스에서 추가할 아이콘의 node ID를 확인합니다.\n2. `scripts/icon-manifest.json`에 항목 추가:\n   ```json\n   \"newIcon\": {\n     \"outline\": \"NODE_ID\",\n     \"filled\": \"NODE_ID\"\n   }\n   ```\n   variant가 하나뿐이면 해당 key만 작성합니다.\n3. 파이프라인 실행: `pnpm --filter @bubbly-design-system/icons generate`\n4. 생성된 파일 커밋\n\n### 환경 설정\n\n루트 `.env` 파일에 다음 값이 필요합니다:\n\n```bash\n# .env (루트)\nFIGMA_PAT=your_figma_personal_access_token\nFIGMA_FILE_KEY=your_figma_file_key\n```\n\n`.env.example`을 참고하세요.\n\n### 빌드 및 테스트\n\n```bash\n# 빌드\npnpm --filter @bubbly-design-system/icons build\n\n# 타입 체크\npnpm --filter @bubbly-design-system/icons check-types\n\n# 테스트 (빌드 스크립트 단위 테스트)\npnpm --filter @bubbly-design-system/icons test\n\n# Storybook 로컬 실행\npnpm storybook:icons\n```\n\n### 코드 구조\n\n```\npackages/icons/\n├── scripts/\n│   ├── fetch-figma.ts          # SVG 다운로드 (외부 소스)\n│   │                           # 429 rate limit 자동 재시도 (Retry-After)\n│   ├── optimize-svgs.ts        # SVGO v4 최적화 (in-place)\n│   │                           # width/height 제거, currentColor 변환\n│   ├── generate-components.ts  # SVG → React 컴포넌트 코드 생성\n│   ├── generate-exports.ts     # src/index.ts barrel export 생성\n│   ├── generate-stories.ts    # Storybook 스토리 자동 생성\n│   ├── generate.ts             # 위 5개를 순서대로 실행하는 오케스트레이터\n│   ├── icon-manifest.json      # 아이콘 이름 → nodeId 매핑\n│   └── __tests__/              # 빌드 스크립트 단위 테스트 (Vitest)\n├── src/\n│   ├── types.ts                # IconProps 인터페이스\n│   ├── index.ts                # barrel export (자동 생성)\n│   ├── icons/                  # 아이콘 컴포넌트 (자동 생성, git 추적)\n│   └── stories/                # Storybook 스토리 (자동 생성, git 추적)\n├── svg/                        # 최적화된 원본 SVG (자동 생성, git 추적)\n├── dist/                       # 빌드 출력 (gitignore)\n├── tsdown.config.ts            # ESM + CJS + dts 빌드 설정\n└── vitest.config.ts            # 테스트 설정\n```\n\n### 컴포넌트 생성 규칙\n\n`generate-components.ts`가 각 아이콘을 다음 규칙으로 변환합니다:\n\n- **이름**: `{camelCase}` → `Icon{PascalCase}` (예: `arrowLeft` → `IconArrowLeft`)\n- **`filled` prop**: outline + filled 둘 다 있는 아이콘만 조건부 렌더링. 단일 variant 아이콘은 prop을 받아도 무시\n- **SVG 변환**: kebab-case 속성 → camelCase (예: `fill-rule` → `fillRule`), `xmlns` 제거\n- **접근성**: `title`/`aria-label`/`aria-labelledby` 중 하나라도 있으면 `role=\"img\"`, 없으면 `aria-hidden={true}`\n","readmeFilename":"README.md","_rev":"1-27992ef862fe04e0dd9eea69a2ce56fe"}