Components · Layout and structure

Call to action

A floating action bar that anchors near the bottom of the viewport so the user always has a clear next step within reach. Charcoal pill, H3 headline, primary action — the canonical Capital-flyer surface.

Documentedby Derek Fidler

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+).

Click to mount the bar near the bottom of the viewport. It stays in view as you scroll.

Anatomy

Three parts, each earning its space.

PartRole
SurfaceCharcoal 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.
HeadlineInter 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.
Action48 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

TopicRule
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.
WidthCapped at 720 px; on narrow viewports tightens to the screen edges via responsive inset (16 / 24 / 32 px from sm to lg+).
Inline modePass `inline` to render in normal flow. Use for embeds, doc previews, and any context where the bar should sit inside a layout column.
Z-indexz-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.
DismissalOff 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

PropTypeDefaultDescription
title*ReactNodeThe headline. Set in 24 px bold on the dark surface.
descriptionReactNodeOptional second line. Keep it to one short sentence — the bar isn't a content surface.
action*CallToActionActionPrimary 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).
secondaryActionCallToActionActionQuieter action rendered before the primary. Use for counter-actions like Discard / Cancel. No default icon — secondaries shouldn't read as forward motion.
onDismiss() => voidRenders 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.
inlinebooleanfalseWhen true, renders in normal flow (no `position: fixed`). For doc previews and prototypes that want the bar inside a layout column.
offsetBottomnumber24Distance from the bottom of the viewport when floating (px). Ignored when inline.
maxWidthnumber | string720Maximum width of the floating pill.
aria-labelstring"Call to action"Region-landmark label. Override when the bar's purpose is specific (e.g. 'Capital offer prompt').
classNamestringForwarded to the wrapper.

CallToActionAction

PropTypeDefaultDescription
label*stringButton label.
hrefstringRenders an <a>. Use for navigation. Mutually exclusive with `onClick`.
onClick(event) => voidRenders a <button>. Use for in-page actions like save / dismiss / fire mutation.
targetstringNative anchor target (e.g. '_blank'). Auto-sets rel='noopener noreferrer' for new tabs.
relstringOverride the default rel attribute.
iconReactNode | nullTrailing 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.
disabledbooleanDisabled state for the button shape.