Astro Customization
Customize
Choose what to change
Astro takes every customization through the c15t() options in
astro.config.mjs, and renders it on the server. The options must survive JSON
serialization, so they hold values, not functions.
| Change | Use |
|---|---|
| Brand colors, radius, type, spacing | theme tokens |
| Which button looks primary | theme.consentActions |
| Wording on every page | i18n.messages |
| Wording on one banner | ConsentBanner props |
| Light or dark surfaces | colorScheme and theme.dark |
| One part of a component | theme.slots, or class on ConsentBanner |
| Animation | disableAnimation |
| Banner position and blocking | presentation |
| Links to your policies | legalLinks, and the banner's legalLinks prop |
| Your own CSS from scratch | noStyle on the banner, or styles: false |
Customization explains how these choices relate across frameworks.
See the design gallery for five banner designs, from a bottom bar to a fully custom one, with tested code for this framework.
Set theme tokens
Put tokens in the integration's theme option:
ConsentScript renders the tokens on the server as a
<style id="c15t-theme"> element, so the banner and the dialog share them from
the first paint. Set hover and text colors together with a brand color, so each
button state stays readable. Theme tokens
lists the tokens.
Put tokens in the integration options, not in the client entrypoint. The
browser does not turn tokens into CSS, so tokens in the client entrypoint's
theme have no effect.
Style the banner buttons
theme.consentActions sets each button's mode and variant. c15t reads the
button's own key first, then primary for the action the policy marks as
primary, then default:
The IAB TCF banner keeps its own button styles.
Change the copy
For wording on every page and in the dialog, set i18n.messages, keyed by
language:
The server picks the visitor's language from Accept-Language. Set
i18n.locale to force one, or detectLanguage: false to use the default.
i18n.messages merges key by key with the copy for that language, so pass
only the keys you change. Setting common.acceptAll keeps every other label in
common. In hosted() and manifest() modes, the copy your backend sends is
the base, and your i18n.messages override it for the same language.
Copy and translations covers the message
groups.
For one banner, pass the title, description, acceptButtonText,
rejectButtonText, customizeButtonText or dismissButtonText props
instead. Translations covers languages
and the message keys the banner reads.
Change the banner's shape and position
presentation.prompt sets the banner's shape and where it sits:
variant is 'floating' (default), 'bar', 'widget' or 'wall'. The policy
still decides which actions the banner must offer, and a notice never becomes a
blocking wall. Keep behavior and appearance separate
explains what presentation can and cannot change.
Set light or dark mode
The banner and dialog turn dark when <html> has the c15t-dark class. The
integration's colorScheme option decides who sets it:
| Value | Behavior |
|---|---|
'system' (default) | Follows prefers-color-scheme, including changes while the page is open |
'dark' | Always adds the class |
'light' | Always removes the class |
'none' or null | Never adds or removes the class. Your site sets it |
With the first three values, ConsentScript sets the class before first paint,
and c15t sets it again after each ClientRouter navigation. Set dark colors in
theme.dark. Dark mode explains why Astro
follows the system setting by default.
Use 'none' when your site has its own theme switch. Toggle c15t-dark on
<html> together with your own dark class. If your theme script does not run
on astro:after-swap, set the class again there, because a ClientRouter
swap replaces the attributes of <html>.
Add links to your policies
Define the links once in the integration options:
ConsentBanner and ConsentDialog show only the ones you list in their
legalLinks prop, such as legalLinks={['privacyPolicy', 'cookiePolicy']}.
A link without a label reads as the translated name for its type, such as
"Privacy Policy". See
ConsentBanner legal links.
Style one part of a component
theme.slots in the integration options adds classes or inline styles to a
named part of the banner, the IAB banner and the dialogs:
Define the classes in a global stylesheet. class on ConsentBanner goes on
the banner root. Component parts lists the slot
keys, and Tailwind CSS shows the Astro setup
for Tailwind 4 and 3.
Turn off animations
disableAnimation in the integration options skips the banner's entry
animation and the dialogs' enter and exit animations. The same prop on
ConsentBanner, ConsentDialog, IABConsentBanner or IABConsentDialog
overrides it for one surface. c15t's stylesheet already stops these
animations for visitors who ask for reduced motion. Motion and animation covers the duration
and easing tokens.
Load the stylesheet yourself
ConsentScript, or the first consent component on a layout without it,
inlines base and configured IAB banner rules once per page as <style>
elements. No c15t stylesheet holds back the first paint. When a visitor
first reaches for a dialog, c15t links its rules and waits for them before
mounting the surface. Svelte also loads its primitives stylesheet. The IAB
panel's rules load only when the IAB dialog opens.
On a Tailwind CSS 3 site, the integration adds c15t/astro/styles.css and,
with IAB configured, c15t/astro/iab/styles.css to every page instead, so
your c15t/postcss-tailwind3 build processes them.
Tailwind 3 sites need no change, but their first paint still waits for that
stylesheet.
Do not import c15t/astro/styles.css as well. To control the cascade layer
order yourself, set styles: false and import the stylesheets from your own
CSS. Stylesheets and CSS layers
lists the files to import.
For markup with no c15t class names at all, pass noStyle to ConsentBanner
and style its data-testid and data-* attributes.
Check your changes
- View the page source. It contains
<style id="c15t-theme">with your tokens. - Open the banner and the preference dialog in light and dark mode. Both use your colors, and every button label is readable.
- Change the browser language and reload. The copy follows it, or stays in
i18n.localewhen you set one. - Navigate with
ClientRouter. The colors and dark mode stay the same.