Skip to main content

Svelte Components

ConsentBanner

Render the banner

Render ConsentBanner once, inside ConsentProvider, next to ConsentDialog so its Customize button has a dialog to open:

src/App.svelte
<script lang="ts">
	import { posthog } from '@c15t/integrations/posthog';
	import {
		ConsentBanner,
		ConsentDialog,
		ConsentDialogLink,
		ConsentProvider,
		hosted,
	} from '@c15t/svelte';

	const scripts = [
		posthog({
			id: 'phc_your_project_key',
			initOptions: { cookieless_mode: 'never' },
			loadMode: 'after-consent',
		}),
	];
</script>

<ConsentProvider mode={hosted()} {scripts}>
	<main>
		<h1>c15t + Svelte</h1>
		<p>Your app goes here.</p>
	</main>
	<footer>
		<ConsentDialogLink>Privacy settings</ConsentDialogLink>
	</footer>
	<ConsentBanner />
	<ConsentDialog />
</ConsentProvider>

The banner decides for itself whether to show. You do not wrap it in a condition.

Change the shape

variant and position set the shape for one banner. The provider's presentation prop sets them for every surface; a prop on ConsentBanner wins. This banner is a full-width bar along the bottom edge:

src/lib/cookie-banner.svelte
<script lang="ts">
	import { ConsentBanner } from '@c15t/svelte';
</script>

<!-- The stock banner, drawn as a full-width bar along the bottom edge. -->
<ConsentBanner variant="bar" position="bottom" />
variantPositions
floatingbottom-left (default), bottom-right, top-left, top-right, bottom-center, top-center
barbottom (default), top
widgetbottom-right (default), bottom-left, top-left, top-right
wallcenter

A notice defaults to bar and a choice prompt to floating. A position that does not fit the variant falls back to the variant's default and logs a warning in development. When the page's text direction is right to left and you set no position, a corner position flips to the other side.

The design gallery has more banner designs with tested Svelte code.

Props

PropTypeDefaultBehavior
variant'floating', 'bar', 'widget' or 'wall'from the policyShape of the banner.
positionsee the table abovethe variant's defaultWhere the banner sits.
blockingbooleanfalseAdds a backdrop, locks scrolling, traps focus and ignores outside clicks. wall is always blocking; a notice never is.
layoutarray of 'accept', 'reject', 'customize', 'dismiss' or nested groupsfrom the policyButton order and grouping, such as [['reject', 'accept'], 'customize'].
primaryButtonone action or an arrayfrom the policyWhich buttons get the primary style.
title, descriptionstringtranslated copyReplace the heading and body for this banner.
acceptButtonText, rejectButtonText, customizeButtonText, dismissButtonTextstringtranslated copyReplace one button label for this banner.
legalLinksarray of LegalLinks keys, or nullevery configured linkWhich of the provider's legalLinks to show after the description. null shows none.
hideBrandingbooleanfalseHides the "Secured by" tag.
modelsModel[]['opt-in', 'opt-out', 'iab']Policy models this banner renders for.
noStylebooleanprovider's noStyleDrops c15t's classes, keeping markup and behavior.
disableAnimationbooleanprovider's valueShows and hides without a transition.
scrollLock, trapFocusbooleanfrom presentationLock page scroll or trap focus while the banner shows.
classstringnoneExtra class on the root element.

The policy wins where it conflicts with layout or primaryButton. A policy that requires a reject button next to accept keeps it, whatever the layout says. For copy used across the site, use the provider's i18n or your backend's translations; see translations.

When the banner shows

ConsentBanner renders when all of these are true:

  • A policy has resolved for the visitor and it asks for a prompt.
  • The visitor has not chosen yet, or the policy asks them to choose again.
  • The active surface is the banner, not the dialog or nothing.
  • The policy's model is in models.

A visitor in a region without a consent law, or one who already chose, gets no banner. That is a policy result; see why the banner may be absent.

A choice prompt shows Accept, Reject and Customize, in the order the policy asks for. A notice, such as an opt-out policy that only informs, shows a dismiss button and its own title and description from the noticeTitle and noticeDescription translations. Dismissing a notice records the dismissal, not a grant.

When the policy grants visitor rights, such as opting out of sale, the banner shows each one as a text button that opens the preference dialog.

Accept and Reject record the choice and close the banner in the same task. The backend request runs after; a failed request is retried and does not reopen the banner. Customize opens ConsentDialog, so render the dialog too.

Accessibility

  • The banner card is a region labelled by the banner title. With blocking, it becomes a dialog with aria-modal="true", and focus stays inside it until the visitor chooses.
  • The title is an h2. Buttons are <button type="button"> with their visible text as the accessible name.
  • The banner does not move focus when it appears, unless focus trapping is on. Visitors reach it with Tab.
  • With prefers-reduced-motion, the banner appears and leaves without a transition.
  • The root sets dir from the active language, so right-to-left copy lays out correctly.

Style the banner

ConsentBanner reads these theme slots from the provider's theme prop: consentBanner, consentBannerCard, consentBannerHeader, consentBannerTitle, consentBannerDescription, consentBannerFooter, consentBannerFooterSubGroup, consentBannerRights and consentBannerRightLink. Customize shows how to pass them.

The root element carries data-testid="consent-banner-root" and these attributes for CSS and tests:

AttributeValue
data-promptchoice or notice
data-modelopt-in, opt-out or iab
data-variantthe resolved variant
data-positionthe resolved position
data-blockingtrue when blocking

Each button carries data-action with accept, reject, customize or dismiss.