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 { Section } from "@/components/Section"
import SectionHeader from "@/components/Section/Header/Deprecated"
import { SectionHeader } from "@/components/Section/Header"
import Card from "@/components/TempDesignSystem/Card"
import Grids from "@/components/TempDesignSystem/Grids"
import LoyaltyCard from "@/components/TempDesignSystem/LoyaltyCard"
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"
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"]
switch (cards_grid.layout) {
@@ -34,10 +46,10 @@ export default function CardsGrid({ cards_grid }: CardsGridProps) {
return (
<Section>
<SectionHeader
title={cards_grid.title}
heading={cards_grid.title}
preamble={cards_grid.preamble}
headingAs="h3"
headingLevel="h2"
headingLevel={headingLevel}
typography={headingTypography}
link={cards_grid.link}
/>
<Grids.Stackable columns={columns}>

View File

@@ -5,15 +5,27 @@ import { useState } from "react"
import { Carousel } from "@/components/Carousel"
import ContentCard from "@/components/ContentCard"
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 TabFilters from "@/components/TabFilters"
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 {
heading,
preamble,
@@ -37,10 +49,10 @@ export default function CarouselCards({ carousel_cards }: CarouselCardsProps) {
return (
<Section>
<SectionHeader
title={heading}
heading={heading}
preamble={preamble}
headingLevel="h2"
headingAs="h3"
headingLevel={headingLevel}
typography={headingTypography}
link={link}
/>
{filterCategories.length > 0 && activeFilter && (

View File

@@ -2,32 +2,27 @@
position: relative;
overflow: hidden;
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 {
text-align: center;
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 {
position: absolute;
inset: 0;
@@ -54,16 +49,10 @@
gap: var(--Space-x1);
}
.buttonWrapper {
.button {
min-width: 180px;
}
@media screen and (max-width: 767px) {
.buttonWrapper:not(:only-child) {
min-width: 135px;
}
}
.image {
max-width: 100%;
height: 100%;
@@ -73,3 +62,15 @@
.scriptedText {
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 { 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 BiroScript from "@/components/TempDesignSystem/Text/BiroScript"
@@ -12,10 +10,15 @@ import type { FullWidthCampaign } from "@/types/trpc/routers/contentstack/startP
interface FullWidthCampaignProps {
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 Hx = headingLevel
return (
<div className={styles.container}>
@@ -36,53 +39,38 @@ export default function FullWidthCampaign({ content }: FullWidthCampaignProps) {
</BiroScript>
</div>
<div className={styles.mainContent}>
<Title
color="baseText"
textAlign="center"
textTransform="capitalize"
level="h3"
>
{content.heading}
</Title>
<Typography className={styles.bodyText} variant="Body/Lead text">
<p>{content.body_text}</p>
<Typography variant="Title/mdLowCase">
<Hx className={styles.bodyText}>{content.heading}</Hx>
</Typography>
<Typography variant="Body/Lead text">
<p className={styles.bodyText}>{content.body_text}</p>
</Typography>
<div className={styles.buttons}>
{content.has_primary_button ? (
<Button
intent="inverted"
size="small"
theme="base"
asChild
className={styles.buttonWrapper}
fullWidth
<ButtonLink
href={primary_button.href}
target={primary_button.openInNewTab ? "_blank" : undefined}
variant="Primary"
color="Inverted"
size="Small"
typography="Body/Supporting text (caption)/smBold"
className={styles.button}
>
<Link
href={primary_button.href}
target={primary_button.openInNewTab ? "_blank" : undefined}
color="none"
>
{primary_button.title}
</Link>
</Button>
{primary_button.title}
</ButtonLink>
) : null}
{content.has_secondary_button ? (
<Button
intent="secondary"
size="small"
theme="primaryStrong"
className={styles.buttonWrapper}
asChild
fullWidth
<ButtonLink
href={secondary_button.href}
target={secondary_button.openInNewTab ? "_blank" : undefined}
variant="Secondary"
size="Small"
color="Inverted"
className={styles.button}
typography="Body/Supporting text (caption)/smBold"
>
<Link
href={secondary_button.href}
target={secondary_button.openInNewTab ? "_blank" : undefined}
color="none"
>
{secondary_button.title}
</Link>
</Button>
{secondary_button.title}
</ButtonLink>
) : null}
</div>
</div>

View File

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

View File

@@ -1,5 +1,3 @@
import { Suspense } from "react"
import { JsonToHtml } from "@scandic-hotels/design-system/JsonToHtml"
import { BlocksEnums } from "@scandic-hotels/trpc/types/blocksEnum"
@@ -13,9 +11,7 @@ import UspGrid from "@/components/Blocks/UspGrid"
import AccordionSection from "./Accordion"
import CardGallery from "./CardGallery"
import Essentials from "./Essentials"
import FullWidthCampaign from "./FullWidthCampaign"
import HotelListing from "./HotelListing"
import JoinScandicFriends from "./JoinScandicFriends"
import Table from "./Table"
import type { BlocksProps } from "@/types/components/blocks"
@@ -107,14 +103,6 @@ export default function Blocks({ blocks }: BlocksProps) {
)
case BlocksEnums.block.UspGrid:
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:
return <Essentials content={block.essentials} />
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 { Typography } from "@scandic-hotels/design-system/Typography"
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
import { BlocksEnums } from "@scandic-hotels/trpc/types/blocksEnum"
import { bookingFlowConfig } from "@/constants/bookingFlowConfig"
import { getStartPage } from "@/lib/trpc/memoizedRequests"
import Blocks from "@/components/Blocks"
import FullWidthCampaign from "@/components/Blocks/FullWidthCampaign"
import { Blocks } from "@/components/ContentType/StartPage/Blocks"
import { getLang } from "@/i18n/serverContext"
import styles from "./startPage.module.css"
@@ -34,7 +32,7 @@ export default async function StartPage({
<div className={styles.headerContent}>
{header.heading.length ? (
<Typography variant="Title/lg">
<h1>{header.heading}</h1>
<p className={styles.heading}>{header.heading}</p>
</Typography>
) : null}
<FloatingBookingWidget
@@ -60,22 +58,7 @@ export default async function StartPage({
</header>
<main className={styles.main}>
{blocks.map((block, index) => {
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>
)
})}
<Blocks blocks={blocks} />
</main>
<TrackingSDK pageData={content.tracking} />
</>

View File

@@ -1,17 +1,17 @@
.header {
height: 560px;
position: relative;
}
.header:after {
content: "";
background: linear-gradient(
182deg,
rgba(38, 32, 30, 0.08) 13%,
rgba(77, 0, 27, 0.4) 100%
);
position: absolute;
inset: 0;
&::after {
content: "";
background: linear-gradient(
182deg,
rgba(38, 32, 30, 0.08) 13%,
rgba(77, 0, 27, 0.4) 100%
);
position: absolute;
inset: 0;
}
}
.headerContent {
@@ -26,10 +26,8 @@
padding: var(--Space-x2) var(--Space-x2) var(--Space-x4);
}
@media screen and (min-width: 768px) {
.headerContent {
grid-gap: var(--Space-x4);
}
.heading {
z-index: 1;
}
.topImage {
@@ -42,23 +40,12 @@
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) {
.headerContent {
grid-gap: var(--Space-x4);
}
.main {
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> {
heading?: string
headingLevel?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6"
preamble?: string
preamble?: string | null
link?: {
href: 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 CampaignOverviewPageBlock } from "@scandic-hotels/trpc/types/campaignOverviewPage"
import type { Block as CampaignPageBlock } from "@scandic-hotels/trpc/types/campaignPage"
@@ -20,7 +19,6 @@ type Blocks =
| DestinationCountryPageBlock
| DestinationOverviewPageBlock
| LoyaltyPageBlock
| StartPageBlock
| PromoCampaignPageBlock
export interface BlocksProps {