@uniai-fe/uds-templates 0.1.1 → 0.1.3

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.
package/README.md CHANGED
@@ -125,13 +125,20 @@ export default function LoginPage() {
125
125
 
126
126
  > modules 레포 내부(Storybook 등)에서는 개발 편의를 위해 `@uniai-fe/uds-templates/src/index.scss`를 직접 import하지만, 외부 서비스/패키지는 `@uniai-fe/uds-templates/css` 엔트리만 사용해야 한다.
127
127
 
128
+ ### 토큰 스코프 & ThemeProvider
129
+
130
+ - templates SCSS는 모든 디자인 토큰을 `:root`에 선언하고, 빌드 시 `scripts/merge-theme-root.mjs`가 토큰 블록을 단일 `:root { ... }`로 합쳐 중복 선언을 제거합니다.
131
+ - 서비스 앱은 foundation ThemeProvider가 주입하는 `.uds-theme-root`를 루트에 유지해야 하며, CSS import 순서는 `@uniai-fe/uds-foundation/css` → `@uniai-fe/uds-primitives/css` → `@uniai-fe/uds-templates/css` 입니다.
132
+ - modules 레포(Storybook 등)는 SCSS 원본을 직접 사용하지만, 외부 프로젝트는 CSS 엔트리만 import한다는 규칙을 README/CODEX-RULES에 명시하고 있습니다.
133
+
128
134
  ## Modal 모듈 사용법
129
135
 
130
136
  ui-legacy에서 사용하던 모달 스택/옵션을 templates 레이어로 옮겨왔습니다. 핵심 흐름은 다음과 같습니다.
131
137
 
132
138
  1. **Provider 1회 장착** — 서비스 레이아웃에서 `<Modal.Provider />`를 렌더합니다.
133
- 2. **훅 사용**`const { newModal, closeModal } = Modal.useModal();`
134
- 3. **템플릿 팩토리** — `Modal.Alert`, `Modal.Dialog`이 `ModalState` 객체를 반환하므로 `newModal(...)`에 그대로 전달합니다.
139
+ 2. **Route Reset 장착** `<Modal.RouteReset />`을 Provider와 같은 레벨에서 렌더해 경로 변경 시 스택을 초기화합니다.
140
+ 3. **훅 사용** — `const { newModal, closeModal } = Modal.useModal();`
141
+ 4. **템플릿 팩토리** — `Modal.Alert`, `Modal.Dialog`이 `ModalState` 객체를 반환하므로 `newModal(...)`에 그대로 전달합니다.
135
142
 
136
143
  ```tsx
137
144
  // app/layout.tsx
@@ -146,8 +153,11 @@ export default function RootLayout({
146
153
  return (
147
154
  <html lang="ko">
148
155
  <body>
149
- {children}
150
- <Modal.Provider />
156
+ <>
157
+ {children}
158
+ <Modal.Provider />
159
+ <Modal.RouteReset />
160
+ </>
151
161
  </body>
152
162
  </html>
153
163
  );
@@ -196,6 +206,7 @@ export function ExampleActions() {
196
206
  ```
197
207
 
198
208
  - footer 버튼은 ui-legacy와 동일하게 `stackKey`, `role`, `defaultOptions/linkOptions` 구조로 관리하며, Alert(Text)/Dialog(Solid) 규격은 templates 내부에서 보장합니다.
209
+ - Route Reset을 누락하면 이전 라우트의 모달 스택이 그대로 남으므로, layout 수준에서 Provider와 함께 반드시 렌더합니다.
199
210
  - 세부 가드레일·확장 기록은 `CONTEXT-MODAL.md`를 참고하고, 새 템플릿을 추가할 때 해당 문서를 선행 업데이트합니다.
200
211
 
201
212
  ## Scripts
package/dist/styles.css CHANGED
@@ -1,6 +1,4 @@
1
1
  @charset "UTF-8";
2
- /* templates styles는 foundation/primitives 순으로 import된 이후를 가정한다. */
3
- /* 템플릿 레벨 스타일을 통합해 서비스 앱이 단일 엔트리만 import하도록 구성한다. */
4
2
  :root {
5
3
  --frame-device-height: 812px;
6
4
  --frame-device-width: 480px;
@@ -10,11 +8,32 @@
10
8
  --page-frame-header-padding-top: var(--frame-safe-area-top);
11
9
  --page-frame-footer-safe-area: var(--frame-safe-area-bottom);
12
10
  --page-frame-max-width: var(
13
- --frame-device-width,
14
- 480px
11
+ --frame-device-width,
12
+ 480px
13
+ );
14
+ --uds-modal-overlay-bg: var(--dialog-overlay-bg, rgba(5, 6, 12, 0.55));
15
+ --uds-modal-surface-bg: var(--color-bg-surface-static-white);
16
+ --uds-modal-surface-radius: var(--theme-radius-large-1);
17
+ --uds-modal-surface-shadow: 0px 18px 40px rgba(8, 11, 30, 0.18);
18
+ --uds-modal-max-width: min(
19
+ 360px,
20
+ calc(100vw - var(--spacing-padding-10) * 2)
15
21
  );
22
+ --uds-modal-max-height: calc(100vh - var(--spacing-padding-10) * 2);
23
+ --auth-container-max-width: 335px;
24
+ --auth-container-gap: var(--spacing-padding-7, 28px);
25
+ --auth-container-padding-inline: var(--spacing-padding-6, 24px);
26
+ --auth-container-padding-top: calc(
27
+ var(--spacing-padding-9, 32px) + env(safe-area-inset-top, 0px)
28
+ );
29
+ --auth-container-padding-bottom: var(--spacing-padding-10, 40px);
16
30
  }
17
31
 
32
+ /* templates styles는 foundation/primitives 순으로 import된 이후를 가정한다. */
33
+ /* 템플릿 레벨 스타일을 통합해 서비스 앱이 단일 엔트리만 import하도록 구성한다. */
34
+ /* namespace 충돌을 막기 위해 모든 @use 경로에 고유 alias를 부여한다. */
35
+
36
+
18
37
  .page-frame-container {
19
38
  display: grid;
20
39
  grid-template-rows: auto 1fr auto;
@@ -67,17 +86,7 @@
67
86
  }
68
87
 
69
88
  /* foundation CSS는 소비 앱 루트에서 한 번만 로드된다는 전제하에 토큰만 참조한다. */
70
- :where(.radix-themes, .theme-root, :root) {
71
- --uds-modal-overlay-bg: var(--dialog-overlay-bg, rgba(5, 6, 12, 0.55));
72
- --uds-modal-surface-bg: var(--color-bg-surface-static-white);
73
- --uds-modal-surface-radius: var(--theme-radius-large-1);
74
- --uds-modal-surface-shadow: 0px 18px 40px rgba(8, 11, 30, 0.18);
75
- --uds-modal-max-width: min(
76
- 360px,
77
- calc(100vw - var(--spacing-padding-10) * 2)
78
- );
79
- --uds-modal-max-height: calc(100vh - var(--spacing-padding-10) * 2);
80
- }
89
+
81
90
 
82
91
  .uds-modal-root {
83
92
  position: fixed;
@@ -260,15 +269,7 @@
260
269
  transition: none;
261
270
  }
262
271
  }
263
- :root {
264
- --auth-container-max-width: 335px;
265
- --auth-container-gap: var(--spacing-padding-7, 28px);
266
- --auth-container-padding-inline: var(--spacing-padding-6, 24px);
267
- --auth-container-padding-top: calc(
268
- var(--spacing-padding-9, 32px) + env(safe-area-inset-top, 0px)
269
- );
270
- --auth-container-padding-bottom: var(--spacing-padding-10, 40px);
271
- }
272
+
272
273
 
273
274
  .auth-container {
274
275
  min-height: min(100svh, 100dvh);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-templates",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "UNIAI Design System; UI Templates Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -39,7 +39,7 @@
39
39
  "lint": "eslint . --max-warnings=0",
40
40
  "typecheck": "tsc --project tsconfig.build.json --noEmit",
41
41
  "build": "pnpm format && pnpm typecheck && pnpm css:build",
42
- "css:build": "sass --load-path=node_modules src/index.scss dist/styles.css --style=expanded --no-source-map",
42
+ "css:build": "sass --load-path=node_modules src/index.scss dist/styles.css --style=expanded --no-source-map && node ./scripts/merge-theme-root.mjs dist/styles.css --selector=:root",
43
43
  "dev": "tsc --project tsconfig.build.json --watch --noEmit",
44
44
  "module:lint": "pnpm lint",
45
45
  "module:typecheck": "pnpm typecheck",
@@ -23,11 +23,13 @@ export default function AuthLoginContainer({
23
23
  footer={footer}
24
24
  >
25
25
  <AuthLoginFormField {...fieldOptions} />
26
- <AuthLoginLinkButtons
27
- hrefFindId={linkOptions.find.id}
28
- hrefFindPassword={linkOptions.find.password}
29
- hrefSignup={linkOptions.signup}
30
- />
26
+ {linkOptions && (
27
+ <AuthLoginLinkButtons
28
+ hrefFindId={linkOptions.find.id}
29
+ hrefFindPassword={linkOptions.find.password}
30
+ hrefSignup={linkOptions.signup}
31
+ />
32
+ )}
31
33
  </AuthContainer>
32
34
  </>
33
35
  );
@@ -91,5 +91,5 @@ export type AuthLoginLinkOptions = {
91
91
  export type AuthLoginProps<TFields extends AuthLoginFields = AuthLoginFields> =
92
92
  Omit<AuthContainerProps, "children"> & {
93
93
  fieldOptions: AuthLoginFieldOptions<TFields>;
94
- linkOptions: AuthLoginLinkOptions;
94
+ linkOptions?: AuthLoginLinkOptions;
95
95
  };
package/src/index.scss CHANGED
@@ -2,14 +2,15 @@
2
2
  // @use "@uniai-fe/uds-primitives/styles";
3
3
 
4
4
  /* 템플릿 레벨 스타일을 통합해 서비스 앱이 단일 엔트리만 import하도록 구성한다. */
5
- @use "./page-frame/container/index.scss" as *;
6
- @use "./page-frame/mobile/index.scss" as *;
7
- @use "./page-frame/navigation/index.scss" as *;
8
- @use "./modal/index.scss" as *;
5
+ /* namespace 충돌을 막기 위해 모든 @use 경로에 고유 alias를 부여한다. */
6
+ @use "./page-frame/container/index.scss" as pageFrameContainer;
7
+ @use "./page-frame/mobile/index.scss" as pageFrameMobile;
8
+ @use "./page-frame/navigation/index.scss" as pageFrameNavigation;
9
+ @use "./modal/index.scss" as modalStyles;
9
10
 
10
- @use "./auth/common/container/index.scss" as *;
11
- @use "./auth/common/complete/index.scss" as *;
12
- @use "./auth/login/index.scss" as *;
13
- @use "./auth/find-id/index.scss" as *;
14
- @use "./auth/find-password/index.scss" as *;
15
- @use "./auth/signup/styles/signup.scss" as *;
11
+ @use "./auth/common/container/index.scss" as authCommonContainer;
12
+ @use "./auth/common/complete/index.scss" as authCommonComplete;
13
+ @use "./auth/login/index.scss" as authLogin;
14
+ @use "./auth/find-id/index.scss" as authFindId;
15
+ @use "./auth/find-password/index.scss" as authFindPassword;
16
+ @use "./auth/signup/styles/signup.scss" as authSignup;
@@ -7,7 +7,7 @@ import clsx from "clsx";
7
7
  import { useModal } from "../hooks/useModal";
8
8
  import { ModalContainer } from "./Container";
9
9
 
10
- import type { ModalProps, ModalShowState, ModalState } from "../../types";
10
+ import type { ModalShowState, ModalState } from "../../types";
11
11
 
12
12
  type ModalRootProps = ModalState & {
13
13
  index: number;
@@ -0,0 +1,25 @@
1
+ "use client";
2
+
3
+ import { useEffect } from "react";
4
+ import { usePathname } from "next/navigation";
5
+ import { useSetAtom } from "jotai";
6
+
7
+ import { modalStackAtom } from "../jotai/atoms";
8
+
9
+ /**
10
+ * Modal Route Reset; 라우트 변경 시 모달 스택 초기화
11
+ * @component
12
+ * @desc
13
+ * - Next.js pathname 변경을 감지해 `modalStackAtom`을 빈 배열로 되돌린다.
14
+ * - 최상위 layout에서 `<Modal.RouteReset />`을 Provider와 함께 렌더링해야 한다.
15
+ */
16
+ export function ModalRouteReset() {
17
+ const pathname = usePathname();
18
+ const resetStack = useSetAtom(modalStackAtom);
19
+
20
+ useEffect(() => {
21
+ resetStack([]);
22
+ }, [pathname, resetStack]);
23
+
24
+ return null;
25
+ }
@@ -1,16 +1,26 @@
1
1
  import "./index.scss";
2
2
 
3
3
  import { ModalProvider } from "./core/components/Provider";
4
+ import { ModalRouteReset } from "./core/components/RouteReset";
4
5
  import { useModal } from "./core/hooks/useModal";
5
6
  import { createAlertModal } from "./templates/Alert";
6
7
  import { createDialogModal } from "./templates/Dialog";
8
+ import { modalStackAtom } from "./core/jotai/atoms";
7
9
 
8
10
  export const Modal = {
9
11
  Provider: ModalProvider,
12
+ RouteReset: ModalRouteReset,
10
13
  useModal,
11
14
  Alert: createAlertModal,
12
15
  Dialog: createDialogModal,
13
16
  };
14
17
 
15
- export { ModalProvider, useModal, createAlertModal, createDialogModal };
18
+ export {
19
+ ModalProvider,
20
+ ModalRouteReset,
21
+ useModal,
22
+ createAlertModal,
23
+ createDialogModal,
24
+ modalStackAtom,
25
+ };
16
26
  export type * from "./types";
@@ -1,5 +1,5 @@
1
1
  /* foundation CSS는 소비 앱 루트에서 한 번만 로드된다는 전제하에 토큰만 참조한다. */
2
- :where(.radix-themes, .theme-root, :root) {
2
+ :root {
3
3
  --uds-modal-overlay-bg: var(--dialog-overlay-bg, rgba(5, 6, 12, 0.55));
4
4
  --uds-modal-surface-bg: var(--color-bg-surface-static-white);
5
5
  --uds-modal-surface-radius: var(--theme-radius-large-1);