fix: align homepage section titles and unify download button color

- Wrap the Categories and Popular sections in the same responsive
  max-width container (820/1080/1180) used by Official and Latest, so all
  four section titles line up vertically on desktop.
- Official carousel arrows: hide the arrow at the edge already reached, and
  snap one card per click (reveal the next/previous card fully, keeping a
  small peek) instead of a fixed-pixel scroll.
- Show the pagination dots on desktop too (was mobile-only).
- RecommendedCard download button icon: text-ark-gold -> text-white to match
  the file bubble / popular / video download buttons.

Desktop-scoped; mobile layout unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
TerryM
2026-05-31 02:32:15 +08:00
parent 320e09cc87
commit e35573083a
2 changed files with 48 additions and 13 deletions

View File

@@ -172,8 +172,8 @@ export function RecommendedCard({
type="button" type="button"
className={ className={
useFigmaDesign useFigmaDesign
? "relative z-20 flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-[#191921] text-ark-gold outline-none transition hover:bg-[#22232D] active:scale-95 focus-visible:ring-2 focus-visible:ring-ark-gold/80 disabled:cursor-wait" ? "relative z-20 flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-[#191921] text-white outline-none transition hover:bg-[#22232D] active:scale-95 focus-visible:ring-2 focus-visible:ring-ark-gold/80 disabled:cursor-wait"
: "relative z-20 shrink-0 rounded-lg p-1 text-ark-gold outline-none transition hover:bg-ark-gold/10 active:scale-95 focus-visible:ring-2 focus-visible:ring-ark-gold/80 disabled:cursor-wait" : "relative z-20 shrink-0 rounded-lg p-1 text-white outline-none transition hover:bg-ark-gold/10 active:scale-95 focus-visible:ring-2 focus-visible:ring-ark-gold/80 disabled:cursor-wait"
} }
title={isDownloading ? t("downloading") : t("download")} title={isDownloading ? t("downloading") : t("download")}
aria-label={isDownloading ? t("downloading") : t("download")} aria-label={isDownloading ? t("downloading") : t("download")}

View File

@@ -196,8 +196,39 @@ export function Home() {
}; };
}, [rec.length]); }, [rec.length]);
// Reveal one more card per click, fully, inside the arrow "lanes" (the left
// and right padding the arrows float in) so the edge card is never half-shown
// or tucked under an arrow.
// - Right arrow: bring the first card clipped on the right so its RIGHT edge
// rests just left of the right arrow.
// - Left arrow: bring the last card clipped on the left so its LEFT edge
// rests just right of the left arrow.
const scrollRec = (dir: 1 | -1) => { const scrollRec = (dir: 1 | -1) => {
recRowRef.current?.scrollBy({ left: dir * 280, behavior: "smooth" }); const row = recRowRef.current;
if (!row) return;
const children = Array.from(row.children) as HTMLElement[];
if (children.length === 0) return;
const rowLeft = row.getBoundingClientRect().left;
const style = getComputedStyle(row);
const padLeft = parseFloat(style.paddingLeft) || 0;
const padRight = parseFloat(style.paddingRight) || 0;
const epsilon = 2;
let delta: number;
if (dir === 1) {
const laneRight = row.clientWidth - padRight;
const next = children
.map((c) => c.getBoundingClientRect().right - rowLeft)
.find((right) => right > laneRight + epsilon);
delta = next !== undefined ? next - laneRight : 0;
} else {
const lefts = children
.map((c) => c.getBoundingClientRect().left - rowLeft)
.filter((left) => left < padLeft - epsilon);
delta = lefts.length ? lefts[lefts.length - 1] - padLeft : -row.scrollLeft;
}
const maxScroll = Math.max(0, row.scrollWidth - row.clientWidth);
const target = Math.max(0, Math.min(maxScroll, row.scrollLeft + delta));
row.scrollTo({ left: target, behavior: "smooth" });
}; };
useEffect(() => { useEffect(() => {
@@ -241,6 +272,7 @@ export function Home() {
<Reveal delay={0}> <Reveal delay={0}>
<section id="categories" className="scroll-mt-16 md:scroll-mt-24"> <section id="categories" className="scroll-mt-16 md:scroll-mt-24">
<div className="mx-auto max-w-full md:max-w-[820px] lg:max-w-[1080px] xl:max-w-[1180px]">
<div className="px-4 md:px-0"> <div className="px-4 md:px-0">
<SectionHeader <SectionHeader
title={t("categorySection")} title={t("categorySection")}
@@ -331,6 +363,7 @@ export function Home() {
</Reveal> </Reveal>
))} ))}
</div> </div>
</div>
</section> </section>
</Reveal> </Reveal>
@@ -358,7 +391,7 @@ export function Home() {
))} ))}
</div> </div>
<div <div
className="flex h-[30px] items-center justify-center gap-1.5 md:hidden" className="flex h-[30px] items-center justify-center gap-1.5"
aria-label="Recommended pagination" aria-label="Recommended pagination"
> >
{Array.from({ length: recommendedDotCount }).map((_, index) => ( {Array.from({ length: recommendedDotCount }).map((_, index) => (
@@ -439,15 +472,17 @@ export function Home() {
{hasPopular ? ( {hasPopular ? (
<Reveal> <Reveal>
<section id="popular" className="scroll-mt-16 md:scroll-mt-24"> <section id="popular" className="scroll-mt-16 md:scroll-mt-24">
<div className="px-4 md:px-0"> <div className="mx-auto max-w-full md:max-w-[820px] lg:max-w-[1080px] xl:max-w-[1180px]">
<SectionHeader <div className="px-4 md:px-0">
title={t("popularSection")} <SectionHeader
viewAllTo="/browse?sort=popular" title={t("popularSection")}
viewAllLabel={t("viewAll")} viewAllTo="/browse?sort=popular"
/> viewAllLabel={t("viewAll")}
</div> />
<div className="mt-2.5 md:mt-7"> </div>
<PopularRankList posts={popularPosts} categories={cats} /> <div className="mt-2.5 md:mt-7">
<PopularRankList posts={popularPosts} categories={cats} />
</div>
</div> </div>
</section> </section>
</Reveal> </Reveal>