임동욱 임동욱 08-18
feat: 앱 기본 서체를 Pretendard로 통일하고 WantedSans 제거
시안(Figma 디자인시스템)이 Pretendard를 지정하는데 앱 셸만 WantedSans를 쓰고 있어 한 화면에
두 서체가 섞여 있었다. 앱도 Pretendard로 맞춘다.

- layout.tsx: `next/font/local`의 wantedSans 선언을 걷어냈다. 앱 기본 서체는 이제 globals.scss의
  `@font-face`가 공급한다 — @fox 토큰이 패밀리명을 리터럴 `Pretendard`로 못 박고 있어
  next/font(패밀리명을 해시로 바꾼다)를 쓸 수 없기 때문이고, 같은 서체를 두 경로로 넣을 이유도
  없다.
- globals.scss: `--app-font-sans: "Pretendard"`를 @font-face 옆에 정의했다. @fox README가 정의한
  호스트 앱 계약의 실제 주입 지점이라, 나중에 서체를 갈 때 이 한 줄만 고치면 된다.
- weight 700(Bold) 추가. 토큰이 쓰는 400·600·800만으로는 어떤 CSS도 굵기를 지정하지 않는
  자리(제목·th·strong 등 UA 기본값 700)가 800으로 올라가 굵어진다.
- public/fonts: WantedSans TTF 4종(9.4MB) 삭제. 참조가 layout.tsx 한 곳뿐이었고 그게 사라졌다.

검증 — `--app-font-sans`가 `"Pretendard"`, body 계산값이 `Pretendard, system-ui, sans-serif`,
document.fonts에 wantedSans가 아예 없다. 굵기별 문자열 폭이 400/600/700/800에서 각각
328.2 / 335.2 / 338.7 / 342.2px로 전부 달라, 700이 800으로 승격되지 않고 실제 Bold 페이스로
잡히는 것까지 확인했다. FoxCarouselIndicator는 59.69px로 종전과 같다(회귀 없음).

저장소 폰트 용량 11.6MB → 3.0MB.

Co-Authored-By: Claude Opus 5 
@4139da307bd41dac469e39f83757bbcae19b7ff2
app/globals.scss
--- app/globals.scss
+++ app/globals.scss
@@ -14,7 +14,8 @@
 // 앱은 정확히 그 이름의 폰트를 공급해야 한다. `next/font/local`은 패밀리명을
 // 해시(`__Pretendard_xxxx`)로 바꿔 그 리터럴과 영영 만나지 못해 쓸 수 없다 — 토큰은 Figma
 // export 자동 생성이라 이름을 코드 쪽에 맞추는 반대 방향도 불가능하다.
-// weight는 토큰이 실제로 쓰는 400·600·800 셋뿐이다(`weight-regular`/`medium`/`bold`).
+// weight는 토큰이 쓰는 400·600·800에 더해, 어떤 CSS도 굵기를 지정하지 않는 자리(제목·`th`·
+// `strong` 등 UA 기본값)가 쓰는 700을 포함한다 — 없으면 700이 800으로 올라가 굵어진다.
 @font-face {
   font-family: "Pretendard";
   font-style: normal;
@@ -34,9 +35,24 @@
 @font-face {
   font-family: "Pretendard";
   font-style: normal;
+  font-weight: 700;
+  font-display: swap;
+  src: url("/fonts/pretendard-bold.woff2") format("woff2");
+}
+
+@font-face {
+  font-family: "Pretendard";
+  font-style: normal;
   font-weight: 800;
   font-display: swap;
   src: url("/fonts/pretendard-extrabold.woff2") format("woff2");
+}
+
+// 앱 기본 서체 — @fox README가 정의한 호스트 앱 계약(`--app-font-sans`)의 실제 주입 지점이다.
+// 폰트 파일을 next/font가 아니라 위 `@font-face`가 공급하므로 변수도 여기서 정의한다.
+// 이 값과 @fox 토큰(`font.family.*`)이 지금은 둘 다 Pretendard라 화면 전체가 한 서체다.
+:root {
+  --app-font-sans: "Pretendard";
 }
 
 // 앱 셸 — 루트 레이아웃이 뷰포트 높이를 채우고 세로로 쌓이는 구조.
@@ -51,9 +67,8 @@
   flex-direction: column;
   min-block-size: 100%;
 
-  // @fox 밖 텍스트의 서체. 디자인 값이 아니라 layout.tsx가 주입하는 계약 변수를 꽂는
-  // 지점이라 여기 둔다 — 이 한 줄이 없으면 `--app-font-sans`를 읽는 곳이 없어 wantedSans가
-  // 로드만 되고 아무 데도 적용되지 않는다. @fox 컴포넌트는 각자 토큰으로 Pretendard를
-  // 덮어쓰므로 영향을 받지 않는다.
+  // @fox 밖 텍스트의 서체. 계약 변수를 거쳐 꽂으므로 서체를 갈 때는 위 `--app-font-sans`
+  // 한 줄만 고치면 된다. @fox 컴포넌트는 이 값이 아니라 토큰(`Pretendard` 리터럴)을 쓰지만,
+  // 지금은 둘이 같은 서체를 가리킨다.
   font-family: var(--app-font-sans, system-ui), system-ui, sans-serif;
 }
app/layout.tsx
--- app/layout.tsx
+++ app/layout.tsx
@@ -1,6 +1,5 @@
 import type { Metadata } from "next";
 import { Geist_Mono } from "next/font/google";
-import localFont from "next/font/local";
 import { FeedbackHost } from "@/app/_components/feedback-host";
 import { FeedbackProvider } from "@/app/_components/feedback-provider";
 import {
@@ -25,38 +24,12 @@
   THEME_ATTRIBUTE_NAME
 )},t)}catch(e){}})()`;
 
-// `@fox` **밖** 앱 텍스트의 기본 서체. 폰트 파일은 앱이 소유하고 `--app-font-sans` 계약으로
-// 넘긴다(@fox/README.md "호스트 앱과의 계약") — 그 변수를 실제로 꽂는 곳은 globals.scss의
-// `body`다. @fox 컴포넌트는 이 값을 쓰지 않는다: 토큰이 패밀리명을 `Pretendard`로 직접
-// 지정하고 그 @font-face도 globals.scss에 있다. 즉 한 화면에 두 서체가 공존한다.
-const wantedSans = localFont({
-  src: [
-    {
-      path: "../public/fonts/wanted-sans-regular.ttf",
-      weight: "400",
-      style: "normal",
-    },
-    {
-      path: "../public/fonts/wanted-sans-medium.ttf",
-      weight: "500",
-      style: "normal",
-    },
-    {
-      path: "../public/fonts/wanted-sans-semibold.ttf",
-      weight: "600",
-      style: "normal",
-    },
-    {
-      path: "../public/fonts/wanted-sans-bold.ttf",
-      weight: "700",
-      style: "normal",
-    },
-  ],
-  variable: "--app-font-sans",
-  display: "swap",
-});
+// 앱 기본 서체는 여기서 선언하지 않는다 — @fox 토큰이 패밀리명을 리터럴 `Pretendard`로 못
+// 박고 있어 `next/font`(패밀리명을 해시로 바꾼다)를 쓸 수 없고, globals.scss의 `@font-face`가
+// 그 이름 그대로 공급한다. 앱과 @fox가 같은 서체를 쓰므로 앱 몫을 따로 둘 이유도 없다.
+// `--app-font-sans` 계약(@fox/README.md "호스트 앱과의 계약")도 그 옆에서 함께 정의한다.
 
-// 코드/숫자 표기용 고정폭 서체 — sans와 동일하게 `--app-font-mono` 계약으로 주입한다.
+// 코드/숫자 표기용 고정폭 서체 — `--app-font-mono` 계약으로 주입한다.
 const geistMono = Geist_Mono({
   variable: "--app-font-mono",
   subsets: ["latin"],
@@ -91,14 +64,13 @@
     <html
       lang="ko"
       suppressHydrationWarning
-      className={`${wantedSans.variable} ${geistMono.variable}`}
+      className={geistMono.variable}
     >
       <head>
         <script dangerouslySetInnerHTML={{ __html: THEME_BOOTSTRAP_SCRIPT }} />
         {/* @font-face는 CSS를 다 읽은 뒤에야 발견돼 본문이 한 번 폴백 서체로 그려진다.
-            거의 모든 화면이 쓰는 regular만 미리 받아 그 깜빡임을 없앤다 — 600/800은
-            빈도가 낮아 선점 비용이 이득보다 크다. next/font로 넣은 wantedSans는 Next.js가
-            알아서 preload하므로 여기 없다. */}
+            거의 모든 화면이 쓰는 regular만 미리 받아 그 깜빡임을 없앤다 — 나머지 굵기는
+            빈도가 낮아 선점 비용이 이득보다 크다. */}
         <link
           rel="preload"
           as="font"
 
public/fonts/pretendard-bold.woff2 (Binary) (added)
+++ public/fonts/pretendard-bold.woff2
Binary file is not shown
 
public/fonts/wanted-sans-bold.ttf (Binary) (deleted)
--- public/fonts/wanted-sans-bold.ttf
Binary file is not shown
 
public/fonts/wanted-sans-medium.ttf (Binary) (deleted)
--- public/fonts/wanted-sans-medium.ttf
Binary file is not shown
 
public/fonts/wanted-sans-regular.ttf (Binary) (deleted)
--- public/fonts/wanted-sans-regular.ttf
Binary file is not shown
 
public/fonts/wanted-sans-semibold.ttf (Binary) (deleted)
--- public/fonts/wanted-sans-semibold.ttf
Binary file is not shown
Add a comment
List