Files
gw-svelte/src/lib/components/contact-form-variants/VariantApplicationFields.svelte
T
2026-05-18 22:25:43 +12:00

695 lines
16 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts">
export let idPrefix: string;
export let lead = '';
export let submitLabel = 'Book a free Meet & Greet';
export let tone: 'default' | 'conversational' | 'minimal' = 'default';
export let layout: 'stacked' | 'carded' = 'stacked';
export let eyebrow = 'Free Meet & Greet';
export let title = 'Tell us about your dog';
export let subtitle =
'A guided first step so we can recommend the right walk, visit, or calmer starting point.';
let submitted = false;
let step = 1;
let ownerStepUnlocked = false;
function handleSubmit() {
if (step === 1) {
ownerStepUnlocked = true;
step = 2;
return;
}
submitted = true;
}
function goToStep(nextStep: number) {
if (nextStep === 2 && !ownerStepUnlocked) {
return;
}
step = nextStep;
}
</script>
<form
class={`vf-form vf-form--${tone} vf-form--${layout}`}
on:submit|preventDefault={handleSubmit}
novalidate
>
<div class="vf-header">
<span class="vf-eyebrow">{eyebrow}</span>
<h3 class="vf-title">{title}</h3>
<p class="vf-subtitle">{subtitle}</p>
<div class="vf-stepper" aria-label="Application steps">
<button
type="button"
class:active={step === 1}
class="vf-step"
on:click={() => goToStep(1)}
>
<span class="vf-step-number">1</span>
<span class="vf-step-label">Dog details</span>
</button>
<span class="vf-step-divider" aria-hidden="true"></span>
<button
type="button"
class:active={step === 2}
class="vf-step"
disabled={!ownerStepUnlocked}
aria-disabled={!ownerStepUnlocked}
on:click={() => goToStep(2)}
>
<span class="vf-step-number">2</span>
<span class="vf-step-label">Owner details</span>
</button>
</div>
</div>
{#if lead}
<p class="vf-lead">{lead}</p>
{/if}
{#if step === 1}
<section class="vf-group" aria-labelledby={`${idPrefix}-dog-group`}>
<div class="vf-group-head">
<p class="vf-kicker" id={`${idPrefix}-dog-group`}>Dog details</p>
<p class="vf-group-copy">
A few calm details first so we can judge fit, pace, and the right kind of outing.
</p>
</div>
<div class="vf-grid vf-grid--two">
<label class="vf-field">
<span class="vf-label">Dog name</span>
<input id={`${idPrefix}-dog-name`} name="dogName" type="text" placeholder="Maya" />
</label>
<label class="vf-field">
<span class="vf-label">Suburb / location</span>
<input
id={`${idPrefix}-location`}
name="location"
type="text"
placeholder="Mt Eden, Grey Lynn, Ponsonby…"
/>
</label>
</div>
<fieldset class="vf-choice-group">
<legend class="vf-label">What feels closest right now?</legend>
<p class="vf-helper">
Not sure is completely fine. We can sort that out at the Meet &amp; Greet.
</p>
<div class="vf-choices">
<label class="vf-choice">
<input type="radio" name={`${idPrefix}-service-fit`} value="tiny-gang" />
<span>Tiny Gang Pack Walks</span>
</label>
<label class="vf-choice">
<input type="radio" name={`${idPrefix}-service-fit`} value="one-to-one" />
<span>Solo Walks</span>
</label>
<label class="vf-choice">
<input type="radio" name={`${idPrefix}-service-fit`} value="puppy-visits" />
<span>Puppy Visits</span>
</label>
<label class="vf-choice">
<input type="radio" name={`${idPrefix}-service-fit`} value="not-sure" checked />
<span>Not sure yet</span>
</label>
</div>
</fieldset>
<label class="vf-field vf-field--full">
<span class="vf-label">About your dog</span>
<textarea
id={`${idPrefix}-about-dog`}
name="aboutDog"
rows="5"
placeholder="Tell us about age, size, confidence, leash manners, how they feel around other dogs, and anything that helps us make the right recommendation."
></textarea>
</label>
<div class="vf-note-row" aria-label="Helpful notes">
<article class="vf-note">
<span class="vf-note-title">Nervous dogs</span>
<p>
If your dog is shy, easily overwhelmed, or still building confidence, mention it here.
That often points us toward a calmer first step.
</p>
</article>
<article class="vf-note">
<span class="vf-note-title">Tiny Gang suitability</span>
<p>
Our small-group walks work best for dogs who enjoy the right company, gentle
structure, and a routine they can settle into.
</p>
</article>
</div>
</section>
{/if}
{#if step === 2}
<section class="vf-group" aria-labelledby={`${idPrefix}-owner-group`}>
<div class="vf-group-head">
<p class="vf-kicker" id={`${idPrefix}-owner-group`}>Owner details</p>
<p class="vf-group-copy">
So Aless can reply properly and arrange a free Meet &amp; Greet without back-and-forth.
</p>
</div>
<div class="vf-grid vf-grid--three">
<label class="vf-field">
<span class="vf-label">Owner name</span>
<input id={`${idPrefix}-owner-name`} name="ownerName" type="text" placeholder="Your full name" />
</label>
<label class="vf-field">
<span class="vf-label">Email</span>
<input id={`${idPrefix}-email`} name="email" type="email" placeholder="you@example.com" />
</label>
<label class="vf-field">
<span class="vf-label">Phone</span>
<input id={`${idPrefix}-phone`} name="phone" type="tel" placeholder="021 123 4567" />
</label>
</div>
<article class="vf-owner-note">
<span class="vf-note-title">What happens next</span>
<p>
Well reply personally, talk through fit, and arrange the free Meet &amp; Greet if it
feels like the right next step.
</p>
</article>
</section>
{/if}
<div class="vf-footer">
<p class="vf-reassurance">
Free Meet &amp; Greet. No pressure. Just the right starting point for your dog.
</p>
<div class="vf-actions">
{#if step === 2}
<button type="button" class="vf-secondary" on:click={() => goToStep(1)}>Back</button>
{/if}
<button type="submit" class="vf-submit">
{step === 1 ? 'Next: owner details' : submitLabel}
</button>
</div>
</div>
{#if submitted}
<p class="vf-success" role="status">
Demo only: this variant would hand the enquiry to Aless and prompt a free Meet &amp; Greet.
</p>
{/if}
</form>
<style>
.vf-form {
display: grid;
gap: 24px;
}
.vf-form--carded {
gap: 18px;
}
.vf-header {
display: grid;
gap: 12px;
}
.vf-eyebrow {
display: inline-flex;
align-items: center;
width: fit-content;
min-height: 30px;
padding: 0 12px;
border-radius: 999px;
background: rgba(33, 48, 33, 0.07);
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.05);
color: var(--gw-green);
font-family: var(--font-head);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.vf-title {
margin: 0;
color: #11171b;
font-family: var(--font-head);
font-size: clamp(26px, 3vw, 34px);
line-height: 1.06;
letter-spacing: -0.04em;
}
.vf-subtitle {
margin: 0;
color: #59606d;
font-size: 15px;
line-height: 1.6;
}
.vf-stepper {
display: flex;
align-items: center;
gap: 10px;
margin-top: 4px;
}
.vf-step {
display: inline-flex;
align-items: center;
gap: 10px;
min-height: 44px;
padding: 0 14px 0 10px;
border: none;
border-radius: 999px;
background: rgba(255, 255, 255, 0.84);
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.06);
color: #50555c;
cursor: pointer;
transition:
background 0.18s ease,
color 0.18s ease,
transform 0.18s cubic-bezier(0.22, 1, 0.36, 1);
}
.vf-step.active {
background: rgba(33, 48, 33, 0.08);
color: #11171b;
}
.vf-step:disabled {
cursor: default;
opacity: 0.55;
}
.vf-step-number {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 10px;
background: var(--gw-green);
color: var(--yellow);
font-family: var(--font-head);
font-size: 12px;
font-weight: 700;
}
.vf-step-label {
font-family: var(--font-head);
font-size: 12px;
font-weight: 700;
line-height: 1.2;
}
.vf-step-divider {
width: 26px;
height: 1px;
background: rgba(17, 20, 24, 0.1);
}
.vf-lead {
margin: 0;
color: #4c5056;
font-size: 15px;
line-height: 1.65;
}
.vf-group {
display: grid;
gap: 18px;
}
.vf-form--carded .vf-group {
padding: 22px;
border-radius: 24px;
background: linear-gradient(180deg, rgba(251, 251, 251, 0.98) 0%, rgba(247, 248, 246, 1) 100%);
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.06),
0 12px 28px rgba(17, 20, 24, 0.06);
}
.vf-group-head {
display: grid;
gap: 6px;
}
.vf-kicker {
margin: 0;
color: var(--gw-green);
font-family: var(--font-head);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.vf-group-copy {
margin: 0;
color: #59606d;
font-size: 14px;
line-height: 1.55;
}
.vf-grid {
display: grid;
gap: 14px;
}
.vf-grid--two {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.vf-grid--three {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.vf-field {
display: grid;
gap: 8px;
}
.vf-field--full {
width: 100%;
}
.vf-label,
.vf-choice-group legend {
color: #16181d;
font-size: 14px;
font-weight: 700;
line-height: 1.35;
}
.vf-field input,
.vf-field textarea {
width: 100%;
border: none;
border-radius: 18px;
background: rgba(255, 255, 255, 0.96);
box-shadow:
inset 0 0 0 1px rgba(17, 20, 24, 0.08),
0 1px 0 rgba(255, 255, 255, 0.3);
color: #1f2421;
padding: 15px 16px;
font-size: 15px;
line-height: 1.45;
outline: none;
transition:
box-shadow 0.18s ease,
transform 0.18s cubic-bezier(0.22, 1, 0.36, 1),
background 0.18s ease;
}
.vf-field textarea {
resize: vertical;
min-height: 132px;
}
.vf-field input:focus-visible,
.vf-field textarea:focus-visible,
.vf-choice input:focus-visible + span,
.vf-step:focus-visible,
.vf-secondary:focus-visible {
box-shadow:
inset 0 0 0 2px rgba(255, 209, 0, 0.7),
0 0 0 4px rgba(255, 209, 0, 0.14);
background: #fff;
outline: none;
}
.vf-choice-group {
display: grid;
gap: 10px;
min-width: 0;
margin: 0;
padding: 0;
border: none;
}
.vf-helper {
margin: 0;
color: #687076;
font-size: 13px;
line-height: 1.5;
}
.vf-choices {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.vf-choice {
position: relative;
}
.vf-choice input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.vf-choice span {
display: inline-flex;
align-items: center;
min-height: 42px;
padding: 0 14px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.9);
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.08);
color: #2f3134;
font-size: 13px;
font-weight: 600;
line-height: 1.2;
transition:
box-shadow 0.18s ease,
background 0.18s ease,
color 0.18s ease,
transform 0.18s cubic-bezier(0.22, 1, 0.36, 1);
}
.vf-choice input:checked + span {
background: rgba(255, 209, 0, 0.18);
box-shadow: inset 0 0 0 1px rgba(255, 209, 0, 0.34);
color: #16181d;
}
.vf-note-row {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.vf-note,
.vf-owner-note {
padding: 16px 16px 15px;
border-radius: 20px;
}
.vf-note {
background: rgba(33, 48, 33, 0.05);
box-shadow: inset 0 0 0 1px rgba(33, 48, 33, 0.06);
}
.vf-owner-note {
background: rgba(255, 209, 0, 0.12);
box-shadow: inset 0 0 0 1px rgba(255, 209, 0, 0.16);
}
.vf-note-title {
display: block;
margin-bottom: 6px;
color: var(--gw-green);
font-family: var(--font-head);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.vf-note p,
.vf-owner-note p {
margin: 0;
color: #4c5056;
font-size: 13px;
line-height: 1.55;
}
.vf-footer {
display: grid;
gap: 14px;
}
.vf-actions {
display: flex;
align-items: center;
gap: 12px;
}
.vf-reassurance {
margin: 0;
color: #59606d;
font-size: 13px;
line-height: 1.5;
}
.vf-secondary {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 52px;
padding: 0 18px;
border: none;
border-radius: 999px;
background: rgba(33, 48, 33, 0.07);
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.06);
color: var(--gw-green);
font-family: var(--font-head);
font-size: 14px;
font-weight: 700;
line-height: 1.2;
cursor: pointer;
}
.vf-submit {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 52px;
width: fit-content;
padding: 0 22px;
border: none;
border-radius: 999px;
background: linear-gradient(135deg, #ffd54a, var(--yellow-soft));
box-shadow:
inset 0 -2px 0 rgba(0, 0, 0, 0.08),
0 12px 24px rgba(17, 20, 24, 0.1);
color: #111;
font-family: var(--font-head);
font-size: 14px;
font-weight: 700;
line-height: 1.2;
cursor: pointer;
transition:
transform 0.18s cubic-bezier(0.22, 1, 0.36, 1),
box-shadow 0.18s ease,
filter 0.18s ease;
}
.vf-submit:focus-visible {
outline: 3px solid rgba(255, 209, 0, 0.28);
outline-offset: 3px;
}
.vf-success {
margin: 0;
padding: 14px 16px;
border-radius: 18px;
background: rgba(33, 48, 33, 0.07);
box-shadow: inset 0 0 0 1px rgba(33, 48, 33, 0.08);
color: var(--gw-green);
font-size: 14px;
line-height: 1.55;
}
.vf-form--conversational .vf-label,
.vf-form--conversational .vf-choice-group legend {
font-family: var(--font-head);
font-size: 16px;
letter-spacing: -0.02em;
}
.vf-form--conversational .vf-field input,
.vf-form--conversational .vf-field textarea {
border-radius: 22px;
padding: 17px 18px;
font-size: 16px;
}
.vf-form--minimal .vf-group-copy,
.vf-form--minimal .vf-helper,
.vf-form--minimal .vf-reassurance,
.vf-form--minimal .vf-subtitle {
color: #6b7077;
}
.vf-form--minimal .vf-field input,
.vf-form--minimal .vf-field textarea,
.vf-form--minimal .vf-choice span {
box-shadow: inset 0 0 0 1px rgba(17, 20, 24, 0.06);
}
@media (hover: hover) {
.vf-step:hover,
.vf-secondary:hover,
.vf-choice:hover span,
.vf-submit:hover {
transform: translateY(-1px);
}
.vf-submit:hover {
box-shadow:
inset 0 -2px 0 rgba(0, 0, 0, 0.08),
0 16px 28px rgba(17, 20, 24, 0.12);
filter: brightness(1.02);
}
}
@media (max-width: 768px) {
.vf-grid--two,
.vf-grid--three,
.vf-note-row {
grid-template-columns: 1fr;
}
.vf-stepper {
align-items: stretch;
gap: 8px;
}
.vf-step {
flex: 1 1 0;
min-width: 0;
}
.vf-step-divider {
display: none;
}
.vf-form {
gap: 20px;
}
.vf-form--carded .vf-group {
padding: 18px;
border-radius: 22px;
}
.vf-field input,
.vf-field textarea {
font-size: 16px;
}
.vf-actions {
flex-direction: column-reverse;
align-items: stretch;
}
.vf-secondary,
.vf-submit {
width: 100%;
}
}
</style>