@loword/loword-design-system
v0.7.9
Published
로워드 디자인 시스템 컴포넌트 패키지(Tailwind v4)
Maintainers
Readme
@loword/loword-design-system
로워드 디자인 시스템 — React 19 컴포넌트 패키지입니다. Tailwind CSS v4를 쓰고 NPM(ESM)으로 배포합니다.
공개 컴포넌트는
LD접두사를 붙입니다 (예:LDButton,LDModal,LDInput).
요구사항
- React 19 / React DOM 19 (peer dependency)
- Tailwind CSS v4 —
theme.css경로로 쓸 때만 필요합니다. 완성된styles.css만 쓴다면 없어도 됩니다. - Next.js 15 (optional) —
LDTanstackTable·LDCollapsibleTanstackTable은 내부에서next/navigation을 무조건 호출하므로 Next 환경에서만 돕니다.
설치
npm install @loword/loword-design-system
# 또는
pnpm add @loword/loword-design-systemEditor·Carousel·Drawer·Table·Chart·DatePicker 같은 일부 컴포넌트는 뒤에 깔린 라이브러리를 optional peer dependency로 둡니다. 해당 컴포넌트를 쓸 때만 같이 설치하면 됩니다 — @tiptap/*(Editor), embla-carousel*(Carousel), vaul(Drawer), @radix-ui/*(Accordion/Modal/Popover/Tabs/Tooltip), @tanstack/react-table·next(Table), chart.js·react-chartjs-2(Chart), react-day-picker·dayjs(DatePicker/Calendar), react-hot-toast(Toast), react-daum-postcode(DaumPostButton).
설정
스타일시트는 앱 진입점에서 한 번만 import 합니다.
import '@loword/loword-design-system/styles.css';styles.css는 컴파일이 끝난 CSS라 이것만으로 컴포넌트가 제대로 보입니다. 반면 자기 코드에서 display40, bg-mono600 같은 디자인 시스템 클래스를 직접 쓰려면 raw @theme/@utility 진입점인 theme.css를 앱의 Tailwind 엔트리에 따로 @import 해야 합니다.
@import 'tailwindcss';
@import '@loword/loword-design-system/theme.css';사용
import { LDButton } from '@loword/loword-design-system';
export default function Example() {
return (
<LDButton variant="solid" color="point" onClick={() => alert('clicked')}>
확인
</LDButton>
);
}명령형 API 예시:
import { LDModal, LDToast } from '@loword/loword-design-system';
const result = await LDModal({ title: '삭제할까요?' /* ... */ });
LDToast.success('저장되었습니다');
openModal은LDModal의 하위호환 별칭(@deprecated)입니다. 새로 쓰는 코드에서는LDModal을 씁니다.
import 방식
// 1) barrel — 이름으로 가져오기
import { LDButton, LDModal, LDInput } from '@loword/loword-design-system';
// 2) 개별 경로 — 트리셰이킹/명시적 경로
import LDButton from '@loword/loword-design-system/components/LDButton/LDButton';컴포넌트
기본/입력: LDButton · LDInput · LDTextarea · LDCheckbox · LDCheckboxGroup · LDRadio · LDRadioGroup · LDSelect · LDMultiSelect · LDAutoComplete · LDToggleButton · LDSegmented · LDSlider · LDRate · LDOTPInput · LDFileUpload · LDFileUploadList · LDImageUpload · LDImageUploadList · LDDaumPostButton
날짜/시간: LDCalendar · LDDataCalendar · LDDatePicker · LDDateRangePicker · LDTimePicker
레이아웃/표시: LDBadge · LDAvatar · LDDivider · LDCallout · LDEmpty · LDSkeleton · LDSpinner · LDLoadingDots · LDProgressBar · LDScrollArea · LDAccordion · LDTeamHistory
내비게이션: LDBreadCrumb · LDMenuBar · LDTabs · LDSectionNav · LDPagination · LDLNB
오버레이/피드백: LDModal · LDModalView · LDDrawer · LDPopover · LDTooltip · LDToast · LDToaster · LDNotification
콘텐츠/데이터: LDEditor · LDViewer · LDCarousel · LDChart · LDTable · LDTanstackTable · LDCollapsibleTanstackTable
폼 연동: LDForm · LDNativeForm · LDFormField · LDFormErrorMessage · LDFormInput · LDFormTextarea · LDFormSelect · LDFormMultiSelect · LDFormCheckbox · LDFormCheckboxGroup · LDFormRadioGroup · LDFormDatePicker · LDFormDateRangePicker · LDFormTimePicker · LDFormToggleButton · LDFormOTPInput · LDFormRate
유틸: Show — 조건부 렌더링 컴포넌트. when이 truthy면 children, falsy면 fallback을 렌더링하고, children을 함수로 넘기면 NonNullable로 좁혀진 값을 인자로 받습니다.
<Show when={user} fallback={<LDSpinner />}>
{(result) => <span>{result.name}</span>}
</Show>createCn — LDS 토큰(커스텀 display·font-weight·spacing·icon-size·숫자 radius)을 아는 clsx + tailwind-merge 병합 함수 cn을 만듭니다. 등록할 토큰이 없으면 createCn(), 있으면 extension을 넘겨 LDS 설정 뒤에 얹습니다(extend는 LDS 값에 추가, override는 LDS가 등록한 것까지 교체). 커스텀 class group id는 타입 인자로 넘깁니다.
// 소비처 lib/cn.ts — 모듈 스코프에서 한 번만 만든다
import { createCn } from '@loword/loword-design-system';
export const cn = createCn<'elevation'>({
extend: {
// 자체 타이포 토큰 text-caption 을 font-size 로 인식시킨다(미등록 시 text-color 로 오인돼 text-red-500 을 지움)
theme: { text: ['caption', 'body1'] },
// 자체 유틸 elevation-* 를 새 그룹으로 등록하고 shadow-* 와 상충시킨다
classGroups: { elevation: [{ elevation: ['1', '2', '3'] }] },
conflictingClassGroups: { elevation: ['shadow'] },
},
});createCn은 호출마다 독립된 tailwind-merge 인스턴스(설정·캐시)를 만들므로 모듈 스코프에서 한 번만 만들어 재사용합니다.
barrel의 값 export는
Show·openModal(LDModal의 하위호환 별칭) ·TextareaContent·createCn넷 말고는 전부LD접두사입니다. 타입 export에는 예외가 조금 더 있습니다.
빠른 시작 (개발)
pnpm install # 의존성 설치
pnpm switch -c <브랜치> # 열려 있는 release/* 위에 작업 브랜치 생성
pnpm storybook # Storybook 개발 서버 (port 6006)
pnpm build # 프로덕션 빌드
pnpm check-types # 타입 체크
pnpm lint # ESLint (--fix 포함, src + .storybook)
pnpm prettier # Prettier (--write, 저장소 전체 — CI 포매팅 검사와 같은 범위)
pnpm test # vitest (utils 유닛 + 스토리 브라우저 실행 + a11y)
pnpm test:coverage # 커버리지 (utils / components 분리 측정)
pnpm changeset # .changes/{브랜치명}.md 생성변경 기록 (기여)
코드를 고쳤으면 pnpm changeset으로 .changes/{브랜치명}.md 를 만들어 같은 PR에 포함합니다. 이 디렉토리가 변경 기록의 유일한 원본이고, docs/CHANGELOG.md와 docs/releases/vX.Y.Z.md는 배포 시점에 여기서 생성되므로 직접 편집하지 않습니다.
작업 브랜치는 main이 아니라 열려 있는 release/*에서 분기하고 PR base도 거기로 잡습니다(pnpm switch -c <브랜치>가 자동으로 처리합니다). 모든 PR은 merge commit으로 머지합니다(squash 금지). 자세한 규칙은 docs/release-process.md.
문서
- 아키텍처와 상세 가이드: docs/architecture.md
- 3-Layer 구조 (Base → Custom → Function)
- 컴포넌트 목록과 특수 구조
- 스타일 체계 (디자인 토큰,
cn(),tv()) - 타입 · 테스트 · 린트/포매팅 체계
- Storybook 스타일 제어와 stories 생성 방법
- 프리셋 스타일 작성 규칙: docs/style-conventions.md
- CSS 캐스케이드 레이어: docs/css-layers.md — 컴포넌트 스타일이 왜 이기고 지는지, className override가 안 먹을 때의 진단 순서
- 타입 컨벤션: docs/type-conventions.md
- 릴리즈/브랜치 운영: docs/release-process.md — changeset 작성법,
release/*브랜치 흐름, 배포 자동화 - 변경 이력: docs/CHANGELOG.md (생성물) · 버전별 릴리즈 노트는 docs/releases/
