diff --git a/src/components/sections/customerCasesEntry/CustomerCasesList.tsx b/src/components/sections/customerCasesEntry/CustomerCasesList.tsx index 44e15b8de..fa04f868f 100644 --- a/src/components/sections/customerCasesEntry/CustomerCasesList.tsx +++ b/src/components/sections/customerCasesEntry/CustomerCasesList.tsx @@ -24,47 +24,49 @@ const CustomerCaseList = ({ customerCasePageSlug, }: CustomerCasesProps) => { const [selectedCustomerCase, setSelectedCustomerCase] = - useState(customerCases[0] || null); + useState(customerCases[0]); const deliveryNames = [ - selectedCustomerCase.projectInfo.deliveries.projectManagement && + selectedCustomerCase?.projectInfo.deliveries.projectManagement && "Project Management", - selectedCustomerCase.projectInfo.deliveries.design && "Design", - selectedCustomerCase.projectInfo.deliveries.development && "Development", + selectedCustomerCase?.projectInfo.deliveries.design && "Design", + selectedCustomerCase?.projectInfo.deliveries.development && "Development", ].filter(Boolean); return ( -
-
-
-
- - - +
+
+
+ - + + + +
+ + {selectedCustomerCase.image && ( + + )} +
- - {selectedCustomerCase.image && ( - - )} -
-
+ ) ); }; export default CustomerCaseList; diff --git a/src/components/sections/hero/Hero.stories.tsx b/src/components/sections/hero/Hero.stories.tsx deleted file mode 100644 index 417427545..000000000 --- a/src/components/sections/hero/Hero.stories.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Meta, StoryObj } from "@storybook/react"; - -import { Hero } from "./Hero"; -import { mockHeroSection } from "./mockData"; - -const meta: Meta = { - title: "Components/Sections/Hero", - component: Hero, - parameters: { - a11y: { - element: "div", - config: {}, - options: {}, - manual: false, - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Landing: Story = { - args: { - hero: mockHeroSection, - isLanding: true, - }, -}; - -export const Default: Story = { - args: { - hero: mockHeroSection, - isLanding: false, - }, -}; diff --git a/src/components/sections/hero/Hero.tsx b/src/components/sections/hero/Hero.tsx index 664908122..704faa095 100644 --- a/src/components/sections/hero/Hero.tsx +++ b/src/components/sections/hero/Hero.tsx @@ -1,6 +1,5 @@ -import LinkButton from "src/components/linkButton/LinkButton"; +import { SanityImage } from "src/components/image/SanityImage"; import Text from "src/components/text/Text"; -import { ILink } from "studio/lib/interfaces/navigation"; import { HeroSection } from "studio/lib/interfaces/pages"; import styles from "./hero.module.css"; @@ -15,38 +14,19 @@ export const Hero = ({ hero, isLanding = false }: HeroProps) => { return; } return ( -
+
{isLanding ? (
- - {hero.basicTitle} - - {hero.description && ( -
- {hero.description} -
- )} -
    - {hero.callToActions?.map((cta: ILink, index) => ( -
  • - 1 && index === 0 - ? "secondary" - : "primary" - } - /> -
  • - ))} -
+ {hero.title} + {hero.description} +
+ +
) : ( + // If splashy segments are added to the hero section in the landing page, this serves as a great fallback option.
- {hero.basicTitle} - {hero.description && {hero.description}} + {hero.description}
)}
diff --git a/src/components/sections/hero/hero.module.css b/src/components/sections/hero/hero.module.css index 49bdb99ff..333c6cf4f 100644 --- a/src/components/sections/hero/hero.module.css +++ b/src/components/sections/hero/hero.module.css @@ -1,84 +1,66 @@ .wrapper { display: flex; flex-direction: column; - align-items: center; overflow: hidden; -} - -.secondaryColor { - padding: 7.5rem 2rem; + padding: var(--padding-l) 0rem; - @media (min-width: 640px) { - text-align: center; - padding: 7.5rem 3rem; + @media (max-width: 834px) { + padding: var(--padding-l) 0rem; } - @media (min-width: 1024px) { - padding: 15rem 7.5rem; - } -} - -.primaryColor { - background-color: var(--background-bg-light-primary); - padding: 7.5rem 2rem 5rem 2rem; - - @media (min-width: 640px) { - padding: 7.5rem 3rem 5rem 3rem; - } - - @media (min-width: 1024px) { - padding: 7.5rem 2rem 5rem 2rem; + @media (max-width: 425px) { + padding: 0rem; } } .shared { width: 100%; - max-width: 1200px; + max-width: 70.125rem; display: flex; flex-direction: column; - gap: 3rem; } .secondary { composes: shared; - align-items: center; -} - -.primary { - composes: shared; - align-items: flex-start; -} + align-self: center; + padding: var(--padding-xl) var(--padding-rem); + gap: var(--padding-rem); -.title { - font-weight: 700; - text-align: center; -} + @media (max-width: 834px) { + padding: var(--padding-l) var(--padding-s); + } -.description { - margin: auto; - align-self: stretch; + @media (max-width: 425px) { + padding: var(--padding-l) var(--padding-s); + } } -.cta { - margin: 0; - padding: 0; +.image { width: 100%; - list-style-type: none; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 1.5rem; + /* max-width: 30rem; */ + justify-content: flex-end; + display: flex !important; + padding: var(--padding-xl) var(--padding-rem) var(--padding-rem) + var(--padding-rem); - @media (min-width: 640px) { - flex-direction: row; - justify-content: center; - gap: 1.5rem; + @media (max-width: 834px) { + padding: var(--padding-l) var(--padding-rem) var(--padding-rem) + var(--padding-rem); } - & > li { - @media (max-width: 640px) { - width: 100%; - } + @media (max-width: 425px) { + padding: var(--padding-s) var(--padding-rem) var(--padding-rem) + var(--padding-rem); } } + +.image img { + display: block; + width: 100%; + height: 100%; + max-width: 42rem !important; +} + +.primary { + composes: shared; +} diff --git a/src/components/sections/hero/mockData.ts b/src/components/sections/hero/mockData.ts deleted file mode 100644 index e5dd6c7d8..000000000 --- a/src/components/sections/hero/mockData.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { LinkType } from "studio/lib/interfaces/navigation"; -import { HeroSection } from "studio/lib/interfaces/pages"; - -export const mockHeroSection: HeroSection = { - _key: "", - _type: "hero", - basicTitle: "Effortless Climate Action", - description: - "Understand your carbon footprint, identify opportunities for reducing emissions, and reach your sustainability goals.", - callToActions: [ - { - _key: "1", - url: "/how-it-works", - linkTitle: "See how it works", - linkType: LinkType.Internal, - newTab: false, - _type: "", - }, - { - _key: "2", - url: "/get-started", - linkTitle: "Get started", - linkType: LinkType.Internal, - newTab: false, - _type: "", - }, - ], -}; diff --git a/src/styles/global.css b/src/styles/global.css index 8dc52e95c..28505ee88 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -142,6 +142,15 @@ html { --radius-medium: 12px; --radius-large: 24px; + /* Padding */ + --padding-xs: 0.25rem; + --padding-s: 0.75rem; + --padding-rem: 1rem; + --padding-m: 1.5rem; + --padding-l: 3rem; + --padding-xl: 6rem; + --padding-xxl: 9rem; + font-size: clamp(1rem, 0.9rem + 0.5vw, 1.2rem); } diff --git a/studio/lib/interfaces/pages.ts b/studio/lib/interfaces/pages.ts index fca06537e..5202444ac 100644 --- a/studio/lib/interfaces/pages.ts +++ b/studio/lib/interfaces/pages.ts @@ -10,9 +10,9 @@ import { ILink } from "./navigation"; export interface HeroSection { _type: "hero"; _key: string; - basicTitle: string; - callToActions: ILink[]; + title: string; description: string; + image: IImage; } export interface LogoSaladSection { diff --git a/studio/lib/queries/pages.ts b/studio/lib/queries/pages.ts index cdb02762c..69766a785 100644 --- a/studio/lib/queries/pages.ts +++ b/studio/lib/queries/pages.ts @@ -8,10 +8,8 @@ const SECTIONS_FRAGMENT = groq` ..., _type == "hero" => { ..., - callToActions[] { - ..., - ${TRANSLATED_LINK_FRAGMENT} - } + "title": ${translatedFieldFragment("title")}, + "description": ${translatedFieldFragment("description")}, }, _type == "article" => { ..., diff --git a/studio/schemas/objects/sections/hero.ts b/studio/schemas/objects/sections/hero.ts index 9b69c4ff6..696437ced 100644 --- a/studio/schemas/objects/sections/hero.ts +++ b/studio/schemas/objects/sections/hero.ts @@ -1,10 +1,6 @@ -// hero.ts -import { StringInputProps, defineField } from "sanity"; +import { defineField } from "sanity"; -import CustomCallToActions from "studio/components/CustomCallToActions"; -import { StringInputWithCharacterCount } from "studio/components/stringInputWithCharacterCount/StringInputWithCharacterCount"; -import callToActionField from "studio/schemas/fields/callToActionFields"; -import { title } from "studio/schemas/fields/text"; +import { allTranslation } from "studio/utils/i18n"; export const heroID = "hero"; @@ -13,51 +9,73 @@ export const hero = defineField({ title: "Hero Section", type: "object", fields: [ - title, + { + name: "title", + title: "title", + type: "internationalizedArrayString", + validation: (rule) => + rule.custom<{ value: string; _type: string; _key: string }[]>( + (value) => { + if (!value) return true; + + const invalidItems = value.filter( + (item) => + typeof item.value === "string" && item.value.length > 200, + ); + + if (invalidItems.length > 0) { + return invalidItems.map((item) => ({ + message: "title cannot be more than 200 characters long.", + path: [{ _key: item._key }, "value"], + })); + } + + return true; + }, + ), + }, { name: "description", title: "Description", - type: "string", - validation: (rule) => rule.max(200), - components: { - input: (props: StringInputProps) => - StringInputWithCharacterCount({ ...props, maxCount: 200 }), - }, + type: "internationalizedArrayString", + validation: (rule) => + rule.custom<{ value: string; _type: string; _key: string }[]>( + (value) => { + if (!value) return true; + + const invalidItems = value.filter( + (item) => + typeof item.value === "string" && item.value.length > 200, + ); + + if (invalidItems.length > 0) { + return invalidItems.map((item) => ({ + message: "Description cannot be more than 200 characters long.", + path: [{ _key: item._key }, "value"], + })); + } + + return true; + }, + ), }, { - name: "callToActions", - title: "Call to Actions", - description: - "Available only for landing pages, this feature helps improve user engagement by directing them to important areas or actions on your site. The first Call to Action (CTA) will be styled as a primary link button.", - type: "array", - of: [ - { - type: "object", - fields: callToActionField.fields, - preview: callToActionField.preview, - }, - ], - validation: (rule) => - rule.custom((callToActions) => { - if (!Array.isArray(callToActions)) return true; - if (callToActions.length > 2) { - return "You can only have two Call to Action links"; - } - return true; - }), - components: { - input: CustomCallToActions, + name: "image", + title: "Image", + type: "image", + options: { + hotspot: true, }, }, ], preview: { select: { - title: "basicTitle", + title: "title", }, prepare(selection) { const { title } = selection; return { - title: title, + title: allTranslation(title) ?? undefined, subtitle: "Hero Section", }; },