Fix narrow PWA layout on tablet with standalone width rules.

Allow any orientation in manifest, widen content and enable dual-column mode in installed apps on tablet viewports, with iOS display-mode fallback.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-11 09:07:48 +08:00
parent 234ce18756
commit 98b83a5f75
8 changed files with 61 additions and 5 deletions
+30
View File
@@ -126,4 +126,34 @@
.zen-card {
@apply bg-card/85 backdrop-blur-sm;
}
/* PWA 安装后:iPad 等平板视口常 <1024px,单独放宽宽度与双栏 */
@media (display-mode: standalone) and (min-width: 768px) and (max-width: 1919px) {
.site-content-shell {
max-width: 72rem;
}
.site-content-shell-narrow {
max-width: 64rem;
}
.mode-workspace-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
}
/* iOS PWA 兜底:部分系统不识别 display-mode 媒体查询 */
@media (min-width: 768px) and (max-width: 1919px) {
html[data-display-mode="standalone"] .site-content-shell {
max-width: 72rem;
}
html[data-display-mode="standalone"] .site-content-shell-narrow {
max-width: 64rem;
}
html[data-display-mode="standalone"] .mode-workspace-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
+6
View File
@@ -3,6 +3,7 @@ import type { Metadata, Viewport } from "next";
import React from "react";
import Umami from "@/components/umami";
import PwaProvider from "@/components/pwa/pwa-provider";
import PwaDisplayMode from "@/components/pwa/pwa-display-mode";
import { ThemeProvider } from "next-themes";
export const metadata: Metadata = {
@@ -14,6 +15,10 @@ export const metadata: Metadata = {
title: "知命阁",
statusBarStyle: "default",
},
other: {
"mobile-web-app-capable": "yes",
"apple-mobile-web-app-capable": "yes",
},
manifest: "/manifest.webmanifest",
};
@@ -49,6 +54,7 @@ export default function RootLayout({
disableTransitionOnChange
>
{children}
<PwaDisplayMode />
<PwaProvider />
</ThemeProvider>
<Umami />
+1 -1
View File
@@ -8,7 +8,7 @@ export default function manifest(): MetadataRoute.Manifest {
"融合周易智慧与人工智能 — 易经学习、六爻算卦、生辰八字、综合测算",
start_url: "/",
display: "standalone",
orientation: "portrait-primary",
orientation: "any",
background_color: "#f5f5f4",
theme_color: "#525252",
lang: "zh-CN",
+1 -1
View File
@@ -6,7 +6,7 @@ function Footer() {
return (
<footer className="relative z-10 flex items-center justify-center gap-1 border-t border-border/40 bg-card/70 py-4 text-xs text-muted-foreground/80">
<div
className={`${SITE_WIDTH_INNER} flex items-center justify-center gap-1`}
className={`site-content-shell ${SITE_WIDTH_INNER} flex items-center justify-center gap-1`}
>
<span className="italic"></span>
<span className="text-muted-foreground/60">·</span>
+1 -1
View File
@@ -37,7 +37,7 @@ export default function Header() {
return (
<header className="relative z-10 border-b border-border/40 bg-card/70 py-3 shadow-sm backdrop-blur-md">
<div
className={`${SITE_WIDTH_INNER} grid grid-cols-[1fr_auto] items-center gap-y-2 sm:flex sm:justify-between`}
className={`site-content-shell ${SITE_WIDTH_INNER} grid grid-cols-[1fr_auto] items-center gap-y-2 sm:flex sm:justify-between`}
>
<Link href="/" className="flex items-center gap-2">
<TaijiIcon />
+1 -1
View File
@@ -13,7 +13,7 @@ export function ModeWorkspace({
}) {
return (
<div className="py-6 lg:py-8">
<div className="grid gap-6 lg:grid-cols-2 lg:items-stretch lg:gap-8 xl:gap-10">
<div className="mode-workspace-grid grid gap-6 lg:grid-cols-2 lg:items-stretch lg:gap-8 xl:gap-10">
<div className="grid h-full min-h-0 auto-rows-fr gap-5 lg:min-h-[calc(100vh-9rem)]">
{children}
</div>
+2 -1
View File
@@ -27,7 +27,8 @@ export default function PageShell({
<Header />
<main
className={cn(
"mx-auto flex w-full flex-1 flex-col border-border/40 bg-background sm:border-x",
"site-content-shell mx-auto flex w-full flex-1 flex-col border-border/40 bg-background sm:border-x",
width === "narrow" && "site-content-shell-narrow",
SHELL_WIDTH[width],
className,
)}
+19
View File
@@ -0,0 +1,19 @@
"use client";
import { useEffect } from "react";
/** iOS / iPad PWA 部分版本不识别 display-mode 媒体查询,用 data 属性兜底 */
export default function PwaDisplayMode() {
useEffect(() => {
const standalone =
window.matchMedia("(display-mode: standalone)").matches ||
(window.navigator as Navigator & { standalone?: boolean }).standalone ===
true;
if (standalone) {
document.documentElement.dataset.displayMode = "standalone";
}
}, []);
return null;
}