fix(BOOK-204): Added dynamic h1 to startpage

Approved-by: Christel Westerberg
This commit is contained in:
Erik Tiekstra
2025-11-26 14:26:11 +00:00
parent 05ed0533fb
commit 6ac8267c31
14 changed files with 199 additions and 155 deletions

View File

@@ -5,16 +5,28 @@ import {
import InfoCard from "@/components/ContentType/StartPage/InfoCard" import InfoCard from "@/components/ContentType/StartPage/InfoCard"
import { Section } from "@/components/Section" import { Section } from "@/components/Section"
import SectionHeader from "@/components/Section/Header/Deprecated" import { SectionHeader } from "@/components/Section/Header"
import Card from "@/components/TempDesignSystem/Card" import Card from "@/components/TempDesignSystem/Card"
import Grids from "@/components/TempDesignSystem/Grids" import Grids from "@/components/TempDesignSystem/Grids"
import LoyaltyCard from "@/components/TempDesignSystem/LoyaltyCard" import LoyaltyCard from "@/components/TempDesignSystem/LoyaltyCard"
import TeaserCard from "@/components/TempDesignSystem/TeaserCard" import TeaserCard from "@/components/TempDesignSystem/TeaserCard"
import type { CardsGridProps } from "@/types/components/blocks/cardsGrid" import type { CardsGrid as CardsGridBlock } from "@scandic-hotels/trpc/types/blocks"
import type { VariantProps } from "class-variance-authority"
import type { headingVariants } from "@/components/Section/Header/headingVariants"
import type { StackableGridProps } from "../TempDesignSystem/Grids/Stackable/stackable" import type { StackableGridProps } from "../TempDesignSystem/Grids/Stackable/stackable"
export default function CardsGrid({ cards_grid }: CardsGridProps) { interface CardsGridProps extends Pick<CardsGridBlock, "cards_grid"> {
headingLevel?: "h1" | "h2"
headingTypography?: VariantProps<typeof headingVariants>["typography"]
}
export default function CardsGrid({
cards_grid,
headingLevel = "h2",
headingTypography = "Title/sm",
}: CardsGridProps) {
let columns: StackableGridProps["columns"] let columns: StackableGridProps["columns"]
switch (cards_grid.layout) { switch (cards_grid.layout) {
@@ -34,10 +46,10 @@ export default function CardsGrid({ cards_grid }: CardsGridProps) {
return ( return (
<Section> <Section>
<SectionHeader <SectionHeader
title={cards_grid.title} heading={cards_grid.title}
preamble={cards_grid.preamble} preamble={cards_grid.preamble}
headingAs="h3" headingLevel={headingLevel}
headingLevel="h2" typography={headingTypography}
link={cards_grid.link} link={cards_grid.link}
/> />
<Grids.Stackable columns={columns}> <Grids.Stackable columns={columns}>

View File

@@ -5,15 +5,27 @@ import { useState } from "react"
import { Carousel } from "@/components/Carousel" import { Carousel } from "@/components/Carousel"
import ContentCard from "@/components/ContentCard" import ContentCard from "@/components/ContentCard"
import { Section } from "@/components/Section" import { Section } from "@/components/Section"
import SectionHeader from "@/components/Section/Header/Deprecated" import { SectionHeader } from "@/components/Section/Header"
import SectionLink from "@/components/Section/Link" import SectionLink from "@/components/Section/Link"
import TabFilters from "@/components/TabFilters" import TabFilters from "@/components/TabFilters"
import styles from "./carouselCards.module.css" import styles from "./carouselCards.module.css"
import type { CarouselCardsProps } from "@/types/components/blocks/carouselCards" import type { CarouselCards as CarouselCardsBlock } from "@scandic-hotels/trpc/types/blocks"
import type { VariantProps } from "class-variance-authority"
export default function CarouselCards({ carousel_cards }: CarouselCardsProps) { import type { headingVariants } from "@/components/Section/Header/headingVariants"
interface CarouselCardsProps
extends Pick<CarouselCardsBlock, "carousel_cards"> {
headingLevel?: "h1" | "h2"
headingTypography?: VariantProps<typeof headingVariants>["typography"]
}
export default function CarouselCards({
carousel_cards,
headingLevel = "h2",
headingTypography = "Title/sm",
}: CarouselCardsProps) {
const { const {
heading, heading,
preamble, preamble,
@@ -37,10 +49,10 @@ export default function CarouselCards({ carousel_cards }: CarouselCardsProps) {
return ( return (
<Section> <Section>
<SectionHeader <SectionHeader
title={heading} heading={heading}
preamble={preamble} preamble={preamble}
headingLevel="h2" headingLevel={headingLevel}
headingAs="h3" typography={headingTypography}
link={link} link={link}
/> />
{filterCategories.length > 0 && activeFilter && ( {filterCategories.length > 0 && activeFilter && (

View File

@@ -2,32 +2,27 @@
position: relative; position: relative;
overflow: hidden; overflow: hidden;
height: 100vh; height: 100vh;
&::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(
180deg,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0.36) 50%,
rgba(0, 0, 0, 0.75) 100%
);
pointer-events: none;
z-index: 1;
}
} }
.bodyText { .bodyText {
text-align: center; text-align: center;
color: var(--Base-Text-Inverted); color: var(--Base-Text-Inverted);
} }
.container::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(
180deg,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0.36) 50%,
rgba(0, 0, 0, 0.75) 100%
);
pointer-events: none;
z-index: 1;
}
@media screen and (min-width: 768px) {
.container {
height: 880px;
}
}
.content { .content {
position: absolute; position: absolute;
inset: 0; inset: 0;
@@ -54,16 +49,10 @@
gap: var(--Space-x1); gap: var(--Space-x1);
} }
.buttonWrapper { .button {
min-width: 180px; min-width: 180px;
} }
@media screen and (max-width: 767px) {
.buttonWrapper:not(:only-child) {
min-width: 135px;
}
}
.image { .image {
max-width: 100%; max-width: 100%;
height: 100%; height: 100%;
@@ -73,3 +62,15 @@
.scriptedText { .scriptedText {
display: inline-block; display: inline-block;
} }
@media screen and (max-width: 767px) {
.button:not(:only-child) {
min-width: 135px;
}
}
@media screen and (min-width: 768px) {
.container {
height: 880px;
}
}

View File

@@ -1,7 +1,5 @@
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
import Image from "@scandic-hotels/design-system/Image" import Image from "@scandic-hotels/design-system/Image"
import { OldDSButton as Button } from "@scandic-hotels/design-system/OldDSButton"
import Link from "@scandic-hotels/design-system/OldDSLink"
import Title from "@scandic-hotels/design-system/Title"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
import BiroScript from "@/components/TempDesignSystem/Text/BiroScript" import BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
@@ -12,10 +10,15 @@ import type { FullWidthCampaign } from "@/types/trpc/routers/contentstack/startP
interface FullWidthCampaignProps { interface FullWidthCampaignProps {
content: FullWidthCampaign content: FullWidthCampaign
headingLevel?: "h1" | "h2"
} }
export default function FullWidthCampaign({ content }: FullWidthCampaignProps) { export default function FullWidthCampaign({
content,
headingLevel = "h2",
}: FullWidthCampaignProps) {
const { background_image, primary_button, secondary_button } = content const { background_image, primary_button, secondary_button } = content
const Hx = headingLevel
return ( return (
<div className={styles.container}> <div className={styles.container}>
@@ -36,53 +39,38 @@ export default function FullWidthCampaign({ content }: FullWidthCampaignProps) {
</BiroScript> </BiroScript>
</div> </div>
<div className={styles.mainContent}> <div className={styles.mainContent}>
<Title <Typography variant="Title/mdLowCase">
color="baseText" <Hx className={styles.bodyText}>{content.heading}</Hx>
textAlign="center" </Typography>
textTransform="capitalize" <Typography variant="Body/Lead text">
level="h3" <p className={styles.bodyText}>{content.body_text}</p>
>
{content.heading}
</Title>
<Typography className={styles.bodyText} variant="Body/Lead text">
<p>{content.body_text}</p>
</Typography> </Typography>
<div className={styles.buttons}> <div className={styles.buttons}>
{content.has_primary_button ? ( {content.has_primary_button ? (
<Button <ButtonLink
intent="inverted" href={primary_button.href}
size="small" target={primary_button.openInNewTab ? "_blank" : undefined}
theme="base" variant="Primary"
asChild color="Inverted"
className={styles.buttonWrapper} size="Small"
fullWidth typography="Body/Supporting text (caption)/smBold"
className={styles.button}
> >
<Link {primary_button.title}
href={primary_button.href} </ButtonLink>
target={primary_button.openInNewTab ? "_blank" : undefined}
color="none"
>
{primary_button.title}
</Link>
</Button>
) : null} ) : null}
{content.has_secondary_button ? ( {content.has_secondary_button ? (
<Button <ButtonLink
intent="secondary" href={secondary_button.href}
size="small" target={secondary_button.openInNewTab ? "_blank" : undefined}
theme="primaryStrong" variant="Secondary"
className={styles.buttonWrapper} size="Small"
asChild color="Inverted"
fullWidth className={styles.button}
typography="Body/Supporting text (caption)/smBold"
> >
<Link {secondary_button.title}
href={secondary_button.href} </ButtonLink>
target={secondary_button.openInNewTab ? "_blank" : undefined}
color="none"
>
{secondary_button.title}
</Link>
</Button>
) : null} ) : null}
</div> </div>
</div> </div>

View File

@@ -11,10 +11,12 @@ import type { JoinScandicFriends } from "@/types/trpc/routers/contentstack/start
interface JoinScandicFriendsProps { interface JoinScandicFriendsProps {
content: JoinScandicFriends content: JoinScandicFriends
headingLevel?: "h1" | "h2"
} }
export default async function JoinScandicFriends({ export default async function JoinScandicFriends({
content, content,
headingLevel = "h2",
}: JoinScandicFriendsProps) { }: JoinScandicFriendsProps) {
const isLoggedIn = await isLoggedInUser() const isLoggedIn = await isLoggedInUser()
if (isLoggedIn) { if (isLoggedIn) {
@@ -22,6 +24,7 @@ export default async function JoinScandicFriends({
} }
const { show_header, show_usp, usp, primary_button } = content const { show_header, show_usp, usp, primary_button } = content
const Hx = headingLevel
return ( return (
<div className={styles.container}> <div className={styles.container}>
@@ -49,7 +52,7 @@ export default async function JoinScandicFriends({
</Typography> </Typography>
) : null} ) : null}
<Typography variant="Title/xs"> <Typography variant="Title/xs">
<h2 className={styles.heading}>{content.title}</h2> <Hx className={styles.heading}>{content.title}</Hx>
</Typography> </Typography>
</header> </header>
) : null} ) : null}

View File

@@ -1,5 +1,3 @@
import { Suspense } from "react"
import { JsonToHtml } from "@scandic-hotels/design-system/JsonToHtml" import { JsonToHtml } from "@scandic-hotels/design-system/JsonToHtml"
import { BlocksEnums } from "@scandic-hotels/trpc/types/blocksEnum" import { BlocksEnums } from "@scandic-hotels/trpc/types/blocksEnum"
@@ -13,9 +11,7 @@ import UspGrid from "@/components/Blocks/UspGrid"
import AccordionSection from "./Accordion" import AccordionSection from "./Accordion"
import CardGallery from "./CardGallery" import CardGallery from "./CardGallery"
import Essentials from "./Essentials" import Essentials from "./Essentials"
import FullWidthCampaign from "./FullWidthCampaign"
import HotelListing from "./HotelListing" import HotelListing from "./HotelListing"
import JoinScandicFriends from "./JoinScandicFriends"
import Table from "./Table" import Table from "./Table"
import type { BlocksProps } from "@/types/components/blocks" import type { BlocksProps } from "@/types/components/blocks"
@@ -107,14 +103,6 @@ export default function Blocks({ blocks }: BlocksProps) {
) )
case BlocksEnums.block.UspGrid: case BlocksEnums.block.UspGrid:
return <UspGrid usp_grid={block.usp_grid} /> return <UspGrid usp_grid={block.usp_grid} />
case BlocksEnums.block.FullWidthCampaign:
return <FullWidthCampaign content={block.full_width_campaign} />
case BlocksEnums.block.JoinScandicFriends:
return (
<Suspense>
<JoinScandicFriends content={block.join_scandic_friends} />
</Suspense>
)
case BlocksEnums.block.Essentials: case BlocksEnums.block.Essentials:
return <Essentials content={block.essentials} /> return <Essentials content={block.essentials} />
default: default:

View File

@@ -0,0 +1,17 @@
.block {
margin-left: auto;
margin-right: auto;
max-width: var(--max-width-content);
width: 100%;
&:empty {
display: none;
}
}
@media screen and (min-width: 768px) {
.block {
padding-left: var(--Space-x5);
padding-right: var(--Space-x5);
}
}

View File

@@ -0,0 +1,62 @@
import { Suspense } from "react"
import { BlocksEnums } from "@scandic-hotels/trpc/types/blocksEnum"
import CardsGrid from "@/components/Blocks/CardsGrid"
import CarouselCards from "@/components/Blocks/CarouselCards"
import FullWidthCampaign from "@/components/Blocks/FullWidthCampaign"
import JoinScandicFriends from "@/components/Blocks/JoinScandicFriends"
import styles from "./blocks.module.css"
import type { Block as StartPageBlock } from "@scandic-hotels/trpc/routers/contentstack/startPage/query"
interface BlocksProps {
blocks: StartPageBlock[]
}
export function Blocks({ blocks }: BlocksProps) {
return blocks.map((block, idx) => {
const headingLevel = idx === 0 ? "h1" : "h2"
switch (block.typename) {
case BlocksEnums.block.CardsGrid:
return (
<div key={`${block.typename}-${idx}`} className={styles.block}>
<CardsGrid
cards_grid={block.cards_grid}
headingLevel={headingLevel}
headingTypography="Title/md"
/>
</div>
)
case BlocksEnums.block.CarouselCards:
return (
<div key={`${block.typename}-${idx}`} className={styles.block}>
<CarouselCards
carousel_cards={block.carousel_cards}
headingLevel={headingLevel}
headingTypography="Title/md"
/>
</div>
)
case BlocksEnums.block.FullWidthCampaign:
return (
<FullWidthCampaign
content={block.full_width_campaign}
headingLevel={headingLevel}
/>
)
case BlocksEnums.block.JoinScandicFriends:
return (
<Suspense>
<div key={`${block.typename}-${idx}`} className={styles.block}>
<JoinScandicFriends content={block.join_scandic_friends} />
</div>
</Suspense>
)
default:
return null
}
})
}

View File

@@ -2,13 +2,11 @@ import { FloatingBookingWidget } from "@scandic-hotels/booking-flow/BookingWidge
import Image from "@scandic-hotels/design-system/Image" import Image from "@scandic-hotels/design-system/Image"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK" import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
import { BlocksEnums } from "@scandic-hotels/trpc/types/blocksEnum"
import { bookingFlowConfig } from "@/constants/bookingFlowConfig" import { bookingFlowConfig } from "@/constants/bookingFlowConfig"
import { getStartPage } from "@/lib/trpc/memoizedRequests" import { getStartPage } from "@/lib/trpc/memoizedRequests"
import Blocks from "@/components/Blocks" import { Blocks } from "@/components/ContentType/StartPage/Blocks"
import FullWidthCampaign from "@/components/Blocks/FullWidthCampaign"
import { getLang } from "@/i18n/serverContext" import { getLang } from "@/i18n/serverContext"
import styles from "./startPage.module.css" import styles from "./startPage.module.css"
@@ -34,7 +32,7 @@ export default async function StartPage({
<div className={styles.headerContent}> <div className={styles.headerContent}>
{header.heading.length ? ( {header.heading.length ? (
<Typography variant="Title/lg"> <Typography variant="Title/lg">
<h1>{header.heading}</h1> <p className={styles.heading}>{header.heading}</p>
</Typography> </Typography>
) : null} ) : null}
<FloatingBookingWidget <FloatingBookingWidget
@@ -60,22 +58,7 @@ export default async function StartPage({
</header> </header>
<main className={styles.main}> <main className={styles.main}>
{blocks.map((block, index) => { <Blocks blocks={blocks} />
if (block.typename === BlocksEnums.block.FullWidthCampaign) {
return (
<FullWidthCampaign
key={block.typename}
content={block.full_width_campaign}
/>
)
}
return (
<div key={`${block.typename}-${index}`} className={styles.section}>
<Blocks blocks={[block]} />
</div>
)
})}
</main> </main>
<TrackingSDK pageData={content.tracking} /> <TrackingSDK pageData={content.tracking} />
</> </>

View File

@@ -1,17 +1,17 @@
.header { .header {
height: 560px; height: 560px;
position: relative; position: relative;
}
.header:after { &::after {
content: ""; content: "";
background: linear-gradient( background: linear-gradient(
182deg, 182deg,
rgba(38, 32, 30, 0.08) 13%, rgba(38, 32, 30, 0.08) 13%,
rgba(77, 0, 27, 0.4) 100% rgba(77, 0, 27, 0.4) 100%
); );
position: absolute; position: absolute;
inset: 0; inset: 0;
}
} }
.headerContent { .headerContent {
@@ -26,10 +26,8 @@
padding: var(--Space-x2) var(--Space-x2) var(--Space-x4); padding: var(--Space-x2) var(--Space-x2) var(--Space-x4);
} }
@media screen and (min-width: 768px) { .heading {
.headerContent { z-index: 1;
grid-gap: var(--Space-x4);
}
} }
.topImage { .topImage {
@@ -42,23 +40,12 @@
padding: calc(var(--Space-x5) * 2) 0 calc(var(--Space-x5) * 4); padding: calc(var(--Space-x5) * 2) 0 calc(var(--Space-x5) * 4);
} }
.section:empty {
display: none;
}
.section {
margin-left: auto;
margin-right: auto;
max-width: var(--max-width-content);
width: 100%;
}
@media screen and (min-width: 768px) { @media screen and (min-width: 768px) {
.headerContent {
grid-gap: var(--Space-x4);
}
.main { .main {
gap: calc(var(--Space-x5) + var(--Space-x4)); gap: calc(var(--Space-x5) + var(--Space-x4));
} }
.section {
padding-left: var(--Space-x5);
padding-right: var(--Space-x5);
}
} }

View File

@@ -15,7 +15,7 @@ interface SectionHeaderProps
VariantProps<typeof headingVariants> { VariantProps<typeof headingVariants> {
heading?: string heading?: string
headingLevel?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" headingLevel?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6"
preamble?: string preamble?: string | null
link?: { link?: {
href: string href: string
text: string text: string

View File

@@ -1,3 +0,0 @@
import type { CardsGrid } from "@scandic-hotels/trpc/types/blocks"
export interface CardsGridProps extends Pick<CardsGrid, "cards_grid"> {}

View File

@@ -1,4 +0,0 @@
import type { CarouselCards } from "@scandic-hotels/trpc/types/blocks"
export interface CarouselCardsProps
extends Pick<CarouselCards, "carousel_cards"> {}

View File

@@ -1,4 +1,3 @@
import type { Block as StartPageBlock } from "@scandic-hotels/trpc/routers/contentstack/startPage/query"
import type { Block as AccountPageBlock } from "@scandic-hotels/trpc/types/accountPage" import type { Block as AccountPageBlock } from "@scandic-hotels/trpc/types/accountPage"
import type { Block as CampaignOverviewPageBlock } from "@scandic-hotels/trpc/types/campaignOverviewPage" import type { Block as CampaignOverviewPageBlock } from "@scandic-hotels/trpc/types/campaignOverviewPage"
import type { Block as CampaignPageBlock } from "@scandic-hotels/trpc/types/campaignPage" import type { Block as CampaignPageBlock } from "@scandic-hotels/trpc/types/campaignPage"
@@ -20,7 +19,6 @@ type Blocks =
| DestinationCountryPageBlock | DestinationCountryPageBlock
| DestinationOverviewPageBlock | DestinationOverviewPageBlock
| LoyaltyPageBlock | LoyaltyPageBlock
| StartPageBlock
| PromoCampaignPageBlock | PromoCampaignPageBlock
export interface BlocksProps { export interface BlocksProps {