Refine dashboard hero, mobile nav, animations and touch polish
- Stack the /me dashboard hero (avatar + info) on small screens instead of crowding them side by side; center the avatar and info on mobile - Respect prefers-reduced-motion in the Reveal scroll animation - Make the desktop pitch-in (NavDropdown) and mobile menu more consistent; give the mobile menu a max-height with overscroll containment so long navs scroll instead of overflowing on small phones - Add safe-area insets for notched devices on header, nav and footer - Polish whole-link content-cards (community tiles) with hover lift and a visible focus ring - Enforce a comfortable min touch height on all .btn buttons
This commit is contained in:
1 parent
c17ef0e7ab
commit
d4677972cd
4 files changed
+38
-8
No files matched your search
@@ -255,7 +255,7 @@ export default async function MePage({
|
|||||||
|
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<SurfaceCard
|
<SurfaceCard
|
||||||
className="relative p-6 flex items-start gap-6"
|
className="relative p-5 sm:p-6 flex flex-col sm:flex-row sm:items-start gap-5 sm:gap-6"
|
||||||
style={{
|
style={{
|
||||||
background: `linear-gradient(135deg, color-mix(in srgb, var(--color-primary) 12%, var(--color-surface)), var(--color-surface))`,
|
background: `linear-gradient(135deg, color-mix(in srgb, var(--color-primary) 12%, var(--color-surface)), var(--color-surface))`,
|
||||||
borderColor:
|
borderColor:
|
||||||
@@ -266,7 +266,7 @@ export default async function MePage({
|
|||||||
className="absolute -top-10 -right-10 w-40 h-40 rounded-full opacity-10"
|
className="absolute -top-10 -right-10 w-40 h-40 rounded-full opacity-10"
|
||||||
style={{ background: "var(--color-primary)" }}
|
style={{ background: "var(--color-primary)" }}
|
||||||
/>
|
/>
|
||||||
<div className="relative shrink-0 flex flex-col items-center gap-3">
|
<div className="relative shrink-0 flex flex-col items-center gap-3 mx-auto sm:mx-0">
|
||||||
<div
|
<div
|
||||||
className="absolute -inset-3 rounded-full opacity-[0.15] blur-[40px]"
|
className="absolute -inset-3 rounded-full opacity-[0.15] blur-[40px]"
|
||||||
style={{ background: "var(--color-primary)" }}
|
style={{ background: "var(--color-primary)" }}
|
||||||
@@ -302,7 +302,7 @@ export default async function MePage({
|
|||||||
Enter {hotelName}
|
Enter {hotelName}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0 pt-1">
|
<div className="flex-1 min-w-0 sm:pt-1 text-center sm:text-left">
|
||||||
<div
|
<div
|
||||||
className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-[11px] font-bold uppercase tracking-wider mb-3 border"
|
className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-[11px] font-bold uppercase tracking-wider mb-3 border"
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -551,6 +551,7 @@ a:hover {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
min-height: 40px;
|
||||||
transition: all 0.18s ease;
|
transition: all 0.18s ease;
|
||||||
background: color-mix(
|
background: color-mix(
|
||||||
in srgb,
|
in srgb,
|
||||||
@@ -892,6 +893,23 @@ tbody tr:hover {
|
|||||||
padding: 1.1rem;
|
padding: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* content-card used as a whole clickable element (e.g. community tiles) */
|
||||||
|
a.content-card {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a.content-card:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
a.content-card:focus-visible {
|
||||||
|
outline: 2px solid var(--color-primary-readable, var(--color-primary));
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
a.content-card {
|
||||||
|
transition:
|
||||||
|
box-shadow 0.2s ease,
|
||||||
|
transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
/* Responsive card grids (atom: 1 / sm2 / md3 / lg4). */
|
/* Responsive card grids (atom: 1 / sm2 / md3 / lg4). */
|
||||||
.card-grid {
|
.card-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -1392,6 +1410,16 @@ tbody tr:hover {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Safe-area insets for notched phones (header/nav/footer sit at screen edges) */
|
||||||
|
@supports (padding: max(0px)) {
|
||||||
|
.site-header,
|
||||||
|
nav,
|
||||||
|
footer {
|
||||||
|
padding-left: max(env(safe-area-inset-left), 0px);
|
||||||
|
padding-right: max(env(safe-area-inset-right), 0px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Admin (HK) semantic token remap ──────────────────────────────────────
|
/* ── Admin (HK) semantic token remap ──────────────────────────────────────
|
||||||
Shared shadcn UI primitives (Button, Badge, Input, Select, Card, Table,
|
Shared shadcn UI primitives (Button, Badge, Input, Select, Card, Table,
|
||||||
Tabs, Dialog, Switch, Checkbox, Tooltip, …) are built on the site's
|
Tabs, Dialog, Switch, Checkbox, Tooltip, …) are built on the site's
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export function MobileNav({
|
|||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{open && (
|
{open && (
|
||||||
<motion.div
|
<motion.div
|
||||||
className="absolute left-0 top-full mt-2 w-[min(86vw,320px)] z-50 origin-top-left"
|
className="absolute left-0 top-full mt-2 w-[min(88vw,360px)] z-50 origin-top-left"
|
||||||
style={{ backgroundColor: "transparent" }}
|
style={{ backgroundColor: "transparent" }}
|
||||||
variants={mobileMenuVariants}
|
variants={mobileMenuVariants}
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
@@ -71,7 +71,7 @@ export function MobileNav({
|
|||||||
exit="exit"
|
exit="exit"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="flex flex-col gap-y-1 p-2 rounded-2xl border shadow-xl
|
className="flex flex-col gap-y-1 p-2 rounded-2xl border shadow-xl max-h-[calc(100dvh-5rem)] overflow-y-auto overscroll-contain
|
||||||
bg-[var(--color-dropdown,var(--color-surface,#fff))]"
|
bg-[var(--color-dropdown,var(--color-surface,#fff))]"
|
||||||
style={{
|
style={{
|
||||||
borderColor:
|
borderColor:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { motion } from "motion/react";
|
import { motion, useReducedMotion } from "motion/react";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
interface RevealProps {
|
interface RevealProps {
|
||||||
@@ -10,12 +10,14 @@ interface RevealProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Reveal({ children, className, delay = 0 }: RevealProps) {
|
export function Reveal({ children, className, delay = 0 }: RevealProps) {
|
||||||
|
const prefersReducedMotion = useReducedMotion();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
className={className}
|
className={className}
|
||||||
initial={{ opacity: 0, y: 24 }}
|
initial={{ opacity: 0, y: prefersReducedMotion ? 0 : 24 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true, margin: "-50px" }}
|
viewport={{ once: true, margin: "-40px" }}
|
||||||
transition={{ duration: 0.45, delay, ease: [0.25, 0.1, 0.25, 1] }}
|
transition={{ duration: 0.45, delay, ease: [0.25, 0.1, 0.25, 1] }}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
Reference in new issue
Block a user