ProcessSteps Sections
Stappen-sectie voor werkwijze of onboarding. Horizontaal of verticaal.
src/components/sections/ProcessSteps.astro
---
/**
* ProcessSteps
* Visuele stappen-sectie: hoe werkt het, onboarding, werkwijze.
*
* Props:
* - headline?: string
* - sub?: string
* - steps: Array<{ title: string; body: string; icon?: string }>
* - layout?: 'horizontal' | 'vertical'
*/
interface Props {
headline?: string;
sub?: string;
steps: { title: string; body: string; icon?: string }[];
layout?: 'horizontal' | 'vertical';
}
const { headline, sub, steps, layout = 'horizontal' } = Astro.props;
---
<section class={`process process--${layout}`} data-process>
<div class="process__inner">
{(headline || sub) && (
<div class="process__header">
{headline && <h2 class="process__headline">{headline}</h2>}
{sub && <p class="process__sub">{sub}</p>}
</div>
)}
<ol class="process__steps">
{steps.map((step, i) => (
<li class="process__step">
<div class="process__step-num">
{step.icon ?? String(i + 1).padStart(2, '0')}
</div>
{layout === 'horizontal' && i < steps.length - 1 && (
<div class="process__connector" aria-hidden="true"></div>
)}
<h3 class="process__step-title">{step.title}</h3>
<p class="process__step-body">{step.body}</p>
</li>
))}
</ol>
</div>
</section>
<style>
.process {
padding: 5rem 1.5rem;
background: var(--color-bg);
}
.process__inner { max-width: 1280px; margin: 0 auto; }
.process__header {
text-align: center;
max-width: 600px;
margin: 0 auto 3.5rem;
}
.process__headline {
font-size: clamp(1.75rem, 3vw, 2.5rem);
font-weight: 800;
letter-spacing: -0.03em;
margin: 0 0 0.75rem;
}
.process__sub { font-size: 1.0625rem; color: var(--color-muted); line-height: 1.6; margin: 0; }
/* Horizontal */
.process--horizontal .process__steps {
list-style: none;
margin: 0; padding: 0;
display: grid;
grid-template-columns: 1fr;
gap: 2rem;
}
@media (min-width: 640px) {
.process--horizontal .process__steps {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
}
.process--horizontal .process__step {
position: relative;
text-align: center;
}
.process__step-num {
display: inline-flex;
align-items: center;
justify-content: center;
width: 3rem; height: 3rem;
background: var(--color-accent);
color: #fff;
font-weight: 800;
font-size: 0.9375rem;
border-radius: 50%;
margin-bottom: 1rem;
}
.process__connector {
display: none;
position: absolute;
top: 1.5rem;
left: calc(50% + 1.75rem);
right: calc(-50% + 1.75rem);
height: 1px;
background: color-mix(in srgb, var(--color-accent) 40%, transparent);
}
@media (min-width: 640px) {
.process--horizontal .process__connector { display: block; }
}
.process__step-title {
font-size: 1rem;
font-weight: 700;
margin: 0 0 0.375rem;
}
.process__step-body {
font-size: 0.9rem;
color: var(--color-muted);
line-height: 1.6;
margin: 0;
}
/* Vertical */
.process--vertical .process__steps {
list-style: none;
margin: 0; padding: 0;
display: flex;
flex-direction: column;
gap: 0;
max-width: 640px;
}
.process--vertical .process__step {
display: grid;
grid-template-columns: 3rem 1fr;
gap: 0 1.5rem;
padding-bottom: 2.5rem;
position: relative;
}
.process--vertical .process__step::before {
content: '';
position: absolute;
top: 3rem;
left: 1.5rem;
bottom: 0;
width: 1px;
background: color-mix(in srgb, var(--color-text) 12%, transparent);
}
.process--vertical .process__step:last-child::before { display: none; }
.process--vertical .process__step-num {
grid-row: 1 / 3;
align-self: start;
}
.process--vertical .process__step-title { margin-top: 0.7rem; }
</style>
Props
| Prop | Type | Default | Beschrijving |
|---|---|---|---|
steps * | { title: string; body: string; icon?: string }[] | — | Stappen array |
headline | string | — | Sectie headline |
sub | string | — | Ondertitel |
layout | 'horizontal' | 'vertical' | 'horizontal' | Layout richting |
* = verplicht