diff --git a/app/[lang]/(live)/layout.tsx b/app/[lang]/(live)/layout.tsx index c159e3926..35f37d653 100644 --- a/app/[lang]/(live)/layout.tsx +++ b/app/[lang]/(live)/layout.tsx @@ -7,8 +7,8 @@ import TrpcProvider from "@/lib/trpc/Provider" import TokenRefresher from "@/components/Auth/TokenRefresher" import AdobeSDKScript from "@/components/Current/AdobeSDKScript" -import Footer from "@/components/Current/Footer" import VwoScript from "@/components/Current/VwoScript" +import Footer from "@/components/Footer" import { ToastHandler } from "@/components/TempDesignSystem/Toasts" import { preloadUserTracking } from "@/components/TrackingSDK" import { getIntl } from "@/i18n" diff --git a/components/Footer/Details/details.module.css b/components/Footer/Details/details.module.css new file mode 100644 index 000000000..adbf86603 --- /dev/null +++ b/components/Footer/Details/details.module.css @@ -0,0 +1,69 @@ +.details { + background: var(--Base-Text-High-contrast); + color: var(--Primary-Dark-On-Surface-Text); + padding: var(--Spacing-x3) var(--Spacing-x2) var(--Spacing-x6); +} + +.topContainer { + display: flex; + justify-content: space-between; + padding-bottom: var(--Spacing-x2); + margin-bottom: var(--Spacing-x2); +} + +.bottomContainer { + display: flex; + justify-content: space-between; + flex-direction: column-reverse; +} + +.socialNav { + display: flex; + gap: var(--Spacing-x2); +} + +.navigationContainer { + display: flex; + justify-content: space-between; + margin-bottom: var(--Spacing-x2); + padding-bottom: var(--Spacing-x2); + border-bottom: 1px solid var(--Base-Text-Medium-contrast); +} + +.navigation { + display: flex; + gap: var(--Spacing-x1); +} + +.link { + &::after { + content: "·"; + margin-left: var(--Spacing-x1); + } + &:last-child { + &::after { + content: ""; + } + } +} + +.copyrightContainer { + display: flex; + gap: var(--Spacing-x1); +} + +@media screen and (min-width: 1367px) { + .details { + padding: var(--Spacing-x6) var(--Spacing-x5) var(--Spacing-x4); + } + .bottomContainer { + border-top: 1px solid var(--Base-Text-Medium-contrast); + padding-top: var(--Spacing-x2); + flex-direction: row; + } + .navigationContainer { + border-bottom: 0; + padding-bottom: 0; + margin-bottom: 0; + } +} diff --git a/components/Footer/Details/index.tsx b/components/Footer/Details/index.tsx new file mode 100644 index 000000000..830abfb2c --- /dev/null +++ b/components/Footer/Details/index.tsx @@ -0,0 +1,92 @@ +import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher" +import { getIconByIconName } from "@/components/Icons/get-icon-by-icon-name" +import Image from "@/components/Image" +import Link from "@/components/TempDesignSystem/Link" +import Footnote from "@/components/TempDesignSystem/Text/Footnote" +import { getLang } from "@/i18n/serverContext" + +import { footer } from "../mockedData" + +import styles from "./details.module.css" + +import type { IconName } from "@/types/components/icon" + +function socialIcon(iconName: string): JSX.Element | null { + const SocialIcon = getIconByIconName(iconName as IconName) + return SocialIcon ? : {iconName} +} + +export default function FooterDetails() { + const lang = getLang() + const currentYear = new Date().getFullYear() + const { + socialMedia, + copyrightCompany, + copyrightInfo, + tertiaryLinks, + languageSwitcher, + } = footer + return ( +
+
+ + Scandic Hotels logo + + +
+
+
+ + © {currentYear} {copyrightCompany} + + + {copyrightInfo} + +
+
+ + { + // This will be changed to the new LangueSwitcher that is done in the header branch, when implementing contentstack + } + +
+
+
+ ) +} diff --git a/components/Footer/Navigation/MainNav/index.tsx b/components/Footer/Navigation/MainNav/index.tsx new file mode 100644 index 000000000..353f1a045 --- /dev/null +++ b/components/Footer/Navigation/MainNav/index.tsx @@ -0,0 +1,31 @@ +import { ArrowRightIcon } from "@/components/Icons" +import Link from "@/components/TempDesignSystem/Link" +import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" + +import styles from "./mainnav.module.css" + +import type { FooterMainNavProps } from "@/types/components/footer/navigation" + +export default function FooterMainNav({ mainLinks }: FooterMainNavProps) { + return ( + + ) +} diff --git a/components/Footer/Navigation/MainNav/mainnav.module.css b/components/Footer/Navigation/MainNav/mainnav.module.css new file mode 100644 index 000000000..933c9e774 --- /dev/null +++ b/components/Footer/Navigation/MainNav/mainnav.module.css @@ -0,0 +1,29 @@ +.mainNavigation { + width: 100%; +} + +.mainNavigationList { + list-style: none; +} + +.mainNavigationItem { + padding: var(--Spacing-x3) 0; + border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider); + &:first-child { + padding-top: 0; + } + &:last-child { + border-bottom: 0; + } +} + +.mainNavigationLink { + display: flex; + justify-content: space-between; +} + +@media screen and (min-width: 1367px) { + .mainNavigation { + max-width: 360px; + } +} diff --git a/components/Footer/Navigation/SecondaryNav/index.tsx b/components/Footer/Navigation/SecondaryNav/index.tsx new file mode 100644 index 000000000..afbf36dd4 --- /dev/null +++ b/components/Footer/Navigation/SecondaryNav/index.tsx @@ -0,0 +1,67 @@ +import Image from "@/components/Image" +import Link from "@/components/TempDesignSystem/Link" +import Body from "@/components/TempDesignSystem/Text/Body" + +import styles from "./secondarynav.module.css" + +import { + AppDownnLoadLinks, + type FooterSecondaryNavProps, +} from "@/types/components/footer/navigation" + +export default function FooterSecondaryNav({ + secondaryLinks, + appDownloads, +}: FooterSecondaryNavProps) { + return ( +
+ + {secondaryLinks.map((link) => ( + + ))} +
+ ) +} diff --git a/components/Footer/Navigation/SecondaryNav/secondarynav.module.css b/components/Footer/Navigation/SecondaryNav/secondarynav.module.css new file mode 100644 index 000000000..3f47d32c7 --- /dev/null +++ b/components/Footer/Navigation/SecondaryNav/secondarynav.module.css @@ -0,0 +1,34 @@ +.secondaryNavigation { + display: flex; + flex-direction: column-reverse; + gap: var(--Spacing-x6); + margin-top: var(--Spacing-x6); +} + +.secondaryNavigationList { + display: flex; + flex-direction: column; + list-style: none; + gap: var(--Spacing-x3); +} + +.secondaryNavigationGroup { + display: flex; + flex-direction: column; + gap: var(--Spacing-x3); +} + +.secondaryNavigationGroupTitle { + color: var(--Base-Text-Medium-contrast); + font-weight: var(--typography-Body-Bold-fontWeight); + font-family: var(--typography-Body-Bold-fontFamily); + margin: 0; +} + +@media screen and (min-width: 1367px) { + .secondaryNavigation { + margin-top: 0; + gap: 80px; + flex-direction: row; + } +} diff --git a/components/Footer/Navigation/index.tsx b/components/Footer/Navigation/index.tsx new file mode 100644 index 000000000..9b5baef2d --- /dev/null +++ b/components/Footer/Navigation/index.tsx @@ -0,0 +1,20 @@ +import { footer } from "../mockedData" +import FooterMainNav from "./MainNav" +import FooterSecondaryNav from "./SecondaryNav" + +import styles from "./navigation.module.css" + +export default function FooterNavigation() { + const { mainLinks, secondaryLinks, appDownloads } = footer + return ( +
+
+ + +
+
+ ) +} diff --git a/components/Footer/Navigation/navigation.module.css b/components/Footer/Navigation/navigation.module.css new file mode 100644 index 000000000..df5dfbcf5 --- /dev/null +++ b/components/Footer/Navigation/navigation.module.css @@ -0,0 +1,21 @@ +.section { + background: var(--Primary-Light-Surface-Normal); + padding: var(--Spacing-x7) var(--Spacing-x2); +} + +.maxWidth { + margin: 0 auto; + display: flex; + justify-content: space-between; + flex-direction: column; + max-width: var(--max-width-content); +} + +@media screen and (min-width: 1367px) { + .section { + padding: var(--Spacing-x9) 0; + } + .maxWidth { + flex-direction: row; + } +} diff --git a/components/Footer/index.tsx b/components/Footer/index.tsx new file mode 100644 index 000000000..66dded2d5 --- /dev/null +++ b/components/Footer/index.tsx @@ -0,0 +1,11 @@ +import FooterDetails from "./Details" +import FooterNavigation from "./Navigation" + +export default function Footer() { + return ( + + ) +} diff --git a/components/Footer/mockedData.ts b/components/Footer/mockedData.ts new file mode 100644 index 000000000..0d6a57780 --- /dev/null +++ b/components/Footer/mockedData.ts @@ -0,0 +1,192 @@ +export const footer = { + mainLinks: [ + { + title: "Travel guides", + href: "/travel-guides", + id: "travel-guides", + openInNewTab: false, + isExternal: false, + }, + { + title: "New hotels", + href: "/new-hotels", + id: "new-hotels", + openInNewTab: false, + isExternal: false, + }, + { + title: "Accessibililty", + href: "/accessibility", + id: "accessibility", + openInNewTab: false, + isExternal: false, + }, + { + title: "Sustanability", + href: "/sustainability", + id: "sustainability", + openInNewTab: false, + isExternal: false, + }, + ], + appDownloads: { + title: "Scandic App", + links: [ + { + title: "App Store", + href: "https://apps.apple.com/se/app/scandic-hotels/id1020208712", + id: "apple", + }, + { + title: "Google Play", + href: "https://play.google.com/store/apps/details?id=com.scandichotels.scandichotels", + id: "google", + }, + ], + }, + + secondaryLinks: [ + { + title: "Customer service", + links: [ + { + title: "Contact us", + href: "/contact-us", + id: "contact-us", + openInNewTab: false, + isExternal: false, + }, + { + title: "Frequntly asked questions", + href: "/frequently-asked-questions", + id: "frequently-asked-questions", + openInNewTab: false, + isExternal: false, + }, + { + title: "Rates and policys", + href: "/rates-and-policies", + id: "rates-and-policies", + openInNewTab: false, + isExternal: false, + }, + { + title: "Terms and conditions", + href: "/terms-and-conditions", + id: "terms-and-conditions", + openInNewTab: false, + isExternal: false, + }, + ], + }, + { + title: "About Scandic Hotels", + links: [ + { + title: "Scandic Group", + href: "/scandic-group", + id: "scandic-group", + openInNewTab: false, + isExternal: false, + }, + { + title: "Investors", + href: "/investors", + id: "investors", + openInNewTab: false, + isExternal: false, + }, + { + title: "Press", + href: "/press", + id: "press", + openInNewTab: false, + isExternal: false, + }, + { + title: "Sponsors", + href: "/sponsors", + id: "sponsors", + openInNewTab: false, + isExternal: false, + }, + { + title: "Partners", + href: "/partners", + id: "partners", + openInNewTab: false, + isExternal: false, + }, + { + title: "Career", + href: "/career", + id: "career", + openInNewTab: false, + isExternal: false, + }, + ], + }, + ], + + copyrightCompany: "Scandic AB", + copyrightInfo: "All rights reserved.", + socialMedia: { + links: [ + { + title: "Facebook", + href: "https://www.facebook.com/scandichotels/", + id: "facebook", + }, + { + title: "Instagram", + href: "https://www.instagram.com/scandichotels/", + id: "instagram", + }, + { + title: "Tripadvisor", + href: "https://www.tripadvisor.com/Hotel_Review-g297628-d1020208712-Reviews-Scandic_Hotels-Stockholm_Sweden.html", + id: "tripadvisor", + }, + ], + }, + tertiaryLinks: [ + { + title: "Cookies", + href: "/cookies", + id: "cookies", + }, + { + title: "Privacy policy", + href: "/privacy", + id: "privacy", + }, + ], + languageSwitcher: { + urls: { + da: { + url: "https://www.scandichotels.com/da", + isExternal: true, + }, + de: { + url: "https://www.scandichotels.com/de", + isExternal: true, + }, + en: { + url: "https://www.scandichotels.com/en", + isExternal: true, + }, + fi: { + url: "https://www.scandichotels.com/fi", + isExternal: true, + }, + no: { + url: "https://www.scandichotels.com/no", + isExternal: true, + }, + sv: { + url: "https://www.scandichotels.com/sv", + isExternal: true, + }, + }, + }, +} diff --git a/components/Icons/Facebook.tsx b/components/Icons/Facebook.tsx new file mode 100644 index 000000000..20e7b6499 --- /dev/null +++ b/components/Icons/Facebook.tsx @@ -0,0 +1,27 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function FacebookIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + ) +} diff --git a/components/Icons/Instagram.tsx b/components/Icons/Instagram.tsx new file mode 100644 index 000000000..5297456d2 --- /dev/null +++ b/components/Icons/Instagram.tsx @@ -0,0 +1,27 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function InstagramIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + ) +} diff --git a/components/Icons/get-icon-by-icon-name.ts b/components/Icons/get-icon-by-icon-name.ts index e7a044f06..e10495d52 100644 --- a/components/Icons/get-icon-by-icon-name.ts +++ b/components/Icons/get-icon-by-icon-name.ts @@ -1,5 +1,8 @@ import { FC } from "react" +import FacebookIcon from "./Facebook" +import InstagramIcon from "./Instagram" +import TripAdvisorIcon from "./TripAdvisor" import { AccessibilityIcon, AccountCircleIcon, @@ -85,6 +88,8 @@ export function getIconByIconName(icon?: IconName): FC | null { return ElectricBikeIcon case IconName.Email: return EmailIcon + case IconName.Facebook: + return FacebookIcon case IconName.Fitness: return FitnessIcon case IconName.Globe: @@ -95,6 +100,8 @@ export function getIconByIconName(icon?: IconName): FC | null { return ImageIcon case IconName.InfoCircle: return InfoCircleIcon + case IconName.Instagram: + return InstagramIcon case IconName.Location: return LocationIcon case IconName.Lock: @@ -115,6 +122,8 @@ export function getIconByIconName(icon?: IconName): FC | null { return RestaurantIcon case IconName.Sauna: return SaunaIcon + case IconName.Tripadvisor: + return TripAdvisorIcon case IconName.TshirtWash: return TshirtWashIcon case IconName.WarningTriangle: diff --git a/components/TempDesignSystem/Link/link.module.css b/components/TempDesignSystem/Link/link.module.css index 0261002de..1356e489f 100644 --- a/components/TempDesignSystem/Link/link.module.css +++ b/components/TempDesignSystem/Link/link.module.css @@ -126,6 +126,10 @@ color: var(--Scandic-Brand-Pale-Peach); } +.peach50 { + color: var(--Scandic-Peach-50); +} + .peach80 { color: var(--Primary-Light-On-Surface-Accent); } diff --git a/components/TempDesignSystem/Link/variants.ts b/components/TempDesignSystem/Link/variants.ts index fa0738c50..b49b34a3a 100644 --- a/components/TempDesignSystem/Link/variants.ts +++ b/components/TempDesignSystem/Link/variants.ts @@ -12,6 +12,7 @@ export const linkVariants = cva(styles.link, { burgundy: styles.burgundy, none: "", pale: styles.pale, + peach50: styles.peach50, peach80: styles.peach80, white: styles.white, }, diff --git a/components/TempDesignSystem/Text/Body/body.module.css b/components/TempDesignSystem/Text/Body/body.module.css index 045e0886d..d13de5b44 100644 --- a/components/TempDesignSystem/Text/Body/body.module.css +++ b/components/TempDesignSystem/Text/Body/body.module.css @@ -34,6 +34,16 @@ text-decoration: var(--typography-Body-Underline-textDecoration); } +.uppercase { + font-family: var(--typography-Body-Regular-fontFamily); + font-size: var(--typography-Body-Regular-fontSize); + font-weight: var(--typography-Body-Bold-fontWeight); + letter-spacing: var(--typography-Body-Regular-letterSpacing); + line-height: var(--typography-Body-Regular-lineHeight); + text-decoration: var(--typography-Body-Regular-textDecoration); + text-transform: uppercase; +} + .textAlignCenter { text-align: center; } @@ -73,3 +83,7 @@ .peach50 { color: var(--Primary-Dark-On-Surface-Accent); } + +.peach80 { + color: var(--Base-Text-Medium-contrast); +} diff --git a/components/TempDesignSystem/Text/Body/index.tsx b/components/TempDesignSystem/Text/Body/index.tsx index 0553375a5..61f71d19f 100644 --- a/components/TempDesignSystem/Text/Body/index.tsx +++ b/components/TempDesignSystem/Text/Body/index.tsx @@ -16,15 +16,15 @@ export default function Body({ const Comp = asChild ? Slot : "p" const classNames = fontOnly ? bodyFontOnlyVariants({ - className, - textAlign, - textTransform, - }) + className, + textAlign, + textTransform, + }) : bodyVariants({ - className, - color, - textAlign, - textTransform, - }) + className, + color, + textAlign, + textTransform, + }) return } diff --git a/components/TempDesignSystem/Text/Body/variants.ts b/components/TempDesignSystem/Text/Body/variants.ts index d044b4286..034f1eeba 100644 --- a/components/TempDesignSystem/Text/Body/variants.ts +++ b/components/TempDesignSystem/Text/Body/variants.ts @@ -13,6 +13,7 @@ const config = { textMediumContrast: styles.textMediumContrast, white: styles.white, peach50: styles.peach50, + peach80: styles.peach80, }, textAlign: { center: styles.textAlignCenter, @@ -22,6 +23,7 @@ const config = { bold: styles.bold, regular: styles.regular, underlined: styles.underlined, + uppercase: styles.uppercase, }, }, defaultVariants: { @@ -43,6 +45,7 @@ const fontOnlyconfig = { bold: styles.bold, regular: styles.regular, underlined: styles.underlined, + uppercase: styles.uppercase, }, }, defaultVariants: { diff --git a/components/TempDesignSystem/Text/Footnote/footnote.module.css b/components/TempDesignSystem/Text/Footnote/footnote.module.css index a020842ae..7c24a8ddc 100644 --- a/components/TempDesignSystem/Text/Footnote/footnote.module.css +++ b/components/TempDesignSystem/Text/Footnote/footnote.module.css @@ -25,6 +25,16 @@ text-decoration: var(--typography-Footnote-Regular-textDecoration); } +.uppercase { + font-family: var(--typography-Footnote-Regular-fontFamily); + font-size: var(--typography-Footnote-Regular-fontSize); + font-weight: var(--typography-Footnote-Bold-fontWeight); + letter-spacing: var(--typography-Footnote-Regular-letterSpacing); + line-height: var(--typography-Footnote-Regular-lineHeight); + text-decoration: var(--typography-Footnote-Regular-textDecoration); + text-transform: uppercase; +} + .center { text-align: center; } @@ -45,6 +55,10 @@ color: var(--Scandic-Brand-Pale-Peach); } +.peach50 { + color: var(--Scandic-Peach-50); +} + .textMediumContrast { color: var(--UI-Text-Medium-contrast); } diff --git a/components/TempDesignSystem/Text/Footnote/variants.ts b/components/TempDesignSystem/Text/Footnote/variants.ts index eb2040177..e07213e6d 100644 --- a/components/TempDesignSystem/Text/Footnote/variants.ts +++ b/components/TempDesignSystem/Text/Footnote/variants.ts @@ -8,6 +8,7 @@ const config = { black: styles.black, burgundy: styles.burgundy, pale: styles.pale, + peach50: styles.peach50, textMediumContrast: styles.textMediumContrast, }, textAlign: { @@ -17,6 +18,7 @@ const config = { textTransform: { bold: styles.bold, regular: styles.regular, + uppercase: styles.uppercase, }, }, defaultVariants: { @@ -35,6 +37,7 @@ const fontOnlyConfig = { textTransform: { bold: styles.bold, regular: styles.regular, + uppercase: styles.uppercase, }, }, defaultVariants: { diff --git a/public/_static/img/app-store-badge.svg b/public/_static/img/app-store-badge.svg new file mode 100644 index 000000000..fe8aeaa23 --- /dev/null +++ b/public/_static/img/app-store-badge.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/_static/img/google-play-badge.svg b/public/_static/img/google-play-badge.svg new file mode 100644 index 000000000..90936a6a4 --- /dev/null +++ b/public/_static/img/google-play-badge.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/types/components/footer/navigation.ts b/types/components/footer/navigation.ts new file mode 100644 index 000000000..5c62059d7 --- /dev/null +++ b/types/components/footer/navigation.ts @@ -0,0 +1,37 @@ +export type FooterMainNav = { + id: string + href: string + title: string + openInNewTab: boolean + isExternal: boolean +} +export type FooterMainNavProps = { + mainLinks: FooterMainNav[] +} + +export type FooterSecondaryNav = { + id: string + href: string + title: string + openInNewTab: boolean + isExternal: boolean +} +export type FooterSecondaryNavProps = { + secondaryLinks: { + title: string + links: FooterSecondaryNav[] + }[] + appDownloads: { + title: string + links: { + title: string + href: string + id: string + }[] + } +} + +export enum AppDownnLoadLinks { + apple = "/_static/img/app-store-badge.svg", + google = "/_static/img/google-play-badge.svg", +} diff --git a/types/components/icon.ts b/types/components/icon.ts index 52ac8c021..d965a4e78 100644 --- a/types/components/icon.ts +++ b/types/components/icon.ts @@ -27,11 +27,13 @@ export enum IconName { DoorOpen = "DoorOpen", ElectricBike = "ElectricBike", Email = "Email", + Facebook = "Facebook", Fitness = "Fitness", Globe = "Globe", House = "House", Image = "Image", InfoCircle = "InfoCircle", + Instagram = "Instagram", Location = "Location", Lock = "Lock", Parking = "Parking", @@ -42,6 +44,7 @@ export enum IconName { PlusCircle = "PlusCircle", Restaurant = "Restaurant", Sauna = "Sauna", + Tripadvisor = "Tripadvisor", TshirtWash = "TshirtWash", Wifi = "Wifi", WarningTriangle = "WarningTriangle",