bluebarry.
ReviewsPricingResources
Book a demoStart for free
Help Center/Quiz builder/Design a quiz with custom code

Design a quiz with custom code

Martijn Douma
Martijn Douma · Co-founder bluebarry
Updated October 21, 2026

The theme settings cover colors, fonts and buttons. When you want the quiz to look completely like your own design, give the theme custom code: one stylesheet for the whole quiz and your own HTML templates for the start page, the questions, the loading screen, the pre-result page and the product check page. bluebarry keeps doing everything the quiz does, so your code only decides where things go and how they look.

Open Custom code

  1. Go to Quizzes, click Edit on your quiz and open the Design tab in the left panel.
  2. Click Edit on the theme this quiz uses. Custom code only opens for that theme, because the editor previews this quiz.
  3. Scroll to the bottom of the theme settings and click Custom code.

The editor has six tabs: CSS, Start page, Question, Loading, Pre-result and Product check. Next to them is a live preview of this quiz with a desktop and a mobile button. The line at the top names the quizzes that use this theme. If it is your store's default theme, quizzes without a theme of their own use it too.

  • An empty tab keeps the built-in screen. Click Start from an example to get working code to edit.
  • Your code saves on its own shortly after you stop typing. If it has an error, the editor shows what to fix under the field and does not save it, so a live quiz never gets a broken design.
  • On the Product check tab, pick a product next to Run the preview on a product page to preview the page as shoppers see it on that product.

CSS

One stylesheet styles every screen of the quiz, including the result page, the product check page and the error page. It only applies inside the quiz, and it wins over the theme's colors without !important. Motion and position properties, such as transform, opacity, transition and position, keep their normal weight so the quiz animations keep working. A custom result page (beta) has its own markup and CSS, so the result page classes below do not apply to it.

.bb-q-answer {
  border: 1px solid var(--bb-color-title);
  border-radius: var(--bb-radius, 8px);
}
.bb-q-answer[data-selected] {
  background: var(--bb-color-button);
  color: var(--bb-color-button-text);
}
@media (max-width: 480px) {
  .bb-q-answers { grid-template-columns: 1fr; }
}
  • A width @media query follows the width of the quiz, not the browser window, so it also works in a pop-up, a side panel and the mobile preview.
  • Classes that start with bb-q- are a stable, public contract, except bb-q-i- classes: those are internal and can change at any time. Give classes you add yourself a name without the bb- prefix.
  • The theme's settings are available as variables: --bb-color-button, --bb-color-button-text, --bb-color-background, --bb-color-title, --bb-color-accent, --bb-color-answer, --bb-color-answer-text, --bb-font-body, --bb-font-title and --bb-radius. A setting the theme leaves empty has no variable, so add a fallback.
  • Style states with [data-selected] on a selected answer, aria-pressed on multi-select answers, :disabled on buttons that cannot be used yet, aria-invalid on an input with an error and data-state on an add to cart button.
  • Answers darken on hover by default. For a hover style of your own, turn that off with .bb-q-answer:hover { filter: none; }.
The main classes
PartClasses
Headerbb-q-header, bb-q-header-bar, bb-q-progress, bb-q-progress-fill, bb-q-controls, bb-q-back, bb-q-restart, bb-q-close, bb-q-logo
Screensbb-q-screen, with bb-q-screen--start, --question, --interstitial, --pre-result or --result; bb-q-template wraps a screen your template renders
Start pagebb-q-start, bb-q-start-image, bb-q-start-title, bb-q-start-description, bb-q-start-button
Questionbb-q-question, bb-q-question-title, bb-q-question-description, bb-q-question-image
Answersbb-q-answers, bb-q-answer, bb-q-answer-text, bb-q-answer-description, bb-q-answer-image, bb-q-answer-check
Inputsbb-q-field, bb-q-field-label, bb-q-input, bb-q-slider, bb-q-phone, bb-q-dropdown, bb-q-dropdown-list, bb-q-dropdown-option, bb-q-consent, bb-q-field-error
Buttonsbb-q-footer, bb-q-next, bb-q-skip, bb-q-error
Loading and pre-resultbb-q-loading, bb-q-loading-dots, bb-q-pre-result, bb-q-pre-result-image, bb-q-pre-result-title, bb-q-pre-result-description
Result pagebb-q-result, bb-q-result-title, bb-q-result-description, bb-q-result-list, bb-q-result-card, bb-q-result-card-image, bb-q-result-card-label, bb-q-result-card-name, bb-q-result-card-price, bb-q-result-card-button, bb-q-result-add, bb-q-result-error, bb-q-result-retry
Product check pagebb-q-check (with data-state="match" or "no-match"), bb-q-check-title, bb-q-check-description, bb-q-check-label, bb-q-check-product, bb-q-check-alternatives

The question screen also has data-bb-display-type, such as standard, picture or dropdown, so you can style one question type on its own. The product check page uses the result card classes for its cards, and the bb-q-check classes on top, for example:

.bb-q-check[data-state="no-match"] .bb-q-check-product { opacity: .6; }
.bb-q-check-label[data-active] { text-transform: uppercase; }

How templates work

  • Templates are Handlebars: {{title}} prints a value, and you can use {{#if}}, {{#each}} and the helpers eq, ne, or, and, inc and lookup. Every value is escaped.
  • You tell bluebarry what an element does with a data-bb- attribute, such as data-bb-start or data-bb-answer. bluebarry then wires the behavior to your element.
  • Your markup has no styling until your CSS styles it. The one exception is the start button and the Next button: they start in the theme's button colors, radius and font, and any rule of yours wins.
  • Put all CSS in the CSS tab. A <style> element in a template is not allowed.

Start page

You can use title, description, buttonText, imageUrl, logoUrl, questionCount and language. The template needs a button or link with data-bb-start, which starts the quiz.

<div class="hero">
  {{#if imageUrl}}<img src="{{imageUrl}}" alt="">{{/if}}
  <h1>{{title}}</h1>
  <p>{{description}}</p>
  <button type="button" data-bb-start>{{buttonText}}</button>
</div>

Question

The question template is used for every question, whatever its type. Pages you add between questions keep their own layout. You can use title, description, imageUrl, displayType, isMultipleChoice, step (the question number in the shopper's path), progress (0 to 100), nextText, skipText, logoUrl and language, plus the lists below.

The template needs one empty element with data-bb-answers. Whatever your template does not render itself, bluebarry puts there: the answers, the dropdown, the input fields, the consent checkbox and error messages. Add an empty element with data-bb-actions to decide where the Next and Skip buttons go.

Your own answers

Loop over answers and render each answer as a button with data-bb-answer. Each answer has answerId, text, description, imageUrl and index. Render all answers or none, and keep links, inputs and other buttons out of the answer button.

{{#each answers}}
  <button type="button" class="choice" data-bb-answer="{{answerId}}">
    {{#if imageUrl}}<img src="{{imageUrl}}" alt="">{{/if}}
    <span>{{text}}</span>
  </button>
{{/each}}
<div data-bb-answers></div>

bluebarry adds bb-q-answer to your buttons, sets data-selected on a selected answer and handles single and multi-select. A dropdown question has its options in options instead. Without your own buttons it keeps the built-in dropdown with its search.

Input fields and consent

Text, email, phone, number, slider and name questions have their inputs in fields, each with answerId, kind, label, placeholder and required. Put data-bb-field on an <input> for each field. bluebarry sets the input type, checks the value and writes a message into an empty element with data-bb-field-error.

For an email or phone question with a consent line, add a checkbox with data-bb-consent and an empty element with data-bb-consent-text. bluebarry writes your consent text there, so shoppers always read the exact wording they agree to.

{{#each fields}}
  <label>{{label}} <input data-bb-field="{{answerId}}" placeholder="{{placeholder}}"></label>
  <span class="hint" data-bb-field-error="{{answerId}}"></span>
{{/each}}
{{#if hasConsent}}
  <label><input type="checkbox" data-bb-consent> <span data-bb-consent-text></span></label>
{{/if}}
<div data-bb-answers></div>

Next, Skip and Back

  • <button data-bb-next>{{nextText}}</button> and <button data-bb-skip>{{skipText}}</button> work like the built-in buttons. bluebarry keeps Next disabled until the answer is valid and hides a button where the question has none. The footer then only shows the buttons your template leaves out.
  • data-bb-back, data-bb-restart and data-bb-close on your own button go back, restart the quiz or close it.
  • A question template with a data-bb-back button owns the top bar. The quiz then hides its own progress bar, Back, Restart and logo on question screens, and only the Close button stays. Draw your own progress from {{progress}} or {{step}}.

Loading and pre-result

The loading template shows while the results load, while a shopper picks up a quiz where they left off and while a screen's code loads, so keep its text general. You can use label, logoUrl and language. label is always in English, so pick visible text based on language or keep the screen decorative. The pre-result template shows while the results are prepared, with the title, description and imageUrl of the quiz's pre-result page. Both screens move on by themselves, so they take no data-bb- attributes.

<div class="spinner" aria-hidden="true"></div>
{{#if logoUrl}}<img src="{{logoUrl}}" alt="">{{/if}}

Product check page

When a quiz runs from a product page, its result is the product check page: is this product a match? The Product check template replaces that page. It works like a custom result page (beta), with the same product fields, helpers and buttons. On top of that you get product (the checked product), products (the alternatives), match, title and description, plus labelOne and labelTwo for the labels over the checked product and the alternatives. Those two are not plain text: print {{labelOne.text}} and check labelOne.active for the highlighted one. The texts come from the product check settings of the quiz.

<h2>{{title}}</h2>
{{#with product}}
  <h3>{{name}}</h3>
  {{#if addsToCart}}<button type="button" data-bb-add="{{variantId}}">{{cta}}</button>
  {{else}}<a href="{{url}}" data-bb-product-link="{{url}}">{{cta}}</a>{{/if}}
{{/with}}

The template needs at least one data-bb-add or data-bb-product-link button or link, so the shopper can always buy. The page has no next step, so data-bb-next, data-bb-add-all and data-bb-ask-chat are not allowed. The CSS of a custom result page does not reach it: style it in the CSS tab.

The Close button

On the start page, on a question that owns the top bar and on a custom result page, the Close button floats in the top right corner, over your template. Keep that corner clear, for example with padding-right: 56px on your own top bar.

To place Close in your own layout instead, add one empty element with data-bb-close-slot to the start page or question template. The button there takes the text color of the element, so give it a color that stands out, and style it with [data-bb-close-slot] .bb-q-close. An embedded quiz has no Close button, so the slot stays empty there.

<header class="topbar">
  <button type="button" data-bb-back>Back</button>
  <span data-bb-close-slot></span>
</header>

Screen transitions

To animate screen changes with your own CSS, open Animations in the quiz's Design tab and pick Custom for desktop, mobile or both. During a screen change both screens are on the page:

  • The screen going out gets data-bb-transition="leave" and the one coming in data-bb-transition="enter". Both get data-bb-direction: forward, or back after Back.
  • Every screen has data-bb-screen (start, question, loading, pre-result, result, product-check and so on), so one screen can move differently.
  • The quiz waits until your animations end, at most 1.2 seconds. Use @keyframes rather than transition, and keep them under half a second.
  • For shoppers who turn on reduced motion, screens swap at once. Without CSS for these attributes, screens change instantly too.
@keyframes brand-in { from { opacity: 0; transform: translateY(24px); } }
@keyframes brand-out { to { opacity: 0; transform: translateY(-12px); } }
[data-bb-transition="enter"] { animation: brand-in .35s ease-out both; }
[data-bb-transition="leave"] { animation: brand-out .2s ease-in both; }
@keyframes brand-in-back { from { opacity: 0; transform: translateY(-24px); } }
[data-bb-transition="enter"][data-bb-direction="back"] { animation-name: brand-in-back; }

Scripts

Every template can hold an inline <script>. It runs in the live quiz and in the preview in Custom code. Use the data-bb- attributes for everything they cover, and script only what markup cannot do, such as a live summary of the choices. Scripts with src or type="module" are not allowed.

bluebarryQuiz(document.currentScript) gives your script the current screen:

  • getState() returns the screen, the question, its answers and what is selected, the field values, the consent, and whether the shopper can go next, skip or back.
  • Actions do exactly what the matching click does: select(id), deselect(id), toggle(id), setField(id, value), setConsent(checked), next(), skip(), back(), restart(), close() and start(). They return false when the quiz does not take them, for example next() before the answer is valid.
  • on(type, listener) listens to the screen's events: screen, select, answer, change, validation and, with the Custom animation, transition. Read the state in a change listener, because it updates once the screen has rendered. A script in the loading template runs, but gets no events.
{{#each answers}}<button type="button" data-bb-answer="{{answerId}}">{{text}}</button>{{/each}}
<p class="summary" aria-live="polite"></p>
<div data-bb-answers></div>
<script>
  const quiz = bluebarryQuiz(document.currentScript);
  const summary = document.currentScript.closest('.bb-q-template').querySelector('.summary');
  const show = (state) => {
    const picked = state.answers.filter((a) => a.selected).map((a) => a.text);
    summary.textContent = picked.length ? 'Your picks: ' + picked.join(', ') : '';
  };
  show(quiz.getState());
  quiz.on('change', (detail) => show(detail.state));
</script>

Each screen gets its own handle, and it stops when the screen goes away. Listen through on or on the template rather than on document, or your listeners pile up across questions.

When a screen falls back

The editor refuses a template that misses a required part, such as a start page without data-bb-start or a question template without one empty data-bb-answers. Some problems only show up for a certain question, for example when the answers slot sits inside an {{#if}}, when your answer buttons do not match the question's answers one to one, or when a template has a consent checkbox on a question without a consent line. That screen then shows the built-in layout and the browser console gets a warning. The shopper can always finish the quiz.

Editing text on custom screens

Where Studio shows a quiz in its custom design on a canvas, such as on Home, you cannot click the text of a screen your template lays out to edit it. Your template decides where each text goes, and it can repeat a text, leave it out or change it with a script, so Studio cannot tie what you click to a setting of the quiz. Change the texts in the quiz editor and the layout in Custom code. The canvas in the quiz editor keeps the built-in layout, so its texts stay editable there. Use the preview in Custom code or Preview to see your design.

Rules and limits

  • The CSS can be up to 40,000 characters and each template up to 60,000.
  • CSS that hides a control shoppers need, such as the answers, the Next button or the Close button, with display: none or visibility: hidden is refused.
  • @keyframes names the quiz uses itself, such as spin, pulse or names that start with advisor-, are refused. Pick a name of your own, such as brand-fade.
  • @import is removed when the theme saves.

Good to know

  • The code belongs to the theme, so it changes every quiz that uses the theme. It is not part of a quiz version: you do not publish it. The preview shows changes right away, and live quizzes pick them up within about an hour. To try something on one quiz only, duplicate the theme first. See Customize the quiz theme and layout.
  • Once a theme has custom code, the header bar above the quiz turns transparent so your background runs to the top. Give .bb-q-header-bar a background for a solid bar.
  • On the first load, a templated start page or question shows the theme's background for a moment while the template code loads.
  • Custom code works the same on Shopify, WooCommerce, Magento and CCV Shop, because the quiz runs in its own frame on every platform.

Related articles

Customize the quiz theme and layout

Match your quiz to your brand with a theme for logo, fonts, colors, buttons, page styles and custom code, and choose how the quiz animates between steps.

Read article →

Design the start page

Set up the welcome screen before your first quiz question, with title, image, description and layout, or skip it and open on a question.

Read article →

Build a custom quiz result page (beta)

Design your own quiz result page with Barry or in code, such as a step-by-step routine, and add whole-kit and single-product discounts.

Read article →

Can’t figure it out? We probably can.

Every article here was written by the people who built the product. If one of them still leaves you stuck, tell us and we’ll fix it.

Contact usJoin Discord, free forever
bluebarry.

Helping beauty, health and outdoor brands reach their dream AOV

contact@bluebarry.ai+31 6 57 16 10 87De Ried 10, 9285KK Buitenpost (The Netherlands)
Martijn DoumaStan van RooyAnco PostmaJelmer Reitsma

The guys that are increasing your AOV.

Platform
Smart searchQuiz funnelsProduct finderShopping assistantRecommendationsDynamic bundlesPopupsPost-purchase upsellLanding pagesPricing
Solutions
Klaviyo quiz builderShopify quizShopify search appMagento product quizMagento search extensionWooCommerce product quizWooCommerce search pluginGuided selling software
Resources
CasesHelp centerFAQCompareIntegrations
Company
Contact usPartners & affiliateReviewsRequest demoSign in
Start for free
© 2026 bluebarry. All rights reserved.
Privacy PolicyCookie PolicyTerms & Conditions
English/Nederlands/Deutsch
bluebarry

Bluebarry is the AI personalization platform for ecommerce, built for beauty brands, health brands and big catalog stores. Bluebarry captures shopper profiles with quizzes, popups, product finders and more, then adapts your store, recommendations and post purchase funnels to each visitor's profile. We work with major brands like Meroda, Loye Cosmetics and Decathlon. Bluebarry works on every ecommerce platform, from Shopify, Magento and WooCommerce to CCV Shop, and is live within minutes.