Skeleton loader for footer

This commit is contained in:
Anton Gunnarsson
2024-11-14 13:25:33 +01:00
parent 1980ae4350
commit 16882fc20a
7 changed files with 151 additions and 14 deletions

View File

@@ -1,11 +1,17 @@
import { env } from "@/env/server" import { env } from "@/env/server"
import CurrentLoadingSpinner from "@/components/Current/LoadingSpinner" import CurrentLoadingSpinner from "@/components/Current/LoadingSpinner"
import LoadingSpinner from "@/components/LoadingSpinner" import { FooterDetailsSkeleton } from "@/components/Footer/Details"
import { FooterNavigationSkeleton } from "@/components/Footer/Navigation"
export default function LoadingFooter() { export default function LoadingFooter() {
if (env.HIDE_FOR_NEXT_RELEASE) { if (env.HIDE_FOR_NEXT_RELEASE) {
return <CurrentLoadingSpinner /> return <CurrentLoadingSpinner />
} }
return <LoadingSpinner /> return (
<footer>
<FooterNavigationSkeleton />
<FooterDetailsSkeleton />
</footer>
)
} }

View File

@@ -3,6 +3,7 @@ import { getFooter, getLanguageSwitcher } from "@/lib/trpc/memoizedRequests"
import { getIconByIconName } from "@/components/Icons/get-icon-by-icon-name" import { getIconByIconName } from "@/components/Icons/get-icon-by-icon-name"
import Image from "@/components/Image" import Image from "@/components/Image"
import LanguageSwitcher from "@/components/LanguageSwitcher" import LanguageSwitcher from "@/components/LanguageSwitcher"
import SkeletonShimmer from "@/components/SkeletonShimmer"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import Footnote from "@/components/TempDesignSystem/Text/Footnote" import Footnote from "@/components/TempDesignSystem/Text/Footnote"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
@@ -92,3 +93,40 @@ export default async function FooterDetails() {
</section> </section>
) )
} }
export async function FooterDetailsSkeleton() {
const lang = getLang()
const intl = await getIntl()
const currentYear = new Date().getFullYear()
return (
<section className={styles.details}>
<div className={styles.topContainer}>
<Link href={`/${lang}`}>
<Image
alt="Scandic Hotels logo"
height={22}
src="/_static/img/scandic-logotype-white.svg"
width={103}
/>
</Link>
<nav className={styles.socialNav}>
<SkeletonShimmer width="10ch" height="20px" contrast="dark" />
</nav>
</div>
<div className={styles.bottomContainer}>
<div className={styles.copyrightContainer}>
<Footnote type="label" textTransform="uppercase">
© {currentYear}{" "}
{intl.formatMessage({ id: "Copyright all rights reserved" })}
</Footnote>
</div>
<div className={styles.navigationContainer}>
<nav className={styles.navigation}>
<SkeletonShimmer width="40ch" height="20px" contrast="dark" />
</nav>
</div>
</div>
</section>
)
}

View File

@@ -1,4 +1,5 @@
import { ArrowRightIcon } from "@/components/Icons" import { ArrowRightIcon } from "@/components/Icons"
import SkeletonShimmer from "@/components/SkeletonShimmer"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
@@ -30,3 +31,24 @@ export default function FooterMainNav({ mainLinks }: FooterMainNavProps) {
</nav> </nav>
) )
} }
export function FooterMainNavSkeleton() {
const items = Array.from({ length: 4 }).map((_, i) => i)
return (
<nav className={styles.mainNavigation}>
<ul className={styles.mainNavigationList}>
{items.map((x) => (
<li key={x} className={styles.mainNavigationItem}>
<Subtitle color="baseTextMediumContrast" type="two" asChild>
<span className={styles.mainNavigationLink}>
<SkeletonShimmer width="80%" />
<ArrowRightIcon color="peach80" />
</span>
</Subtitle>
</li>
))}
</ul>
</nav>
)
}

View File

@@ -1,6 +1,6 @@
import Image from "@/components/Image" import Image from "@/components/Image"
import SkeletonShimmer from "@/components/SkeletonShimmer"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption" import Caption from "@/components/TempDesignSystem/Text/Caption"
import { getLang } from "@/i18n/serverContext" import { getLang } from "@/i18n/serverContext"
@@ -80,3 +80,46 @@ export default function FooterSecondaryNav({
</div> </div>
) )
} }
export function FooterSecondaryNavSkeleton() {
return (
<div className={styles.secondaryNavigation}>
<nav className={styles.secondaryNavigationGroup}>
<SkeletonShimmer width="10ch" />
<ul className={styles.secondaryNavigationList}>
<li className={styles.appDownloadItem}>
<SkeletonShimmer width="16ch" />
</li>
<li className={styles.appDownloadItem}>
<SkeletonShimmer width="16ch" />
</li>
</ul>
</nav>
<nav className={styles.secondaryNavigationGroup}>
<SkeletonShimmer width="20ch" />
<ul className={styles.secondaryNavigationList}>
<li className={styles.secondaryNavigationItem}>
<SkeletonShimmer width="25ch" />
</li>
</ul>
</nav>
<nav className={styles.secondaryNavigationGroup}>
<SkeletonShimmer width="15ch" />
<ul className={styles.secondaryNavigationList}>
<li className={styles.secondaryNavigationItem}>
<SkeletonShimmer width="30ch" />
</li>
<li className={styles.secondaryNavigationItem}>
<SkeletonShimmer width="36ch" />
</li>
<li className={styles.secondaryNavigationItem}>
<SkeletonShimmer width="12ch" />
</li>
<li className={styles.secondaryNavigationItem}>
<SkeletonShimmer width="20ch" />
</li>
</ul>
</nav>
</div>
)
}

View File

@@ -1,7 +1,7 @@
import { getFooter } from "@/lib/trpc/memoizedRequests" import { getFooter } from "@/lib/trpc/memoizedRequests"
import FooterMainNav from "./MainNav" import FooterMainNav, { FooterMainNavSkeleton } from "./MainNav"
import FooterSecondaryNav from "./SecondaryNav" import FooterSecondaryNav, { FooterSecondaryNavSkeleton } from "./SecondaryNav"
import styles from "./navigation.module.css" import styles from "./navigation.module.css"
@@ -10,6 +10,7 @@ export default async function FooterNavigation() {
if (!footer) { if (!footer) {
return null return null
} }
return ( return (
<section className={styles.section}> <section className={styles.section}>
<div className={styles.maxWidth}> <div className={styles.maxWidth}>
@@ -22,3 +23,14 @@ export default async function FooterNavigation() {
</section> </section>
) )
} }
export function FooterNavigationSkeleton() {
return (
<section className={styles.section}>
<div className={styles.maxWidth}>
<FooterMainNavSkeleton />
<FooterSecondaryNavSkeleton />
</div>
</section>
)
}

View File

@@ -3,13 +3,15 @@ import styles from "./skeleton.module.css"
export default function SkeletonShimmer({ export default function SkeletonShimmer({
height, height,
width, width,
contrast = "light",
}: { }: {
height?: string height?: string
width?: string width?: string
contrast?: "light" | "dark"
}) { }) {
return ( return (
<div <div
className={styles.shimmer} className={`${styles.shimmer} ${styles[contrast]}`}
style={{ style={{
height: height, height: height,
width: width, width: width,

View File

@@ -1,9 +1,29 @@
.shimmer.dark {
--shimmer-background: rgba(255, 255, 255, 0.1);
--shimmer: linear-gradient(
120deg,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.1) 40%,
rgba(255, 255, 255, 0.1) 60%,
rgba(255, 255, 255, 0) 100%
);
}
.shimmer { .shimmer {
background-color: hsla(0, 0%, 85%, 0.5); --shimmer-background: rgba(217, 217, 217, 0.5);
--shimmer: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.2) 20%,
rgba(255, 255, 255, 0.5) 60%,
rgba(255, 255, 255, 0) 100%
);
background-color: var(--shimmer-background);
position: relative; position: relative;
overflow: hidden; overflow: hidden;
border-radius: 4px; border-radius: 4px;
min-height: 1em; min-height: 1em;
min-width: 2ch;
} }
.shimmer::after { .shimmer::after {
position: absolute; position: absolute;
@@ -12,13 +32,7 @@
bottom: 0; bottom: 0;
left: 0; left: 0;
transform: translateX(-100%); transform: translateX(-100%);
background-image: linear-gradient( background-image: var(--shimmer);
90deg,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.2) 20%,
rgba(255, 255, 255, 0.5) 60%,
rgba(255, 255, 255, 0) 100%
);
animation: shimmer 3s infinite; animation: shimmer 3s infinite;
content: ""; content: "";
} }