diff --git a/src/app/collabora/page.tsx b/src/app/collabora/page.tsx index f680bbd..b4598a5 100644 --- a/src/app/collabora/page.tsx +++ b/src/app/collabora/page.tsx @@ -4,11 +4,15 @@ import { AssociationSection } from "@/components/collabora/association-section" import { CollaborationSection } from "@/components/collabora/collaboration-section" import { CompanySection } from "@/components/collabora/company-section" import { ContactSection } from "@/components/collabora/contact-section" +import { sectionContainerClass } from "@/components/collabora/section" import { Hero } from "@/components/ui/hero" +import { cn } from "@/lib/utils" import { CollaboraAboutShapes, + CollaboraAssociationPhotoShapes, CollaboraAssociationShapes, CollaboraCollaborationShapes, + CollaboraCompanyPhotoShapes, CollaboraCompanyShapes, CollaboraContactShapes, CollaboraHeroShapes, @@ -22,32 +26,35 @@ export const metadata: Metadata = { export default function CollaboraPage() { return ( -
- {/* This page is broken on mobile */} - - - -
+
+
+ + {/* Title at y=262 (mobile) / 286 (desktop) as in Figma; Hero adds 16px / 56px above it */} +
+ +
+
+
-
+
-
+
- + } />
-
+
- + } />
-
+
diff --git a/src/app/collabora/shapes.tsx b/src/app/collabora/shapes.tsx index 014aa58..05e07cd 100644 --- a/src/app/collabora/shapes.tsx +++ b/src/app/collabora/shapes.tsx @@ -1,133 +1,218 @@ -import { Shape, ShapeLayer } from "@/components/shapes" +import { FigmaShape, type FigmaShapePlacement, ShapeLayer } from "@/components/shapes" /** - * `left` offsets are `calc(50% + Npx)`, relative to the horizontal center of - * the 1728px (desktop) / 402px (mobile) Figma frames — see - * src/components/home/shapes.tsx. + * Decorative shapes of the "Collabora con Noi" Figma frames (desktop node 5248:13597, 1728px wide; mobile node + * 5246:13012, 402px wide). Values are the Figma layers' centers, sizes and rotations, in paint order. * - * The real sections are much shorter/taller than the Figma frames (up to - * ~300px drift on desktop, and the mobile page is ~60% taller), so each - * section's shapes are `top`-anchored to that section's heading (the section's - * top edge): `top = figmaY - figmaHeadingY`. The hero shapes are instead on a - * layer over the whole `
`, where page y = Figma y. + * Background shapes are grouped by the section that contains their center and `y` is measured from that section's + * top, so they stay next to their section however the copy reflows. `x` and the size scale with the page width. * - * On mobile the photo decorations of "associazione"/"azienda" are anchored to - * the section bottom, since the photo is the last element there. - * - * Section layers use `-inset-x-4` because the sections sit inside `
`'s - * `px-4`: without it the layer (and its horizontal clip) would stop 16px short - * of the viewport edges. + * Shapes Figma draws over the photos are anchored to the photo itself (see `PhotoShapes`). */ -export function CollaboraHeroShapes() { - return ( - - {/* Mobile Shapes */} - - - +type Placement = FigmaShapePlacement - {/* Desktop Shapes */} - - - - - - +const DESKTOP_WIDTH = 1728 +const MOBILE_WIDTH = 402 + +function SectionShapes({ desktop = [], mobile = [] }: { desktop?: Placement[]; mobile?: Placement[] }) { + return ( + + {mobile.map((shape) => ( + + ))} + {desktop.map((shape) => ( + + ))} ) } -export function CollaboraAboutShapes() { +const ROTATE_MINUS_34 = [0.828, -0.56, 0.56, 0.828] as const +const ROTATE_MINUS_98 = [-0.132, -0.991, 0.991, -0.132] as const +const ROTATE_MINUS_45 = [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2] as const + +export function CollaboraHeroShapes() { return ( - - {/* Mobile Shapes */} - - + + ) +} - {/* Desktop Shapes */} - - - - +export function CollaboraAboutShapes() { + return ( + ) } export function CollaboraCollaborationShapes() { return ( - - {/* Mobile Shapes */} - - - - - - - {/* Desktop Shapes */} - - - + ) } export function CollaboraAssociationShapes() { return ( - - {/* Mobile Shapes */} - - {/* Photo decoration, bottom-anchored to the photo */} - - - - - {/* Desktop Shapes */} - - - - - - + ) } export function CollaboraCompanyShapes() { return ( - - {/* Mobile Shapes */} - - {/* Photo decoration, bottom-anchored to the photo */} - - - - - {/* Desktop Shapes */} - - - - + ) } export function CollaboraContactShapes() { return ( - - {/* Mobile Shapes */} - - + + ) +} - {/* Desktop Shapes */} - - - - +/** + * Shapes drawn over a photo, positioned in the photo's own Figma frame (611px wide on desktop, 354px on mobile) so + * they follow it at every size. + */ +function PhotoShapes({ + desktop, + mobile, +}: { + desktop: { width: number; height: number; shapes: Placement[] } + mobile: { width: number; height: number; shapes: Placement[] } +}) { + return ( + <> + {mobile.shapes.map((shape) => ( + + ))} + {desktop.shapes.map((shape) => ( + + ))} + + ) +} + +export function CollaboraAssociationPhotoShapes() { + return ( + + ) +} + +export function CollaboraCompanyPhotoShapes() { + return ( + ) } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 6530047..3dd9300 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,4 +1,5 @@ import { ConditionalFooter } from "@/components/conditional-footer" +import { Footer } from "@/components/footer" import { Header } from "@/components/header/header" import { ThemeProvider } from "@/components/theme-provider" import "@/styles/globals.css" @@ -76,7 +77,9 @@ export default function RootLayout({ children }: Readonly<{ children: React.Reac
{children} - + +
+
diff --git a/src/assets/icons/collabora/chat.svg b/src/assets/icons/collabora/chat.svg new file mode 100644 index 0000000..621a837 --- /dev/null +++ b/src/assets/icons/collabora/chat.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/collabora/favorite.svg b/src/assets/icons/collabora/favorite.svg new file mode 100644 index 0000000..4d0fdb2 --- /dev/null +++ b/src/assets/icons/collabora/favorite.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/collabora/group.svg b/src/assets/icons/collabora/group.svg new file mode 100644 index 0000000..09e9ad1 --- /dev/null +++ b/src/assets/icons/collabora/group.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/icons/collabora/handshake.svg b/src/assets/icons/collabora/handshake.svg new file mode 100644 index 0000000..eaecc31 --- /dev/null +++ b/src/assets/icons/collabora/handshake.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/collabora/mail.svg b/src/assets/icons/collabora/mail.svg new file mode 100644 index 0000000..b21fa79 --- /dev/null +++ b/src/assets/icons/collabora/mail.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/collabora/prompt-suggestion.svg b/src/assets/icons/collabora/prompt-suggestion.svg new file mode 100644 index 0000000..4a39f71 --- /dev/null +++ b/src/assets/icons/collabora/prompt-suggestion.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/collabora/school.svg b/src/assets/icons/collabora/school.svg new file mode 100644 index 0000000..e1847a6 --- /dev/null +++ b/src/assets/icons/collabora/school.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/tiktok.svg b/src/assets/icons/tiktok.svg new file mode 100644 index 0000000..9e877ef --- /dev/null +++ b/src/assets/icons/tiktok.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/collabora_company.jpg b/src/assets/images/collabora_company.jpg new file mode 100644 index 0000000..3fb82b0 Binary files /dev/null and b/src/assets/images/collabora_company.jpg differ diff --git a/src/assets/images/collabora_company.png b/src/assets/images/collabora_company.png deleted file mode 100644 index 2d59297..0000000 Binary files a/src/assets/images/collabora_company.png and /dev/null differ diff --git a/src/components/card-multiple-icons/index.tsx b/src/components/card-multiple-icons/index.tsx index b2d60d8..3c05e7f 100644 --- a/src/components/card-multiple-icons/index.tsx +++ b/src/components/card-multiple-icons/index.tsx @@ -9,11 +9,12 @@ export function CardMultipleIcons({ icons, className }: CardMultipleIconsProps) return ( -
+ {/* Figma "glass-icone": 48px tall, icons 34px from the edges and evenly spread */} +
{iconItems.map((icon) => ( - Membri del nostro gruppo Telegram principale + Membri del nostro gruppo Telegram principale ), - icon: FiMessageSquare, + mobileDescription: "Membri del nostro gruppo Telegram", + icon: chatIcon, + iconClassName: "mr-3.75 h-7 w-7.5 md:mt-px md:mr-0 md:h-12.5 md:w-13.25", + descriptionClassName: "md:-mr-px", }, { title: "45k+", description: ( <> - Studenti raggiunti da PoliNetwork ogni anno + Studenti raggiunti da PoliNetwork ogni anno ), - icon: MdOutlineSchool, + mobileDescription: "Studenti raggiunti ogni anno", + icon: schoolIcon, + iconClassName: "mr-3.75 h-7 w-8.5 md:-mt-px md:mr-0 md:-ml-0.75 md:h-12.5 md:w-15.25", + descriptionClassName: "md:-mr-px", }, { title: "1000+", description: ( <> - Persone nel nostro evento più grande + Persone nel nostro evento +
+ più grande ), - - icon: FiUsers, + mobileDescription: "Persone al nostro evento più grande", + icon: groupIcon, + iconClassName: "mr-3.75 h-7 w-9.25 md:-mt-px md:mr-0 md:h-12.5 md:w-16.75", + descriptionClassName: "md:-mr-px", }, { title: "5", description: ( <> - Team Operativi pronti a collaborare + Team Operativi pronti a collaborare ), - icon: MdOutlineHandshake, + mobileDescription: "Team operativi pronti a collaborare", + icon: handshakeIcon, + iconClassName: "mr-3.75 h-7 w-7.75 md:mr-0 md:h-12.5 md:w-13.75", + descriptionClassName: "md:-mr-px", }, -] as const +] export function AboutSection() { return ( -
-
-

Chi siamo?

- -
-

- PoliNetwork è l'infrastruttura di comunicazione studentesca del Politecnico di Milano. Gestiamo oltre 500 - gruppi Telegram e WhatsApp, una presenza social in crescita e strumenti digitali usati ogni anno da decine - di migliaia di studenti di ingegneria, architettura e design. -

-
-

Chi entra al PoliMi, prima o poi, incontra PoliNetwork.

-

(e persino studenti di liceo o altri atenei)

+
+
+
+
+ Chi siamo? +
+

PoliNetwork è l'infrastruttura di comunicazione studentesca del Politecnico di Milano.

+

+ Gestiamo oltre 500 gruppi Telegram e WhatsApp, una presenza social in crescita e strumenti digitali + usati ogni anno da decine di migliaia di studenti di ingegneria, architettura e design. +

+

+ Chi entra al PoliMi, prima o poi, incontra PoliNetwork. +

+

+
+
+
+
- -
- -
- {cards.map((card) => ( - - ))} -
+
+ {cards.map((card) => ( + + ))} +
-
- - + {/* Bleeds to the screen edges like the Figma carousel, so the next card peeks in */} + + {cards.map((card) => ( - - + + ))} - + {/* The dots only render once Embla has measured the slides: reserve their row to avoid a layout shift */} +
+ +
diff --git a/src/components/collabora/association-section.tsx b/src/components/collabora/association-section.tsx index c5b8144..705e9d5 100644 --- a/src/components/collabora/association-section.tsx +++ b/src/components/collabora/association-section.tsx @@ -1,47 +1,37 @@ -import Image from "next/image" +import type { ReactNode } from "react" import associationsImage from "@/assets/images/collabora_associations.jpg" +import { PhotoSection } from "./photo-section" +import { Highlight } from "./section" -export function AssociationSection() { +export function AssociationSection({ photoDecorations }: { photoDecorations: ReactNode }) { return ( -
-
-

- Sei un’associazione studentesca? -

- -
-

- Se fai parte dell'ecosistema universitario del Politecnico di Milano o di un'altra università, possiamo - valutare collaborazioni su - - {" "} - eventi congiunti, visibilità reciproca sui canali o iniziative rivolte agli studenti. - -

-

- Le collaborazioni più efficaci che abbiamo avuto nel tempo sono nate da gruppi studenteschi che - condividevano una parte degli obiettivi che perseguiamo: - - {" "} - rendere la vita universitaria meno complicata e più ricca per chi studia al Politecnico. - -

-

- Il processo è semplice: ci scrivi, ci descrivi il progetto e valutiamo insieme se c'è una base comune (per - poi mettere i nostri talent a disposizione di questa partnership). -

-
-
- -
- -
-
+ + {/* The 402px Figma frame shortens the title to fit one line */} + Sei un’associazione? + Sei un’associazione studentesca? + + } + image={associationsImage} + frameClassName="aspect-[354/210] md:aspect-[611/370]" + imageClassName="top-0 left-[-0.14%] h-[112.55%] w-[100.14%] md:top-[-10.27%] md:left-[-0.08%] md:h-[110.27%] md:w-[100.15%]" + decorations={photoDecorations} + className="py-30 md:py-37.5" + > +

+ Se fai parte dell'ecosistema universitario del Politecnico di Milano o di un'altra università, possiamo valutare + collaborazioni su{" "} + eventi congiunti, visibilità reciproca sui canali o iniziative rivolte agli studenti. +

+

+ Le collaborazioni più efficaci che abbiamo avuto nel tempo sono nate da gruppi studenteschi che condividevano +
una parte degli obiettivi che perseguiamo:{" "} + + rendere la vita universitaria meno complicata e più ricca per chi studia +
al Politecnico. +
+

+
) } diff --git a/src/components/collabora/collaboration-section.tsx b/src/components/collabora/collaboration-section.tsx index 13973fd..e615b72 100644 --- a/src/components/collabora/collaboration-section.tsx +++ b/src/components/collabora/collaboration-section.tsx @@ -1,28 +1,23 @@ +import { cn } from "@/lib/utils" +import { Aside, Highlight, SectionTitle, sectionContainerClass, sectionTextClass } from "./section" + export function CollaborationSection() { return ( -
-
-

- Cosa possiamo fare insieme? -

- -
-

- Collaborare con noi significa{" "} - accedere a questa community in modo diretto. Non siamo un canale - pubblicitario e non accettiamo promozioni commerciali generiche: ogni collaborazione viene valutata in - termini di valore concreto per gli studenti del Politecnico - - {" "} - (e nel rispetto dei nostri valori sanciti sul nostro statuto). - +

+
+ Cosa possiamo fare insieme? +
+

+ Collaborare con noi significa accedere a questa community in modo diretto. Non siamo + un canale pubblicitario e non accettiamo promozioni commerciali generiche: ogni collaborazione viene + valutata in termini di valore concreto per gli studenti del Politecnico{" "} +

+

+

+ Se il progetto ha senso per la nostra community, siamo disponibili a costruirlo insieme!

-

Se il progetto ha senso per la nostra community, siamo disponibili a costruirlo insieme!

- - {/* Placeholder */} -
) } diff --git a/src/components/collabora/company-section.tsx b/src/components/collabora/company-section.tsx index 5c56bda..55723cf 100644 --- a/src/components/collabora/company-section.tsx +++ b/src/components/collabora/company-section.tsx @@ -1,40 +1,29 @@ -import Image from "next/image" -import companyImage from "@/assets/images/collabora_company.png" +import type { ReactNode } from "react" +import companyImage from "@/assets/images/collabora_company.jpg" +import { PhotoSection } from "./photo-section" +import { Highlight } from "./section" -export function CompanySection() { +export function CompanySection({ photoDecorations }: { photoDecorations: ReactNode }) { return ( -
-
- -
- -
-

- Sei un’azienda? -

- -
-

- PoliNetwork è un punto di accesso diretto alla community studentesca del Politecnico di Milano: migliaia di - studenti di corsi tecnici e progettuali, da tutta Italia, in tutti gli anni di corso. Se hai - un'iniziativa che porta valore reale agli studenti, siamo - disponibili a valutarla. -

-

- Siamo aperti a proposte su - eventi, iniziative di recruiting o progetti di visibilità. - Ogni proposta viene discussa nel rispetto della nostra indipendenza: non facciamo cose che compromettono la - fiducia che gli studenti ripongono in noi. -

-
-
-
+ +

+ PoliNetwork è un punto di accesso diretto alla community studentesca del Politecnico di Milano: migliaia di + studenti di corsi tecnici e progettuali, da tutta Italia, in tutti gli anni di corso. Se hai{" "} + un'iniziativa che porta valore reale agli studenti, siamo disponibili a valutarla. +

+

+ Siamo aperti a proposte su eventi, iniziative di recruiting o progetti di visibilità. + Ogni proposta viene discussa nel rispetto della nostra indipendenza: non facciamo cose che compromettono la + fiducia che gli studenti ripongono in noi. +

+
) } diff --git a/src/components/collabora/contact-section.tsx b/src/components/collabora/contact-section.tsx index af4d985..cdd23e4 100644 --- a/src/components/collabora/contact-section.tsx +++ b/src/components/collabora/contact-section.tsx @@ -1,60 +1,68 @@ -import { FiHeart, FiMail } from "react-icons/fi" -import { TbArrowBack } from "react-icons/tb" +import { Fragment } from "react" +import favoriteIcon from "@/assets/icons/collabora/favorite.svg" +import mailIcon from "@/assets/icons/collabora/mail.svg" +import promptSuggestionIcon from "@/assets/icons/collabora/prompt-suggestion.svg" import { cn } from "@/lib/utils" -import { CardIcon } from "../card-icon" +import { InfoCard, type InfoCardData } from "./info-card" +import { SectionTitle, sectionContainerClass } from "./section" -const cards = [ +const cards: InfoCardData[] = [ { title: "events@polinetwork.org", description: "Ci scrivi una mail descrivendo chi sei e cosa vorresti fare", - icon: FiMail, + mobileDescription: "Ci scrivi una mail spiegando chi sei e cosa vorresti fare", + icon: mailIcon, + iconClassName: "mr-2.5 h-5.5 w-7 md:mr-0 md:h-12 md:w-15", + descriptionClassName: "max-w-56", }, { title: "Condividi la tua visione", description: "Quale valore porta il tuo progetto agli studenti del Politecnico?", - icon: FiHeart, + mobileDescription: "Quale valore porta il progetto agli studenti del Poli?", + icon: favoriteIcon, + iconClassName: "mr-2.5 h-5.5 w-6 md:mt-0.5 md:mr-0 md:ml-0.5 md:h-12.5 md:w-13.5", + descriptionClassName: "max-w-65.75", }, { title: "Ricevi il nostro feedback", - description: "Rispondiamo al più presto a tutte le richieste", - icon: TbArrowBack, + description: ( + <> + Rispondiamo al più presto +
a tutte le richieste + + ), + mobileDescription: "Rispondiamo al più presto a tutte le richieste!", + icon: promptSuggestionIcon, + iconClassName: "mr-2.5 h-5.5 w-7 md:mt-0.5 md:mr-0 md:h-11.75 md:w-15", + descriptionClassName: "max-w-56", }, -] as const +] -const alignVariants = [ - { align: "start", size: "sm", visibility: "max-sm:hidden" }, - { align: "inline", size: "inline-sm", visibility: "sm:hidden" }, -] as const +/** Figma's connectors between the steps; vertical while the cards are stacked. */ +const connectorClassNames = [ + "from-[#51a2ff] to-[#0069a8]", // Vector 4 + "from-[#0069a8] to-[#74d4ff]", // Vector 5 +] export function ContactSection() { return ( -
-

Come contattarci

+
+ Come contattarci -
+
{cards.map((card, index) => ( -
- {alignVariants.map(({ align, size, visibility }) => ( - + + {index < connectorClassNames.length && ( +
svg]:scale-x-[-1]" + "h-10 w-1 shrink-0 bg-linear-to-b md:max-[1615px]:ml-51.5 min-[1616px]:h-1 min-[1616px]:w-auto min-[1616px]:min-w-10 min-[1616px]:flex-1 min-[1616px]:bg-linear-to-r", + connectorClassNames[index] )} /> - ))} -
+ )} + ))}
diff --git a/src/components/collabora/info-card.tsx b/src/components/collabora/info-card.tsx new file mode 100644 index 0000000..5c413a6 --- /dev/null +++ b/src/components/collabora/info-card.tsx @@ -0,0 +1,61 @@ +import Image, { type StaticImageData } from "next/image" +import type { ReactNode } from "react" +import { Glass } from "@/components/glass" +import { cn } from "@/lib/utils" + +export type InfoCardData = { + title: string + description: ReactNode + /** Mobile copy, when the 402px Figma frame uses a shorter text than the desktop one. Figma lets it run 8px into + * the card padding. */ + mobileDescription?: ReactNode + icon: StaticImageData + /** Icon size, offset and mobile gap to the title per breakpoint, as drawn in Figma. */ + iconClassName: string + /** Desktop description width: Figma wraps these copies at fixed widths (+1px, as Figma rounds text widths down). */ + descriptionClassName?: string +} + +/** + * Glass card with a Material icon, a gradient title and a description. Desktop (≥ md): icon above the title, as in + * the 1728px frame. Mobile: icon inline with the title, as in the 402px frame. + */ +export function InfoCard({ + card, + className, + descriptionColorClassName = "text-text-primary", +}: { + card: InfoCardData + className?: string + descriptionColorClassName?: string +}) { + const { title, description, mobileDescription, icon, iconClassName, descriptionClassName } = card + + return ( + // Glass draws a 1px border inside the Figma box: padding is 1px less than in Figma (25px / 40px) + +
+
+ +
+ {/* On the narrowest phones an email title may not fit: let it wrap after the "@" */} +

+ {title.replace("@", "@\u200B")} +

+
+ {mobileDescription !== undefined && ( +

{mobileDescription}

+ )} +

+ {description} +

+
+ ) +} diff --git a/src/components/collabora/photo-section.tsx b/src/components/collabora/photo-section.tsx new file mode 100644 index 0000000..dc53590 --- /dev/null +++ b/src/components/collabora/photo-section.tsx @@ -0,0 +1,64 @@ +import Image, { type StaticImageData } from "next/image" +import type { ReactNode } from "react" +import { cn } from "@/lib/utils" +import { SectionTitle, sectionContainerClass, sectionTextClass } from "./section" + +type PhotoSectionProps = { + title: ReactNode + children: ReactNode + image: StaticImageData + /** Figma crop of the photo inside its frame (mobile and desktop), as absolute size/offset classes. */ + imageClassName: string + /** Frame aspect ratio (mobile and desktop) of the photo. */ + frameClassName: string + /** Decorative shapes Figma draws over the photo. */ + decorations: ReactNode + /** Photo on the left on desktop. */ + photoFirst?: boolean + className?: string +} + +/** + * Heading + copy next to a photo. Stacked (copy, then photo) up to `xl`; from `xl` the two columns keep the + * 719:611 ratio of the 1728px Figma frame, bottom-aligned. + */ +export function PhotoSection({ + title, + children, + image, + imageClassName, + frameClassName, + decorations, + photoFirst = false, + className, +}: PhotoSectionProps) { + return ( +
+
+
+ {title} +
{children}
+
+ +
+
+ +
+
+ {decorations} +
+
+
+
+ ) +} diff --git a/src/components/collabora/section.tsx b/src/components/collabora/section.tsx new file mode 100644 index 0000000..03da89d --- /dev/null +++ b/src/components/collabora/section.tsx @@ -0,0 +1,41 @@ +import type { ReactNode } from "react" +import { cn } from "@/lib/utils" + +/** + * Horizontal frame shared by every Collabora section: 24px gutters on mobile (402px Figma frame), growing to the + * 145px gutters of the 1728px desktop frame, then centered so the content column never exceeds 1438px. + */ +export const sectionContainerClass = "mx-auto w-full max-w-432 px-6 md:px-[clamp(1.5rem,8.391%,9.0625rem)]" + +export function SectionTitle({ children, className }: { children: ReactNode; className?: string }) { + return ( +

+ {children} +

+ ) +} + +/** + * Section body copy: Red Hat 16px centered on mobile, Poppins 24px on desktop. Browsers add the letter-spacing after + * the last glyph of a line too, so lines that exactly fill their Figma box would wrap: the box gets 1px of slack. + */ +export const sectionTextClass = + "typo-body-large md:typo-headline-small -mx-[0.5px] text-center text-black md:mr-[-1px] md:ml-0 md:text-start" + +export function Highlight({ children }: { children: ReactNode }) { + return {children} +} + +/** The small parenthetical asides that follow a paragraph (Figma: body/small with 21px leading, body/large with 28px). */ +export function Aside({ children, className }: { children: ReactNode; className?: string }) { + return ( + + {children} + + ) +} diff --git a/src/components/conditional-footer.tsx b/src/components/conditional-footer.tsx index 248f2a3..35ce8eb 100644 --- a/src/components/conditional-footer.tsx +++ b/src/components/conditional-footer.tsx @@ -1,15 +1,19 @@ "use client" import { usePathname } from "next/navigation" -import { Footer } from "@/components/footer" +import type { ReactNode } from "react" import { matchesAnyRoutePrefix } from "@/utils/route-match" const HIDDEN_EXACT = ["/matricole/guida"] const HIDDEN_PREFIXES = ["/groups"] -export function ConditionalFooter() { +/** + * Hides the footer on full-screen routes. The footer itself is passed as `children` so it stays a server component + * instead of being bundled under this client boundary. + */ +export function ConditionalFooter({ children }: { children: ReactNode }) { const pathname = usePathname() const hidden = HIDDEN_EXACT.includes(pathname) || matchesAnyRoutePrefix(pathname, HIDDEN_PREFIXES) if (hidden) return null - return