Ready to get started?
Overview
The call-to-action lives in @flatpay-dk/uiand renders as a floating pill anchored near the bottom of the viewport. It's page chrome, not page content — the page below scrolls; the bar stays. Reach for it when the user's next move is obvious and you want it one click away regardless of where they are on the page.
One per page
Two competing CTAs at the bottom of the viewport read as a busy interface and erode the “clear next step” the bar exists to provide. If the page genuinely has more than one outcome, the bar is the wrong tool — use a Page sticky footer or split the content into its own surfaces.
Variants
Title-only, title + description, primary + secondary, dismissable, and a light tone for inverted surfaces. Every example below mounts with inline so it sits inside the doc tile — see Floating below for the real position-fixed behaviour.
Ready to get started?
title only
The canonical Capital-flyer shape — one-line headline, single primary action with the arrow_outward affordance.
One step left
Verify your business details to unlock daily payouts.
title + description
Reach for description when the headline alone doesn't communicate the offer. Keep it to one short sentence; the bar isn't a paragraph surface.
You have unsaved changes
Save now or discard to return to the previous values.
primary + secondary
Form-page pattern: unsaved changes get a Save (primary) and a Discard (secondary). Secondaries don't carry the trailing arrow — they're counter-actions, not forward motion.
Capital is now available
Up to €50.000 in growth funding, repaid as a fixed share of sales.
dismissable
`onDismiss` reveals a close button. Use sparingly — the bar is meant to stay; only allow dismissal when the action is genuinely optional.
Ready to get started?
tone — light
Inverts the surface to off-white with charcoal content. Use on dark page backgrounds or when the dark pill would compete with surrounding chrome.
Floating behaviour
The bar renders position: fixed by default — anchored to the bottom of the viewport, centred horizontally, capped at maxWidth (720 px), with a deep drop shadow so it floats above page content. On narrow viewports it tightens to the screen edges via responsive inset (16 / 24 / 32 px from sm to lg+).
Anatomy
Three parts, each earning its space.
| Part | Role |
|---|---|
| Surface | Charcoal pill, 16 px radius, 32 px horizontal × 24 px vertical padding. Drop shadow `0 8px 16px -4px rgba(26,26,26,0.22)` lifts it above page content. |
| Headline | Inter Tight Bold 24 px / 32 px leading. The page's most prominent type after the H1, but rendered as a paragraph so it doesn't fight the document outline. |
| Action | 48 px button, white-on-charcoal pill. Default trailing icon is arrow_outward — the canonical 'navigate forward' affordance. Optional secondary action sits to its left. |
Behaviour
| Topic | Rule |
|---|---|
| Default position | `position: fixed` anchored to the bottom of the viewport, centred horizontally with a 24 px offset. Stays in view as the page scrolls. |
| Width | Capped at 720 px; on narrow viewports tightens to the screen edges via responsive inset (16 / 24 / 32 px from sm to lg+). |
| Inline mode | Pass `inline` to render in normal flow. Use for embeds, doc previews, and any context where the bar should sit inside a layout column. |
| Z-index | z-50 — above the navigation rail (z-30) and the docs sidebar so the action is never covered. Lower than dialogs (z-50+) so a modal can take focus. |
| Dismissal | Off by default. Set `onDismiss` to reveal a close button. Reserve for genuinely optional prompts — never on save / unsaved-changes flows. |
Accessibility
The bar is a labelled landmark — assistive tech users can jump to it with their region-navigation shortcut. The headline is a paragraph, not a heading, because the bar is page chrome and adding a heading would interrupt the page's outline.
ARIA
The wrapper renders role="region" with aria-label defaulting to “Call to action”. Override via the aria-labelprop when you want the landmark to read with the action's purpose (e.g. “Capital offer prompt”).
Code
Capital flyer (the canonical case)
tsx
import { CallToAction } from "@flatpay-dk/ui";
<CallToAction
title="Ready to get started?"
action={{ label: "View offers", href: "/capital/offers" }}
/>;Unsaved changes (primary + secondary)
tsx
<CallToAction
title="You have unsaved changes"
description="Save now or discard to return to the previous values."
action={{ label: "Save changes", onClick: handleSave }}
secondaryAction={{ label: "Discard", onClick: handleDiscard }}
/>;Dismissable announcement
tsx
const [visible, setVisible] = useState(true);
if (!visible) return null;
return (
<CallToAction
title="Capital is now available"
description="Up to €50.000 in growth funding, repaid as a fixed share of sales."
action={{ label: "View offers", href: "/capital/offers" }}
onDismiss={() => setVisible(false)}
/>
);Inline (no float — embeds and prototypes)
tsx
<CallToAction
inline
title="Verify your business"
action={{ label: "Verify details", href: "/verify" }}
/>;Best practices
Ready to get started?
Do
One headline, one verb. Keep the title to a question or short prompt the action plainly answers.
Take a look at all the great new things you can do with our improved Capital product
Don't
Don't write a paragraph. The bar is page chrome, not a content surface — keep the headline ≤ 6 words and the description ≤ 1 sentence.
Capital is now available
Do
Use onDismiss only when the action is truly optional — announcements, marketing nudges. Never on save / unsaved-changes prompts.
You have unsaved changes
Don't
Don't allow dismissing a save prompt. The whole point of the bar is to keep the user from forgetting their unsaved work.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title* | ReactNode | — | The headline. Set in 24 px bold on the dark surface. |
| description | ReactNode | — | Optional second line. Keep it to one short sentence — the bar isn't a content surface. |
| action* | CallToActionAction | — | Primary action. Pass either { label, href } for a link or { label, onClick } for a button. Default trailing icon is arrow_outward; override with `icon` (or set to null to remove). |
| secondaryAction | CallToActionAction | — | Quieter action rendered before the primary. Use for counter-actions like Discard / Cancel. No default icon — secondaries shouldn't read as forward motion. |
| onDismiss | () => void | — | Renders a trailing close button. Use sparingly — the bar is meant to stay. Never on save / unsaved-changes prompts. |
| tone | "dark" | "light" | "dark" | Charcoal-on-white (default) or white-on-charcoal. Light is for dark page backgrounds or surfaces where the dark pill would compete with surrounding chrome. |
| inline | boolean | false | When true, renders in normal flow (no `position: fixed`). For doc previews and prototypes that want the bar inside a layout column. |
| offsetBottom | number | 24 | Distance from the bottom of the viewport when floating (px). Ignored when inline. |
| maxWidth | number | string | 720 | Maximum width of the floating pill. |
| aria-label | string | "Call to action" | Region-landmark label. Override when the bar's purpose is specific (e.g. 'Capital offer prompt'). |
| className | string | — | Forwarded to the wrapper. |
CallToActionAction
| Prop | Type | Default | Description |
|---|---|---|---|
| label* | string | — | Button label. |
| href | string | — | Renders an <a>. Use for navigation. Mutually exclusive with `onClick`. |
| onClick | (event) => void | — | Renders a <button>. Use for in-page actions like save / dismiss / fire mutation. |
| target | string | — | Native anchor target (e.g. '_blank'). Auto-sets rel='noopener noreferrer' for new tabs. |
| rel | string | — | Override the default rel attribute. |
| icon | ReactNode | null | — | Trailing icon. Default = arrow_outward on primary, none on secondary. Pass null to suppress. |
| type | "button" | "submit" | "reset" | "button" | Native button type. Use 'submit' inside forms. |
| disabled | boolean | — | Disabled state for the button shape. |