@uniai-fe/uds-primitives
v0.0.17
Published
UNIAI Design System; Primitives Components Package
Downloads
1,815
Readme
@uniai-fe/uds-primitives
@uniai-fe/uds-foundation 토큰 위에 Radix UI 컴포넌트를 얇게 감싼 기초 UI 컴포넌트 컬렉션입니다. Next.js 등 React 런타임에서 바로 import해 버튼·입력·네비게이션 등 공통 요소를 일관된 스타일로 사용할 수 있습니다. Templates(@uniai-fe/uds-templates) 패키지에서 사용하는 Phone Input/Email Verification Input/OneTimeCode Input 등의 인증 시나리오 컴포넌트도 이곳에서 제공합니다.
설치
pnpm add @uniai-fe/uds-foundation @uniai-fe/util-functions @uniai-fe/uds-primitivesPeer Dependencies
@uniai-fe/uds-primitives는 디자인 토큰과 유틸리티 로직을 공유하기 위해 다음 패키지를 같이 설치해야 합니다.
@uniai-fe/uds-foundation@^0.0.1@uniai-fe/util-functions@^0.2.3react@>=19,react-dom@>=19react-hook-form@>=7
peer dependency가 빠져 있을 경우 앱 번들 시점에 에러가 발생하니, 위 목록을 프로젝트 package.json에 명시해 주세요.
// next.config.ts
const nextConfig = {
transpilePackages: ["@uniai-fe/uds-foundation", "@uniai-fe/uds-primitives"],
};
export default nextConfig;앱 루트에서 foundation CSS를 1회 import 하면 모든 primitives가 토큰을 공유합니다.
import "@uniai-fe/uds-foundation/css";사용 예시
import { Button } from "@uniai-fe/uds-primitives";
export default function Page() {
return (
<Button.Default scale="solid-medium" priority="primary">
확인
</Button.Default>
);
}Link/Anchor로 사용하기
import Link from "next/link";
import { Button } from "@uniai-fe/uds-primitives";
function LinkButton() {
return (
<Button.Default
as={Link}
href="/dashboard"
scale="outlined-medium"
priority="secondary"
>
대시보드로 이동
</Button.Default>
);
}Button 역할 클래스 & Props 미리보기
- 기본 클래스:
.button(padding/height/radius/타이포/커서 담당) - 슬롯:
button-label,button-icon,button-left,button-right,button-loading - Modifier:
button-scale-*,button-fill-*,button-priority-*,button-state-*,button-size-*,button-block,button-icon-left/right - 차기 리팩토링에서는 디자인 관련 props를
scale(레이아웃/spacing 집합)과priority(color set) 두 축으로 단순화하고, 나머지는 상태/slot 위주의 역할 props만 남긴다. 자세한 계획은docs/CONTEXT-BUTTON.md에서 확인할 수 있다. loading상태는 readonly와 동일하게 잠기며 hover/pressed 반응을 막는다. anchor 등 커스텀 요소도aria-disabled="true"를 통해 동일한 스타일을 적용받는다.
TextButton / RoundButton 템플릿
Button 객체는 템플릿별 컴포넌트를 포함한다.
import { Button } from "@uniai-fe/uds-primitives";
function Templates() {
return (
<>
<Button.Text priority="secondary" size="medium">
링크 스타일
</Button.Text>
<Button.Rounded aria-label="추가" size="small">
<span aria-hidden="true">+</span>
</Button.Rounded>
</>
);
}- TextButton(
Button.Text)은size="small|medium|large"+priority="secondary|tertiary"만 허용한다. - RoundButton(
Button.Rounded)은 size만 지정하면 되고 priority는 기본 Button과 동일하게primary|secondary|tertiary를 사용한다. - 템플릿별 클래스를 추가로 노출한다:
.button-template-text,.button-template-text-size-*,.button-template-round,.button-template-round-size-*. - 스토리북
primitives/ButtonStory에서 solid/outlined/텍스트/라운드 4가지 카테고리를 한 번에 확인할 수 있다.
스타일 내보내기
@uniai-fe/uds-primitives/styles 엔트리를 import하면 foundation CSS, theme override, 컴포넌트 SCSS가 한 번에 로드된다.
@use "@uniai-fe/uds-primitives/styles";Next.js/webpack 환경에서 Sass를 사용하지 않는 경우에는 ESM import로 @uniai-fe/uds-primitives/css를 불러오면 된다. 이 CSS에는 foundation 토큰과 primitives 스타일이 이미 번들링되어 있다.
import "@uniai-fe/uds-primitives/css";컴포넌트 단위로 필요한 스타일만 선택해 불러오려면 각 경로(components/{name}/index.scss)를 직접 import하면 된다.
@use "@uniai-fe/uds-primitives/button/index.scss";
@use "@uniai-fe/uds-primitives/navigation/index.scss";ThemeProvider는 CSS를 import하지 않으므로 위 styles 엔트리를 앱 루트에서 1회만 로드하면 중복 없이 토큰 매핑이 적용된다.
Next.js 통합 예시
Next.js 15(app router 기준)에서 primitives를 사용하는 최소 구성 예시는 다음과 같다.
// next.config.ts
const nextConfig = {
transpilePackages: ["@uniai-fe/uds-foundation", "@uniai-fe/uds-primitives"],
sassOptions: {
// monorepo가 아니어도 node_modules 경로를 자동 탐색하지만,
// 필요 시 디자인 토큰 경로를 명시해 두면 빌드 환경 차이를 줄일 수 있다.
includePaths: ["./node_modules"],
},
};
export default nextConfig;/* app/globals.scss */
@use "@uniai-fe/uds-primitives/styles";// app/layout.tsx
import type { ReactNode } from "react";
import "@uniai-fe/uds-primitives/styles";
import { ThemeProvider } from "@uniai-fe/uds-primitives";
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="ko">
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
);
}위 예시는 ThemeProvider가 CSS를 다시 import하지 않는 현재 구조를 기준으로 하므로, globals.scss 또는 루트에서 styles/css 엔트리를 반드시 한 번 로드해야 한다. Sass 기반 프로젝트는 @use "@uniai-fe/uds-primitives/styles";, CSS-only 프로젝트는 import "@uniai-fe/uds-primitives/css";를 사용한다.
모든 컴포넌트는 .component 클래스 + CSS 변수 기반으로 override가 가능하며, 버튼처럼 Slot(left/right/icon 등)을 제공하는 항목은 CONTEXT-*.md 문서에 상세 API를 기록했습니다. 불필요한 data-* attribute는 제거했고, 상태 표시는 :disabled, [aria-busy="true"] 같은 표준 attribute만 사용합니다.
구조
src/components/{category}/
markup/ // 컴포넌트 구현
types/ // 외부 노출 타입
styles/ // SCSS (foundation 토큰 기반)
hooks/ // 카테고리 전용 훅- 배럴(
components/{category}/index.tsx)은 항상import "./index.scss"를 포함합니다. - 스타일은 foundation CSS 변수만 사용하며,
.button.button-priority-*/.button.button-fill-*클래스 조합으로 상태를 분기합니다.
스크립트
pnpm module:lintpnpm module:typecheckpnpm module:build
루트에서는 pnpm --filter @uniai-fe/uds-primitives <command>로 실행할 수 있습니다.
문서
CONTEXT.md및CONTEXT-*.md: 각 컴포넌트의 상태/진행/디자인 근거CONTEXT-INPUT.md: Phone/Email/OneTimeCode 등 인증 입력 시나리오 규칙을 포함하며, templatesCONTEXT-SIGNUP*.md와 항상 동기화해야 한다.RADIX-SIZE-GUIDE.md: primitives 사이즈 체계와 Radix 매핑 규칙
Signup 인증 입력 컴포넌트
- PhoneInput: 기본은 마스킹된 전화번호 입력만 제공한다.
onRequestCode(optional)를 주입하면 인증요청 버튼이 우측 right 슬롯에 노출된다. Step1 User Info에서는 단순 입력만 필요하므로 optional props를 생략한다. - EmailInput: 이메일 입력 + 인증요청 버튼 + countdown + OneTimeCode 입력을 하나로 묶는다.
countdownText,codeVisible,codeLength,codeLabel,codeHelper,codeState,onCodeComplete로 Step2 Verify & Agreement 상태를 제어한다. - AuthCodeInput: length 지정형 OneTimeCode grid. EmailInput 내부에서 사용하지만 서비스 앱도 직접 import할 수 있다.
- 변경 시에는 다음 문서를 함께 업데이트한다:
packages/design/primitives/docs/CONTEXT-INPUT.md,packages/design/templates/docs/CONTEXT-SIGNUP.md,CONTEXT-SIGNUP-FLOW.md,packages/design/templates/docs/STORYBOOK.md,apps/design-storybook/src/stories/templates/auth/AuthSignup.stories.tsx.
- 컨벤션: 모든 컴포넌트/스토리/문서는 slot/prefix/suffix 용어를 사용하지 않고, 레이아웃 기준(
header/body/footer, 2단 구조는upper/lower)과util*키워드를 사용한다. 인터랙션 함수는on*접두사를 사용하고, JSDoc@param은 depth 전체를 풀어 쓴다.
필요한 컨텍스트를 확인한 뒤 컴포넌트를 import해 사용하면 됩니다.
