@janbox/storefront-ui
v4.0.15
Published
Storefront UI component library for Janbox
Readme
@janbox/storefront-ui
Thư viện component React nội bộ của Janbox — được thiết kế để AI agents và developers có thể làm việc hiệu quả.
Tổng quan
@janbox/storefront-ui là design system component library được xây dựng trên React 19, Emotion CSS, và Vite. Thư viện cung cấp 60+ components có thể customize, type-safe hoàn toàn, và tuân thủ các convention nhất quán để AI agents có thể hiểu và sử dụng dễ dàng.
Phiên bản hiện tại: v2.0.29
Đặc điểm nổi bật
- Pure ESM — Module format hiện đại, tree-shakeable
- Type-safe 100% — TypeScript strict mode, generic types đầy đủ
- Convention-driven — Mọi component tuân thủ cùng một pattern, dễ dự đoán
- Polymorphic components —
Primitivecomponent vớiasprop linh hoạt - Responsive-first — Built-in responsive props cho mọi component
- Emotion CSS — CSS-in-JS với type safety và performance tốt
- 60+ Components — Từ primitive đến complex UI patterns
- AI-friendly — Documentation rõ ràng, naming nhất quán, patterns dễ học
Installation
Yêu cầu
- Node.js ≥ 18
- pnpm ≥ 8 (khuyến nghị)
- React ≥ 19
Cài đặt qua pnpm
pnpm add @janbox/storefront-uiPeer dependencies
Đảm bảo đã cài đặt các peer dependencies:
pnpm add react react-dom @emotion/reactNếu sử dụng Button với routing:
pnpm add react-routerQuick Start
1. Wrap app với ThemeProvider
import { ThemeProvider } from '@janbox/storefront-ui/theme';
function App() {
return <ThemeProvider>{/* Your app */}</ThemeProvider>;
}2. Import và sử dụng components
import { Button, Input, Box, Text } from '@janbox/storefront-ui';
function LoginForm() {
return (
<Box sx={{ md: { maxWidth: 400 } }}>
<Text size="2xl" sx={{ marginBottom: 16 }}>
Đăng nhập
</Text>
<Input type="email" placeholder="Email" size="md" sx={{ marginBottom: 12 }} />
<Input type="password" placeholder="Mật khẩu" size="md" sx={{ marginBottom: 16 }} />
<Button variant="contained" color="primary" size="lg" fullWidth>
Đăng nhập
</Button>
</Box>
);
}3. Import stylesheet (nếu dùng component có CSS)
Một số component (như DatePicker) import CSS từ third-party libraries. Styles này được tách ra thành file dist/style.css riêng biệt trong quá trình build. Consumer app cần import file này khi sử dụng các component đó:
// Import CSS stylesheet cho các component có external CSS (DatePicker, v.v.)
import '@janbox/storefront-ui/style.css';Nếu app không dùng
DatePickerhay component nào import CSS, bạn có thể bỏ qua bước này.
4. Sử dụng responsive props
import { Flexbox, Box, Button } from '@janbox/storefront-ui';
function ResponsiveLayout() {
return (
<Flexbox direction="column" md={{ direction: 'row' }} gap={16}>
<Box sx={{ sm: { flex: 1 } }}>Nội dung chính</Box>
<Box sx={{ sm: { width: 300 } }}>Sidebar</Box>
</Flexbox>
);
}Features
1. Convention-Driven Architecture
Mọi component tuân thủ cùng một pattern:
src/lib/<component>/
├── index.ts → Barrel export
├── types.ts → TypeScript interfaces
├── helpers.ts → CSS helpers + merge props logic
├── <component>.tsx → Component implementation
└── <component>.stories.tsx → Storybook storiesPattern nhất quán:
// Mọi component đều nhận props theo format này
export const Button = ({ ref, ..._props }: ButtonProps) => {
// 1. Merge với default props qua helper
const { size, variant, color, children, ...rest } = getButtonProps(_props);
// 2. Build CSS từ helpers
const css = [
getButtonCssBySize(size),
getButtonCssByVariant(variant),
getButtonCssByColor(color),
createSxInterpolation(sx),
];
// 3. Render với Primitive hoặc native element
return (
<button css={css} ref={ref} {...rest}>
{children}
</button>
);
};2. Type-Safe Responsive Props
import { Box } from '@janbox/storefront-ui';
// Base props (mobile-first)
<Box display="none" />
// Responsive overrides theo breakpoint
<Box
display="none"
md={{ display: 'flex' }}
lg={{ display: 'grid' }}
/>Breakpoints:
| Screen | Min-width |
| ------ | --------------------------------- |
| xs | 0px (default, không cần khai báo) |
| sm | 768px (tablet) |
| md | 1280px (desktop) |
| lg | 1680px (large desktop) |
3. Polymorphic Components
import { Primitive } from '@janbox/storefront-ui';
// Render as div (mặc định)
<Primitive>Content</Primitive>
// Render as button
<Primitive as="button" onClick={handleClick}>
Click me
</Primitive>
// Render as Link từ react-router
<Primitive as={Link} to="/home">
Go home
</Primitive>4. Design Token System
import { getColorVar, getTypographyVar, mediaQuery } from '@janbox/storefront-ui/theme';
// Color tokens
const style = {
backgroundColor: getColorVar('primary.600'),
color: getColorVar('neutral.50'),
};
// Typography tokens
const { fontSize, lineHeight } = getTypographyVar('lg');
// Media queries
const responsiveStyle = {
[mediaQuery('md')]: { fontSize: 16 },
[mediaQuery('lg')]: { fontSize: 18 },
};5. Utility Functions
import {
cn, // Class names combiner
mergeComponentProps, // Deep merge props
getColorShadesByVariant, // Color utilities
getSizeVariantState, // Size utilities
formatNumber, // Number formatter
formatPrice, // Price formatter
formatDateTime, // DateTime formatter
} from '@janbox/storefront-ui/utils';
// Example: merge props
const props = mergeComponentProps(defaultProps, userProps);
// Example: get color shades
const { main, dark, light, contrast } = getColorShadesByVariant('primary');
// Example: format price
const formatted = formatPrice(1000000, 'VND', 'vi-VN'); // "1.000.000 ₫"Usage
Component Categories
Thư viện cung cấp 60+ components được tổ chức theo các nhóm. Xem danh sách README theo từng component tại Component Documentation.
Export Paths
// Main components
import { Button, Input, Box } from '@janbox/storefront-ui';
// Theme utilities
import { ThemeProvider, getColorVar } from '@janbox/storefront-ui/theme';
// Hooks
import { useWindowScreen, useControllableState } from '@janbox/storefront-ui/hooks';
// Types
import type { SizeVariant, ColorScale, StyledCSS } from '@janbox/storefront-ui/types';
// Utils
import { cn, formatPrice } from '@janbox/storefront-ui/utils';
// Constants
import { HTMLDatasetAttributes } from '@janbox/storefront-ui/constants';Size Variants
Hầu hết components hỗ trợ 4 size variants:
<Button size="xs">Extra Small</Button>
<Button size="sm">Small</Button>
<Button size="md">Medium (default)</Button>
<Button size="lg">Large</Button>Size mapping:
| size | iconSize | inputSize | inputPaddingX | textVariant |
| ---- | -------- | --------- | ------------- | ----------- |
| xs | 20px | 24px | 8px | xs |
| sm | 20px | 32px | 12px | sm |
| md | 24px | 40px | 12px | sm |
| lg | 24px | 48px | 16px | base |
Color Variants
<Button color="primary">Primary</Button>
<Button color="secondary">Secondary</Button>
<Button color="green">Success</Button>
<Button color="red">Error</Button>
<Button color="orange">Warning</Button>
<Button color="blue">Info</Button>
<Button color="neutral">Neutral</Button>sx Prop — Inline Styling
Mọi component đều hỗ trợ sx prop để override styles:
<Button
sx={{
borderRadius: 8,
fontWeight: 600,
md: { fontSize: 16 },
lg: { fontSize: 18 },
}}
>
Custom Button
</Button>Theming
ThemeProvider
Wrap root app với ThemeProvider để inject design tokens:
import { ThemeProvider } from '@janbox/storefront-ui/theme';
function App() {
return (
<ThemeProvider>
<YourApp />
</ThemeProvider>
);
}Design Tokens
Design tokens được expose qua CSS variables:
/* Colors */
--color-primary-50
--color-primary-100
...
--color-primary-900
/* Typography */
--typography-xs-font-size
--typography-xs-line-height
...
--typography-6xl-font-size
--typography-6xl-line-heightAccessing Tokens in JS
import { getColorVar, getTypographyVar, breakpoint } from '@janbox/storefront-ui/theme';
// Get color CSS variable
const primaryColor = getColorVar('primary.600'); // 'var(--color-primary-600)'
// Get typography values
const { fontSize, lineHeight } = getTypographyVar('lg');
// Get breakpoint values
console.log(breakpoint.md); // 1280Development
Prerequisites
- Node.js ≥ 18
- pnpm ≥ 8
Setup
# Clone repo
git clone <repo-url>
cd storefront-ui
# Install dependencies
pnpm install
# Start Storybook dev server
pnpm storybookStorybook sẽ chạy tại http://localhost:6006
Development Commands
| Command | Description |
| ---------------------- | ------------------------ |
| pnpm storybook | Dev server (port 6006) |
| pnpm build | Build thư viện → dist/ |
| pnpm dev | Build với watch mode |
| pnpm type-check | TypeScript type checking |
| pnpm lint | ESLint |
| pnpm build-storybook | Build Storybook static |
Creating New Components
Tham khảo CLAUDE.md để hiểu đầy đủ về component architecture và conventions.
Quick steps:
- Tạo folder
src/lib/<component>/ - Tạo files:
types.ts,helpers.ts,<component>.tsx,<component>.stories.tsx,index.ts - Implement theo pattern chuẩn (xem CLAUDE.md section 4)
- Export trong
src/lib/index.ts - Viết Storybook stories
Pattern template:
// types.ts
export type XxxProps = PropsWithSx<
ShallowMerge<
React.HTMLAttributes<HTMLDivElement>,
ToResponsiveProps<XxxResponsiveProps> & {
children?: React.ReactNode;
}
>
>;
// helpers.ts
const defaultProps = { size: 'md' as const } satisfies Partial<XxxProps>;
export const getXxxProps = (p: XxxProps) => mergeComponentProps(defaultProps, p);
// xxx.tsx
export const Xxx = ({ ref, ..._props }: XxxProps) => {
const { size, children, ...rest } = getXxxProps(_props);
return (
<Primitive ref={ref} {...rest}>
{children}
</Primitive>
);
};Component Documentation
Mọi component đều có README riêng với API reference, examples, và usage notes.
- Accordion
- Avatar
- Badge
- Box
- Breadcrumbs
- Button
- Cascader
- Cell
- Checkbox
- Chip
- Collapse
- Container
- CountUp
- CountdownTimer
- DatePicker
- Dialog
- Drawer
- FilterPanel
- Flag
- Flexbox
- Floating
- FormHelperText
- FormLabel
- Grid
- HighlightWords
- Icon
- IconButton
- Image
- Input
- InputMask
- InputNumber
- InputRange
- Lightbox
- LinearProgress
- Link
- Loading
- Marquee
- Menu
- MultipleSelect
- NavLink
- Notifications
- OtpInput
- Pagination
- PhoneInput
- Popover
- PriceLabel
- Primitive
- Progress
- RadioButton
- RippleEffect
- Select
- StarRating
- Stepper
- SuspenseQuery
- Swiper
- Switch
- Table
- Tabs
- Text
- Textarea
- TimePicker
- Tooltip
- UnorderedList
- Video
API Reference
Documentation
- Storybook: Chạy
pnpm storybookđể xem interactive docs - CLAUDE.md: Convention guide đầy đủ cho AI agents và developers
- TypeScript: Mọi component đều có JSDoc comments và type definitions
Key Types
// Size variants
type SizeVariant = 'xs' | 'sm' | 'md' | 'lg';
// Color variants
type ColorScale = 'primary' | 'secondary' | 'green' | 'red' | 'orange' | 'blue' | 'neutral' | 'yellow' | 'violet';
// Button variants
type ButtonVariant = 'contained' | 'outlined' | 'text';
// Responsive props wrapper
type ToResponsiveProps<T> = T & Partial<Record<'sm' | 'md' | 'lg', T>>;
// sx prop type
type PropsWithSx<T> = T & { sx?: ToResponsiveProps<StyledCSS> };
// Polymorphic component props
type PrimitiveProps<Props, ElementType> = Props & { as?: ElementType };Hooks
// Window screen detection
useWindowScreen(): Screen;
// Controllable state pattern
useControllableState<T>(value, defaultValue, onChange): [T, Dispatch<T>];
// Countdown timer
useCountdownTimer(target, options): CountdownState;
// Query params sync
useQueryParams<T>(key, defaultValue): [T, (value: T) => void];
// First mount detection
useFirstMountState(): boolean;
// Update effect (skip first mount)
useUpdateEffect(effect, deps);
// Deep compare effect
useDeepCompareEffect(effect, deps);
// Formatters
useFormatter(): FormatterUtils;AI Agent Guidelines
Thư viện này được thiết kế để AI agents có thể làm việc hiệu quả. Dưới đây là các nguyên tắc quan trọng:
1. Convention nhất quán
- Mọi component tuân thủ cùng một pattern:
types.ts→helpers.ts→<component>.tsx→index.ts - Props destructuring:
({ ref, ..._props })→getXxxProps(_props)→ destructure - Default props luôn ở
helpers.ts, không trong component - CSS helpers luôn có prefix
getXxxCssBy...
2. Type-first approach
- Mọi component có TypeScript types đầy đủ
- Sử dụng generic types:
PropsWithSx,ToResponsiveProps,ShallowMerge - Props type luôn export cùng với component
3. File naming
- Kebab-case cho tất cả files:
input-number.tsx,ripple-effect.tsx - PascalCase cho component exports:
export const InputNumber = ...
4. Import patterns
// ✅ Preferred — từ main entry
import { Button, Input } from '@janbox/storefront-ui';
// ✅ Subpath exports
import { getColorVar } from '@janbox/storefront-ui/theme';
import { cn } from '@janbox/storefront-ui/utils';
// ❌ Avoid — deep imports
import { Button } from '@janbox/storefront-ui/lib/button';5. lodash-es requirement
// ✅ Tree-shakeable
import { isNil, debounce } from 'lodash-es';
// ❌ Non-tree-shakeable
import { isNil } from 'lodash';6. Emotion CSS pragma
Mọi file .tsx sử dụng css={} prop phải có:
/** @jsxImportSource @emotion/react */Lưu ý: Nếu component chỉ dùng <Primitive> + sx prop, không cần pragma này.
7. Responsive props pattern
// Base (xs) — mobile-first
<Box display="flex" />
// Tablet breakpoint (sm: 768px)
<Box display="flex" sm={{ display: 'grid' }} />
// Desktop breakpoint (md: 1280px)
<Box display="flex" md={{ flexDirection: 'row' }} />
// Large desktop breakpoint (lg: 1680px)
<Box display="flex" lg={{ gap: 32 }} />8. Testing trong Storybook
- Mọi component mới bắt buộc có
.stories.tsx - Storybook là môi trường dev/test chính
- Chưa có unit test runner (Jest/Vitest)
License & Contributing
License
Private package — chỉ dùng nội bộ Janbox.
Contributing
- Đọc kỹ CLAUDE.md trước khi contribute
- Mọi component mới phải tuân thủ conventions trong CLAUDE.md
- Bắt buộc có Storybook stories
- Type-check và lint phải pass:
pnpm type-check && pnpm lint - Tạo PR, không commit trực tiếp lên
main
Workflow
# 1. Create feature branch
git checkout -b feat/my-component
# 2. Develop với Storybook
pnpm storybook
# 3. Type-check
pnpm type-check
# 4. Lint
pnpm lint
# 5. Build
pnpm build
# 6. Commit & push
git add .
git commit -m "feat: add MyComponent"
git push origin feat/my-component
# 7. Create PRSupport
Để được hỗ trợ hoặc báo lỗi, liên hệ team Janbox qua internal channels.
Phiên bản: v2.0.29
Last updated: 2026-06-02
