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/e10-single-fold-layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@app/ratewise': minor
---

單幣別頁改為整頁 fold 佈局:首屏固定顯示金額輸入、快速金額列與匯率結果卡(含加入歷史紀錄按鈕),臺銀四價詳情與匯率趨勢卡移至下方捲動區;小高度螢幕不再隱藏快速金額列,指南與幣別頁的趨勢查看說明同步改為「往下捲動」。
4 changes: 2 additions & 2 deletions apps/ratewise/src/components/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,10 @@ export function AppLayout() {
<RouteAnalytics />
{/* 根容器:固定視口高度,啟用 flex 滾動
* 使用 --app-height(由 JS 設定)而非 100dvh,確保 WebView 環境高度正確。
* Fallback:100dvh(JS 尚未執行時,或 SSG 初始渲染)。 */}
* Fallback:100svh(JS 尚未執行時,或 SSG 初始渲染;E10 fold 以最保守可視高度計)。 */}
<div
className="w-full flex flex-col font-sans bg-[rgb(var(--color-background))] text-[rgb(var(--color-text))] overflow-hidden"
style={{ height: 'var(--app-height, 100dvh)' }}
style={{ height: 'var(--app-height, 100svh)' }}
>
{location.pathname === '/' ? (
<div className="sr-only">
Expand Down
21 changes: 15 additions & 6 deletions apps/ratewise/src/config/design-tokens.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,21 @@ describe('Design Token System - BDD', () => {

expect(singleConverterLayoutTokens).toBeDefined();

// 隱藏優先順序:快速金額(來源) → 快速金額(結果) → 交換按鈕 → 資料來源
// #591:SE(short 區間)保留來源快速金額,隱藏門檻自 short/tiny 下移至 tiny/micro
expect(singleConverterLayoutTokens.quickAmounts.fromVisibility).toBe('tiny:hidden');
expect(singleConverterLayoutTokens.quickAmounts.toVisibility).toBe('micro:hidden');
// E10 fold 合約:快速金額列屬首屏內容,fold 矩陣(最小 640px 高)內不得隱藏;
// 隱藏門檻下移至矩陣外(來源 micro ≤600px → 結果 nano ≤560px)
expect(singleConverterLayoutTokens.quickAmounts.fromVisibility).toBe('micro:hidden');
expect(singleConverterLayoutTokens.quickAmounts.toVisibility).toBe('nano:hidden');
expect(singleConverterLayoutTokens.swap.visibility).toBe('micro:hidden');

// E10 fold 容器:以 --app-height(fallback 100svh)計算首屏預算,不用 100vh/dvh;
// 內部 pb 抵銷固定底導覽(slim ≤360px 加大),md+ 歸零。
expect(singleConverterLayoutTokens.fold.container).toContain('var(--app-height,100svh)');
expect(singleConverterLayoutTokens.fold.container).not.toContain('100vh');
expect(singleConverterLayoutTokens.fold.container).not.toContain('100dvh');
expect(singleConverterLayoutTokens.fold.container).toContain('slim:pb-[calc(92px');
expect(singleConverterLayoutTokens.fold.container).toContain('md:min-h-0');
expect(singleConverterLayoutTokens.fold.resultAnchor).toBe('mt-auto');

// 流體縮放配置
expect(singleConverterLayoutTokens.rateCard.chartHeight).toContain('compact:h-16');
expect(singleConverterLayoutTokens.rateCard.rateTypeContainer).toContain('absolute');
Expand All @@ -282,8 +291,8 @@ describe('Design Token System - BDD', () => {
expect(singleConverterLayoutTokens.rateCard.rateTypeButton).toContain('xnarrow:px-1');
expect(singleConverterLayoutTokens.rateCard.infoPadding).toContain('micro:pt-10');
expect(singleConverterLayoutTokens.rateCard.infoPadding).toContain('nano:pt-10');
expect(singleConverterLayoutTokens.addToHistory.className).toContain('micro:min-h-11');
expect(singleConverterLayoutTokens.addToHistory.className).toContain('nano:min-h-11');
// E10 fold 合約:歷史動作全梯次維持 44px 最小觸控高度。
expect(singleConverterLayoutTokens.addToHistory.className).toMatch(/^min-h-11 /);
});
});

Expand Down
70 changes: 43 additions & 27 deletions apps/ratewise/src/config/design-tokens/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,10 @@ export const pageLayoutTokens = {
* 2. **漸進隱藏**:依重要性順序隱藏次要元素
* 3. **等比維持**:漸層/光暈使用 `aspect-ratio` 保持比例
*
* ## 元素隱藏優先順序(由先到後)
* ## 元素隱藏優先順序(由先到後;E10 fold 後快速金額門檻下移至矩陣外
*
* 1. 快速金額(來源) - short (≤700px) 隱藏
* 2. 快速金額(結果) - tiny (≤650px) 隱藏
* 1. 快速金額(來源) - micro (≤600px) 隱藏
* 2. 快速金額(結果) - nano (≤560px) 隱藏
* 3. 交換按鈕光暈 - short (≤700px) 隱藏
* 4. 交換按鈕 - micro (≤600px) 隱藏
* 5. 資料來源 - nano (≤560px) 隱藏(最後)
Expand Down Expand Up @@ -282,6 +282,33 @@ export const multiConverterLayoutTokens = {
* @version 1.1.0
*/
export const singleConverterLayoutTokens = {
/**
* E10 fold 佈局(v1 單幣別整頁)
*
* fold 合約:首屏底界 = 匯率結果卡(含加入歷史紀錄動作)底緣;
* 下方(四價詳情、趨勢卡、資訊區)自然捲動。
*
* fold 預算以 --app-height 為 SSOT(JS 寫入 visualViewport.height;
* CSS fallback 100svh——webview 動態工具列下 svh 為最保守可視高度,不用 100vh/dvh)。
* 80px = header 48px + 內容區 pt 16px + 卡片 pt 16px(不扣 1px border:寧可略高,
* 確保 fold 下方第一元素落在視口外)。
* 內部 pb 抵銷固定底導覽(56px;slim ≤360px 為 84px,對齊 mainScroll token)+8px 呼吸。
* md+ 無行動 fold 壓力(#643 兩欄佈局),全部歸零。
*/
fold: {
/** fold 容器:撐滿首屏可用高度,結果卡以 mt-auto 錨定 fold 底緣 */
container:
'flex flex-col min-h-[calc(var(--app-height,100svh)-80px-env(safe-area-inset-top,0px))] pb-[calc(64px+env(safe-area-inset-bottom,0px))] slim:pb-[calc(92px+env(safe-area-inset-bottom,0px))] md:min-h-0 md:pb-0',
/** 結果卡錨定:tall 視口推至 fold 底緣,短視口自然堆疊 */
resultAnchor: 'mt-auto',
},

/** fold 下方區塊(四價詳情、趨勢卡)間距 */
belowFold: {
section: 'mt-4 compact:mt-3.5 short:mt-3 tiny:mt-2.5 micro:mt-2 nano:mt-2',
heading: 'mb-2 text-xs font-semibold text-text-muted',
},

/** 區塊間距 - 線性遞減 */
section: {
className: 'mb-4 compact:mb-3.5 short:mb-3 tiny:mb-2.5 micro:mb-2 nano:mb-2',
Expand All @@ -298,18 +325,12 @@ export const singleConverterLayoutTokens = {
'py-3 text-2xl compact:py-2.5 compact:text-xl short:py-2 short:text-lg tiny:py-1.5 tiny:text-base micro:py-1.5 micro:text-base nano:py-1 nano:text-sm',
},

/** 匯率卡片區塊 */
/** 匯率結果卡(E10:趨勢圖移出後即 fold 底界卡片) */
rateCard: {
/** 區塊容器 */
section:
'flex flex-col items-center mb-4 compact:mb-3.5 short:mb-3 tiny:mb-2.5 micro:mb-2 nano:mb-2',

/** 卡片底部間距 */
cardSpacing: 'mb-3 compact:mb-2.5 short:mb-2 tiny:mb-1.5 micro:mb-1.5 nano:mb-1',

/** 匯率資訊區內距 - micro/nano 頂部 ≥40px,避免 RateSelector 與匯率文字重疊 */
/** 匯率資訊區內距 - 各梯次頂部 ≥40px,避免絕對定位 RateSelector 與匯率文字重疊
* (E10:short/tiny 原 pt-8/pt-6 不足 pills 佔位 36px,補齊至 40px) */
infoPadding:
'pt-12 pb-6 compact:pt-10 compact:pb-5 short:pt-8 short:pb-4 tiny:pt-6 tiny:pb-3 micro:pt-10 micro:pb-2.5 nano:pt-10 nano:pb-2',
'pt-12 pb-6 compact:pt-10 compact:pb-5 short:pt-10 short:pb-4 tiny:pt-10 tiny:pb-3 micro:pt-10 micro:pb-2.5 nano:pt-10 nano:pb-2',

/** 匯率類型按鈕容器定位 */
rateTypeContainer:
Expand Down Expand Up @@ -337,10 +358,6 @@ export const singleConverterLayoutTokens = {
/** 計價基準 pill 槽位(無 pill 時仍保留高度) */
rateBasisSlot: 'mt-1.5 flex min-h-6 items-center justify-center',

/** 匯率卡片本體最小高度(資訊區 + 圖表) */
cardMinHeight:
'min-h-[10.5rem] compact:min-h-[9.5rem] short:min-h-[8.75rem] tiny:min-h-[8rem] micro:min-h-[7.25rem] nano:min-h-[6.75rem]',

/** 趨勢圖高度 - 線性遞減 */
chartHeight: 'h-20 compact:h-16 short:h-14 tiny:h-12 micro:h-10 nano:h-8',

Expand All @@ -366,20 +383,20 @@ export const singleConverterLayoutTokens = {
/**
* 快速金額區塊
*
* 隱藏優先順序:來源 (tiny) → 結果 (micro)
* 快速金額為輔助功能,優先於資料來源隱藏
* #591:iPhone SE(667px 高,short 區間)需保留來源快速金額,隱藏門檻下移一級
* E10 fold 合約:快速金額列屬 fold 首屏內容(趨勢卡已移至 fold 下方釋放空間),
* fold 視口矩陣(最小 360×640,tiny 區間)內不得隱藏;隱藏門檻下移至矩陣之外。
* 隱藏優先順序:來源 (micro ≤600px) → 結果 (nano ≤560px)
*/
quickAmounts: {
/** 容器樣式(snap-x proximity:滑動後 chip 對齊起點,避免半截停留) */
container:
'flex gap-2 mt-2 compact:mt-1.5 short:mt-1 tiny:mt-1 micro:mt-0.5 nano:mt-0.5 min-w-0 overflow-x-auto snap-x snap-proximity scrollbar-hide [overflow-y:hidden] [-webkit-overflow-scrolling:touch]',

/** 來源快速金額:tiny (≤650px) 隱藏 */
fromVisibility: 'tiny:hidden',
/** 來源快速金額:micro (≤600px) 隱藏(fold 矩陣下限 640px 內保持可見) */
fromVisibility: 'micro:hidden',

/** 結果快速金額:micro (≤600px) 隱藏 */
toVisibility: 'micro:hidden',
/** 結果快速金額:nano (≤560px) 隱藏 */
toVisibility: 'nano:hidden',
},

/**
Expand All @@ -398,10 +415,9 @@ export const singleConverterLayoutTokens = {
glowHidden: 'short:hidden',
},

/** 加入歷史按鈕 - micro/nano 維持 WCAG 44px 最小觸控高度 */
/** 加入歷史按鈕 - 全梯次維持 WCAG 44px 最小觸控高度(E10 fold 合約) */
addToHistory: {
className:
'py-3.5 compact:py-3 short:py-2.5 tiny:py-2 micro:min-h-11 micro:py-2 nano:min-h-11 nano:py-2',
className: 'min-h-11 py-3.5 compact:py-3 short:py-2.5 tiny:py-2 micro:py-2 nano:py-2',
},
} as const;

Expand Down
4 changes: 2 additions & 2 deletions apps/ratewise/src/config/seo-metadata/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,7 @@ export const HOMEPAGE_HOW_TO: HowToData = {
{
position: 3,
name: '查看結果與趨勢',
text: '換算結果即時顯示,可展開查看 7~30 天歷史趨勢圖,並切換現金或即期匯率以符合不同換匯情境。',
text: '換算結果即時顯示,往下捲動可查看 30 天歷史趨勢圖,並切換現金或即期匯率以符合不同換匯情境。',
},
{
position: 4,
Expand Down Expand Up @@ -1158,7 +1158,7 @@ export const GUIDE_HOW_TO_STEPS = [
{
position: 7,
name: '查看歷史趨勢',
text: '展開匯率卡片可查看過去 30 天的歷史趨勢圖與波動資訊。',
text: '往下捲動即可查看過去 30 天的歷史趨勢圖與波動資訊。',
image: '/screenshots/desktop-features.png',
},
{
Expand Down
4 changes: 2 additions & 2 deletions apps/ratewise/src/config/seo-metadata/currency-landing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ export function getCurrencyLandingPageContent(
{
position: 4,
name: '查看趨勢與歷史',
text: '展開匯率卡片可查看 7~30 天歷史趨勢圖,幫助判斷換匯時機。',
text: '往下捲動即可查看 30 天歷史趨勢卡,幫助判斷換匯時機。',
},
],
// A03 修正:highlights 收斂為 3 條,其中 2 條來自 persona 特化欄位。
Expand Down Expand Up @@ -592,7 +592,7 @@ export function getReverseCurrencyLandingPageContent(
{
position: 4,
name: '觀察趨勢,決定換匯時機',
text: '展開匯率卡片查看 7~30 天歷史趨勢,了解近期匯率高低區間,協助判斷換匯時機。',
text: '往下捲動查看 30 天歷史趨勢卡,了解近期匯率高低區間,協助判斷換匯時機。',
},
],
// A03 修正:highlights 收斂為 3 條,其中 2 條來自 persona 特化欄位。
Expand Down
Loading
Loading