+43
src/components/common/Tooltip.tsx
+43
src/components/common/Tooltip.tsx
···
1
+
import { ReactNode, useState } from "react";
2
+
import { Info } from "lucide-react";
3
+
4
+
interface TooltipProps {
5
+
content: ReactNode;
6
+
children?: ReactNode;
7
+
className?: string;
8
+
}
9
+
10
+
export default function Tooltip({ content, children, className = "" }: TooltipProps) {
11
+
const [isVisible, setIsVisible] = useState(false);
12
+
13
+
return (
14
+
<div className="relative inline-block">
15
+
<button
16
+
type="button"
17
+
className={`inline-flex items-center ${className}`}
18
+
onMouseEnter={() => setIsVisible(true)}
19
+
onMouseLeave={() => setIsVisible(false)}
20
+
onFocus={() => setIsVisible(true)}
21
+
onBlur={() => setIsVisible(false)}
22
+
aria-label="More information"
23
+
>
24
+
{children || (
25
+
<Info className="w-4 h-4 text-purple-750/70 dark:text-cyan-250/70 hover:text-purple-900 dark:hover:text-cyan-100 transition-colors" />
26
+
)}
27
+
</button>
28
+
29
+
{isVisible && (
30
+
<div
31
+
className="absolute left-1/2 -translate-x-1/2 bottom-full mb-2 w-72 px-3 py-2 bg-slate-900 dark:bg-slate-800 text-white text-sm rounded-lg shadow-lg z-50 pointer-events-none"
32
+
role="tooltip"
33
+
>
34
+
<div className="relative">
35
+
{content}
36
+
{/* Arrow */}
37
+
<div className="absolute left-1/2 -translate-x-1/2 -bottom-5 w-0 h-0 border-l-8 border-l-transparent border-r-8 border-r-transparent border-t-8 border-t-slate-900 dark:border-t-slate-800" />
38
+
</div>
39
+
</div>
40
+
)}
41
+
</div>
42
+
);
43
+
}
+1
src/components/common/index.tsx
+1
src/components/common/index.tsx
···
17
17
export { default as SetupPrompt } from "./SetupPrompt";
18
18
export { default as Skeleton } from "./Skeleton";
19
19
export { default as Toggle } from "./Toggle";
20
+
export { default as Tooltip } from "./Tooltip";
20
21
21
22
// Export Stats components
22
23
export { StatItem, StatBadge, StatsGroup } from "./Stats";
+15
-4
src/components/login/HeroSection.tsx
+15
-4
src/components/login/HeroSection.tsx
···
1
1
import FireflyLogo from "../../assets/at-firefly-logo.svg?react";
2
+
import Tooltip from "../common/Tooltip";
2
3
3
4
interface HeroSectionProps {
4
5
reducedMotion?: boolean;
···
16
17
<h1 className="text-5xl md:text-6xl font-bold bg-gradient-to-r from-purple-600 via-cyan-500 to-pink-500 dark:from-cyan-300 dark:via-purple-300 dark:to-pink-300 bg-clip-text text-transparent mb-3 md:mb-4">
17
18
ATlast
18
19
</h1>
19
-
<p className="text-xl md:text-2xl lg:text-2xl text-purple-900 dark:text-cyan-100 mb-2 font-medium">
20
-
Find Your Light in the ATmosphere
20
+
<p className="text-xl md:text-2xl lg:text-2xl text-purple-900 dark:text-cyan-100 mb-2 font-medium inline-flex items-center gap-2 justify-center md:justify-start w-full">
21
+
<span>Find Your Light in the ATmosphere</span>
22
+
<Tooltip
23
+
content={
24
+
<div>
25
+
<p className="font-semibold mb-1">What's the ATmosphere?</p>
26
+
<p className="text-xs leading-relaxed">
27
+
The <strong>ATmosphere</strong> is an open network of social apps that use one login, including Bluesky, Blacksky, Tangled, Flashes, Leaflet, and more. Once you login, you can explore the social apps that let you follow different people!
28
+
</p>
29
+
</div>
30
+
}
31
+
/>
21
32
</p>
22
-
<p className="text-purple-750 dark:text-cyan-250 mb-6">
23
-
Reconnect with your internet, one firefly at a time ✨
33
+
<p className="text-purple-750 dark:text-cyan-250 mb-2">
34
+
Reconnect with your internet, one firefly at a time
24
35
</p>
25
36
26
37
{/* Decorative firefly trail - only show if motion enabled */}