fix(BOOK-579): Multiple small fixes in regards to hotel branding

Approved-by: Bianca Widstam
Approved-by: Matilda Haneling
This commit is contained in:
Erik Tiekstra
2025-11-20 13:07:11 +00:00
parent 36cd1a5cdf
commit c60f7928ba
16 changed files with 195 additions and 66 deletions

View File

@@ -1,12 +1,14 @@
/* eslint-disable formatjs/no-literal-string-in-jsx */
import { cx, type VariantProps } from "class-variance-authority"
import { DEFAULT_THEME, type Theme } from "@scandic-hotels/common/utils/theme"
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
import { Divider } from "@scandic-hotels/design-system/Divider"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import Image from "@scandic-hotels/design-system/Image"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { getButtonColor } from "./utils"
import { variants } from "./variants"
import styles from "./hero.module.css"
@@ -18,6 +20,7 @@ interface HeroProps
VariantProps<typeof variants>,
Omit<Hero, "theme"> {
pageType?: "campaign" | "overview" | "hotelPage"
hotelTheme?: Theme | null
}
export default async function CampaignHero({
@@ -29,11 +32,14 @@ export default async function CampaignHero({
button,
theme,
pageType = "campaign",
hotelTheme = DEFAULT_THEME,
}: HeroProps) {
const classNames = variants({
theme,
})
const buttonColor = getButtonColor(theme, hotelTheme)
const visibleBenefits =
pageType === "campaign" ? benefits : benefits.slice(0, 3)
@@ -95,8 +101,8 @@ export default async function CampaignHero({
<Divider
color={
theme === "Peach"
? "Surface/Brand/Primary 1/OnSurface/Accent Secondary"
: "white"
? "Border/Divider/Brand/OnAccent/Default"
: "Border/Divider/Brand/OnPrimary 3/Default"
}
/>
{rate_text ? (
@@ -117,7 +123,7 @@ export default async function CampaignHero({
{button ? (
<ButtonLink
variant="Secondary"
color={theme === "Peach" ? "Primary" : "Inverted"}
color={buttonColor}
href={button.url}
typography="Body/Paragraph/mdBold"
size="Medium"

View File

@@ -0,0 +1,49 @@
import { Theme } from "@scandic-hotels/common/utils/theme"
import type { buttonVariants } from "@scandic-hotels/design-system/Button"
import type { VariantProps } from "class-variance-authority"
import type { variants as campaignHeroVariants } from "./variants"
type ButtonVariants = VariantProps<typeof buttonVariants>
// Determine button color based on hero theme and hotel theme.
// This is done to avoid low contrast issues and conflicting colors in
// certain combinations and according to design guidelines.
export function getButtonColor(
heroTheme: VariantProps<typeof campaignHeroVariants>["theme"],
hotelTheme: Theme | null = Theme.scandic
): ButtonVariants["color"] {
let buttonColor: ButtonVariants["color"] = "Inverted"
switch (hotelTheme) {
case Theme.scandic:
if (heroTheme === "Peach") {
buttonColor = "Primary"
}
break
case Theme.scandicGo:
buttonColor = "Primary"
break
case Theme.grandHotel:
buttonColor = "Primary"
break
case Theme.hotelNorge:
if (heroTheme === "Peach") {
buttonColor = "Primary"
}
break
case Theme.theDock:
if (heroTheme === "Peach") {
buttonColor = "Primary"
}
break
case Theme.downtownCamper:
case Theme.haymarket:
case Theme.marski:
default:
break
}
return buttonColor
}

View File

@@ -1,3 +1,4 @@
import { getTheme } from "@scandic-hotels/common/utils/theme/serverContext"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { Carousel } from "@/components/Carousel"
@@ -31,6 +32,7 @@ export default async function HotelCampaigns({
campaigns,
}: HotelCampaignsProps) {
const intl = await getIntl()
const hotelTheme = getTheme()
const [topCampaign, ...campaignCards] = campaigns
@@ -63,6 +65,7 @@ export default async function HotelCampaigns({
url: topCampaign.url,
}}
pageType="hotelPage"
hotelTheme={hotelTheme}
/>
{campaignCards.length ? (
<div className={styles.campaignCards}>

View File

@@ -0,0 +1,18 @@
export function ScandicGoSwirl({ className }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="356"
height="150"
viewBox="0 0 356 150"
fill="none"
aria-hidden="true"
className={className}
>
<path
d="M20.739 32.264C32.89 19.8937 48.5814 10.5998 67.7054 4.49248C83.6161 -0.595019 105.915 -3.91314 121.775 9.10431C130.599 16.3523 140.391 31.4529 136.894 62.2291C132.516 100.771 143.898 102.703 157.082 104.936C175.51 108.061 204.893 110.065 227.564 108.337C227.739 108.201 227.925 108.064 228.1 107.927C230.667 105.985 233.084 104.152 235.428 102.299C249.294 91.3561 258.856 93.5589 263.313 95.8489C268.547 98.5315 271.635 103.776 271.375 109.528C271.084 115.876 266.76 121.493 260.085 124.184C253.472 126.853 245.113 128.599 235.912 129.651C226.842 136.696 217.455 144.849 218.809 149.964C219.067 150.927 219.413 151.946 219.967 152.963C237.604 143.688 256.687 134.681 274.565 127.308C332.504 103.423 349.768 107.43 357.711 112.528C364.177 116.678 367.162 123.786 365.892 132.043C364.955 138.128 360.244 143.322 349.634 149.956C342.219 154.593 332.539 159.495 321.621 164.128C309.365 169.339 277.749 181.608 248.885 183.128C236.671 183.771 226.353 182.359 218.217 178.924C208.412 184.372 199.379 189.777 191.719 194.832L179.599 176.457C185.887 172.307 193.009 167.957 200.681 163.55C199.335 161.097 198.279 158.434 197.527 155.587C195.357 147.366 197.172 139.089 202.926 130.938C183.345 130.547 164.501 128.516 153.384 126.628C121.345 121.191 110.223 101.811 115.005 59.7267C116.082 50.2627 116.569 33.3162 107.792 26.1041C99.1459 19.0102 83.1547 22.6508 74.3896 25.4482C46.4073 34.385 28.3294 50.695 19.1159 75.3008C15.8472 84.032 17.5039 90.576 19.7362 92.4293C21.4457 93.8514 25.3148 93.054 29.8362 90.3464C46.8532 80.1534 53.6171 73.4872 58.5446 68.6173C61.5947 65.6064 64.4803 62.7629 68.4902 60.3037C74.3528 56.7124 82.6222 55.9275 87.8836 62.2681C93.1711 68.6448 94.6413 81.5886 71.6018 107.076C47.7557 133.468 14.7479 147.919 -8.72787 155.388L-15.4018 134.412C-2.6058 130.338 13.3093 124 28.4434 114.467C16.8011 117 8.97396 112.119 5.62808 109.337C-4.91677 100.553 -7.72024 84.1598 -1.51206 67.5772C3.63765 53.9048 11.0717 42.1059 20.739 32.264ZM338.815 130.791C330.498 131.436 313.561 134.919 281.177 148.409C272.213 152.143 262.951 156.294 253.754 160.673C266.636 159.265 282.102 155.448 298.783 149.451C317.481 142.73 331.236 135.627 338.815 130.791Z"
fill="#73FCEE"
/>
</svg>
)
}

View File

@@ -0,0 +1,11 @@
@media screen and (max-width: 767px) {
.logoLarge {
display: none;
}
}
@media screen and (min-width: 768px) {
.logoSmall {
display: none;
}
}

View File

@@ -1,3 +1,7 @@
"use client"
import { cx } from "class-variance-authority"
import { Theme } from "@scandic-hotels/common/utils/theme"
import ScandicLogoIcon from "@scandic-hotels/design-system/Icons/ScandicLogoIcon"
@@ -16,61 +20,67 @@ import { ScandicGoLogoSmall } from "./ScandicGoSmall"
import { TheDockLogoLarge } from "./TheDockLarge"
import { TheDockLogoSmall } from "./TheDockSmall"
import styles from "./brandedHotelLogo.module.css"
interface BrandedHotelLogoProps {
theme: Theme
size?: "small" | "large"
className?: string
}
export function BrandedHotelLogo({
theme,
size = "small",
className,
}: BrandedHotelLogoProps) {
const isSmall = size === "small"
export function BrandedHotelLogo({ theme, className }: BrandedHotelLogoProps) {
switch (theme) {
case Theme.downtownCamper:
return isSmall ? (
<DowntownCamperLogoSmall className={className} />
) : (
<DowntownCamperLogoLarge className={className} />
return (
<>
<DowntownCamperLogoSmall
className={cx(styles.logoSmall, className)}
/>
<DowntownCamperLogoLarge
className={cx(styles.logoLarge, className)}
/>
</>
)
case Theme.grandHotel:
return isSmall ? (
<GrandHotelLogoSmall className={className} />
) : (
<GrandHotelLogoLarge className={className} />
return (
<>
<GrandHotelLogoSmall className={cx(styles.logoSmall, className)} />
<GrandHotelLogoLarge className={cx(styles.logoLarge, className)} />
</>
)
case Theme.haymarket:
return isSmall ? (
<HaymarketLogoSmall className={className} />
) : (
<HaymarketLogoLarge className={className} />
return (
<>
<HaymarketLogoSmall className={cx(styles.logoSmall, className)} />
<HaymarketLogoLarge className={cx(styles.logoLarge, className)} />
</>
)
case Theme.hotelNorge:
return isSmall ? (
<HotelNorgeLogoSmall className={className} />
) : (
<HotelNorgeLogoLarge className={className} />
return (
<>
<HotelNorgeLogoSmall className={cx(styles.logoSmall, className)} />
<HotelNorgeLogoLarge className={cx(styles.logoLarge, className)} />
</>
)
case Theme.marski:
return isSmall ? (
<MarskiLogoSmall className={className} />
) : (
<MarskiLogoLarge className={className} />
return (
<>
<MarskiLogoSmall className={cx(styles.logoSmall, className)} />
<MarskiLogoLarge className={cx(styles.logoLarge, className)} />
</>
)
case Theme.scandicGo:
return isSmall ? (
<ScandicGoLogoSmall className={className} />
) : (
<ScandicGoLogoLarge className={className} />
return (
<>
<ScandicGoLogoSmall className={cx(styles.logoSmall, className)} />
<ScandicGoLogoLarge className={cx(styles.logoLarge, className)} />
</>
)
case Theme.theDock:
return isSmall ? (
<TheDockLogoSmall className={className} />
) : (
<TheDockLogoLarge className={className} />
return (
<>
<TheDockLogoSmall className={cx(styles.logoSmall, className)} />
<TheDockLogoLarge className={cx(styles.logoLarge, className)} />
</>
)
case Theme.scandic:
default:

View File

@@ -17,10 +17,25 @@
}
.logoWrapper {
position: relative;
background-color: var(--Surface-Brand-Primary-1-Default);
display: flex;
align-items: center;
padding: var(--Space-x1) 0;
z-index: 0;
overflow: hidden;
}
.logo {
z-index: 1;
}
.scandicGoSwirl {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: auto;
}
.seeAllButton {
@@ -39,6 +54,10 @@
height: 252px;
}
}
.scandicGoSwirl {
display: none;
}
}
@media screen and (min-width: 768px) {
@@ -47,11 +66,22 @@
height: 270px;
}
.logoWrapper {
border-radius: var(--Corner-radius-sm);
justify-content: center;
}
.logo {
height: 100%;
}
}
@media screen and (min-width: 768px) and (max-width: 1366px) {
.scandicGoSwirl {
bottom: -20px;
}
}
@media screen and (min-width: 1367px) {
.hotelPageHero {
padding: 0 var(--Space-x5);
@@ -83,8 +113,6 @@
.logoWrapper {
padding: var(--Space-x3);
border-radius: var(--Corner-radius-sm);
justify-content: center;
}
.seeAllButton {

View File

@@ -3,12 +3,12 @@
import { cx } from "class-variance-authority"
import { useState } from "react"
import { useIntl } from "react-intl"
import { useMediaQuery } from "usehooks-ts"
import { DEFAULT_THEME, type Theme } from "@scandic-hotels/common/utils/theme"
import { DEFAULT_THEME, Theme } from "@scandic-hotels/common/utils/theme"
import { Button } from "@scandic-hotels/design-system/Button"
import Lightbox from "@scandic-hotels/design-system/Lightbox"
import { ScandicGoSwirl } from "./Illustrations/ScandicGoSwirl"
import { ImageButton } from "./ImageButton"
import { BrandedHotelLogo } from "./Logos"
@@ -28,7 +28,6 @@ export function HotelPageHero({
theme,
}: HotelPageHeroProps) {
const intl = useIntl()
const isMobile = useMediaQuery("(max-width: 767px)")
const [lightboxState, setLightboxState] = useState({
activeIndex: 0,
isOpen: false,
@@ -49,11 +48,10 @@ export function HotelPageHero({
{isThemed ? (
<>
<div className={styles.logoWrapper}>
<BrandedHotelLogo
className={styles.logo}
theme={theme}
size={isMobile ? "small" : "large"}
/>
<BrandedHotelLogo className={styles.logo} theme={theme} />
{theme === Theme.scandicGo ? (
<ScandicGoSwirl className={styles.scandicGoSwirl} />
) : null}
</div>
<ImageButton
className={styles.imageButton}

View File

@@ -20,16 +20,16 @@ export default function MapCard({ hotelName, pois }: MapCardProps) {
return (
<div className={styles.mapCard}>
<span className={styles.heading}>
<Typography variant="Title/Overline/sm" className={styles.nearby}>
<span>
<Typography variant="Title/Overline/sm">
<span className={styles.nearby}>
{intl.formatMessage({
id: "hotel.mapCard.nearby",
defaultMessage: "Nearby",
})}
</span>
</Typography>
<Typography variant="Title/smLowCase" className={styles.hotelName}>
<h3>{hotelName}</h3>
<Typography variant="Title/smLowCase">
<h3 className={styles.hotelName}>{hotelName}</h3>
</Typography>
</span>
<ul className={styles.poiList}>
@@ -44,11 +44,8 @@ export default function MapCard({ hotelName, pois }: MapCardProps) {
<Typography>
<span>{poi.name} </span>
</Typography>
<Typography
variant="Body/Supporting text (caption)/smRegular"
className={styles.distance}
>
<span>
<Typography variant="Body/Supporting text (caption)/smRegular">
<span className={styles.distance}>
{intl.formatMessage(
{
id: "common.distanceKm",

View File

@@ -35,6 +35,7 @@
.nearby {
color: var(--Text-Interactive-Secondary);
text-transform: uppercase;
}
.hotelName {

View File

@@ -23,11 +23,11 @@
padding: var(--Space-x1) var(--Space-x2);
background-color: transparent;
border-radius: 2.5rem;
color: var(--Text-Accent-Primary);
color: var(--Component-Button-Brand-Secondary-On-fill-Default);
&.active {
background-color: var(--Surface-Brand-Primary-2-Default);
color: var(--Text-Inverted);
background-color: var(--Component-Button-Brand-Primary-Fill-Default);
color: var(--Component-Button-Brand-Primary-On-fill-Default);
}
&:not(.active) {
@@ -37,7 +37,7 @@
.link {
display: contents;
color: var(--Text-Accent-Primary);
color: var(--Component-Button-Brand-Secondary-On-fill-Default);
}
@media screen and (min-width: 1367px) {