Files
twenty/packages/twenty-website/src/ui/Signoff.tsx
T
Abdullah. d58ec64e10 feat(website): swap product Demo for a register Signoff; make signoff headings width-driven (#22232)
## Summary

Reworks the product page's closing section, and in doing so makes every
signoff closer width-driven.

### 1. Replace the Demo section with a register Signoff (`6a5f0c34bf`)

The product page ended on a static `AppPreview` mockup ("Try it live").
Swap it for the shared `Signoff` closer — a focused register CTA, the
same component the customers / why-twenty / partners pages use — so the
page ends by driving sign-ups instead of re-showing the product.

- New `ProductSignoff`: heading "Start moving faster today.", a
supporting line, and the **Get started + Talk to us** pair (matching
`CustomersCatalogSignoff`). Uses `SITE_URLS.appWelcome` per the
site-urls rule rather than inlining the URL.
- Removes the now-unused `product-demo` section and its 192KB background
asset.

### 2. Make Signoff headings width-driven (`0365c0d2ea`)

The signoff headings forced their two-line break with a literal `\n` in
the translated string — against the site's typography principle (fluid
type + `text-wrap: balance`, no `<br>`).

- `Signoff` now carries one default **`615px`** heading measure; all
five closers drop `\n`.
- The width was **measured, not guessed**: each heading was rendered
headlessly in the real production fonts (Aleo 300 body, Host Grotesk 300
accent — each heading mixes both) at the desktop size with `text-wrap:
balance`, then I found the common window where every heading breaks
identically: whyTwenty `[565–820]`, customers `[425–800]`, partner
`[380–730]`, product `[380–660]` — `615` sits in all of them. Each was
then verified to reproduce its exact current break at 615px.
- **Affects 4 other pages** (why-twenty, customers, customers/[slug],
partners) — all verified to render identically, and headings now reflow
on narrow viewports instead of being pinned by `\n`.
- Drops the one-off `headingMaxWidth` prop (now unused).

## Test plan

- oxfmt, oxlint, check-conventions, typecheck — all green.
- Headless measurement confirms all five signoff headings reproduce
their current desktop break at 615px (no visual regression).
- Visually confirmed the product closer and the four sibling signoffs.
2026-06-26 18:45:39 +02:00

95 lines
2.0 KiB
TypeScript

import { styled } from '@linaria/react';
import { type ReactNode } from 'react';
import { mediaUp, type Scheme, spacing } from '@/tokens';
import { Body } from './Body';
import { GuideCrosshair } from './GuideCrosshair';
import { Heading } from './Heading';
import { SectionShell } from './SectionShell';
type SignoffCrosshairSide = 'left' | 'right';
const SIGNOFF_CROSSHAIR_Y = '198px';
const SIGNOFF_CROSSHAIR_X: Record<SignoffCrosshairSide, string> = {
left: 'calc(50% - 334px)',
right: 'calc(50% + 334px)',
};
const SignoffStack = styled.div`
align-items: center;
display: flex;
flex-direction: column;
padding-block: ${spacing(20)};
text-align: center;
${mediaUp('md')} {
justify-content: center;
min-height: 759px;
padding-block: 0;
}
`;
const HeadingMeasure = styled.div`
max-width: 615px;
width: 100%;
`;
// Measure tuned to a clean break without coupling a <br> into the translation.
const Subline = styled.div`
margin-top: ${spacing(2)};
max-width: 400px;
width: 100%;
`;
const Actions = styled.div`
display: flex;
flex-wrap: wrap;
gap: ${spacing(3)};
justify-content: center;
margin-top: ${spacing(6)};
`;
export type SignoffProps = {
body: string;
children: ReactNode;
crosshairSide?: SignoffCrosshairSide;
heading: string;
scheme?: Scheme;
};
export function Signoff({
body,
children,
crosshairSide = 'right',
heading,
scheme = 'light',
}: SignoffProps) {
return (
<SectionShell
background={
<GuideCrosshair
crossX={SIGNOFF_CROSSHAIR_X[crosshairSide]}
crossY={SIGNOFF_CROSSHAIR_Y}
/>
}
rhythm="flush"
scheme={scheme}
>
<SignoffStack>
<HeadingMeasure>
<Heading as="h2" size="lg" weight="light">
{heading}
</Heading>
</HeadingMeasure>
<Subline>
<Body muted size="sm">
{body}
</Body>
</Subline>
<Actions>{children}</Actions>
</SignoffStack>
</SectionShell>
);
}