mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-10 13:15:47 -05:00
Front page styling tweaks (#2906)
This commit is contained in:
@@ -150,6 +150,7 @@
|
||||
.firstPlacers {
|
||||
margin-inline: auto;
|
||||
margin-top: var(--s-5);
|
||||
margin-block-end: var(--s-4);
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,39 +49,49 @@ export default function FrontPage() {
|
||||
);
|
||||
}
|
||||
|
||||
function SeasonBanner() {
|
||||
const { t } = useTranslation(["front"]);
|
||||
const season = Seasons.next(new Date()) ?? Seasons.currentOrPrevious()!;
|
||||
const _previousSeason = Seasons.previous();
|
||||
function useSeasonData() {
|
||||
const season = Seasons.next() ?? Seasons.currentOrPrevious()!;
|
||||
const previousSeason = Seasons.previous();
|
||||
|
||||
const isInFuture = new Date() < season.starts;
|
||||
const isShowingPreviousSeason = previousSeason?.nth === season.nth;
|
||||
|
||||
return { season, isInFuture, isShowingPreviousSeason };
|
||||
}
|
||||
|
||||
function SeasonDates({
|
||||
season,
|
||||
className,
|
||||
}: {
|
||||
season: ReturnType<typeof useSeasonData>["season"];
|
||||
className: string;
|
||||
}) {
|
||||
const isMounted = useIsMounted();
|
||||
const { formatDate } = useTimeFormat();
|
||||
|
||||
const isInFuture = new Date() < season.starts;
|
||||
const isShowingPreviousSeason = _previousSeason?.nth === season.nth;
|
||||
return isMounted ? (
|
||||
<div className={className}>
|
||||
{formatDate(season.starts, { month: "long", day: "numeric" })} -{" "}
|
||||
{formatDate(season.ends, { month: "long", day: "numeric" })}
|
||||
</div>
|
||||
) : (
|
||||
<div className={clsx(className, "invisible")}>X</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SeasonBanner() {
|
||||
const { t } = useTranslation(["front"]);
|
||||
const { season, isInFuture, isShowingPreviousSeason } = useSeasonData();
|
||||
|
||||
if (isShowingPreviousSeason) return null;
|
||||
|
||||
return (
|
||||
<div className="stack xs">
|
||||
<div className={styles.seasonBannerMobileOnly}>
|
||||
<Link to={SENDOUQ_PAGE} className={styles.seasonBanner}>
|
||||
<div className={styles.seasonBannerHeader}>
|
||||
{t("front:sq.season", { nth: season.nth })}
|
||||
</div>
|
||||
{isMounted ? (
|
||||
<div className={styles.seasonBannerDates}>
|
||||
{formatDate(season.starts, {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}{" "}
|
||||
-{" "}
|
||||
{formatDate(season.ends, {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<div className={clsx(styles.seasonBannerDates, "invisible")}>X</div>
|
||||
)}
|
||||
<SeasonDates season={season} className={styles.seasonBannerDates} />
|
||||
<Image
|
||||
className={styles.seasonBannerImg}
|
||||
path={sqHeaderGuyImageUrl(season.nth)}
|
||||
@@ -99,6 +109,33 @@ function SeasonBanner() {
|
||||
);
|
||||
}
|
||||
|
||||
function SeasonCard() {
|
||||
const { t } = useTranslation(["front"]);
|
||||
const { season, isInFuture, isShowingPreviousSeason } = useSeasonData();
|
||||
|
||||
if (isShowingPreviousSeason) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Link to={SENDOUQ_PAGE} className={styles.seasonCard}>
|
||||
<div className={styles.seasonCardHeader}>
|
||||
{t("front:sq.season", { nth: season.nth })}
|
||||
</div>
|
||||
<SeasonDates season={season} className={styles.seasonCardDates} />
|
||||
<Image
|
||||
className={styles.seasonCardImg}
|
||||
path={sqHeaderGuyImageUrl(season.nth)}
|
||||
alt=""
|
||||
/>
|
||||
</Link>
|
||||
<Link to={SENDOUQ_PAGE} className={styles.seasonCardButton}>
|
||||
<Image path={navIconUrl("sendouq")} size={16} alt="" />
|
||||
{isInFuture ? t("front:sq.prepare") : t("front:sq.participate")}
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function LeagueBanner() {
|
||||
const showBannerFor = import.meta.env.VITE_SHOW_BANNER_FOR_SEASON;
|
||||
if (!showBannerFor) return null;
|
||||
@@ -113,7 +150,7 @@ function LeagueBanner() {
|
||||
}
|
||||
|
||||
function ResultHighlights() {
|
||||
const { t } = useTranslation(["front"]);
|
||||
const { t } = useTranslation(["front", "common"]);
|
||||
const data = useLoaderData<typeof loader>();
|
||||
|
||||
// should not happen
|
||||
@@ -127,33 +164,21 @@ function ResultHighlights() {
|
||||
|
||||
const season = Seasons.currentOrPrevious()!;
|
||||
|
||||
const recentResults = (
|
||||
<>
|
||||
<h2
|
||||
className={clsx(
|
||||
styles.resultHighlightsTitle,
|
||||
styles.resultHighlightsTitleTournaments,
|
||||
)}
|
||||
>
|
||||
{t("front:showcase.results")}
|
||||
</h2>
|
||||
<div className={styles.tournamentCardsSpacer}>
|
||||
{data.tournaments.results.map((tournament) => (
|
||||
<TournamentCard
|
||||
key={tournament.id}
|
||||
tournament={tournament}
|
||||
withRelativeTime
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={clsx(styles.resultHighlights, "overflow-x-auto scrollbar")}
|
||||
className={clsx(
|
||||
styles.resultHighlights,
|
||||
styles.resultHighlightsTop,
|
||||
"overflow-x-auto scrollbar",
|
||||
)}
|
||||
>
|
||||
<div className={styles.seasonCardDesktopOnly}>
|
||||
<h2 className={styles.resultHighlightsTitle}>
|
||||
{t("common:pages.sendouq")}
|
||||
</h2>
|
||||
<SeasonCard />
|
||||
</div>
|
||||
<div className="stack sm text-center">
|
||||
<h2 className={styles.resultHighlightsTitle}>
|
||||
{t("front:leaderboards.topPlayers")}
|
||||
@@ -178,15 +203,26 @@ function ResultHighlights() {
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<div className="stack sm text-center mobile-hidden">
|
||||
{recentResults}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={clsx(styles.resultHighlights, "overflow-x-auto scrollbar")}
|
||||
>
|
||||
<div className="stack sm text-center desktop-hidden">
|
||||
{recentResults}
|
||||
<div className={clsx(styles.resultHighlights, "scrollbar")}>
|
||||
<div className="stack sm text-center">
|
||||
<h2
|
||||
className={clsx(
|
||||
styles.resultHighlightsTitle,
|
||||
styles.resultHighlightsTitleTournaments,
|
||||
)}
|
||||
>
|
||||
{t("front:showcase.results")}
|
||||
</h2>
|
||||
<div className={styles.tournamentCardsSpacer}>
|
||||
{data.tournaments.results.map((tournament) => (
|
||||
<TournamentCard
|
||||
key={tournament.id}
|
||||
tournament={tournament}
|
||||
withRelativeTime
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -97,6 +97,30 @@
|
||||
transition: all 0.2s ease-out;
|
||||
}
|
||||
|
||||
.seasonBannerMobileOnly {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--s-1);
|
||||
}
|
||||
|
||||
.seasonCardDesktopOnly {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: var(--s-2);
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 750px) {
|
||||
.seasonBannerMobileOnly {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.seasonCardDesktopOnly {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.seasonBannerLink:hover svg {
|
||||
translate: 2px;
|
||||
}
|
||||
@@ -105,7 +129,8 @@
|
||||
display: flex;
|
||||
gap: var(--s-2);
|
||||
height: 100%;
|
||||
padding: var(--s-2);
|
||||
padding: 0 var(--s-2) var(--s-2) var(--s-2);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.resultHighlights {
|
||||
@@ -114,6 +139,20 @@
|
||||
padding: var(--s-2);
|
||||
}
|
||||
|
||||
.resultHighlights > div {
|
||||
flex: 3 1 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.resultHighlights > .seasonCardDesktopOnly {
|
||||
flex: 2 1 0;
|
||||
}
|
||||
|
||||
.resultHighlightsTop {
|
||||
--card-width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.resultHighlightsTitle {
|
||||
font-size: var(--font-xs);
|
||||
}
|
||||
@@ -123,9 +162,68 @@
|
||||
margin-inline-start: 65px;
|
||||
}
|
||||
|
||||
.leaderboard {
|
||||
.seasonCard {
|
||||
--l: 50%;
|
||||
|
||||
min-width: var(--card-width);
|
||||
max-width: var(--card-width);
|
||||
flex: 1;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
var(--color-bg-higher),
|
||||
var(--l),
|
||||
var(--color-bg)
|
||||
);
|
||||
border-radius: var(--radius-box);
|
||||
border: var(--border-style-high);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: var(--s-2);
|
||||
position: relative;
|
||||
color: var(--color-text);
|
||||
overflow: hidden;
|
||||
transition: --l 0.2s ease-out;
|
||||
}
|
||||
|
||||
.seasonCard:hover {
|
||||
--l: 75%;
|
||||
}
|
||||
|
||||
.seasonCardHeader {
|
||||
font-size: var(--font-lg);
|
||||
font-weight: var(--weight-bold);
|
||||
padding-block-start: var(--s-2);
|
||||
}
|
||||
|
||||
.seasonCardDates {
|
||||
font-size: var(--font-xs);
|
||||
font-weight: var(--weight-semi);
|
||||
color: var(--color-text-lighter);
|
||||
}
|
||||
|
||||
.seasonCardImg {
|
||||
position: absolute;
|
||||
width: 140px;
|
||||
bottom: 0;
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
.seasonCardButton {
|
||||
font-size: var(--font-2xs);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg-higher);
|
||||
border-radius: var(--radius-selector);
|
||||
width: max-content;
|
||||
height: var(--selector-size);
|
||||
display: flex;
|
||||
gap: var(--s-1);
|
||||
padding: var(--s-0-5) var(--s-1-5);
|
||||
}
|
||||
|
||||
.leaderboard {
|
||||
width: 100%;
|
||||
min-width: 14rem;
|
||||
max-width: var(--card-width);
|
||||
background-color: var(--color-bg-higher);
|
||||
border-radius: var(--radius-box);
|
||||
display: flex;
|
||||
|
||||
@@ -98,6 +98,8 @@ test.describe("SendouQ", () => {
|
||||
test("Challenge flow - send challenge, report match, seasons page, quick rejoin with replay", async ({
|
||||
page,
|
||||
}) => {
|
||||
test.slow();
|
||||
|
||||
await seed(page); // DEFAULT seed includes full groups for ADMIN and NZAP
|
||||
await impersonate(page, ADMIN_ID);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user