Viewport: - Add `viewport` export with `viewport-fit=cover` (enables CSS safe-area-inset-* on bezel-less iOS/Android) and `interactive-widget=resizes-content` (prevents layout jumps from virtual keyboard open/close) iOS zoom prevention: - input.tsx, textarea.tsx: change text-sm (14px) → text-base sm:text-sm (16px) on mobile; iOS Safari auto-zooms any input with font-size < 16px Touch targets: - button.tsx: default h-10 → h-11 (44px), sm h-8 → h-9 (36px), icon h-10→h-11 matching the 44×44px iOS HIG and WCAG 2.5.5 minimum - Add touch-manipulation to buttons, inputs, textarea to eliminate 300ms tap delay Dialog → mobile bottom sheet: - On mobile (< 640px): anchored to bottom with full width, rounded top corners, slide-in-from-bottom / slide-out-to-bottom animation, drag handle affordance, max-h-[90svh] + overflow-y-auto to prevent viewport overflow - Safe area padding: pb-[max(1.25rem,env(safe-area-inset-bottom))] - On desktop (sm+): unchanged centered modal with zoom animation - Close button gets a 32×32 tap area on mobile Mobile navigation: - New bottom-nav.tsx: persistent 3-tab bottom bar (مسیر/مرکز/لیگ) shown below md: breakpoint, hidden on desktop; 56px height + safe-area-inset-bottom; active tab gets heavier stroke and scale - Only shown for authenticated non-admin users - app-shell.tsx: injects BottomNav; main content gets dynamic padding-bottom to clear the bar (56px + safe area) on mobile, reverts to py-10 on md: - Footer: safe-area-inset-bottom padding so content clears iPhone home indicator Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
67 lines
2.3 KiB
TypeScript
67 lines
2.3 KiB
TypeScript
import type { Metadata, Viewport } from "next";
|
|
import localFont from "next/font/local";
|
|
import "./globals.css";
|
|
import { Providers } from "@/components/providers";
|
|
import { AppShell } from "@/components/app-shell";
|
|
import { PageTransition } from "@/components/page-transition";
|
|
|
|
/** Vazirmatn FD — default UI; Latin digits render as Persian glyphs. */
|
|
const vazirmatnFd = localFont({
|
|
src: [
|
|
{ path: "./fonts/Vazirmatn-FD-Thin.woff2", weight: "100", style: "normal" },
|
|
{ path: "./fonts/Vazirmatn-FD-ExtraLight.woff2", weight: "200", style: "normal" },
|
|
{ path: "./fonts/Vazirmatn-FD-Light.woff2", weight: "300", style: "normal" },
|
|
{ path: "./fonts/Vazirmatn-FD-Regular.woff2", weight: "400", style: "normal" },
|
|
{ path: "./fonts/Vazirmatn-FD-Medium.woff2", weight: "500", style: "normal" },
|
|
{ path: "./fonts/Vazirmatn-FD-SemiBold.woff2", weight: "600", style: "normal" },
|
|
{ path: "./fonts/Vazirmatn-FD-Bold.woff2", weight: "700", style: "normal" },
|
|
{ path: "./fonts/Vazirmatn-FD-ExtraBold.woff2", weight: "800", style: "normal" },
|
|
{ path: "./fonts/Vazirmatn-FD-Black.woff2", weight: "900", style: "normal" },
|
|
],
|
|
display: "swap",
|
|
variable: "--font-vazirmatn",
|
|
fallback: ["Tahoma", "Segoe UI", "Arial", "sans-serif"],
|
|
});
|
|
|
|
/** Plain Vazirmatn — Latin digits stay Latin (passwords, usernames, captcha, URLs…). */
|
|
const vazirmatnLatin = localFont({
|
|
src: "./fonts/Vazirmatn-Variable.woff2",
|
|
weight: "100 900",
|
|
display: "swap",
|
|
variable: "--font-vazirmatn-latin",
|
|
fallback: ["Tahoma", "Segoe UI", "Arial", "sans-serif"],
|
|
});
|
|
|
|
export const metadata: Metadata = {
|
|
title: "پرگار",
|
|
description: "مسیر یادگیری واقعی پرگار",
|
|
};
|
|
|
|
export const viewport: Viewport = {
|
|
width: "device-width",
|
|
initialScale: 1,
|
|
viewportFit: "cover",
|
|
interactiveWidget: "resizes-content",
|
|
};
|
|
|
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<html
|
|
lang="fa"
|
|
dir="rtl"
|
|
className={`${vazirmatnFd.variable} ${vazirmatnLatin.variable}`}
|
|
suppressHydrationWarning
|
|
>
|
|
<body
|
|
className={`${vazirmatnFd.className} min-h-screen bg-background font-sans text-foreground antialiased`}
|
|
>
|
|
<Providers>
|
|
<AppShell>
|
|
<PageTransition>{children}</PageTransition>
|
|
</AppShell>
|
|
</Providers>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|