ContactCentered Forms
Gecentreerd contactformulier met 2-koloms velden grid. Volledig gevalideerd met success state.
src/components/forms/ContactCentered.astro
---
/**
* ContactCentered
* Centered contact formulier — headline, sub, 2-koloms grid form.
*/
interface Props {
eyebrow?: string;
headline: string;
sub?: string;
submitLabel?: string;
successMessage?: string;
}
const {
eyebrow,
headline,
sub,
submitLabel = 'Verstuur bericht',
successMessage = 'Bedankt! We nemen snel contact met je op.',
} = Astro.props;
---
<section class="cc" data-component="contact-centered">
<div class="cc__inner">
<div class="cc__header">
{eyebrow && <p class="cc__eyebrow">{eyebrow}</p>}
<h2 class="cc__headline">{headline}</h2>
{sub && <p class="cc__sub">{sub}</p>}
</div>
<form class="cc__form" id="cc-form" novalidate>
<div class="cc__grid">
<div class="cc__field">
<label class="cc__label" for="cc-firstname">Voornaam <span class="cc__req">*</span></label>
<input id="cc-firstname" name="firstname" type="text" class="cc__input" required autocomplete="given-name" />
</div>
<div class="cc__field">
<label class="cc__label" for="cc-lastname">Achternaam</label>
<input id="cc-lastname" name="lastname" type="text" class="cc__input" autocomplete="family-name" />
</div>
<div class="cc__field cc__field--full">
<label class="cc__label" for="cc-email">E-mailadres <span class="cc__req">*</span></label>
<input id="cc-email" name="email" type="email" class="cc__input" required autocomplete="email" />
</div>
<div class="cc__field cc__field--full">
<label class="cc__label" for="cc-phone">Telefoonnummer</label>
<input id="cc-phone" name="phone" type="tel" class="cc__input" autocomplete="tel" />
</div>
<div class="cc__field cc__field--full">
<label class="cc__label" for="cc-subject">Onderwerp</label>
<select id="cc-subject" name="subject" class="cc__input cc__select">
<option value="">Kies een onderwerp...</option>
<option>Algemene vraag</option>
<option>Samenwerking</option>
<option>Support</option>
<option>Anders</option>
</select>
</div>
<div class="cc__field cc__field--full">
<label class="cc__label" for="cc-message">Bericht <span class="cc__req">*</span></label>
<textarea id="cc-message" name="message" class="cc__input cc__textarea" required rows={5}></textarea>
</div>
</div>
<div class="cc__footer">
<p class="cc__privacy">Door te verzenden ga je akkoord met ons <a href="#">privacybeleid</a>.</p>
<button type="submit" class="cc__submit">{submitLabel}</button>
</div>
</form>
<div class="cc__success" id="cc-success" style="display:none">
<svg width="48" height="48" viewBox="0 0 48 48" fill="none">
<circle cx="24" cy="24" r="24" fill="currentColor" opacity="0.1"/>
<path d="M14 24l8 8 12-14" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<p>{successMessage}</p>
</div>
</div>
</section>
<style>
.cc {
background: var(--color-bg, #fff);
padding: 5rem 1.5rem;
}
.cc__inner {
max-width: 680px;
margin: 0 auto;
}
.cc__header {
text-align: center;
margin-bottom: 3rem;
}
.cc__eyebrow {
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--color-accent, #6366f1);
margin-bottom: 0.75rem;
}
.cc__headline {
font-size: clamp(1.875rem, 3vw, 2.75rem);
font-weight: 800;
letter-spacing: -0.03em;
line-height: 1.15;
color: var(--color-primary, #0a0a0a);
margin-bottom: 0.875rem;
}
.cc__sub {
font-size: 1.0625rem;
color: var(--color-muted, #6b7280);
line-height: 1.6;
}
.cc__grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.25rem;
}
.cc__field { }
.cc__field--full { grid-column: 1 / -1; }
.cc__label {
display: block;
font-size: 0.8125rem;
font-weight: 600;
color: var(--color-primary, #0a0a0a);
margin-bottom: 0.5rem;
}
.cc__req { color: var(--color-accent, #6366f1); margin-left: 0.1em; }
.cc__input {
width: 100%;
padding: 0.75rem 1rem;
border: 1.5px solid rgba(0,0,0,0.1);
border-radius: var(--radius, 0.5rem);
font-size: 0.9375rem;
font-family: inherit;
color: var(--color-primary, #0a0a0a);
background: #fff;
transition: border-color 0.15s;
outline: none;
-webkit-appearance: none;
}
.cc__input:focus { border-color: var(--color-accent, #6366f1); }
.cc__textarea {
resize: vertical;
min-height: 120px;
}
.cc__select { cursor: pointer; }
.cc__footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
margin-top: 1.5rem;
flex-wrap: wrap;
}
.cc__privacy {
font-size: 0.8125rem;
color: var(--color-muted, #6b7280);
}
.cc__privacy a {
color: var(--color-accent, #6366f1);
text-decoration: none;
}
.cc__submit {
background: var(--color-accent, #6366f1);
color: #fff;
border: none;
border-radius: var(--radius, 0.5rem);
padding: 0.875rem 2rem;
font-size: 1rem;
font-weight: 700;
font-family: inherit;
cursor: pointer;
transition: filter 0.2s, transform 0.2s;
white-space: nowrap;
}
.cc__submit:hover {
filter: brightness(1.1);
transform: translateY(-1px);
}
/* Success */
.cc__success {
text-align: center;
padding: 3rem;
color: var(--color-accent, #6366f1);
}
.cc__success p {
margin-top: 1rem;
font-size: 1.125rem;
font-weight: 600;
color: var(--color-primary, #0a0a0a);
}
@media (max-width: 540px) {
.cc__grid { grid-template-columns: 1fr; }
.cc__footer { flex-direction: column; align-items: stretch; }
.cc__submit { width: 100%; text-align: center; }
}
</style>
<script>
const form = document.getElementById('cc-form') as HTMLFormElement;
const success = document.getElementById('cc-success');
form?.addEventListener('submit', (e) => {
e.preventDefault();
if (form) form.style.display = 'none';
if (success) success.style.display = 'block';
});
</script>
Props
| Prop | Type | Default | Beschrijving |
|---|---|---|---|
headline * | string | — | Formulier titel |
eyebrow | string | — | Klein label boven titel |
sub | string | — | Ondertitel |
submitLabel | string | 'Verstuur bericht' | Submit knop tekst |
* = verplicht