Customizing & Styling
Croissant's surfaces are built to inherit your theme rather than impose a look. This page is the complete reference: every setting, the CSS escape hatch for what the settings do not cover, and how to get a new setting added when CSS is the wrong answer.
The Four Layers
Work down this list in order. Each layer is more work and less durable than the one above it, so stop as soon as one gets you what you need.
| Layer | Where | Use it for |
|---|---|---|
| 1. Block settings | Theme editor / checkout editor | Placement, alignment, spacing, sizes, copy variant. Survives app updates |
| 2. Style mirroring | Button style source / Link style source | Fonts, colors, radii, hover states — copied from a button you already have. Almost always the right answer |
| 3. Custom CSS | Your theme's stylesheet | Theme blocks only. The escape hatch for anything with no setting — unsupported, and yours to re-verify after updates |
| 4. Ask Croissant | support@croissant.com | Checkout block styling, copy changes, and anything you had to reach for CSS to do — see requesting a change |
Theme Block Settings
Set these under Online Store → Themes → Customize → App embeds. Placement settings are per surface — the cart drawer and the cart page are configured independently. Everything else is shared across both surfaces, which is the single most important thing to know about these blocks: the button looks identical on both surfaces and sits differently. If you need the two to look different, that is a CSS job.
Pay with Croissant Card
| Setting | Values | Default |
|---|---|---|
| Cart injection target (drawer) | CSS selector | button[name='checkout'] |
| Cart page injection target | CSS selector | empty — cart page off |
| Injection position | before, after | before |
| Alignment | left, center, right | center |
| Text position | above, below | above |
| Button width | full, auto | full |
| Margin / padding top and bottom | −48px to 48px, per surface | 0 |
| Cart drawer close button | CSS selector | empty — falls back to common drawer patterns |
| Hide info icon | Boolean | Off |
| Hide logo | Boolean | Off |
| Logo size | 12px to 48px | 22px |
| Button style source | CSS selector to mirror classes from | button[name='checkout'] |
| Button custom classes | Space-separated classes | empty |
| Text font size / line height / letter spacing | Fixed px steps, or inherit | inherit |
| Button height / padding X / padding Y | Fixed px steps, or inherit | inherit |
| Button font size / line height / letter spacing | Fixed px steps, or inherit | inherit |
| Button/text gap | 0px to 48px | 0 |
| Label vertical offset | −8px to 8px | 0 |
inherit before you debug anything else. Custom classes are the fallback for when there is no button on screen to mirror.Pay with Croissant Link
The Link block is the lighter inline variant. It has the same per-surface placement settings, with these differences:
| Setting | Values | Default |
|---|---|---|
| Copy variant | short, medium, long, minimal, save | medium |
| Custom link text | Free text, or empty to use the selected copy variant | empty |
| Injection position | before, after | after — note this differs from the Card block |
| Margin top | −48px to 48px | 8px — again unlike the Card block |
| Internal gap | 0px to 32px, or inherit | inherit |
| Link style source / custom classes | CSS selector / space-separated classes | empty |
| Link font size / line height / letter spacing | Fixed px steps, or inherit | inherit |
It has no text position, button width, logo, height, padding, button/text gap, label offset, or hide-info-icon setting. The copy variant or custom link text is what you tune instead, and the info icon has to be hidden with CSS if you want it gone.
A Worked Example
A typical Dawn-derived theme, running the Card block in the drawer and on the cart page, mirroring the theme's own checkout button:
Flyout placement
Cart injection target #CartDrawer button[name='checkout']
Injection position before
Alignment center
Button width full
Margin bottom -8
Cart page placement
Cart page injection target .cart__ctas
Injection position before
Alignment center
Behavior
Cart drawer close button .drawer__close
Button
Button style source button[name='checkout']
Button custom classes (empty — mirroring covers it)Before saving a selector, confirm it resolves to exactly one element on the surface you are configuring. Run this in the console with the drawer open, then again on /cart:
// Should log exactly 1
document.querySelectorAll("#CartDrawer button[name='checkout']").length;
// Dump the settings the block actually received, as resolved by the theme
console.table(
document.getElementById("croissant-pay-card-template").dataset
);data-* attributes on its template element, so what dataset shows is exactly what the block is working from. An empty value means that setting is unset — and an empty injection target means that surface is switched off.Checkout Block Settings
Set these under Settings → Checkout → Customize on the Croissant app block. The editor shows every field empty rather than pre-filled; empty means the default. In effect:
{
"display_variant": "default",
"button_kind": "primary",
"button_text": "Redeem Croissant Credit",
"supporting_link_text": "",
"logo_style": "filled_dark",
"show_logo": true,
"info_modal_variant": "steps",
"hide_offer": false,
"show_border": true
}Full option lists are on the Checkout Block page. Two behaviors are worth calling out because they are not obvious from the field names:
hide_offerhides the default offer and its info icon. Setsupporting_link_textwhen you want concise supporting copy to remain as a route to the explainer modal.simple_buttonis button-only by default. It ignoresshow_borderand renders no offer copy. Settingsupporting_link_textadds a link and info icon beneath the button while keeping the compact layout.
Compact Copy-Forward Example
Use this when the design calls for a text-only redemption button with a short value-proposition link beneath it:
Display Variant simple_button
Button Type primary
Button Text Redeem Croissant Credit
Supporting Link Text Shop, Earn, Resell — with Croissant
Show Button Logo false
Hide Offer true
Show Border falseThe supporting link opens the same Croissant explainer modal as the standard offer's info icon. Change the copy freely per brand; leaving the field empty restores the previous button-only behavior.
Styling the Checkout Button
There is no CSS escape hatch in checkout, and this is Shopify's design rather than a gap in ours: checkout UI extensions render in a sandboxed frame using Shopify's own components, and no app — Croissant included — can inject a stylesheet into it. What you get in exchange is that the Croissant button is a native Shopify checkout button, so your checkout branding already applies to it.
So the levers are:
button_kind— maps straight onto Shopify'sprimary,secondary, andplainbutton styles. This is how you make Croissant subordinate to your main Pay now button.- Settings → Checkout → Customize → Style — your brand colors, typography, and corner radius flow into the Croissant button because it is one of Shopify's buttons.
show_borderanddisplay_variant— the only structural controls: card-with-border, or bare button.
On Plus you can also drive checkout branding from the Admin API, which reaches the Croissant button for the same reason:
# Shopify's API, not Croissant's — this restyles every native checkout
# button, including the Croissant one. See Shopify's checkout branding
# docs for the full field and enum list.
mutation RestyleCheckoutButtons($profileId: ID!) {
checkoutBrandingUpsert(
checkoutProfileId: $profileId
checkoutBrandingInput: {
customizations: { primaryButton: { cornerRadius: BASE } }
}
) {
checkoutBranding { customizations { primaryButton { cornerRadius } } }
userErrors { field message }
}
}Custom CSS: The Escape Hatch
The theme blocks render into your own DOM, so your stylesheet can reach them. This is the supported way to handle the last 5% — a heading you want gone, per-surface differences, a logo gap the settings do not expose.
Where to Put It
In order of preference: your theme's Custom CSS field (Theme editor → Theme settings → Custom CSS), then your theme's own stylesheet, then a snippet you include from layout/theme.liquid. Do not edit the Croissant extension's stylesheets — they are app assets and every update overwrites them.
Load order between your theme's CSS and an app embed's CSS is not guaranteed, and the blocks set some values as inline styles. Assume you need !important and one of the class hooks below.
The Rendered DOM
This is what the Card block injects next to your target. Target the injected .croissant-pay-card, never the <template> it is cloned from — styles never apply inside a template.
<div class="croissant-pay-card" data-croissant-surface="flyout">
<span class="croissant-pay-card__heading">Use Croissant at Checkout.</span>
<span class="croissant-pay-card__incentive">
<span class="croissant-pay-card__incentive-text">Get $50 off your order of $250+</span>
<button class="croissant-pay-card__info-btn"><svg>…</svg></button>
</span>
<button class="croissant-pay-card__cta">
<div class="croissant-pay-card__cta-inner">
<svg>…</svg>
<span class="croissant-pay-card__cta-full">Continue with Croissant</span>
<span class="croissant-pay-card__cta-short">Pay with Croissant</span>
</div>
</button>
</div>On non-Plus plans, an authorized session replaces the CTA subtree entirely:
<div class="croissant-pay-card" data-croissant-surface="cart">
<div class="croissant-pay-card__status">
<span class="croissant-pay-card__status-title">Croissant Payment Authorized</span>
<span class="croissant-pay-card__status-sub">You're earning $12 back</span>
<!-- present only in the "cart changed" state -->
<button class="croissant-pay-card__reauthorize">Reauthorize with Croissant</button>
<button class="croissant-pay-card__void">Remove Croissant</button>
</div>
</div>The Link block and the modal follow the same naming:
| Hook | What it is |
|---|---|
.croissant-pay-link | Link block root |
.croissant-pay-link__text | Offer copy. Absent on the minimal and save variants |
.croissant-pay-link__cta-line | Inline row holding the link and info icon |
.croissant-pay-link__cta | The link itself |
.croissant-pay-link__prefix | The words when you, on the long variant only |
.croissant-pay-link__info-btn | Info icon. No setting hides this one |
.croissant-pay-link__status / __status-title / __status-sub | Authorized and cart-changed states |
.croissant-pay-link__reauthorize / __void | Reauthorize and remove controls |
#croissant-info-modal | Modal root. Gets .is-open while open |
.croissant-modal__overlay / __card / __close / __header | Modal chrome |
.croissant-modal__step / __step-circle / __step-title / __step-body | The three steps in the steps variant |
What CSS Can and Cannot Reach
The blocks apply your settings as inline styles. Most are normal declarations, so an !important rule in your stylesheet wins. A few are set as inline !important, which no stylesheet can beat — those are settings-only, permanently.
| Property | On | Override with |
|---|---|---|
margin, padding, text-align | Card / Link root | CSS with !important |
height, padding, font-size, line-height, letter-spacing | Card CTA | CSS with !important |
font-size, line-height, letter-spacing | Card heading and offer text | CSS with !important |
| Everything not listed below | Anywhere | CSS — plain specificity is usually enough |
width, max-width, align-self | Card CTA | Settings only. Set inline as !important — use Button width and Alignment |
font-size, line-height, letter-spacing | Link CTA | Set the matching Link setting back to inherit to hand control back to CSS — while it holds a px value it is inline !important |
display: none inline when the integration is not live or the currency is not USD. Never override that. The button would render into a flow that cannot complete — a shopper who clicks it reaches a session your store cannot honor.Worked Overrides
Each of these covers something the settings genuinely do not expose. They are the cases we see most often.
/* The heading copy is fixed. Hide it and let the offer line carry the message. */
.croissant-pay-card__heading {
display: none !important;
}
/* Style the two surfaces differently — styling settings are shared, so this is
the only way to do it. */
.croissant-pay-card[data-croissant-surface="cart"] .croissant-pay-card__cta {
text-transform: uppercase !important;
letter-spacing: 0.08em !important;
}
/* Gap between the logo and the button label. Fixed at 8px, no setting for it. */
.croissant-pay-card__cta-inner {
gap: 4px !important;
}
/* The Link block has no "hide info icon" setting. */
.croissant-pay-link__info-btn {
display: none !important;
}
/* Recolor the info icon. The block locks its stroke against theme bleed, so you
have to match that specificity. */
.croissant-pay-card__info-btn svg circle,
.croissant-pay-card__info-btn svg path {
stroke: currentColor !important;
}
/* The Croissant logo inherits the button's text color, so recoloring the label
recolors the mark with it. */
.croissant-pay-card__cta {
color: #171717 !important;
}
/* The label swaps to a shorter string under 480px. Force one everywhere. */
@media (max-width: 480px) {
.croissant-pay-card__cta-full { display: inline !important; }
.croissant-pay-card__cta-short { display: none !important; }
}
/* Make the authorized state match your theme instead of the default underline. */
.croissant-pay-card__void {
opacity: 1 !important;
text-decoration: none !important;
border-bottom: 1px solid currentColor !important;
}Two Things Not to Do
- Do not restyle with JavaScript. The blocks watch the DOM and re-inject themselves whenever your theme tears the cart down — a drawer close and reopen, a quantity change. Every re-injection is a fresh clone, so your mutations are gone and your listeners are on detached nodes. CSS survives this; JS does not.
- Do not reposition with CSS. Placement is what the injection target and position settings are for, and they run before paint. Absolutely-positioning the card into place instead means it moves the next time your theme's cart markup changes, with no console warning to tell you.
Opening the Modal Yourself
The info modal listens on document for a croissant:open-modal event, so any trigger you own can open it. This is the one supported programmatic hook — useful when you want the explainer behind your own How does this work? link rather than the built-in icon.
document.querySelector("#my-croissant-explainer-link")
.addEventListener("click", (event) => {
document.dispatchEvent(
new CustomEvent("croissant:open-modal", {
detail: {
// Croissant closes the cart drawer first so the modal is not
// trapped behind it. Pass your trigger so it can find the drawer,
// or a selector for the drawer's close button.
trigger: event.currentTarget,
drawerCloseButton: ".drawer__close",
},
})
);
});Both detail fields are optional. With neither, Croissant falls back to walking up from the trigger and matching common drawer patterns — cart-drawer, #CartDrawer, .mini-cart, an open <details> or <dialog> — which is also what happens when you leave the Cart drawer close button setting empty.
steps / covers layout choice.Requesting a New Customization
Every CSS override you write is a setting we should probably ship. Send them to support@croissant.com or your integration contact — including the ones you already worked around, so the next store does not have to.
Include this much and we can usually act on it without a call:
Store domain: yourstore.myshopify.com
Surface: cart drawer / cart page / checkout
Block: Pay with Croissant Card / Link / Info Modal / checkout block
What you want: e.g. control the gap between the logo and the label
Why settings
don't cover it: e.g. cta-inner gap is fixed at 8px; no setting exposes it
Current stopgap: the CSS you are shipping today, if any
Screenshot: before / desired| Kind of request | What happens |
|---|---|
| Placement, spacing, sizing, alignment, or a new copy variant | Strong candidate for a new theme-editor setting — these are per-store by nature |
| Anything at all in the checkout block | Has to be built by Croissant. There is no CSS path, so these are always requests |
| One-off cosmetic fit against an unusual theme | Stays CSS on your side. Still tell us — a pattern across stores becomes a setting |
| Croissant logo and wordmark, the modal's explainer copy, the 10% figure, discount titles on the order summary | Not configurable, and not something CSS should be used to defeat. Talk to us about what the shopper needs to see instead |
Next: Discounts & Cart Data → how authorization becomes discount lines