Skip to content

Commit 248fbb4

Browse files
committed
Polish landing page after Tailwind 4 migration
- Sticky scroll-aware nav: transparent over the hero, zinc-800/95 once scrolled (IntersectionObserver, no backdrop-blur to avoid scroll jank, instant toggle), inset to align with the content width - Logo declared at its true 340x100 ratio (eliminates layout shift) - Hero screenshot fades in only once decoded (no white half-load flash) - Background: html black backstop (no mobile white flicker) and nudge the object-position up so the robot isn't clipped - Center the carousel/Middle section and hero to the same width as the nav - Remove overflow:hidden ancestors that broke position: sticky
1 parent 8c7775e commit 248fbb4

6 files changed

Lines changed: 106 additions & 79 deletions

File tree

src/components/Menu.astro

Lines changed: 80 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,60 +4,91 @@ import maintainerrLogo from '../assets/logo.svg'
44
import '../styles/global.css'
55
---
66

7-
<nav class="relative max-w-6xl mx-auto flex items-center justify-between p-4">
8-
<Image
9-
src={maintainerrLogo}
10-
alt="Maintainerr Logo"
11-
width={200}
12-
height={50}
13-
class=""
14-
loading="eager"
15-
/>
7+
<!-- Sentinel marking the top of the page; the nav goes opaque once it scrolls out. -->
8+
<div
9+
data-nav-sentinel
10+
aria-hidden="true"
11+
class="absolute left-0 top-0 h-10 w-px"
12+
>
13+
</div>
1614

17-
<button
18-
data-dropdown-toggle="mobile-menu"
19-
class="text-white md:hidden"
20-
aria-controls="mobile-menu"
21-
aria-expanded="false"
22-
>
23-
<svg
24-
xmlns="http://www.w3.org/2000/svg"
25-
class="h-5 w-5"
26-
viewBox="0 0 20 20"
27-
fill="currentColor"
28-
>
29-
<path
30-
fill-rule="evenodd"
31-
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
32-
clip-rule="evenodd"></path>
33-
</svg>
34-
</button>
15+
<nav class="sticky top-0 z-50">
16+
<div class="mx-auto max-w-7xl px-4">
17+
<div data-nav class="flex items-center justify-between px-4 py-2">
18+
<Image
19+
src={maintainerrLogo}
20+
alt="Maintainerr Logo"
21+
width={340}
22+
height={100}
23+
class="w-[150px]"
24+
loading="eager"
25+
/>
3526

36-
<div class="hidden md:flex">
37-
<a
38-
href="https://docs.maintainerr.info"
39-
class="text-white hover:text-amber-500 px-4 font-semibold"
40-
>Documentation</a
41-
>
42-
<a
43-
class="text-white hover:text-amber-500 px-4 font-semibold"
44-
href="https://features.maintainerr.info/?view=most-wanted"
45-
target="_blank">Feature Requests</a
46-
>
47-
<a
48-
class="text-white hover:text-amber-500 px-4 font-semibold"
49-
href="https://discord.maintainerr.info"
50-
target="_blank"
51-
>Discord
52-
</a>
53-
<a
54-
class="text-white hover:text-amber-500 px-4 font-semibold"
55-
href="https://github.com/maintainerr/maintainerr"
56-
target="_blank">Github</a
57-
>
27+
<button
28+
data-dropdown-toggle="mobile-menu"
29+
class="text-white md:hidden"
30+
aria-controls="mobile-menu"
31+
aria-expanded="false"
32+
>
33+
<svg
34+
xmlns="http://www.w3.org/2000/svg"
35+
class="h-5 w-5"
36+
viewBox="0 0 20 20"
37+
fill="currentColor"
38+
>
39+
<path
40+
fill-rule="evenodd"
41+
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
42+
clip-rule="evenodd"></path>
43+
</svg>
44+
</button>
45+
46+
<div class="hidden md:flex">
47+
<a
48+
href="https://docs.maintainerr.info"
49+
class="text-white hover:text-amber-500 px-4 font-semibold"
50+
>Documentation</a
51+
>
52+
<a
53+
class="text-white hover:text-amber-500 px-4 font-semibold"
54+
href="https://features.maintainerr.info/?view=most-wanted"
55+
target="_blank">Feature Requests</a
56+
>
57+
<a
58+
class="text-white hover:text-amber-500 px-4 font-semibold"
59+
href="https://discord.maintainerr.info"
60+
target="_blank"
61+
>Discord
62+
</a>
63+
<a
64+
class="text-white hover:text-amber-500 px-4 font-semibold"
65+
href="https://github.com/maintainerr/maintainerr"
66+
target="_blank">Github</a
67+
>
68+
</div>
69+
</div>
5870
</div>
5971
</nav>
6072

73+
<script>
74+
// Transparent over the hero so the background image isn't covered; fade in
75+
// the zinc-800/40 bar only once scrolled into the content. Driven by an
76+
// IntersectionObserver (fires once per crossing) rather than the scroll
77+
// event, so a fast fling to the top doesn't flicker around the threshold.
78+
// No backdrop-blur: re-blurring every scroll frame caused noticeable jank.
79+
const nav = document.querySelector('[data-nav]')
80+
const sentinel = document.querySelector('[data-nav-sentinel]')
81+
if (nav && sentinel) {
82+
const io = new IntersectionObserver(
83+
([entry]) => {
84+
nav.classList.toggle('bg-zinc-800/95', !entry.isIntersecting)
85+
},
86+
{ threshold: 0 },
87+
)
88+
io.observe(sentinel)
89+
}
90+
</script>
91+
6192
<!-- Mobile dropdown -->
6293
<div id="mobile-menu" class="hidden z-50 w-64 mt-4 px-2">
6394
<ul

src/components/Middle.astro

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import StorageMetrics from '../assets/screenshots/StorageMetrics.png'
1313
import Calendar from '../assets/screenshots/Calendar.png'
1414
---
1515

16-
<div class="px-[2rem] py-[4rem]">
17-
<div class="gap-14 max-w-7xl grid grid-cols-1 md:grid-cols-2 m-auto">
16+
<div class="py-[4rem]">
17+
<div class="gap-14 max-w-7xl grid grid-cols-1 md:grid-cols-2 m-auto px-4">
1818
<div class="text-stone-400 font-semibold">
1919
<div
2020
class="text-transparent font-extrabold text-2xl mb-4 bg-clip-text bg-gradient-to-br from-amber-600 to-amber-400 tracking-tight"
@@ -60,7 +60,7 @@ import Calendar from '../assets/screenshots/Calendar.png'
6060
to find unwatched/unwanted media, and get rid of it.
6161
</p>
6262
</div>
63-
<div class="md:-mr-[6rem]">
63+
<div class="md:self-center">
6464
<div id="default-carousel" class="relative w-full" data-carousel="slide">
6565
<div class="relative h-40 overflow-hidden rounded-lg md:h-96">
6666
<div class="hidden duration-500 ease-linear" data-carousel-item>

src/components/Top.astro

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,28 @@ import mainSs from '../assets/screenshots/Collections.png'
1010
about your precious hard drive space being taken up by media, that isn't even
1111
being watched.
1212
</p>
13-
<div class="max-w-7xl px-3 flex items-center m-auto">
13+
<div class="max-w-7xl px-4 flex items-center justify-center m-auto">
1414
<Image
15-
class="rounded-lg shadow-lg"
15+
id="hero-screenshot"
16+
class="rounded-lg shadow-lg opacity-0 transition-opacity duration-700"
1617
src={mainSs}
1718
alt="Main Screenshot of Maintainerr"
1819
width={1280}
1920
height={800}
2021
loading="eager"
22+
decoding="async"
2123
/>
2224
</div>
25+
26+
<script>
27+
// Hold the hero screenshot hidden until it has fully decoded, then fade it
28+
// in — avoids the white half-loaded flash on first paint.
29+
const hero = document.getElementById(
30+
'hero-screenshot',
31+
) as HTMLImageElement | null
32+
if (hero) {
33+
const reveal = () => hero.classList.remove('opacity-0')
34+
if (hero.complete && hero.naturalWidth > 0) reveal()
35+
else hero.addEventListener('load', reveal, { once: true })
36+
}
37+
</script>

src/layouts/Layout.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ const siteLd = {
6969
<script type="application/ld+json" set:html={JSON.stringify(siteLd)} />
7070
</head>
7171
<body class="m-0 w-full h-full">
72-
<div class="relative min-h-screen w-full overflow-hidden">
72+
<div class="relative min-h-screen w-full">
7373
<slot />
7474
</div>
7575
<script>

src/pages/index.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import background from '../assets/background.png'
2424
format="webp"
2525
quality={60}
2626
loading="eager"
27-
class="fixed top-0 left-0 w-full h-full object-cover z-[-5]"
27+
class="fixed top-0 left-0 w-full h-full object-cover object-[center_25%] z-[-5]"
2828
/>
2929

3030
<!-- Gradient Overlay -->

src/styles/global.css

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,9 @@
22
/* Load the legacy config (content globs + Flowbite plugin) under Tailwind v4. */
33
@config '../../tailwind.config.cjs';
44

5-
/* Tailwind v4 ships a refreshed OKLCH palette; pin amber to the exact hex
6-
scale the Maintainerr app uses so the brand color doesn't shift on upgrade. */
7-
@theme {
8-
--color-amber-50: #fffbeb;
9-
--color-amber-100: #fef3c7;
10-
--color-amber-200: #fde68a;
11-
--color-amber-300: #fcd34d;
12-
--color-amber-400: #fbbf24;
13-
--color-amber-500: #f59e0b;
14-
--color-amber-600: #d97706;
15-
--color-amber-700: #b45309;
16-
--color-amber-800: #92400e;
17-
--color-amber-900: #78350f;
18-
--color-amber-950: #451a03;
19-
}
20-
21-
html,
22-
body {
23-
overflow-x: hidden;
24-
/* Dark backstop + no overscroll bounce: the page's visible background is a
25-
position:fixed image, so on mobile the address-bar resize and rubber-band
26-
overscroll would otherwise expose the transparent root as a white flicker. */
27-
overscroll-behavior: none;
5+
/* Black canvas backstop so the fixed background image never flashes white on
6+
mobile (address-bar resize / overscroll). Set on <html> only — setting it on
7+
<body> would paint over the z-index:-5 background image and hide it. */
8+
html {
289
background-color: #000;
2910
}

0 commit comments

Comments
 (0)