@hua-labs/hua-ux 0.1.0-alpha.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/README.md +839 -0
  2. package/dist/framework/a11y/components/LiveRegion.d.ts +64 -0
  3. package/dist/framework/a11y/components/LiveRegion.d.ts.map +1 -0
  4. package/dist/framework/a11y/components/LiveRegion.js +43 -0
  5. package/dist/framework/a11y/components/SkipToContent.d.ts +62 -0
  6. package/dist/framework/a11y/components/SkipToContent.d.ts.map +1 -0
  7. package/dist/framework/a11y/components/SkipToContent.js +60 -0
  8. package/dist/framework/a11y/hooks/useFocusManagement.d.ts +60 -0
  9. package/dist/framework/a11y/hooks/useFocusManagement.d.ts.map +1 -0
  10. package/dist/framework/a11y/hooks/useFocusManagement.js +71 -0
  11. package/dist/framework/a11y/hooks/useFocusTrap.d.ts +64 -0
  12. package/dist/framework/a11y/hooks/useFocusTrap.d.ts.map +1 -0
  13. package/dist/framework/a11y/hooks/useFocusTrap.js +185 -0
  14. package/dist/framework/a11y/hooks/useLiveRegion.d.ts +56 -0
  15. package/dist/framework/a11y/hooks/useLiveRegion.d.ts.map +1 -0
  16. package/dist/framework/a11y/hooks/useLiveRegion.js +60 -0
  17. package/dist/framework/a11y/index.d.ts +16 -0
  18. package/dist/framework/a11y/index.d.ts.map +1 -0
  19. package/dist/framework/a11y/index.js +11 -0
  20. package/dist/framework/branding/context.d.ts +52 -0
  21. package/dist/framework/branding/context.d.ts.map +1 -0
  22. package/dist/framework/branding/context.js +96 -0
  23. package/dist/framework/branding/css-vars.d.ts +34 -0
  24. package/dist/framework/branding/css-vars.d.ts.map +1 -0
  25. package/dist/framework/branding/css-vars.js +95 -0
  26. package/dist/framework/branding/tailwind-config.d.ts +38 -0
  27. package/dist/framework/branding/tailwind-config.d.ts.map +1 -0
  28. package/dist/framework/branding/tailwind-config.js +66 -0
  29. package/dist/framework/components/BrandedButton.d.ts +53 -0
  30. package/dist/framework/components/BrandedButton.d.ts.map +1 -0
  31. package/dist/framework/components/BrandedButton.js +40 -0
  32. package/dist/framework/components/BrandedCard.d.ts +52 -0
  33. package/dist/framework/components/BrandedCard.d.ts.map +1 -0
  34. package/dist/framework/components/BrandedCard.js +73 -0
  35. package/dist/framework/components/ErrorBoundary.d.ts +92 -0
  36. package/dist/framework/components/ErrorBoundary.d.ts.map +1 -0
  37. package/dist/framework/components/ErrorBoundary.js +121 -0
  38. package/dist/framework/components/HuaUxLayout.d.ts +29 -0
  39. package/dist/framework/components/HuaUxLayout.d.ts.map +1 -0
  40. package/dist/framework/components/HuaUxLayout.js +32 -0
  41. package/dist/framework/components/HuaUxPage.d.ts +48 -0
  42. package/dist/framework/components/HuaUxPage.d.ts.map +1 -0
  43. package/dist/framework/components/HuaUxPage.js +105 -0
  44. package/dist/framework/components/Providers.d.ts +17 -0
  45. package/dist/framework/components/Providers.d.ts.map +1 -0
  46. package/dist/framework/components/Providers.js +72 -0
  47. package/dist/framework/components/WelcomePage.d.ts +44 -0
  48. package/dist/framework/components/WelcomePage.d.ts.map +1 -0
  49. package/dist/framework/components/WelcomePage.js +80 -0
  50. package/dist/framework/config/index.d.ts +182 -0
  51. package/dist/framework/config/index.d.ts.map +1 -0
  52. package/dist/framework/config/index.js +329 -0
  53. package/dist/framework/config/merge.d.ts +26 -0
  54. package/dist/framework/config/merge.d.ts.map +1 -0
  55. package/dist/framework/config/merge.js +160 -0
  56. package/dist/framework/config/schema.d.ts +25 -0
  57. package/dist/framework/config/schema.d.ts.map +1 -0
  58. package/dist/framework/config/schema.js +122 -0
  59. package/dist/framework/hooks/useMotion.d.ts +45 -0
  60. package/dist/framework/hooks/useMotion.d.ts.map +1 -0
  61. package/dist/framework/hooks/useMotion.js +40 -0
  62. package/dist/framework/index.d.ts +37 -0
  63. package/dist/framework/index.d.ts.map +1 -0
  64. package/dist/framework/index.js +42 -0
  65. package/dist/framework/license/errors.d.ts +15 -0
  66. package/dist/framework/license/errors.d.ts.map +1 -0
  67. package/dist/framework/license/errors.js +52 -0
  68. package/dist/framework/license/index.d.ts +70 -0
  69. package/dist/framework/license/index.d.ts.map +1 -0
  70. package/dist/framework/license/index.js +124 -0
  71. package/dist/framework/license/loader.d.ts +26 -0
  72. package/dist/framework/license/loader.d.ts.map +1 -0
  73. package/dist/framework/license/loader.js +137 -0
  74. package/dist/framework/license/types.d.ts +67 -0
  75. package/dist/framework/license/types.d.ts.map +1 -0
  76. package/dist/framework/license/types.js +18 -0
  77. package/dist/framework/loading/components/SkeletonGroup.d.ts +44 -0
  78. package/dist/framework/loading/components/SkeletonGroup.d.ts.map +1 -0
  79. package/dist/framework/loading/components/SkeletonGroup.js +34 -0
  80. package/dist/framework/loading/components/SuspenseWrapper.d.ts +58 -0
  81. package/dist/framework/loading/components/SuspenseWrapper.d.ts.map +1 -0
  82. package/dist/framework/loading/components/SuspenseWrapper.js +40 -0
  83. package/dist/framework/loading/hoc/withSuspense.d.ts +46 -0
  84. package/dist/framework/loading/hoc/withSuspense.d.ts.map +1 -0
  85. package/dist/framework/loading/hoc/withSuspense.js +54 -0
  86. package/dist/framework/loading/hooks/useDelayedLoading.d.ts +56 -0
  87. package/dist/framework/loading/hooks/useDelayedLoading.d.ts.map +1 -0
  88. package/dist/framework/loading/hooks/useDelayedLoading.js +97 -0
  89. package/dist/framework/loading/hooks/useLoadingState.d.ts +69 -0
  90. package/dist/framework/loading/hooks/useLoadingState.d.ts.map +1 -0
  91. package/dist/framework/loading/hooks/useLoadingState.js +59 -0
  92. package/dist/framework/loading/index.d.ts +16 -0
  93. package/dist/framework/loading/index.d.ts.map +1 -0
  94. package/dist/framework/loading/index.js +13 -0
  95. package/dist/framework/middleware/i18n.d.ts +90 -0
  96. package/dist/framework/middleware/i18n.d.ts.map +1 -0
  97. package/dist/framework/middleware/i18n.js +99 -0
  98. package/dist/framework/plugins/index.d.ts +8 -0
  99. package/dist/framework/plugins/index.d.ts.map +1 -0
  100. package/dist/framework/plugins/index.js +6 -0
  101. package/dist/framework/plugins/registry.d.ts +95 -0
  102. package/dist/framework/plugins/registry.d.ts.map +1 -0
  103. package/dist/framework/plugins/registry.js +160 -0
  104. package/dist/framework/plugins/types.d.ts +97 -0
  105. package/dist/framework/plugins/types.d.ts.map +1 -0
  106. package/dist/framework/plugins/types.js +6 -0
  107. package/dist/framework/seo/geo/examples.d.ts +87 -0
  108. package/dist/framework/seo/geo/examples.d.ts.map +1 -0
  109. package/dist/framework/seo/geo/examples.js +295 -0
  110. package/dist/framework/seo/geo/generateGEOMetadata.d.ts +107 -0
  111. package/dist/framework/seo/geo/generateGEOMetadata.d.ts.map +1 -0
  112. package/dist/framework/seo/geo/generateGEOMetadata.js +404 -0
  113. package/dist/framework/seo/geo/index.d.ts +19 -0
  114. package/dist/framework/seo/geo/index.d.ts.map +1 -0
  115. package/dist/framework/seo/geo/index.js +21 -0
  116. package/dist/framework/seo/geo/presets.d.ts +52 -0
  117. package/dist/framework/seo/geo/presets.d.ts.map +1 -0
  118. package/dist/framework/seo/geo/presets.js +47 -0
  119. package/dist/framework/seo/geo/structuredData.d.ts +187 -0
  120. package/dist/framework/seo/geo/structuredData.d.ts.map +1 -0
  121. package/dist/framework/seo/geo/structuredData.js +354 -0
  122. package/dist/framework/seo/geo/test-utils.d.ts +78 -0
  123. package/dist/framework/seo/geo/test-utils.d.ts.map +1 -0
  124. package/dist/framework/seo/geo/test-utils.js +139 -0
  125. package/dist/framework/seo/geo/types.d.ts +225 -0
  126. package/dist/framework/seo/geo/types.d.ts.map +1 -0
  127. package/dist/framework/seo/geo/types.js +51 -0
  128. package/dist/framework/types/index.d.ts +577 -0
  129. package/dist/framework/types/index.d.ts.map +1 -0
  130. package/dist/framework/types/index.js +6 -0
  131. package/dist/framework/utils/data-fetching.d.ts +45 -0
  132. package/dist/framework/utils/data-fetching.d.ts.map +1 -0
  133. package/dist/framework/utils/data-fetching.js +74 -0
  134. package/dist/framework/utils/file-structure.d.ts +29 -0
  135. package/dist/framework/utils/file-structure.d.ts.map +1 -0
  136. package/dist/framework/utils/file-structure.js +72 -0
  137. package/dist/framework/utils/metadata.d.ts +109 -0
  138. package/dist/framework/utils/metadata.d.ts.map +1 -0
  139. package/dist/framework/utils/metadata.js +105 -0
  140. package/dist/index.d.ts +15 -0
  141. package/dist/index.d.ts.map +1 -0
  142. package/dist/index.js +21 -0
  143. package/dist/presets/index.d.ts +8 -0
  144. package/dist/presets/index.d.ts.map +1 -0
  145. package/dist/presets/index.js +7 -0
  146. package/dist/presets/marketing.d.ts +41 -0
  147. package/dist/presets/marketing.d.ts.map +1 -0
  148. package/dist/presets/marketing.js +81 -0
  149. package/dist/presets/product.d.ts +41 -0
  150. package/dist/presets/product.d.ts.map +1 -0
  151. package/dist/presets/product.js +74 -0
  152. package/package.json +91 -0
  153. package/src/framework/README.md +329 -0
  154. package/src/framework/__tests__/branding/css-vars.test.ts +147 -0
  155. package/src/framework/__tests__/components/ErrorBoundary.test.tsx +146 -0
  156. package/src/framework/__tests__/config/defineConfig.test.ts +138 -0
  157. package/src/framework/__tests__/hooks/useMotion.test.ts +105 -0
  158. package/src/framework/__tests__/seo/geo/generateGEOMetadata.test.ts +207 -0
  159. package/src/framework/__tests__/seo/geo/structuredData.test.ts +262 -0
  160. package/src/framework/a11y/components/LiveRegion.tsx +89 -0
  161. package/src/framework/a11y/components/SkipToContent.tsx +103 -0
  162. package/src/framework/a11y/hooks/useFocusManagement.ts +125 -0
  163. package/src/framework/a11y/hooks/useFocusTrap.ts +239 -0
  164. package/src/framework/a11y/hooks/useLiveRegion.ts +95 -0
  165. package/src/framework/a11y/index.ts +17 -0
  166. package/src/framework/branding/context.tsx +135 -0
  167. package/src/framework/branding/css-vars.ts +110 -0
  168. package/src/framework/branding/tailwind-config.ts +90 -0
  169. package/src/framework/components/BrandedButton.tsx +94 -0
  170. package/src/framework/components/BrandedCard.tsx +87 -0
  171. package/src/framework/components/ErrorBoundary.tsx +215 -0
  172. package/src/framework/components/HuaUxLayout.tsx +36 -0
  173. package/src/framework/components/HuaUxPage.tsx +138 -0
  174. package/src/framework/components/Providers.tsx +98 -0
  175. package/src/framework/components/WelcomePage.tsx +207 -0
  176. package/src/framework/config/index.ts +349 -0
  177. package/src/framework/config/merge.ts +190 -0
  178. package/src/framework/config/schema.ts +140 -0
  179. package/src/framework/hooks/useMotion.ts +57 -0
  180. package/src/framework/index.ts +122 -0
  181. package/src/framework/license/errors.ts +63 -0
  182. package/src/framework/license/index.ts +137 -0
  183. package/src/framework/license/loader.ts +158 -0
  184. package/src/framework/license/types.ts +95 -0
  185. package/src/framework/loading/components/SkeletonGroup.tsx +70 -0
  186. package/src/framework/loading/components/SuspenseWrapper.tsx +88 -0
  187. package/src/framework/loading/hoc/withSuspense.tsx +96 -0
  188. package/src/framework/loading/hooks/useDelayedLoading.ts +127 -0
  189. package/src/framework/loading/hooks/useLoadingState.ts +103 -0
  190. package/src/framework/loading/index.ts +19 -0
  191. package/src/framework/middleware/i18n.ts +161 -0
  192. package/src/framework/middleware/index.ts +7 -0
  193. package/src/framework/plugins/index.ts +13 -0
  194. package/src/framework/plugins/registry.ts +186 -0
  195. package/src/framework/plugins/types.ts +106 -0
  196. package/src/framework/seo/geo/examples.tsx +415 -0
  197. package/src/framework/seo/geo/generateGEOMetadata.ts +441 -0
  198. package/src/framework/seo/geo/index.ts +61 -0
  199. package/src/framework/seo/geo/presets.ts +58 -0
  200. package/src/framework/seo/geo/structuredData.ts +422 -0
  201. package/src/framework/seo/geo/test-utils.ts +179 -0
  202. package/src/framework/seo/geo/types.ts +315 -0
  203. package/src/framework/types/index.ts +623 -0
  204. package/src/framework/utils/data-fetching.ts +95 -0
  205. package/src/framework/utils/file-structure.ts +88 -0
  206. package/src/framework/utils/metadata.ts +152 -0
  207. package/src/index.ts +31 -0
  208. package/src/presets/index.ts +8 -0
  209. package/src/presets/marketing.ts +88 -0
  210. package/src/presets/product.ts +81 -0
package/README.md ADDED
@@ -0,0 +1,839 @@
1
+ # @hua-labs/hua-ux
2
+
3
+ **Ship UX faster**: UI + motion + i18n, pre-wired.
4
+
5
+ A framework for React product teams that provides pre-wired UX defaults for spacing, components, motion, and internationalization.
6
+
7
+ ## 왜 hua-ux인가?
8
+
9
+ 프로덕트 팀이 매번 UI 컴포넌트, 모션 라이브러리, i18n 설정을 처음부터 구성하는 것은 시간 낭비입니다. **hua-ux**는 이 세 가지를 하나의 패키지로 통합하여, 5분 안에 프로덕트에 바로 적용할 수 있도록 설계되었습니다.
10
+
11
+ **핵심 가치:**
12
+ - ✅ **가볍고 바로 붙는다**: Framer Motion 대비 가볍고, Next.js에 바로 통합 가능
13
+ - ✅ **타입 안전**: TypeScript로 모든 것이 타입 안전하게 제공
14
+ - ✅ **SSR 지원**: Next.js App Router와 완벽하게 작동
15
+ - ✅ **통합 경험**: UI, Motion, i18n이 하나의 생태계에서 작동
16
+ - ✅ **에러 처리 자동화**: ErrorBoundary가 HuaUxPage에 기본 내장
17
+ - ✅ **접근성 우선**: WCAG 2.1 준수, 스크린 리더 지원, 키보드 탐색 최적화 (useFocusManagement, useFocusTrap, SkipToContent, LiveRegion)
18
+ - ✅ **로딩 UX 최적화**: 깜빡임 방지, Skeleton UI, Suspense 자동화 (useDelayedLoading, useLoadingState, SuspenseWrapper)
19
+
20
+ ## 5분 시작
21
+
22
+ ### 1. 설치
23
+
24
+ ```bash
25
+ pnpm add @hua-labs/hua-ux zustand
26
+ # or
27
+ npm install @hua-labs/hua-ux zustand
28
+ # or
29
+ yarn add @hua-labs/hua-ux zustand
30
+ ```
31
+
32
+ ### 2. 기본 설정
33
+
34
+ **두 가지 사용 방법이 있습니다:**
35
+
36
+ #### 방법 1: 프레임워크 레이어 사용 (권장) ⭐
37
+
38
+ 프레임워크 레이어를 사용하면 자동으로 모든 Provider가 설정됩니다:
39
+
40
+ ```tsx
41
+ // hua-ux.config.ts
42
+ import { defineConfig } from '@hua-labs/hua-ux/framework';
43
+
44
+ export default defineConfig({
45
+ preset: 'product',
46
+ i18n: {
47
+ defaultLanguage: 'ko',
48
+ supportedLanguages: ['ko', 'en'],
49
+ namespaces: ['common'],
50
+ translationLoader: 'api',
51
+ translationApiPath: '/api/translations',
52
+ },
53
+ });
54
+ ```
55
+
56
+ ```tsx
57
+ // app/layout.tsx
58
+ import { HuaUxLayout } from '@hua-labs/hua-ux/framework';
59
+
60
+ export default function RootLayout({ children }) {
61
+ return (
62
+ <html lang="ko">
63
+ <body>
64
+ <HuaUxLayout>{children}</HuaUxLayout>
65
+ </body>
66
+ </html>
67
+ );
68
+ }
69
+ ```
70
+
71
+ **장점**: 설정 파일만으로 모든 Provider 자동 설정, 간단함
72
+
73
+ #### 방법 2: 직접 사용 (세밀한 제어)
74
+
75
+ 더 세밀한 제어가 필요한 경우 직접 설정할 수 있습니다:
76
+
77
+ ```tsx
78
+ // store/useAppStore.ts
79
+ import { create } from 'zustand';
80
+ import { persist } from 'zustand/middleware';
81
+
82
+ interface AppState {
83
+ language: 'ko' | 'en';
84
+ setLanguage: (lang: 'ko' | 'en') => void;
85
+ }
86
+
87
+ export const useAppStore = create<AppState>()(
88
+ persist(
89
+ (set) => ({
90
+ language: 'ko',
91
+ setLanguage: (lang) => set({ language: lang }),
92
+ }),
93
+ {
94
+ name: 'app-storage',
95
+ partialize: (state) => ({ language: state.language }),
96
+ }
97
+ )
98
+ );
99
+ ```
100
+
101
+ ```tsx
102
+ // lib/i18n-setup.ts
103
+ import { createZustandI18n } from '@hua-labs/i18n-core-zustand';
104
+ import { createI18nStore } from '@hua-labs/state';
105
+ import { useAppStore } from '../store/useAppStore';
106
+
107
+ // createI18nStore로 언어 상태 관리 스토어 생성
108
+ const i18nStore = createI18nStore({
109
+ defaultLanguage: 'ko',
110
+ supportedLanguages: ['ko', 'en'],
111
+ persist: true,
112
+ ssr: true,
113
+ });
114
+
115
+ // createZustandI18n으로 i18n Provider 생성
116
+ export const I18nProvider = createZustandI18n(i18nStore, {
117
+ fallbackLanguage: 'en',
118
+ namespaces: ['common'],
119
+ translationLoader: 'api',
120
+ translationApiPath: '/api/translations',
121
+ defaultLanguage: 'ko',
122
+ });
123
+ ```
124
+
125
+ ```tsx
126
+ // app/layout.tsx
127
+ import { I18nProvider } from './lib/i18n-setup';
128
+
129
+ export default function RootLayout({ children }) {
130
+ return (
131
+ <html lang="ko">
132
+ <body>
133
+ <I18nProvider>{children}</I18nProvider>
134
+ </body>
135
+ </html>
136
+ );
137
+ }
138
+ ```
139
+
140
+ **장점**: 세밀한 제어 가능, 커스텀 설정 용이
141
+
142
+ **언제 사용하나요?**
143
+ - **프레임워크 레이어**: 빠른 시작, 표준 설정으로 충분한 경우
144
+ - **직접 사용**: 커스텀 Provider 조합, 특수한 요구사항이 있는 경우
145
+
146
+ ### 3. 사용하기
147
+
148
+ ```tsx
149
+ // app/page.tsx
150
+ 'use client';
151
+
152
+ import { Button, Card } from '@hua-labs/hua-ux';
153
+ import { useFadeIn, useSlideUp } from '@hua-labs/hua-ux';
154
+ import { useTranslation } from '@hua-labs/hua-ux';
155
+
156
+ export default function HomePage() {
157
+ const { t } = useTranslation();
158
+ const fadeInRef = useFadeIn();
159
+ const slideUpRef = useSlideUp();
160
+
161
+ return (
162
+ <div>
163
+ <Card ref={fadeInRef}>
164
+ <h1>{t('common:welcome')}</h1>
165
+ <Button ref={slideUpRef}>Get Started</Button>
166
+ </Card>
167
+ </div>
168
+ );
169
+ }
170
+ ```
171
+
172
+ ## Showcase
173
+
174
+ 라이브 데모를 확인하세요:
175
+
176
+ ```bash
177
+ cd apps/hua-ux-showcase
178
+ pnpm install
179
+ pnpm dev
180
+ ```
181
+
182
+ **Showcase 페이지**:
183
+ - `/` - 홈 (3개 Showcase 링크)
184
+ - `/ui` - UI 컴포넌트 데모
185
+ - `/motion` - Motion 훅 데모
186
+ - `/i18n` - 다국어 지원 데모
187
+
188
+ 또는 [Showcase App 소스 코드](../../apps/hua-ux-showcase)를 참고하세요.
189
+
190
+ ## 프로젝트 생성
191
+
192
+ 스캐폴딩 도구를 사용하여 새 프로젝트를 생성할 수 있습니다:
193
+
194
+ ```bash
195
+ pnpm create hua-ux my-app
196
+ cd my-app
197
+ pnpm install
198
+ pnpm dev
199
+ ```
200
+
201
+ 자세한 내용은 [create-hua-ux README](../create-hua-ux/README.md)를 참고하세요.
202
+
203
+ ## 패키지 구조
204
+
205
+ **hua-ux**는 다음 패키지들을 통합합니다:
206
+
207
+ - **`@hua-labs/ui`** - UI 컴포넌트 라이브러리
208
+ - Button, Card, Input, Modal 등 50+ 컴포넌트
209
+ - 일관된 스타일링 시스템
210
+ - 다크 모드 지원
211
+
212
+ - **`@hua-labs/motion-core`** - Motion 훅 라이브러리
213
+ - `useFadeIn`, `useSlideUp`, `useScaleIn` 등 기본 모션
214
+ - `useHoverMotion`, `useScrollReveal` 등 인터랙션
215
+ - 프리셋 시스템으로 빠른 설정
216
+
217
+ - **`@hua-labs/i18n-core`** - i18n 핵심 기능
218
+ - 타입 안전한 번역 시스템
219
+ - SSR/CSR 지원
220
+ - 네임스페이스 기반 번역 관리
221
+
222
+ - **`@hua-labs/i18n-core-zustand`** - Zustand 어댑터
223
+ - Zustand와 완벽한 통합
224
+ - 하이드레이션 에러 방지
225
+ - 언어 상태 자동 동기화
226
+
227
+ - **`@hua-labs/state`** - 통합 상태관리 (프레임워크 전용)
228
+ - Zustand 기반 상태관리
229
+ - SSR/Persistence 지원
230
+ - i18n 통합 스토어 제공
231
+
232
+ ## 서브패키지
233
+
234
+ ### `@hua-labs/hua-ux/framework`
235
+
236
+ 프레임워크 레이어 - Next.js를 감싸서 구조와 규칙을 강제하는 레이어
237
+
238
+ **주요 기능**:
239
+ - `HuaUxLayout`: 자동 프로바이더 설정
240
+ - `HuaUxPage`: 페이지 래퍼 (자동 모션)
241
+ - `defineConfig`: 타입 안전한 설정 시스템
242
+ - `useData`, `fetchData`: 데이터 페칭 유틸리티
243
+ - `createI18nMiddleware`: i18n 미들웨어 (Edge Runtime)
244
+
245
+ 자세한 내용은 [프레임워크 레이어 문서](./src/framework/README.md)를 참고하세요.
246
+
247
+ ### `@hua-labs/hua-ux/presets`
248
+
249
+ 사전 구성된 Presets
250
+
251
+ **제공되는 Presets**:
252
+ - `productPreset`: 제품 페이지용 (빠른 전환, 최소 딜레이)
253
+ - `marketingPreset`: 랜딩 페이지용 (드라마틱한 모션, 긴 딜레이)
254
+
255
+ ```tsx
256
+ import { productPreset, marketingPreset } from '@hua-labs/hua-ux/presets';
257
+ ```
258
+
259
+ ## 프레임워크 레이어 사용하기
260
+
261
+ 프레임워크 레이어를 사용하면 더 간단하게 설정할 수 있습니다:
262
+
263
+ ### 1. 설정 파일 생성
264
+
265
+ ```tsx
266
+ // hua-ux.config.ts
267
+ import { defineConfig } from '@hua-labs/hua-ux/framework';
268
+
269
+ export default defineConfig({
270
+ i18n: {
271
+ defaultLanguage: 'ko',
272
+ supportedLanguages: ['ko', 'en'],
273
+ namespaces: ['common'],
274
+ translationLoader: 'api',
275
+ translationApiPath: '/api/translations',
276
+ },
277
+ motion: {
278
+ defaultPreset: 'product',
279
+ enableAnimations: true,
280
+ },
281
+ state: {
282
+ persist: true,
283
+ ssr: true,
284
+ },
285
+ });
286
+ ```
287
+
288
+ **타입 안전성을 위한 명시적 import (권장)**:
289
+
290
+ 프로덕션 환경에서는 설정 파일을 명시적으로 import하여 타입 안전성을 보장하는 것을 권장합니다:
291
+
292
+ ```tsx
293
+ // app/layout.tsx 또는 초기화 파일
294
+ import config from '../hua-ux.config';
295
+ import { setConfig } from '@hua-labs/hua-ux/framework';
296
+
297
+ // 설정을 명시적으로 로드 (타입 안전성 보장)
298
+ setConfig(config);
299
+ ```
300
+
301
+ 이 방법을 사용하면:
302
+ - ✅ 타입 안전성 보장
303
+ - ✅ Next.js 빌드 경고 방지
304
+ - ✅ 런타임 에러 방지
305
+
306
+ ### 2. Layout 설정
307
+
308
+ ```tsx
309
+ // app/layout.tsx
310
+ import { HuaUxLayout } from '@hua-labs/hua-ux/framework';
311
+
312
+ export default function RootLayout({ children }) {
313
+ return (
314
+ <html lang="ko">
315
+ <body>
316
+ <HuaUxLayout>{children}</HuaUxLayout>
317
+ </body>
318
+ </html>
319
+ );
320
+ }
321
+ ```
322
+
323
+ ### 3. 페이지 사용
324
+
325
+ ```tsx
326
+ // app/page.tsx
327
+ import { HuaUxPage } from '@hua-labs/hua-ux/framework';
328
+
329
+ export default function HomePage() {
330
+ return (
331
+ <HuaUxPage title="Home" description="Welcome page">
332
+ <h1>Welcome</h1>
333
+ </HuaUxPage>
334
+ );
335
+ }
336
+ ```
337
+
338
+ 자세한 내용은 [프레임워크 레이어 문서](./src/framework/README.md)를 참고하세요.
339
+
340
+ ## 주요 기능
341
+
342
+ ### 🎯 통합 Motion Hook (성능 최적화)
343
+
344
+ **useMotion Hook** - 모든 motion hook을 통합하여 코드 가독성 및 유지보수성 향상:
345
+
346
+ ```tsx
347
+ import { useMotion } from '@hua-labs/hua-ux/framework';
348
+
349
+ const motion = useMotion({
350
+ type: 'fadeIn',
351
+ duration: 600,
352
+ autoStart: false,
353
+ });
354
+
355
+ return <div ref={motion.ref} style={motion.style}>Content</div>;
356
+ ```
357
+
358
+ **HuaUxPage에서 자동 사용** - 별도 설정 없이 자동으로 최적화된 motion 적용됩니다.
359
+
360
+ ### 🛡️ ErrorBoundary (에러 처리 자동화)
361
+
362
+ **HuaUxPage에 기본 내장** - 별도 설정 없이 모든 페이지에서 에러를 자동으로 캐치합니다.
363
+
364
+ **프로덕션 에러 리포팅 지원** - Sentry, LogRocket 등과 통합 가능:
365
+
366
+ ```ts
367
+ // 프로덕션 환경에서 에러 리포팅 설정
368
+ window.__ERROR_REPORTER__ = (error, errorInfo) => {
369
+ Sentry.captureException(error, {
370
+ contexts: { react: errorInfo },
371
+ });
372
+ };
373
+ ```
374
+
375
+ ```tsx
376
+ // 자동으로 ErrorBoundary가 적용됩니다
377
+ <HuaUxPage title="Home">
378
+ <MyComponent /> {/* 에러 발생 시 fallback UI 표시 */}
379
+ </HuaUxPage>
380
+ ```
381
+
382
+ **커스텀 fallback UI**:
383
+ ```tsx
384
+ <HuaUxPage
385
+ title="Home"
386
+ errorBoundaryFallback={(error, reset) => (
387
+ <div>
388
+ <h1>에러: {error.message}</h1>
389
+ <button onClick={reset}>다시 시도</button>
390
+ </div>
391
+ )}
392
+ >
393
+ <MyComponent />
394
+ </HuaUxPage>
395
+ ```
396
+
397
+ **독립적으로 사용** (HuaUxPage 외부):
398
+ ```tsx
399
+ import { ErrorBoundary } from '@hua-labs/hua-ux/framework';
400
+
401
+ <ErrorBoundary>
402
+ <MyComponent />
403
+ </ErrorBoundary>
404
+ ```
405
+
406
+ ### 🎨 브랜딩 (White Labeling)
407
+
408
+ **SSR 지원 CSS 변수 주입** - 서버 사이드에서도 브랜딩 CSS 변수가 즉시 적용되어 FOUC를 방지합니다:
409
+
410
+ ```tsx
411
+ // hua-ux.config.ts
412
+ export default defineConfig({
413
+ branding: {
414
+ colors: {
415
+ primary: '#3B82F6',
416
+ secondary: '#8B5CF6',
417
+ },
418
+ },
419
+ });
420
+ ```
421
+
422
+ 브랜딩 설정을 하면 모든 컴포넌트에 자동으로 적용됩니다.
423
+
424
+ ### 🤖 GEO (Generative Engine Optimization)
425
+
426
+ **AI 검색 엔진 최적화** - ChatGPT, Claude, Gemini, Perplexity가 당신의 소프트웨어를 잘 찾고 추천하도록 최적화:
427
+
428
+ #### 기본 사용법
429
+
430
+ ```tsx
431
+ import { generateGEOMetadata, renderJSONLD } from '@hua-labs/hua-ux/framework';
432
+ import Script from 'next/script';
433
+
434
+ // GEO 메타데이터 생성
435
+ const geoMeta = generateGEOMetadata({
436
+ name: 'My App',
437
+ description: 'Built with hua-ux framework',
438
+ version: '1.0.0',
439
+ applicationCategory: ['UX Framework', 'Developer Tool'],
440
+ programmingLanguage: ['TypeScript', 'React', 'Next.js'],
441
+ features: ['i18n', 'Motion', 'Accessibility'],
442
+ useCases: ['Multilingual apps', 'Accessible UX'],
443
+ keywords: ['nextjs', 'react', 'ux', 'i18n'],
444
+ codeRepository: 'https://github.com/your-org/your-app',
445
+ license: 'MIT',
446
+ });
447
+
448
+ // Next.js metadata와 통합
449
+ export const metadata = {
450
+ title: 'My App',
451
+ description: geoMeta.meta.find(m => m.name === 'description')?.content,
452
+ };
453
+
454
+ // JSON-LD 추가
455
+ export default function Page() {
456
+ return (
457
+ <>
458
+ <Script {...renderJSONLD(geoMeta.jsonLd[0])} />
459
+ <main>...</main>
460
+ </>
461
+ );
462
+ }
463
+ ```
464
+
465
+ #### Layout에서 사용 (앱 전체)
466
+
467
+ ```tsx
468
+ // app/layout.tsx
469
+ import { generateGEOMetadata, renderJSONLD } from '@hua-labs/hua-ux/framework';
470
+ import Script from 'next/script';
471
+
472
+ const appGeoMeta = generateGEOMetadata({
473
+ name: 'My App',
474
+ description: 'My amazing application',
475
+ // ... 앱 전체 설정
476
+ });
477
+
478
+ export const metadata = {
479
+ title: appGeoMeta.meta.find(m => m.name === 'description')?.content,
480
+ };
481
+
482
+ export default function RootLayout({ children }) {
483
+ return (
484
+ <html>
485
+ <head>
486
+ <Script {...renderJSONLD(appGeoMeta.jsonLd[0])} />
487
+ </head>
488
+ <body>{children}</body>
489
+ </html>
490
+ );
491
+ }
492
+ ```
493
+
494
+ #### FAQ, HowTo, TechArticle 구조화된 데이터
495
+
496
+ ```tsx
497
+ import { generateFAQPageLD, generateHowToLD, generateTechArticleLD } from '@hua-labs/hua-ux/framework';
498
+
499
+ // FAQ 페이지
500
+ const faqLD = generateFAQPageLD([
501
+ { question: 'What is hua-ux?', answer: 'A UX framework for Next.js' },
502
+ ]);
503
+
504
+ // 튜토리얼 페이지
505
+ const howToLD = generateHowToLD({
506
+ name: 'How to get started',
507
+ steps: [
508
+ { name: 'Install', text: 'Run: pnpm create hua-ux my-app' },
509
+ { name: 'Configure', text: 'Edit hua-ux.config.ts' },
510
+ ],
511
+ });
512
+
513
+ // 기술 문서
514
+ const articleLD = generateTechArticleLD({
515
+ headline: 'Getting Started with hua-ux',
516
+ datePublished: '2025-12-29',
517
+ author: { name: 'hua-labs' },
518
+ });
519
+ ```
520
+
521
+ ### ♿ 접근성 (Accessibility)
522
+
523
+ WCAG 2.1 준수를 위한 완벽한 도구 세트를 제공합니다.
524
+
525
+ #### 1. Skip to Content (네비게이션 건너뛰기)
526
+
527
+ 키보드 사용자를 위한 필수 기능 - Tab 키로 메인 콘텐츠로 바로 이동:
528
+
529
+ ```tsx
530
+ // app/layout.tsx
531
+ import { SkipToContent } from '@hua-labs/hua-ux/framework';
532
+
533
+ export default function RootLayout({ children }) {
534
+ return (
535
+ <html>
536
+ <body>
537
+ <SkipToContent />
538
+ <nav>{/* navigation */}</nav>
539
+ <main id="main-content" tabIndex={-1}>
540
+ {children}
541
+ </main>
542
+ </body>
543
+ </html>
544
+ );
545
+ }
546
+ ```
547
+
548
+ #### 2. Focus Management (포커스 관리)
549
+
550
+ 페이지 전환 시 자동으로 메인 콘텐츠에 포커스:
551
+
552
+ ```tsx
553
+ import { useFocusManagement } from '@hua-labs/hua-ux/framework';
554
+
555
+ function MyPage() {
556
+ const mainRef = useFocusManagement({ autoFocus: true });
557
+
558
+ return (
559
+ <main ref={mainRef} tabIndex={-1}>
560
+ <h1>Page Title</h1>
561
+ </main>
562
+ );
563
+ }
564
+ ```
565
+
566
+ **모달/드로어용 Focus Trap**:
567
+ ```tsx
568
+ import { useFocusTrap } from '@hua-labs/hua-ux/framework';
569
+
570
+ function Modal({ isOpen, onClose }) {
571
+ const modalRef = useFocusTrap({ isActive: isOpen, onEscape: onClose });
572
+
573
+ return (
574
+ <div ref={modalRef} role="dialog" aria-modal="true">
575
+ <button>Close</button>
576
+ </div>
577
+ );
578
+ }
579
+ ```
580
+
581
+ #### 3. Live Region (스크린 리더 알림)
582
+
583
+ 동적 상태 변화를 스크린 리더 사용자에게 알림:
584
+
585
+ ```tsx
586
+ import { LiveRegion, useLiveRegion } from '@hua-labs/hua-ux/framework';
587
+
588
+ // 선언적 사용
589
+ function MyForm() {
590
+ const [message, setMessage] = useState('');
591
+
592
+ const handleSubmit = async () => {
593
+ setMessage('저장 중...');
594
+ await saveData();
595
+ setMessage('저장되었습니다!');
596
+ };
597
+
598
+ return (
599
+ <div>
600
+ <form onSubmit={handleSubmit}>{/* fields */}</form>
601
+ <LiveRegion message={message} />
602
+ </div>
603
+ );
604
+ }
605
+
606
+ // Hook 사용 (프로그래밍 방식)
607
+ function MyComponent() {
608
+ const { announce, LiveRegionComponent } = useLiveRegion();
609
+
610
+ const handleClick = () => {
611
+ announce('버튼이 클릭되었습니다');
612
+ };
613
+
614
+ return (
615
+ <div>
616
+ <button onClick={handleClick}>Click me</button>
617
+ {LiveRegionComponent}
618
+ </div>
619
+ );
620
+ }
621
+ ```
622
+
623
+ ### ⏳ 로딩 상태 최적화 (Loading State)
624
+
625
+ 깜빡임 없는 부드러운 로딩 경험을 제공합니다.
626
+
627
+ #### 1. useDelayedLoading (깜빡임 방지)
628
+
629
+ **문제**: 빠른 API 응답 시 로딩 UI가 깜빡거림
630
+ **해결**: 300ms 이하로 끝나면 로딩 UI를 아예 안보여줌
631
+
632
+ ```tsx
633
+ import { useDelayedLoading } from '@hua-labs/hua-ux/framework';
634
+
635
+ function MyComponent() {
636
+ const [isLoading, setIsLoading] = useState(false);
637
+ const showLoading = useDelayedLoading(isLoading);
638
+
639
+ const fetchData = async () => {
640
+ setIsLoading(true);
641
+ await api.getData(); // 빠르게 끝나면 로딩 UI 안보임
642
+ setIsLoading(false);
643
+ };
644
+
645
+ return showLoading ? <Spinner /> : <Content />;
646
+ }
647
+ ```
648
+
649
+ **편의성 hook**:
650
+ ```tsx
651
+ import { useLoadingState } from '@hua-labs/hua-ux/framework';
652
+
653
+ function MyComponent() {
654
+ const { showLoading, startLoading, stopLoading } = useLoadingState();
655
+
656
+ const fetchData = async () => {
657
+ startLoading();
658
+ try {
659
+ await api.getData();
660
+ } finally {
661
+ stopLoading();
662
+ }
663
+ };
664
+
665
+ return showLoading && <Spinner />;
666
+ }
667
+ ```
668
+
669
+ #### 2. Skeleton (로딩 중 콘텐츠 미리보기)
670
+
671
+ 로딩 시간이 체감적으로 짧게 느껴지고, 레이아웃 시프트를 방지합니다.
672
+
673
+ ```tsx
674
+ import { Skeleton, SkeletonGroup } from '@hua-labs/hua-ux/framework';
675
+
676
+ // 텍스트 스켈레톤
677
+ <Skeleton width="80%" />
678
+ <Skeleton width="60%" />
679
+
680
+ // 원형 (아바타)
681
+ <Skeleton variant="circular" width={40} height={40} />
682
+
683
+ // 직사각형 (이미지)
684
+ <Skeleton variant="rectangular" width={300} height={200} />
685
+
686
+ // 카드 스켈레톤
687
+ <div className="card">
688
+ <Skeleton variant="rectangular" height={200} />
689
+ <SkeletonGroup className="p-4">
690
+ <Skeleton width="60%" height={24} />
691
+ <Skeleton width="80%" />
692
+ <Skeleton width="40%" />
693
+ </SkeletonGroup>
694
+ </div>
695
+ ```
696
+
697
+ **useDelayedLoading + Skeleton 조합**:
698
+ ```tsx
699
+ function MyComponent() {
700
+ const { data, isLoading } = useQuery('data', fetchData);
701
+ const showLoading = useDelayedLoading(isLoading);
702
+
703
+ if (showLoading) {
704
+ return (
705
+ <SkeletonGroup>
706
+ <Skeleton width="60%" height={32} />
707
+ <Skeleton width="80%" />
708
+ <Skeleton width="70%" />
709
+ </SkeletonGroup>
710
+ );
711
+ }
712
+
713
+ return <div>{data?.content}</div>;
714
+ }
715
+ ```
716
+
717
+ #### 3. SuspenseWrapper (React Suspense 편의성)
718
+
719
+ React Suspense를 더 쉽게 사용할 수 있습니다.
720
+
721
+ ```tsx
722
+ import { SuspenseWrapper } from '@hua-labs/hua-ux/framework';
723
+
724
+ // 기본 사용 (자동 Skeleton fallback)
725
+ <SuspenseWrapper>
726
+ <AsyncComponent />
727
+ </SuspenseWrapper>
728
+
729
+ // 커스텀 fallback
730
+ <SuspenseWrapper fallback={<Spinner />}>
731
+ <AsyncComponent />
732
+ </SuspenseWrapper>
733
+
734
+ // Next.js Server Component
735
+ async function Posts() {
736
+ const posts = await fetchPosts();
737
+ return <div>{posts.map(p => <div key={p.id}>{p.title}</div>)}</div>;
738
+ }
739
+
740
+ export default function PostsPage() {
741
+ return (
742
+ <SuspenseWrapper>
743
+ <Posts />
744
+ </SuspenseWrapper>
745
+ );
746
+ }
747
+ ```
748
+
749
+ **HOC 패턴**:
750
+ ```tsx
751
+ import { withSuspense } from '@hua-labs/hua-ux/framework';
752
+
753
+ const AsyncPosts = withSuspense(Posts, <Skeleton height={200} />);
754
+
755
+ function MyPage() {
756
+ return <AsyncPosts />;
757
+ }
758
+ ```
759
+
760
+ ## Use Cases
761
+
762
+ ### 1. 제품 페이지 (Product Preset)
763
+
764
+ ```tsx
765
+ import { productPreset } from '@hua-labs/hua-ux/presets';
766
+
767
+ // 빠른 전환, 최소 딜레이
768
+ const motionConfig = productPreset.motion;
769
+ ```
770
+
771
+ ### 2. 랜딩 페이지 (Marketing Preset)
772
+
773
+ ```tsx
774
+ import { marketingPreset } from '@hua-labs/hua-ux/presets';
775
+
776
+ // 드라마틱한 모션, 긴 딜레이
777
+ const motionConfig = marketingPreset.motion;
778
+ ```
779
+
780
+ ### 3. 다국어 지원
781
+
782
+ ```tsx
783
+ import { useTranslation } from '@hua-labs/hua-ux';
784
+
785
+ function MyComponent() {
786
+ const { t } = useTranslation();
787
+ return <h1>{t('common:welcome')}</h1>;
788
+ }
789
+ ```
790
+
791
+ ### 4. 상태관리 (State Package)
792
+
793
+ ```tsx
794
+ import { createHuaStore } from '@hua-labs/hua-ux';
795
+ // 또는
796
+ import { createHuaStore } from '@hua-labs/state';
797
+
798
+ const useAppStore = createHuaStore((set) => ({
799
+ theme: 'light',
800
+ setTheme: (theme) => set({ theme }),
801
+ }), {
802
+ persist: true,
803
+ ssr: true,
804
+ });
805
+ ```
806
+
807
+ ## 테스트
808
+
809
+ 프레임워크의 주요 기능에 대한 테스트가 포함되어 있습니다:
810
+
811
+ ```bash
812
+ cd packages/hua-ux
813
+ pnpm test
814
+ ```
815
+
816
+ **테스트 커버리지**:
817
+ - ✅ Motion hooks (`useMotion`)
818
+ - ✅ GEO 메타데이터 생성 (`generateGEOMetadata`, `createAIContext`)
819
+ - ✅ 구조화된 데이터 (`generateSoftwareApplicationLD`, `generateFAQPageLD`, etc.)
820
+ - ✅ CSS 변수 생성 (`generateCSSVariables`)
821
+ - ✅ Config 시스템 (`defineConfig`, `getConfig`, `setConfig`)
822
+ - ✅ ErrorBoundary 컴포넌트
823
+ - 🔄 Accessibility 모듈 (구현 완료, 테스트 예정)
824
+ - 🔄 Loading 모듈 (구현 완료, 테스트 예정)
825
+
826
+ ## 버전
827
+
828
+ 현재 버전: **0.1.0** (Alpha)
829
+
830
+ - `0.x`: Alpha 단계, API 변경 가능
831
+ - `1.x`: 안정화 후
832
+
833
+ ## 라이선스
834
+
835
+ MIT
836
+
837
+ ## 이슈 및 문의
838
+
839
+ 문제가 발생하거나 제안사항이 있으시면 [GitHub Issues](https://github.com/HUA-Labs/HUA-Labs-public/issues)에 등록해주세요.