Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/content-anchor-fontface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@app/ratewise': patch
---

修復 PWA standalone 下內容頁頁內錨點跳轉(隱私頁目錄、使用指南步驟)被變高的返回列遮蔽的問題;修復品牌標準字體在正式站永遠未載入(全站 unused preload 警告、頁首 wordmark 回落系統字體)的問題。
11 changes: 2 additions & 9 deletions apps/ratewise/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,8 @@
line-height: 1.6;
-webkit-text-size-adjust: 100%;
}
/* 品牌標準字(showcase-twincoins SSOT):Nunito 900 子集,僅含 HaoRate 六個字符。 */
@font-face {
font-family: 'Nunito Wordmark';
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('__BASE_PATH__fonts/nunito-wordmark-900.woff2') format('woff2');
unicode-range: U+0048, U+0052, U+0061, U+0065, U+006F, U+0074;
}
/* 品牌標準字 @font-face 移至 src/index.css(與 .brand-wordmark 同 sheet):
Beasties 逐 sheet 判定 critical fonts,內嵌 sheet 無使用者會整條剝除 @font-face。 */
/* Theme-aware skeleton variables
* Maps to user's selected theme for seamless loading experience
* @see index.css - theme color definitions
Expand Down
4 changes: 2 additions & 2 deletions apps/ratewise/src/components/content/ContentSections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export function ContentSectionCard({
return (
<section
id={id}
className="rounded-card border border-border/60 bg-surface shadow-card p-5 scroll-mt-20"
className="rounded-card border border-border/60 bg-surface shadow-card p-5 scroll-mt-[calc(5rem+env(safe-area-inset-top,0px))]"
>
{title && <h2 className="mb-4 text-xl font-bold leading-tight text-text">{title}</h2>}
{children}
Expand Down Expand Up @@ -175,7 +175,7 @@ function renderSection(section: ContentSection) {
);
case 'faq':
return (
<section id={section.id} className="scroll-mt-20">
<section id={section.id} className="scroll-mt-[calc(5rem+env(safe-area-inset-top,0px))]">
{section.title && (
<h2 className="mb-4 text-xl font-bold leading-tight text-text">{section.title}</h2>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@ describe('ContentPageLayout', () => {
const container = screen.getByTestId('content-page').firstElementChild;
expect(container?.className).toContain('pb-24');
});

// sticky 返回列 safe-area 適配由 PageNavHeader.safe-area.test.tsx(#605)守門;
// 此處守門錨點連動:sticky 列在 standalone 下變高,錨點目標 scroll-margin 必須同步吃 safe-area(#600)。
it('錨點 section 的 scroll-margin-top 疊加 safe-area-inset-top', () => {
render(
<BrowserRouter>
<ContentSections
sections={[{ kind: 'text', id: 'anchor-a', title: '錨點區', paragraphs: ['內容'] }]}
/>
</BrowserRouter>,
);
const section = document.getElementById('anchor-a');
expect(section?.className).toContain('scroll-mt-[calc(5rem+env(safe-area-inset-top,0px))]');
});
});

describe('ContentSections', () => {
Expand Down
13 changes: 12 additions & 1 deletion apps/ratewise/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,19 @@
/* ========================================
品牌標準字 Wordmark(SSOT: scripts/source-images/brand/showcase-twincoins.html)
Nunito 900、字距 -0.02em;「Rate」使用主題主色。
@font-face 定義於 index.html critical CSS(__BASE_PATH__ SSOT 替換)。
======================================== */
/* 品牌標準字子集(Nunito 900,僅 wordmark 六字符,1.2KB)。
必須與 .brand-wordmark 同 sheet:Beasties 逐 sheet 判定 critical fonts,
放在 index.html 內嵌樣式會於 SSG 階段被整條剝除(unused preload 根因)。 */
@font-face {
font-family: 'Nunito Wordmark';
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/fonts/nunito-wordmark-900.woff2') format('woff2');
unicode-range: U+0048, U+0052, U+0061, U+0065, U+006F, U+0074;
}

.brand-wordmark {
font-family:
'Nunito Wordmark',
Expand Down
14 changes: 10 additions & 4 deletions apps/ratewise/src/index.html.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,17 @@ describe('index.html - Static Template (SEOHelmet Architecture)', () => {
expect(indexHtmlContent).toContain('<link rel="icon"');
});

it('should preload and define the brand wordmark font subset with base path SSOT', () => {
// 品牌標準字(Nunito 900 子集):preload + @font-face 均須走 __BASE_PATH__ 佔位符。
it('should preload the brand wordmark font subset with base path SSOT', () => {
// 品牌標準字(Nunito 900 子集):preload 走 __BASE_PATH__ 佔位符。
// @font-face 必須在 src/index.css(與 .brand-wordmark 同 sheet),
// 否則 Beasties 逐 sheet 判定 critical fonts 時會整條剝除(unused preload 根因)。
expect(indexHtmlContent).toContain('__BASE_PATH__fonts/nunito-wordmark-900.woff2');
expect(indexHtmlContent).toContain("font-family: 'Nunito Wordmark'");
expect(indexHtmlContent).toContain('font-display: swap');
expect(indexHtmlContent).not.toMatch(/@font-face\s*\{/);

const indexCssContent = readFileSync(resolve(__dirname, 'index.css'), 'utf-8');
expect(indexCssContent).toContain("font-family: 'Nunito Wordmark'");
expect(indexCssContent).toContain('font-display: swap');
expect(indexCssContent).toContain("url('/fonts/nunito-wordmark-900.woff2')");
});

it('should retain PWA manifest hints', () => {
Expand Down
2 changes: 1 addition & 1 deletion apps/ratewise/src/pages/Guide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ const Guide = () => {
<section
key={step.position}
id={`step-${step.position}`}
className="scroll-mt-20 rounded-card border border-border/60 bg-surface p-5 shadow-card"
className="scroll-mt-[calc(5rem+env(safe-area-inset-top,0px))] rounded-card border border-border/60 bg-surface p-5 shadow-card"
>
<div className="flex items-start gap-4">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-primary-strong text-lg font-bold text-white">
Expand Down
7 changes: 7 additions & 0 deletions apps/ratewise/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,13 @@ export default defineConfig(({ mode }) => {
script: 'async',
formatting: 'beautify',
dirStyle: 'nested',
// Beasties 預設 inlineFonts:false 會剝除 index.html inline @font-face 並注入重複
// font preload,導致品牌字體永遠未被使用(unused preload console warning)。
// inlineFonts:true 保留 @font-face;preloadFonts:false 交由 index.html 靜態 preload SSOT。
beastiesOptions: {
inlineFonts: true,
preloadFonts: false,
},
// vite-react-ssg + Beasties may read freshly rendered nested amount pages during
// writeout. Serial rendering avoids intermittent ENOENT on CI/pre-push.
concurrency: 1,
Expand Down
17 changes: 16 additions & 1 deletion docs/dev/002_development_reward_penalty_log.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> 版本:outline-v2-ultra
> 原則:每筆只保留日期、ID、原因、解法。
> 本次分數變化:+1(reward 1、penalty 0、neutral 0)|累計總分:+147
> 本次分數變化:+2(reward 2、penalty 0、neutral 1)|累計總分:+149

## 新增模板(4 行)

Expand All @@ -13,6 +13,21 @@

## 條目(新→舊)

- 日期:2026-07-06
- ID:reward-rw-600-anchor-scroll-mt-safe-area
- 原因:#605 修了 sticky 返回列 safe-area 但錨點目標 `scroll-mt-20` 固定 80px 未連動,standalone 下 sticky 列變高(+59px)後隱私目錄與 Guide 步驟跳轉被遮蔽(#600 連動項)
- 解法:三處錨點(Privacy 目錄、Guide 步驟、ContentSections)改 `scroll-mt-[calc(5rem+env(safe-area-inset-top,0px))]`,CDP inset 59px 實測 targetTop 139 > headerBottom 124,補 ContentPageLayout 錨點斷言

- 日期:2026-07-06
- ID:reward-rw-font-preload-beasties-fontface-strip
- 原因:品牌字體 @font-face 放 index.html 內嵌樣式,Beasties 逐 sheet 判定 critical fonts 時因該 sheet 無使用者而整條剝除,全站 unused preload 警告且 wordmark 永遠回落系統字體(正式站取證:HTML 無任何 @font-face)
- 解法:@font-face 移至 src/index.css(與 .brand-wordmark 同 sheet)+ ssgOptions.beastiesOptions 設 inlineFonts:true/preloadFonts:false,preview 驗證警告歸零、document.fonts.check 為 true、preload 單一化

- 日期:2026-07-06
- ID:neutral-rw-612-staging-manifest-cross-origin
- 原因:staging manifest `scope`/`start_url` 由 generate-manifest.mjs 以 APP_INFO.siteUrl 硬編正式站絕對 URL,對 staging origin 跨網域;屬刻意設計(PWA partition/HTTPS-First)連動 SSOT 生成鏈
- 解法:取證後立案 #612(附 curl 證據與範圍)不硬修;React #418 staging 複驗無殘留(#595 已修)

- 日期:2026-07-06
- ID:reward-rw-e5b-currency-page-uiux-six-section-ia
- 原因:34 幣別頁+金額頁為牆文式段落佈局、視覺語言與 app 端割裂且行動版缺底部導覽,未達韓系 app 水準(E5 wave-B)
Expand Down
Loading