From 984d44a18b2c1e6a3a7053c3c7f19d11f7879e4d Mon Sep 17 00:00:00 2001 From: alireza Date: Wed, 26 Aug 2026 14:36:31 +0330 Subject: [PATCH] style: enforce Vazirmatn-FD Persian digits across world economy, bonds and entire dashboard --- frontend/src/routes/dashboard.tsx | 42 +++++++++++++++++-------------- frontend/src/styles.css | 15 ++++++----- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/frontend/src/routes/dashboard.tsx b/frontend/src/routes/dashboard.tsx index a2a6c35..82f9f96 100644 --- a/frontend/src/routes/dashboard.tsx +++ b/frontend/src/routes/dashboard.tsx @@ -79,7 +79,12 @@ import { LmeView } from "@/components/LmeView"; import { ImeView } from "@/components/ImeView"; import { Building2 } from "lucide-react"; -const API = API_BASE; // dev: "" (same-origin via Vite proxy); prod: VITE_API_BASE +const API = API_BASE; + +const faDigits = (s: string | number | null | undefined): string => { + if (s == null) return "—"; + return String(s).replace(/[0-9]/g, (d) => "۰۱۲۳۴۵۶۷۸۹"[+d]); +}; // dev: "" (same-origin via Vite proxy); prod: VITE_API_BASE export const Route = createFileRoute("/dashboard")({ head: () => ({ @@ -1365,8 +1370,7 @@ const STEEL_FLAGS: Record = { "Saudi Arabia": "🇸🇦", }; -// ASCII→Persian digits for any already-formatted string (keeps $ % T B . , # ▼ ▲) -const faDigits = (s: string) => s.replace(/[0-9]/g, (d) => "۰۱۲۳۴۵۶۷۸۹"[+d]); +// faDigits declared globally at top const usd = (n: number, frac = 2) => faDigits(n.toLocaleString("en-US", { minimumFractionDigits: frac, maximumFractionDigits: frac })); @@ -2747,8 +2751,8 @@ function WorldEconomyView() { const fmtCell = (col: (typeof WE_COLS)[number], val: number | null) => { if (val == null) return "—"; - if (col.gdp) return (val / 1000).toLocaleString("fa-IR", { maximumFractionDigits: 2 }) + " T$"; - return formatNumber(val) + (col.suffix || ""); + if (col.gdp) return faDigits((val / 1000).toFixed(2) + " T$"); + return faDigits(formatNumber(val) + (col.suffix || "")); }; const us10y = bonds.find((b) => b.code === "US10Y"); @@ -2851,12 +2855,12 @@ function WorldEconomyView() {
- {us10y?.yield ? us10y.yield.toFixed(3) + "%" : "—"} + {us10y?.yield ? faDigits(us10y.yield.toFixed(3) + "%") : "—"} {us10y && = 0 ? "up" : "down"} />}
- تغییر: {us10y ? (us10y.change_abs >= 0 ? "+" : "") + (us10y.change_abs * 100).toFixed(1) + " bps" : "—"} + تغییر: {us10y ? faDigits((us10y.change_abs >= 0 ? "+" : "") + (us10y.change_abs * 100).toFixed(1) + " bps") : "—"}
@@ -2867,12 +2871,12 @@ function WorldEconomyView() {
- {de10y?.yield ? de10y.yield.toFixed(3) + "%" : "—"} + {de10y?.yield ? faDigits(de10y.yield.toFixed(3) + "%") : "—"} {de10y && = 0 ? "up" : "down"} />}
- تغییر: {de10y ? (de10y.change_abs >= 0 ? "+" : "") + (de10y.change_abs * 100).toFixed(1) + " bps" : "—"} + تغییر: {de10y ? faDigits((de10y.change_abs >= 0 ? "+" : "") + (de10y.change_abs * 100).toFixed(1) + " bps") : "—"}
@@ -2883,12 +2887,12 @@ function WorldEconomyView() {
- {gb10y?.yield ? gb10y.yield.toFixed(3) + "%" : "—"} + {gb10y?.yield ? faDigits(gb10y.yield.toFixed(3) + "%") : "—"} {gb10y && = 0 ? "up" : "down"} />}
- تغییر: {gb10y ? (gb10y.change_abs >= 0 ? "+" : "") + (gb10y.change_abs * 100).toFixed(1) + " bps" : "—"} + تغییر: {gb10y ? faDigits((gb10y.change_abs >= 0 ? "+" : "") + (gb10y.change_abs * 100).toFixed(1) + " bps") : "—"}
@@ -2899,7 +2903,7 @@ function WorldEconomyView() {
= 0 ? "text-emerald-700" : "text-red-600")}> - {spread10_2 != null ? (spread10_2 >= 0 ? "+" : "") + (spread10_2 * 100).toFixed(1) + " bps" : "—"} + {spread10_2 != null ? faDigits((spread10_2 >= 0 ? "+" : "") + (spread10_2 * 100).toFixed(1) + " bps") : "—"} {(spread10_2 ?? 0) >= 0 ? "نرمال" : "وارونه (Inverted)"} @@ -2943,28 +2947,28 @@ function WorldEconomyView() {
- {b.tenor} + {faDigits(b.tenor)} - {b.yield != null ? b.yield.toFixed(3) + "%" : "—"} + {b.yield != null ? faDigits(b.yield.toFixed(3) + "%") : "—"} - {b.change_abs != null ? (b.change_abs >= 0 ? "+" : "") + (b.change_abs * 100).toFixed(1) : "—"} + {b.change_abs != null ? faDigits((b.change_abs >= 0 ? "+" : "") + (b.change_abs * 100).toFixed(1)) : "—"} - {b.low != null && b.high != null ? `${b.low.toFixed(2)} - ${b.high.toFixed(2)}` : "—"} + {b.low != null && b.high != null ? faDigits(`${b.low.toFixed(2)} - ${b.high.toFixed(2)}`) : "—"} = 0 ? "text-emerald-700" : "text-red-600")}> - {b.perf_w != null ? (b.perf_w >= 0 ? "+" : "") + b.perf_w.toFixed(2) + "%" : "—"} + {b.perf_w != null ? faDigits((b.perf_w >= 0 ? "+" : "") + b.perf_w.toFixed(2) + "%") : "—"} = 0 ? "text-emerald-700" : "text-red-600")}> - {b.perf_m != null ? (b.perf_m >= 0 ? "+" : "") + b.perf_m.toFixed(2) + "%" : "—"} + {b.perf_m != null ? faDigits((b.perf_m >= 0 ? "+" : "") + b.perf_m.toFixed(2) + "%") : "—"} = 0 ? "text-emerald-700" : "text-red-600")}> - {b.perf_y != null ? (b.perf_y >= 0 ? "+" : "") + b.perf_y.toFixed(2) + "%" : "—"} + {b.perf_y != null ? faDigits((b.perf_y >= 0 ? "+" : "") + b.perf_y.toFixed(2) + "%") : "—"} {b.sparkline && b.sparkline.length > 1 && ( diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 4d35acd..526c54c 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -1,3 +1,4 @@ +@import "vazirmatn/misc/Farsi-Digits/Vazirmatn-FD-font-face.css"; @import "vazirmatn/Vazirmatn-font-face.css"; @import "tailwindcss" source(none); @source "../src"; @@ -257,8 +258,8 @@ --color-chart-3: var(--chart-3); --color-chart-4: var(--chart-4); --color-chart-5: var(--chart-5); - --font-sans: Vazirmatn, system-ui, sans-serif; - --font-mono: Vazirmatn, system-ui, sans-serif; + --font-sans: "Vazirmatn FD", Vazirmatn, system-ui, sans-serif; + --font-mono: "Vazirmatn FD", Vazirmatn, system-ui, sans-serif; } :root { @@ -299,7 +300,7 @@ @layer base { * { border-color: var(--color-border); } /* Unzoomable PWA: block double-tap zoom (pinch handled by the viewport meta) */ - html, body { font-family: var(--font-sans); touch-action: manipulation; } + html, body { font-family: "Vazirmatn FD", Vazirmatn, system-ui, sans-serif; touch-action: manipulation; } body { background-color: var(--color-background); background-image: @@ -312,12 +313,14 @@ color: var(--color-foreground); direction: rtl; } - /* Enforce Vazirmatn for all numbers, tabular-nums, and monospace elements */ + /* Enforce Vazirmatn with Persian digits for all numbers, tabular-nums, and monospace elements */ .font-mono, .tabular-nums, span.font-mono, - div.font-mono { - font-family: Vazirmatn, system-ui, sans-serif !important; + div.font-mono, + td.tabular-nums, + span.tabular-nums { + font-family: "Vazirmatn FD", Vazirmatn, system-ui, sans-serif !important; font-feature-settings: "tnum" 1; font-variant-numeric: tabular-nums; }