Skip to content

Commit 32d1631

Browse files
committed
feat: add "best viewed on desktop" tip modal for mobile visitors
Shows a dismissible glassmorphic modal on viewports <= 768px warning that interactive visualizations work best on larger screens. Dismissed state persists for the session via sessionStorage.
1 parent 555c358 commit 32d1631

2 files changed

Lines changed: 133 additions & 0 deletions

File tree

apps/app/src/app/page.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useGenerativeUIExamples, useExampleSuggestions } from "@/hooks";
66
import { ExplainerCardsPortal } from "@/components/explainer-cards";
77
import { DemoGallery, type DemoItem } from "@/components/demo-gallery";
88
import { GridIcon } from "@/components/demo-gallery/grid-icon";
9+
import { DesktopTipModal } from "@/components/desktop-tip-modal";
910
import { CopilotChat, useAgent, useCopilotKit } from "@copilotkit/react-core/v2";
1011

1112
export default function HomePage() {
@@ -115,6 +116,8 @@ export default function HomePage() {
115116
onClose={() => setDemoDrawerOpen(false)}
116117
onTryDemo={handleTryDemo}
117118
/>
119+
120+
<DesktopTipModal />
118121
</>
119122
);
120123
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
"use client";
2+
3+
import { useEffect, useState } from "react";
4+
5+
const DISMISSED_KEY = "desktop-tip-dismissed";
6+
7+
export function DesktopTipModal() {
8+
const [visible, setVisible] = useState(false);
9+
10+
useEffect(() => {
11+
// Only show on narrow viewports and if not previously dismissed
12+
const mq = window.matchMedia("(max-width: 768px)");
13+
if (mq.matches && !sessionStorage.getItem(DISMISSED_KEY)) {
14+
setVisible(true);
15+
}
16+
}, []);
17+
18+
if (!visible) return null;
19+
20+
const dismiss = () => {
21+
sessionStorage.setItem(DISMISSED_KEY, "1");
22+
setVisible(false);
23+
};
24+
25+
return (
26+
<div
27+
onClick={dismiss}
28+
style={{
29+
position: "fixed",
30+
inset: 0,
31+
zIndex: 9999,
32+
display: "flex",
33+
alignItems: "center",
34+
justifyContent: "center",
35+
background: "rgba(0,0,0,0.45)",
36+
backdropFilter: "blur(6px)",
37+
WebkitBackdropFilter: "blur(6px)",
38+
padding: 24,
39+
}}
40+
>
41+
<div
42+
onClick={(e) => e.stopPropagation()}
43+
style={{
44+
background: "var(--color-glass-dark, rgba(255,255,255,0.9))",
45+
backdropFilter: "blur(16px)",
46+
WebkitBackdropFilter: "blur(16px)",
47+
border: "1px solid var(--color-border-glass, rgba(255,255,255,0.3))",
48+
borderRadius: "var(--radius-xl, 16px)",
49+
boxShadow: "var(--shadow-glass, 0 4px 30px rgba(0,0,0,0.1))",
50+
padding: "32px 28px",
51+
maxWidth: 340,
52+
width: "100%",
53+
textAlign: "center" as const,
54+
fontFamily: "var(--font-family)",
55+
}}
56+
>
57+
{/* Monitor icon */}
58+
<div
59+
style={{
60+
width: 56,
61+
height: 56,
62+
margin: "0 auto 20px",
63+
borderRadius: 14,
64+
background: "linear-gradient(135deg, rgba(190,194,255,0.15), rgba(133,224,206,0.12))",
65+
display: "flex",
66+
alignItems: "center",
67+
justifyContent: "center",
68+
}}
69+
>
70+
<svg
71+
width="28"
72+
height="28"
73+
viewBox="0 0 24 24"
74+
fill="none"
75+
stroke="var(--color-lilac-dark, #9599CC)"
76+
strokeWidth="1.8"
77+
strokeLinecap="round"
78+
strokeLinejoin="round"
79+
>
80+
<rect x="2" y="3" width="20" height="14" rx="2" />
81+
<path d="M8 21h8" />
82+
<path d="M12 17v4" />
83+
</svg>
84+
</div>
85+
86+
<h2
87+
style={{
88+
fontSize: 18,
89+
fontWeight: 700,
90+
color: "var(--text-primary, #374151)",
91+
margin: "0 0 8px",
92+
letterSpacing: "-0.01em",
93+
}}
94+
>
95+
Best viewed on desktop
96+
</h2>
97+
98+
<p
99+
style={{
100+
fontSize: 14,
101+
color: "var(--text-secondary, #6b7280)",
102+
margin: "0 0 24px",
103+
lineHeight: 1.5,
104+
}}
105+
>
106+
This experience includes interactive visualizations that work best on larger screens.
107+
</p>
108+
109+
<button
110+
onClick={dismiss}
111+
style={{
112+
width: "100%",
113+
padding: "10px 20px",
114+
borderRadius: 999,
115+
fontSize: 14,
116+
fontWeight: 600,
117+
fontFamily: "var(--font-family)",
118+
color: "#fff",
119+
background: "linear-gradient(135deg, var(--color-lilac-dark, #9599CC), var(--color-mint-dark, #1B936F))",
120+
border: "none",
121+
boxShadow: "0 1px 4px rgba(149,153,204,0.3)",
122+
cursor: "pointer",
123+
}}
124+
>
125+
Continue anyway
126+
</button>
127+
</div>
128+
</div>
129+
);
130+
}

0 commit comments

Comments
 (0)