Hypamoon

Find your perfect coffee

A Typeform-style product quiz: one question at a time, pictures to tap, a personal coffee at the end and a working Order button.

Type this in your chat

Build the Hypamoon recipe coffee-quiz

In Claude or ChatGPT with Hypamoon connected. Your chat fetches the full recipe below and builds it in one go, without questions, and gives you the live link.

What you get

  • Five questions, one per screen, with your first name in the questions that follow, in a dark, calm design
  • Picture choices, one question that depends on your answer, and a strength scale
  • A personal recommendation with its picture, why it fits and the price
  • An Order this button that goes through the real checkout (a test payment while payments are in test mode)
  • Every response saved with a bot check, and an email to the owner for each new one
  • Works with the keyboard and on a phone

Cost

0 euros a month on Hypamoon Free

  • Up to 10,000 responses per form and 50 email notifications a day.
  • The checkout is a test payment until you turn on payments; real sales pay the usual payment fees.
  • On Free the site shows a small Hypamoon badge.

All plans and limits

The full recipe

Show what your chat builds
Build "Find your perfect coffee" on Hypamoon: a Typeform-style product quiz for a small roastery (no brand name), live on its own link. It must look and feel like a finished product: dark, calm, premium, one question at a time. No style switcher, no extra pages.

FILES (one deploy_site, all at once)
- /index.html, /style.css, /quiz.js, and the 12 illustrations under /img/ taken with source_url (do not draw your own, do not hotlink): /img/espresso.svg from https://hypamoon.com/r/coffee-quiz/img/espresso.svg; /img/milk.svg from https://hypamoon.com/r/coffee-quiz/img/milk.svg; /img/filter.svg from https://hypamoon.com/r/coffee-quiz/img/filter.svg; /img/iced.svg from https://hypamoon.com/r/coffee-quiz/img/iced.svg; /img/fruity.svg from https://hypamoon.com/r/coffee-quiz/img/fruity.svg; /img/chocolate.svg from https://hypamoon.com/r/coffee-quiz/img/chocolate.svg; /img/caramel.svg from https://hypamoon.com/r/coffee-quiz/img/caramel.svg; /img/intense.svg from https://hypamoon.com/r/coffee-quiz/img/intense.svg; /img/bag-sunrise.svg from https://hypamoon.com/r/coffee-quiz/img/bag-sunrise.svg; /img/bag-cocoa.svg from https://hypamoon.com/r/coffee-quiz/img/bag-cocoa.svg; /img/bag-caramel.svg from https://hypamoon.com/r/coffee-quiz/img/bag-caramel.svg; /img/bag-nightshift.svg from https://hypamoon.com/r/coffee-quiz/img/bag-nightshift.svg.
- Plain HTML, CSS and JavaScript. System fonts only. The only external scripts: https://api.hypamoon.com/form.js and https://api.hypamoon.com/checkout.js (both defer).
- The html lang attribute for English, title "Find your perfect coffee", meta description "Five quick questions and we match you with a coffee you will love.", viewport meta with viewport-fit=cover, theme-color #0F1115.

DESIGN TOKENS (put them on :root and use only these)
--bg #0F1115; --surface #181B22; --text #F2F1EE; --muted #A9ADB6; --line #2C313C; --accent #F2B544; --accent-text #1A1306; --tile #232733; --pick #2B2A22;
--radius 16px (cards, choices, scale); --radius-btn 12px (buttons); --font system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--shadow 0 1px 0 rgba(255,255,255,.04), 0 10px 30px rgba(0,0,0,.35).
For the checkout window also set on :root: --hm-accent var(--accent); --hm-accent-text var(--accent-text); --hm-font var(--font); --hm-font-heading var(--font); --hm-radius var(--radius); --hm-bg var(--bg); --hm-surface var(--surface); --hm-text var(--text); --hm-muted var(--muted); --hm-line var(--line); --hm-border var(--line); --hm-focus var(--accent).
body: margin 0, min-height 100svh, background var(--bg), color var(--text), font 17px/1.5 var(--font), antialiased. kbd: small key cap (min-width 1.6em, height 1.6em, 1px solid var(--line), radius 6px, 600 12px, color var(--muted), background var(--surface)).

LAYOUT
- A progress bar fixed at the very top: 4px high, track = accent at 14% (color-mix), fill = accent, width animates .5s cubic-bezier(.2,.8,.2,1). Width = index of the current step / (number of steps - 1).
- A top row fixed under the bar (padding 10px 16px): left a 40x40 back button (chevron up icon as inline SVG, 1px border var(--line), background var(--surface), radius var(--radius-btn); hidden on the intro and the end screen), right "Question 2 of 5" in 13px var(--muted), tabular numbers ("Last step" on the email step, empty on intro and end).
- main: max-width 760px, centered, padding 76px 16px 112px, min-height 100svh, display grid, align-items center: every step sits in the vertical middle of the screen.
- Only one step visible (.step.on). On forward the new step slides in from translateY(14px), on back from translateY(-14px), .45s cubic-bezier(.2,.8,.2,1). Never animate opacity. The first step on load does not animate. Respect prefers-reduced-motion.

TYPE
- Kicker: 14px, 600, uppercase, letter-spacing .06em, color var(--accent).
- h1: weight 700, letter-spacing -0.02em, font-size clamp(34px, 7vw, 52px), line-height 1.04, text-wrap balance.
- Question (.q): weight 700, letter-spacing -0.02em, font-size clamp(22px, 4.6vw, 30px), line-height 1.2, margin-bottom 24px, padding-left 1.5em, with the question number absolutely placed on the left in var(--accent) at .55em, 700.
- Sub text: 19px, var(--muted), max-width 34em.
- Main button (.go): min-height 52px, padding 0 26px, radius var(--radius-btn), background var(--accent), color var(--accent-text), 700 18px, no border; hover brightness(1.05), active translateY(1px). Next to it a hint "press Enter" in 13px var(--muted) with a kbd.

THE STEPS
0. Intro: kicker "2 minutes · 5 questions", h1 "Find your perfect coffee", sub "Tell us how you drink it. We match you with one of our four roasts, and you can order it right away.", button "Start".
1. Name: label as question "First, what should we call you?" + a big text input (no box: only a 2px bottom border var(--line) that turns var(--accent) on focus, font-size clamp(22px, 5vw, 30px), transparent background, placeholder "Type your first name"), button "OK". Empty name: stay and focus the input.
2. Drink: "Nice to meet you, <name>. How do you drink your coffee?" with 4 picture cards: Espresso (espresso.svg), Cappuccino or latte (milk.svg), Filter (filter.svg), Iced (iced.svg). Values espresso, milk, filter, iced.
3. The follow-up, by the answer to 2, as text choices:
   - espresso or milk: "What do you make it with?" An espresso machine (espresso_machine), A bean-to-cup machine (bean_to_cup), A moka pot on the stove (moka_pot). Field machine.
   - filter: "Good taste, <name>. Which brewer do you use?" Pour-over (V60, Chemex) (pour_over), French press (french_press), A drip machine (drip_machine), AeroPress (aeropress). Field filter_method.
   - iced: "Cold it is. How do you take it?" Cold brew, steeped overnight (cold_brew), Iced latte (iced_latte), Espresso tonic (espresso_tonic). Field iced_style.
4. Flavour: "Which flavours pull you in, <name>?" with 4 picture cards: Fruity and bright (fruity), Chocolate and nuts (chocolate), Caramel and sweet (caramel), Dark and intense (intense).
5. Strength: "How strong do you like it?" a scale of 5 buttons in a 5-column grid (max-width 520px, gap 8px, height 60px, 22px 700, 1.5px border var(--line), background var(--surface), radius var(--radius), shadow), under it "Gentle" left and "As strong as it gets" right in 14px var(--muted), and the hint "press 1 to 5".
6. Email: "Where should we send your match, <name>?" a big email input like the name, a submit button "Show my coffee". Check the address before sending; show "Please enter a valid email address." in #FF8A80 when it is not valid.
Then the end screen (see below).

PICTURE CARDS
- Grid of 2 columns on a phone (gap 12px), 4 columns from 760px wide (gap 14px).
- Each card is a <button>: padding 8px, 1.5px solid var(--line), background var(--surface), radius var(--radius), shadow var(--shadow). Inside a tile (background var(--tile), radius calc(var(--radius) - 6px), aspect-ratio 5 / 4, overflow hidden) with the illustration (object-fit contain, padding 4%), and under it the label row (padding 10px 4px 4px, 15.5px 600, line-height 1.25) with a kbd A, B, C or D before the text.
- Hover: border var(--accent) and the image scales to 1.05 (.35s). Picked: border var(--accent), background var(--pick), and its kbd filled var(--accent) with var(--accent-text). After a pick wait 260ms, then go on by itself.

TEXT CHOICES
- A column of buttons (max-width 520px, gap 10px), each min-height 54px, padding 12px 16px, text left, 17px, 1.5px solid var(--line), background var(--surface), radius var(--radius), shadow var(--shadow), a kbd letter first. Same hover and picked states as the cards.
- Focus ring everywhere: outline 3px solid var(--accent), offset 3px (focus-visible).

KEYBOARD AND FOCUS
- Enter goes on (on the email step Enter submits). A to D picks, 1 to 5 on the scale, arrow up goes back. Ignore letter keys while typing in an input. Stop Enter in the name field from submitting the form.
- On each step focus the input, else the main button, else the question itself (tabindex -1). Never focus the first answer. Scroll to the top on each step.
- Personalise every <name> with the first name (fallback "there").

THE END SCREEN (outside the form, shown after hypamoon:submitted)
- A two-column match card from 700px (260px image column, gap 40px), stacked on a phone (gap 24px): left a square panel (background var(--tile), radius var(--radius), max-width 260px) with the bag illustration at 82% width; right: kicker "Your match", h1 "<name>, meet <roast>" (font-size clamp(30px, 6vw, 46px)), the notes in 600 var(--accent), one sentence why it fits (sub text style), then a row with the "Order this" button (.go) and the price in 20px 700 tabular numbers, then the checkout status line and a 13px var(--muted) line "Demo checkout: a test payment, no money moves.", and a text button "Take the quiz again" (underlined, var(--muted)) that resets everything.
- The roasts (product, notes, price, bag):
  sunrise: Sunrise Ethiopia, "Blueberry, jasmine, lemon", 13.50 euros, bag-sunrise.svg
  cocoa: Cocoa Brazil, "Milk chocolate, hazelnut", 11.50 euros, bag-cocoa.svg
  caramel: Caramel Colombia, "Caramel, red apple, brown sugar", 12.50 euros, bag-caramel.svg
  nightshift: Night Shift Espresso, "Dark chocolate, molasses, long finish", 12.50 euros, bag-nightshift.svg
- Which one: intense gives nightshift; fruity gives sunrise (nightshift if strength 5 and espresso); chocolate gives cocoa (nightshift if strength 4 or more and espresso or milk); caramel gives caramel.
- Why, with how = "your espresso machine", "your bean-to-cup machine", "your moka pot", "your pour-over", "your French press", "your drip machine", "your AeroPress", "an overnight cold brew", "an iced latte" or "an espresso tonic", and taste = strong (4 or 5), gentle (1 or 2) or balanced:
  sunrise: "A light, bright roast that tastes like fruit, not like burnt toast. Made for <how>, and lovely when you like it <taste>."
  cocoa: "Smooth and round with almost no bite: it stays chocolatey in <how>, even with milk or ice."
  caramel: "Sweet without sugar. A medium roast that brings out caramel in <how>, <taste> the way you like it."
  nightshift: "Our darkest roast for a <taste> cup. It cuts through milk and keeps its body in <how>."
- Read the answers for the end screen from event.detail.values of hypamoon:submitted (form.js resets the form after that event). Back from the checkout (?hypamoon_order= in the URL) checkout.js shows its own confirmation; keep the page as it is.

SAVING AND EMAIL
- create_collection coffee_quiz_responses with access public_write, triggers email_on_insert, and schema: name (string, max 60), drink (enum espresso, milk, filter, iced), machine (enum espresso_machine, bean_to_cup, moka_pot), filter_method (enum pour_over, french_press, drip_machine, aeropress), iced_style (enum cold_brew, iced_latte, espresso_tonic), flavour (enum fruity, chocolate, caramel, intense), strength (integer 1 to 5), recommendation (enum sunrise, cocoa, caramel, nightshift), email (string, format email); required name, drink, flavour, strength, recommendation, email; additionalProperties false. If coffee_quiz_responses already exists, reuse it when its schema has these fields; otherwise use coffee_quiz_responses_2.
- The form wraps all question steps: <form id="quiz" data-hypamoon-collection="coffee_quiz_responses" data-hypamoon-token="hm_pub_REPLACE" data-hypamoon-check="auto" data-hypamoon-success=" " novalidate> with a honeypot (<input type="text" name="website" data-hypamoon-honeypot tabindex="-1" autocomplete="off" aria-hidden="true">, moved off screen) and hidden inputs for every field. Keep the hidden inputs in sync with the answers; set disabled on machine, filter_method and iced_style unless they belong to the chosen branch, so only that one is sent. deploy_site fills in the token.
- Hide form.js's status line unless it shows an error (.hypamoon-status[data-state="success"], .hypamoon-status:empty { display: none }).

PRODUCTS AND THE BUY BUTTON
- The 4 roasts are products in the project's product collection: create_collection with role products if there is none, then insert_document for each (name, price_cents 1350, 1150, 1250, 1250, currency EUR, active true); reuse a product that already has the same name. Keep their ids.
- The button: <button type="button" class="go" id="order" data-hypamoon-checkout data-hypamoon-token="hm_pub_REPLACE_WITH_YOUR_SHOP_TOKEN" data-hypamoon-product="<id>" data-hypamoon-shipping="true">Order this</button>, then <p data-hypamoon-checkout-status role="status"></p>. The quiz script sets data-hypamoon-product to the id of the recommended roast. deploy_site fills in the shop token.

BEFORE YOU DEPLOY: CHECK YOUR OWN CODE
1. Every token above is used; no other colours; contrast of text on bg and of accent-text on accent at least 4.5:1.
2. The 12 illustrations are in the deploy as source_url files and every img points to /img/<name>.svg.
3. Each step is vertically centred; nothing overlaps the fixed top row at 375 px; no horizontal scroll at 375 px; cards are 2 by 2 on a phone and 4 in a row on desktop.
4. Progress bar and "Question n of 5" change on every step, and the back button returns to the previous question with the slide down.
5. A to D, 1 to 5, Enter and arrow up work, and Enter in the name field never submits.
6. Only the chosen branch field is sent; the end screen reads event.detail.values; the Order button gets the right product id.
7. No console errors: every element the script looks up exists in the html.
Fix anything that fails before the deploy.

PUBLISH
- One deploy_site with all files, name "Find your perfect coffee", public, no slug (take the address Hypamoon proposes and do not ask about it).
- Then fetch_site_page on /, /quiz.js and /img/espresso.svg to check they are live, and give the link.

Done when

  • The site is live on its own link and opens without an account.
  • One question per screen with a progress bar; Enter, A to D, 1 to 5 and arrow up work.
  • The first name appears in the later questions.
  • The follow-up question changes with the answer to question 2; only the chosen branch is sent.
  • The end screen shows one of the 4 roasts with its bag, notes, a reason and the price, in the dark design of the recipe.
  • Order this opens the checkout for the recommended roast.
  • A response lands in coffee_quiz_responses and the owner gets an email.
  • No horizontal scroll at 375 px; no console errors.

What you need

  • Claude or ChatGPT with Hypamoon connected. You connect once, and it takes a few minutes. Connected before today? Reconnect once so your chat knows the recipes.
  • Nothing else. No hosting, no form builder, no payment account for the test checkout.

Connect your chat How recipes work in the docs