fix: TopBar mobile compact + map markers positioning
- Make TopBar buttons smaller on mobile (icon-only) - Remove language label on mobile, reduce padding/gaps - Add scrollbar-hide utility for clean overflow - Fix marker CSS conflict that broke positioning - Remove conflicting .sw-appear animation that overrode marker styles Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
a1ffc300ff
commit
91dd114544
|
|
@ -90,13 +90,14 @@ function ActionButton({ icon, label, active, color = "default", badge, onClick,
|
||||||
<motion.button
|
<motion.button
|
||||||
type="button"
|
type="button"
|
||||||
className={`
|
className={`
|
||||||
relative h-8 px-3 rounded-full
|
relative h-7 w-7 sm:h-8 sm:w-auto sm:px-3 rounded-full
|
||||||
bg-gradient-to-br ${colorClasses[color]}
|
bg-gradient-to-br ${colorClasses[color]}
|
||||||
border backdrop-blur-sm
|
border backdrop-blur-sm
|
||||||
font-semibold text-sm
|
font-semibold text-sm
|
||||||
inline-flex items-center justify-center gap-2
|
inline-flex items-center justify-center gap-1.5
|
||||||
transition-colors duration-200
|
transition-colors duration-200
|
||||||
disabled:opacity-50 disabled:cursor-not-allowed
|
disabled:opacity-50 disabled:cursor-not-allowed
|
||||||
|
flex-shrink-0
|
||||||
${active ? "ring-2 ring-accent/50 shadow-glow-sm" : ""}
|
${active ? "ring-2 ring-accent/50 shadow-glow-sm" : ""}
|
||||||
`}
|
`}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
|
@ -106,15 +107,15 @@ function ActionButton({ icon, label, active, color = "default", badge, onClick,
|
||||||
whileHover={!disabled ? "hover" : undefined}
|
whileHover={!disabled ? "hover" : undefined}
|
||||||
whileTap={!disabled ? "tap" : undefined}
|
whileTap={!disabled ? "tap" : undefined}
|
||||||
>
|
>
|
||||||
<i className={`${icon} text-sm`} />
|
<i className={`${icon} text-xs sm:text-sm`} />
|
||||||
{label && <span className="hidden sm:inline text-xs">{label}</span>}
|
{label && <span className="hidden sm:inline text-xs">{label}</span>}
|
||||||
|
|
||||||
{badge > 0 && (
|
{badge > 0 && (
|
||||||
<motion.span
|
<motion.span
|
||||||
className="absolute -top-1.5 -right-1.5 min-w-[18px] h-[18px] px-1
|
className="absolute -top-1 -right-1 min-w-[16px] h-[16px] px-0.5
|
||||||
rounded-full bg-red-500 text-white text-[10px] font-bold
|
rounded-full bg-red-500 text-white text-[9px] font-bold
|
||||||
flex items-center justify-center
|
flex items-center justify-center
|
||||||
border-2 border-surface-900 shadow-lg"
|
border border-surface-900 shadow-lg"
|
||||||
variants={badgeVariants}
|
variants={badgeVariants}
|
||||||
initial="initial"
|
initial="initial"
|
||||||
animate="animate"
|
animate="animate"
|
||||||
|
|
@ -189,15 +190,15 @@ function UserChip({ authenticated, loading, username, avatarUrl, onClick }) {
|
||||||
const displayUsername = useMemo(() => {
|
const displayUsername = useMemo(() => {
|
||||||
const raw = (username || "user").toString();
|
const raw = (username || "user").toString();
|
||||||
if (raw.includes("@")) return "user";
|
if (raw.includes("@")) return "user";
|
||||||
if (raw.length <= 9) return raw;
|
if (raw.length <= 6) return raw;
|
||||||
return raw.slice(0, 9) + "..";
|
return raw.slice(0, 6) + "..";
|
||||||
}, [username]);
|
}, [username]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.button
|
<motion.button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex items-center gap-2 px-2 py-1.5 rounded-full
|
className="flex items-center gap-1 sm:gap-2 px-1.5 sm:px-2 py-1 sm:py-1.5 rounded-full
|
||||||
bg-surface-900/40 backdrop-blur-sm
|
bg-surface-900/40 backdrop-blur-sm flex-shrink-0
|
||||||
border border-surface-600/30
|
border border-surface-600/30
|
||||||
hover:border-accent/50 hover:shadow-glow-sm
|
hover:border-accent/50 hover:shadow-glow-sm
|
||||||
transition-all duration-200"
|
transition-all duration-200"
|
||||||
|
|
@ -529,8 +530,9 @@ export default function TopBarNew({ theme = "dark", onChangeTheme, onIslandsClic
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right: Actions */}
|
{/* Right: Actions */}
|
||||||
<div className="flex items-center gap-2 px-2 py-1.5 rounded-full
|
<div className="flex items-center gap-1 sm:gap-2 px-1 sm:px-2 py-1 sm:py-1.5 rounded-full
|
||||||
bg-surface-900/50 backdrop-blur-sm border border-surface-700/30">
|
bg-surface-900/50 backdrop-blur-sm border border-surface-700/30
|
||||||
|
flex-shrink-0 max-w-[60vw] sm:max-w-none overflow-x-auto scrollbar-hide">
|
||||||
|
|
||||||
{/* User chip */}
|
{/* User chip */}
|
||||||
<UserChip
|
<UserChip
|
||||||
|
|
@ -541,17 +543,19 @@ export default function TopBarNew({ theme = "dark", onChangeTheme, onIslandsClic
|
||||||
onClick={() => authenticated && onUserIsland?.(username)}
|
onClick={() => authenticated && onUserIsland?.(username)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* PWA Install */}
|
{/* PWA Install - hide on very small screens */}
|
||||||
{showInstallBtn && (
|
{showInstallBtn && (
|
||||||
|
<div className="hidden xs:block">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon="fa-solid fa-download"
|
icon="fa-solid fa-download"
|
||||||
color="purple"
|
color="purple"
|
||||||
onClick={() => promptInstall()}
|
onClick={() => promptInstall()}
|
||||||
title={t('topbar.installApp')}
|
title={t('topbar.installApp')}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Theme - mobile */}
|
{/* Theme - mobile only */}
|
||||||
<div className="md:hidden" ref={themeBtnRef}>
|
<div className="md:hidden" ref={themeBtnRef}>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon="fa-solid fa-palette"
|
icon="fa-solid fa-palette"
|
||||||
|
|
@ -562,11 +566,10 @@ export default function TopBarNew({ theme = "dark", onChangeTheme, onIslandsClic
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Language */}
|
{/* Language - icon only on mobile */}
|
||||||
<div ref={langBtnRef}>
|
<div ref={langBtnRef}>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon="fa-solid fa-globe"
|
icon="fa-solid fa-globe"
|
||||||
label={i18n.language?.toUpperCase().slice(0, 2)}
|
|
||||||
color="cyan"
|
color="cyan"
|
||||||
active={showLangMenu}
|
active={showLangMenu}
|
||||||
onClick={toggleLangMenu}
|
onClick={toggleLangMenu}
|
||||||
|
|
@ -586,10 +589,9 @@ export default function TopBarNew({ theme = "dark", onChangeTheme, onIslandsClic
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Auth */}
|
{/* Auth - icon only on mobile */}
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={authenticated ? "fa-solid fa-right-from-bracket" : "fa-solid fa-right-to-bracket"}
|
icon={authenticated ? "fa-solid fa-right-from-bracket" : "fa-solid fa-right-to-bracket"}
|
||||||
label={authenticated ? t('topbar.logout') : t('topbar.login')}
|
|
||||||
color="green"
|
color="green"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (authenticated) { trackEvent("logout_click"); logout(); }
|
if (authenticated) { trackEvent("logout_click"); logout(); }
|
||||||
|
|
|
||||||
|
|
@ -1562,21 +1562,8 @@ body[data-theme="light"] .sw-modal-x{
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Entry animation for markers */
|
/* Note: .sw-appear and .sw-appear-in are defined earlier in this file
|
||||||
.sw-appear {
|
for .post-pin and .post-pin--simple elements. Don't override here. */
|
||||||
opacity: 0;
|
|
||||||
animation: markerFadeSlide 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sw-appear-in {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stagger effect for multiple markers */
|
|
||||||
.sw-appear:nth-child(2) { animation-delay: 0.05s; }
|
|
||||||
.sw-appear:nth-child(3) { animation-delay: 0.1s; }
|
|
||||||
.sw-appear:nth-child(4) { animation-delay: 0.15s; }
|
|
||||||
.sw-appear:nth-child(5) { animation-delay: 0.2s; }
|
|
||||||
|
|
||||||
/* Cluster marker styles */
|
/* Cluster marker styles */
|
||||||
.sw-cluster-marker {
|
.sw-cluster-marker {
|
||||||
|
|
|
||||||
|
|
@ -584,3 +584,32 @@ body[data-theme="light"] .text-high-contrast {
|
||||||
.animate-enter-right {
|
.animate-enter-right {
|
||||||
animation: enterFromRight 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
animation: enterFromRight 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
Scrollbar Utilities
|
||||||
|
───────────────────────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
/* Hide scrollbar but keep functionality */
|
||||||
|
.scrollbar-hide {
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollbar-hide::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Thin scrollbar */
|
||||||
|
.scrollbar-thin {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollbar-thin::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(96, 165, 250, 0.3);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue