184 lines
5.2 KiB
TypeScript
184 lines
5.2 KiB
TypeScript
import React from 'react';
|
|
import ReactDOM from 'react-dom/client';
|
|
import { BrowserRouter } from 'react-router-dom';
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
import { ConfigProvider, type ThemeConfig, message } from 'antd';
|
|
import koKR from 'antd/locale/ko_KR';
|
|
import 'dayjs/locale/ko';
|
|
import {
|
|
COLOR_PRIMARY, COLOR_PRIMARY_LIGHT,
|
|
COLOR_SUCCESS, COLOR_WARNING, COLOR_ERROR, COLOR_INFO,
|
|
GRAY, FONT_FAMILY, FONT_SIZE, RADIUS, SPACING, LAYOUT,
|
|
} from './theme/tokens';
|
|
import App from './App';
|
|
import './index.css';
|
|
|
|
// 토스트 메시지 전역 설정 (top-right, 최대 5개, 3초)
|
|
message.config({ top: 16, maxCount: 5, duration: 3 });
|
|
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: { queries: { retry: 1, staleTime: 30_000 } },
|
|
});
|
|
|
|
const theme: ThemeConfig = {
|
|
token: {
|
|
// ── 브랜드 — 토스 블루 ──
|
|
colorPrimary: COLOR_PRIMARY,
|
|
colorPrimaryBg: COLOR_PRIMARY_LIGHT,
|
|
colorSuccess: COLOR_SUCCESS,
|
|
colorWarning: COLOR_WARNING,
|
|
colorError: COLOR_ERROR,
|
|
colorInfo: COLOR_INFO,
|
|
|
|
// ── 배경 ──
|
|
colorBgContainer: '#ffffff',
|
|
colorBgLayout: GRAY[50],
|
|
colorBgElevated: '#ffffff',
|
|
colorBgSpotlight: GRAY[100],
|
|
|
|
// ── 텍스트 — 토스 단계 ──
|
|
colorText: GRAY[800],
|
|
colorTextSecondary: GRAY[600],
|
|
colorTextTertiary: GRAY[500],
|
|
colorTextDisabled: GRAY[400],
|
|
|
|
// ── 테두리 — 거의 보이지 않게 ──
|
|
colorBorder: GRAY[200],
|
|
colorBorderSecondary: GRAY[100],
|
|
|
|
// ── 폰트 ──
|
|
fontFamily: FONT_FAMILY,
|
|
fontSize: FONT_SIZE.base, // 15px
|
|
fontSizeHeading1: FONT_SIZE['2xl'], // 32px
|
|
fontSizeHeading2: FONT_SIZE.xl, // 24px
|
|
fontSizeHeading3: FONT_SIZE.lg, // 20px
|
|
fontSizeHeading4: FONT_SIZE.md, // 16px
|
|
fontSizeHeading5: FONT_SIZE.base, // 15px
|
|
|
|
// ── Border Radius — 토스 비율 ──
|
|
borderRadius: RADIUS.md, // 12 (버튼, 입력)
|
|
borderRadiusLG: RADIUS.lg, // 16 (카드)
|
|
borderRadiusSM: RADIUS.sm, // 8
|
|
borderRadiusXS: 4,
|
|
|
|
// ── 공간 ──
|
|
padding: SPACING[4],
|
|
paddingLG: SPACING[6],
|
|
paddingSM: SPACING[2],
|
|
paddingXS: SPACING[1],
|
|
margin: SPACING[4],
|
|
marginLG: SPACING[6],
|
|
marginSM: SPACING[2],
|
|
|
|
// ── 컨트롤 높이 — 토스는 큼직하게 ──
|
|
controlHeight: 40, // 기본 입력/버튼
|
|
controlHeightSM: 32,
|
|
controlHeightLG: 48, // large 입력/버튼
|
|
|
|
// ── 그림자 — 옅은 합성 ──
|
|
boxShadow: '0 1px 3px rgba(0,0,0,0.04), 0 4px 16px rgba(0,0,0,0.04)',
|
|
boxShadowSecondary: '0 2px 8px rgba(0,0,0,0.06), 0 8px 24px rgba(0,0,0,0.06)',
|
|
},
|
|
|
|
components: {
|
|
// ── 메뉴 ──
|
|
Menu: {
|
|
itemHeight: 44,
|
|
itemMarginInline: 8,
|
|
itemBorderRadius: RADIUS.sm,
|
|
subMenuItemBg: 'transparent',
|
|
itemSelectedBg: COLOR_PRIMARY_LIGHT,
|
|
itemSelectedColor: COLOR_PRIMARY,
|
|
itemHoverBg: GRAY[50],
|
|
itemHoverColor: GRAY[800],
|
|
itemColor: GRAY[600],
|
|
},
|
|
|
|
// ── 테이블 ──
|
|
Table: {
|
|
headerBg: GRAY[50],
|
|
rowHoverBg: GRAY[50],
|
|
cellPaddingBlock: 14,
|
|
cellPaddingInline: 16,
|
|
headerSplitColor: GRAY[200],
|
|
},
|
|
|
|
// ── 버튼 — 토스 CTA 스타일 ──
|
|
Button: {
|
|
paddingInline: 20,
|
|
fontWeight: 600,
|
|
borderRadius: RADIUS.md,
|
|
primaryShadow: 'none',
|
|
defaultShadow: 'none',
|
|
dangerShadow: 'none',
|
|
},
|
|
|
|
// ── 입력 — 토스 filled 느낌 ──
|
|
Input: {
|
|
paddingInline: 12,
|
|
paddingBlock: 12,
|
|
borderRadius: RADIUS.md,
|
|
hoverBorderColor: COLOR_PRIMARY,
|
|
},
|
|
|
|
// ── 카드 ──
|
|
Card: {
|
|
bodyPadding: SPACING[6], // 24px
|
|
headerBg: '#ffffff',
|
|
borderRadius: RADIUS.lg, // 16px
|
|
},
|
|
|
|
// ── 레이아웃 ──
|
|
Layout: {
|
|
headerHeight: LAYOUT.headerHeight,
|
|
headerBg: '#ffffff',
|
|
siderBg: '#ffffff',
|
|
bodyBg: GRAY[50],
|
|
},
|
|
|
|
// ── 폼 ──
|
|
Form: {
|
|
itemMarginBottom: 20,
|
|
labelFontSize: 13,
|
|
labelColor: GRAY[600],
|
|
},
|
|
|
|
// ── 배지 ──
|
|
Badge: {
|
|
textFontSize: 11,
|
|
},
|
|
|
|
// ── 태그/칩 ──
|
|
Tag: {
|
|
borderRadius: RADIUS.full,
|
|
},
|
|
|
|
// ── Select ──
|
|
Select: {
|
|
borderRadius: RADIUS.md,
|
|
},
|
|
|
|
// ── DatePicker ──
|
|
DatePicker: {
|
|
borderRadius: RADIUS.md,
|
|
},
|
|
|
|
// ── 모달 ──
|
|
Modal: {
|
|
borderRadius: RADIUS.xl, // 20px
|
|
},
|
|
},
|
|
};
|
|
|
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
<React.StrictMode>
|
|
<ConfigProvider locale={koKR} theme={theme}>
|
|
<QueryClientProvider client={queryClient}>
|
|
<BrowserRouter>
|
|
<App />
|
|
</BrowserRouter>
|
|
</QueryClientProvider>
|
|
</ConfigProvider>
|
|
</React.StrictMode>,
|
|
);
|