Website
Websites gebouwd op JAMstack: snel, veilig, duurzaam en makkelijk bij te werken. Design, code, hosting en AI-integratie in één samenhangend traject.
Websites gebouwd op JAMstack: snel, veilig, duurzaam en makkelijk bij te werken. Design, code, hosting en AI-integratie in één samenhangend traject.
We duiken in je merkidentiteit. Wat maakt je onderscheidend, waar sta je vandaag, waar wil je naartoe. Dat wordt het fundament voor het design.
HEX: F4E5C0
R:244 G:229 B:192
C:0 M:6 Y:21 K:4
Market position
Premium, sustainability-led
Proposition
Dynamic chairs for active minds
Tone of voice
Confident, direct, a little playful
HEX: F4364C
R:244 G:54 B:76
C:0 M:78 Y:69 K:4
HEX: 5280DE
R:82 G:128 B:222
C:63 M:42 Y:0 K:13
We werken samen naar een design dat past bij wie je bent. Je geeft feedback in een stagingomgeving, voordat er iets live gaat.
Op maat, snel en precies zoals ontworpen. Geen concessies aan plugins.
Gebouwd om jaren mee te gaan.
{{/* Chat: de chatbox van dex-chatbot (chatbot D-026). Openen, sluiten en de chips met een
vast antwoord (chatbot D-009) gebeuren volledig in de pagina. Een getypte vraag, een
startchip zonder antwoord en een vervolgsuggestie gaan als POST naar /chat van de
chat-service (chatbot D-027, D-050). Logica in static/js/chat.js (setupChat), opmaak
in static/css/chat.css. Zie D-116.
Alle teksten komen uit de frontmatter van de sectie. Het endpoint ook: hugo server
krijgt endpoint_lokaal (de mock), elke andere build endpoint. Zo kan een
localhost-adres nooit live gaan.
De chatstart (bot_naam, welkomstberichten, startchips) haalt de build op bij
<endpoint>/start (chatbot D-048, contract hoofdstuk 12). Alleen bij status "actief"
komt hij als JSON in data-start. Faalt het ophalen, of is de status iets anders, dan
een waarschuwing in de build en een chat zonder welkomst en zonder chips; de build
faalt er nooit op.
Let op: dit is de testpagina. Op een echte pagina hoort de chatbox fail-closed te
zijn (chatbot D-028): geen chatbox als de service tijdens de build niet "actief"
zegt. Hier blijft hij juist staan, zodat je ook de "nog niet verbonden"-toestand
kunt zien. */}}
{{ with $section := .data }}
{{- $endpoint := cond hugo.IsServer ($section.endpoint_lokaal | default "") ($section.endpoint | default "") | strings.TrimSuffix "/" -}}
{{- $max := $section.max_tekens | default 250 -}}
{{- $id := print "chat-" $.index -}}
{{- $start := dict -}}
{{- with $endpoint -}}
{{- $bron := print . "/start" -}}
{{- $fout := "geen of leeg antwoord, of 404" -}}
{{- with try (resources.GetRemote $bron) -}}
{{- with .Err -}}{{- $fout = . -}}{{- else with .Value -}}
{{- with try (transform.Unmarshal .Content) -}}
{{- with .Err -}}{{- $fout = . -}}{{- else with .Value -}}
{{- if and (reflect.IsMap .) (eq .status "actief") -}}
{{- $start = dict
"bot_naam" (.bot_naam | default "")
"welkomstberichten" (cond (reflect.IsSlice .welkomstberichten) .welkomstberichten slice)
"startchips" (cond (reflect.IsSlice .startchips) (first 5 .startchips) slice)
-}}
{{- else -}}
{{- $fout = "status is niet actief" -}}
{{- if reflect.IsMap . -}}{{- $fout = printf "status %v" .status -}}{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $start -}}
{{- warnf "Chatstart niet opgehaald van %s (%v). De chat heeft geen welkomst en geen startchips." $bron $fout -}}
{{- end -}}
{{- end -}}
<section class="chatbot">
<div class="container">
<div class="chatbox" data-endpoint="{{ $endpoint }}" data-limit="{{ $max }}" data-teller-vanaf="{{ $section.teller_vanaf | default 200 }}" data-wachttijd="{{ $section.wachttijd | default 30 }}" data-contact="{{ $section.contact_url }}" data-teksten='{{ $section.statussen | jsonify }}' data-start='{{ $start | jsonify }}'>
<button class="toggle" type="button" aria-expanded="false" aria-controls="{{ $id }}-paneel">
<span class="open-label">{{ $section.knop_open }}</span>
<span class="close-label">{{ $section.knop_dicht }}</span>
</button>
<div class="panel" id="{{ $id }}-paneel">
{{/* De privacyregel is het eerste bericht in het gesprek en scrollt mee. chat.js zet
de berichten erachter. */}}
<div class="log" role="log" aria-live="polite" {{ with $start.bot_naam }}aria-label="{{ . }}" {{ end }}data-lenis-prevent>
{{ with $section.privacy }}<p class="privacy">{{ .tekst }} <a href="{{ .link_url }}">{{ .link_tekst }}</a></p>{{ end }}
</div>
<div class="typing" hidden>
<span class="visually-hidden">{{ $section.typt }}</span>
<span class="dot" aria-hidden="true"></span>
<span class="dot" aria-hidden="true"></span>
<span class="dot" aria-hidden="true"></span>
</div>
{{/* Startchips en vervolgsuggesties: chat.js vult deze rij. */}}
<ul class="chips" hidden data-lenis-prevent></ul>
<p class="notice" role="status"></p>
{{/* Het veld staat uit tot chat.js weet dat de service actief is (/health). */}}
<form>
<label class="visually-hidden" for="{{ $id }}-vraag">{{ $section.invoer_label }}</label>
<textarea id="{{ $id }}-vraag" name="vraag" rows="2" placeholder="{{ $section.invoer_hint }}" aria-describedby="{{ $id }}-teller" disabled></textarea>
<div class="actions">
<p class="counter" id="{{ $id }}-teller" hidden><span class="count">0</span>/{{ $max }} <span class="visually-hidden">{{ $section.teller_label }}</span></p>
<button class="send" type="submit" disabled>{{ $section.verstuur }}</button>
</div>
</form>
<p class="foot">
{{ with $section.ai_melding }}<span class="ai-melding">{{ . }}</span>{{ end }}
{{ with $section.powered_by }}<span class="powered-by">{{ .tekst }} <a href="{{ .link_url }}">{{ .link_tekst }}</a></span>{{ end }}
</p>
</div>
</div>
</div>
</section>
<script defer type="text/javascript" src="/js/chat.js"></script>
<link rel="stylesheet" href="/css/chat.css">
{{ end }}
{{ with $section := .data }}
<section class="feature-browser">
<div class="container">
<div class="browser">
<span></span>
{{ with .tag }}<p class="has-tags"><span>{{ . }}</span></p>{{ end }}
{{ with .inners }}
<div class="inners">
{{ range . }}
{{ $naam := ._type | urlize }}
{{ if templates.Exists (print "_partials/bento/" $naam ".html") }}
<div class="inner" data-widget="{{ $naam }}" data-cursor='You'>
{{ partial (print "bento/" $naam) . }}
{{ with .cursors }}{{ partial "cursors" . }}{{ end }}
</div>
{{ else }}
{{ warnf "Feature Browser: widget '%s' bestaat niet. Kopieer deze bestandsnaam: %s" ._type $naam }}
{{ end }}
{{ end }}
</div>
{{ end }}
{{ with .features }}
<ul class="features activate-children">
{{ range . }}
<li>
<p class="title">{{ with .icon }}{{ partial "svg" (. | urlize) }}{{ end }}{{ .title }}</p>
<div class="prose">{{ partial "description" . }}</div>
</li>
{{ end }}
</ul>
{{ end }}
</div>
</div>
</section>
{{ end }}
{{ with $section := .data }}
<section class="who">
<div class="container">
<article class='activate-children'>
{{- if $.firstSection -}}
{{ with .title }}<h1 class='kicker'>{{ . | markdownify }}</h1>{{ end }}
{{- else -}}
{{ with .title }}<h2 class='kicker'>{{ . | markdownify }}</h2>{{ end }}
{{- end -}}
{{ range $i, $j := .items }}
<div {{ if eq $i 0 }} class='active' {{ end }} >
<div class="title-box" data-triggers='.2:done'>
{{ partial "sub-h1" (dict "title" .title "firstSection" $.firstSection) }}
</div>
<div class="prose">{{ partial "description" . }}</div>
</div>
{{ end }}
</article>
{{ with .figure }}
<figure class='browser'>
<span></span>
{{ with .tags }}<p class="has-tags">{{ range . }} <span>{{ . }}</span>{{ end }}</p>{{ end }}
{{ partial "media" (dict "media" .media "fit" true) }}
</figure>
{{ end }}
</div>
</section>
{{ end }}
{{ with $section := .data }}
<section class="intro{{ if .has_faded_dex_background }} has-glow{{ end }}">
<div class="container {{ if not .has_faded_dex_background }} activate-children {{ end }}">
{{ if .has_faded_dex_background }}<span class='dex activate' aria-hidden='true'>{{ partial "image" (dict "path" "/img/dex-blurred.png" "size" "525x500" "fit" true "sizes" "(min-width: 768px) 70rem, 216vw" "lazy" false "non_drag" true) }}</span>{{ end }}
{{ partial "h1-huge" $ }}
<div class="prose">
{{ partial "description" . }}
</div>
{{ partial "btn" . }}
{{ partial "cursors" .cursors }}
</div>
</section>
{{ end }}
{{ with $section := .data }}
{{ with where (partial "vacancies-articles" $.page) "Permalink" "!=" $.page.Permalink }}
<section class="other-vacancies">
<div class="container">
<div class="top">
{{ partial "h1" $ }}
{{ partial "btn" $section }}
</div>
<ul class="vacancy-list">
{{ range . }}{{ partial "vacancy-card" . }}{{ end }}
</ul>
</div>
</section>
{{ end }}
{{ end }}
{{ with $section := .data }}
{{ $p := $.page }}
{{ $koppen := slice }}
{{ range $p.Fragments.Headings }}{{ range .Headings }}{{ if eq .Level 2 }}{{ $koppen = $koppen | append . }}{{ end }}{{ end }}{{ end }}
<section class="blog-content detail-content">
<div class="container">
<article class="prose">
{{ with $p.Params.details.description }}<div class="intro">{{ . | markdownify }}</div>{{ end }}
{{ $p.Content }}
{{ with $p.Params.details.key_takeaways }}
<div class="takeaways">
<h2 class="h5">{{ T "De belangrijkste punten" }}</h2>
<ul>{{ range . }}<li>{{ . | markdownify }}</li>{{ end }}</ul>
</div>
{{ end }}
</article>
<aside class='slow-sticky'>
{{ with $koppen }}
<nav class="browser toc" aria-label="{{ T "In dit artikel" }}">
<span></span>
<p class="has-tags"><span>{{ T "In dit artikel" }}</span></p>
<ol>{{ range . }}<li><a href="#{{ .ID }}">{{ .Title | plainify }}</a></li>{{ end }}</ol>
</nav>
{{ end }}
</aside>
</div>
</section>
{{ end }}
Je site is live, gehost, bewaakt, en makkelijk bij te werken met Dex.
se7en.com
Waarom JAMstack
12 days ago
{{ with $section := .data }}
{{ $team := (index hugo.Data (site.Language.Label | lower)).team }}
<section class="team big-title-fader">
<div class="container">
<div class="title-box calc-perc" data-triggers='.7:show;.9:hide' data-done='.75'>
{{ partial "h1" $ }}
</div>
<ul class="members">
{{ range $i, $naam := where $team "hide_in_team" "!=" true }}
{{ $stapel := lt $i 8 }}
<li {{ if $stapel }} class="stapel" style="--i: {{ $i }}"{{ end }}>
{{ partial "image" (dict "path" (.polaroid_image | default .thumbnail_image) "size" "600x940" "alt" .title "fit" true "non_drag" true) }}
<p class="name">{{ .title }}</p>
{{ with .role }}<p class="role">{{ . }}</p>{{ end }}
</li>
{{ end }}
</ul>
</div>
</section>
{{ end }}
{{ with $section := .data }}
<section class="catalog" data-cursor='You'>
<div class="container">
<article class='base'>
{{ partial "kicker" . }}
{{ partial "h1-huge" $ }}
<div class="prose activate-children">
{{ partial "description" . }}
{{ partial "btn" . }}
</div>
</article>
{{ with $browser := .browser }}
<div class="browser">
<span aria-hidden='true'></span>
{{ if .media }}
<div class="top">
{{ partial "media" (dict
"media" .media
"size" "1400x1400"
"eager" $.firstSection
"fit" true
) }}
{{ if .optional_client_logo_image }}
{{ partial "logo-collab" (dict "optional_client_logo_image" .optional_client_logo_image "alt_text" .alt_text) }}
{{ end }}
</div>
{{ end }}
<div class="views">
<ul>
{{ range $i, $j := .views }}
<li><button {{ with .data_color }} class='has-dot {{ . | urlize }}' {{ end }}>{{ .title }}</button></li>
{{ end }}
</ul>
</div>
<div class="figures">
{{ range $i, $j := .views }}
<div class='list {{ if eq $i 0 }} active {{ end }}'>
{{ partial "data-visualisations" .visualisations }}
</div>
{{ end }}
</div>
</div>
{{ end }}
</div>
{{ with .cursors }}{{ partial "cursors" . }}{{ end }}
</section>
{{ end }}
{{ with $section := .data }}
<section class="vacancy-content detail-content">
<div class="container">
<div class="prose">{{ $.page.Content }}</div>
{{ with .form }}<aside class="browser slow-sticky" id="solliciteren">{{ partial "form" (merge . (dict "page" $.page)) }}</aside>{{ end }}
</div>
</section>
{{ end }}
{{ with $section := .data }}
<section class="text-accordion">
<div class="container">
<article class='base slow-sticky'>
{{ if $.firstSection }}
{{ partial "breadcrumbs" $.page }}
{{ else }}
{{ partial "kicker" . }}
{{ end }}
{{ partial "h1-huge" $ }}
<div class="prose">
{{ partial "description" . }}
</div>
{{ partial "btn" . }}
</article>
{{ partial "faq-questions" (merge . (dict "altijd_een" true)) }}
</div>
</section>
{{ end }}
{{ with $section := .data }}
<section class="intro{{ if .has_faded_dex_background }} has-glow{{ end }}">
<div class="container {{ if not .has_faded_dex_background }} activate-children {{ end }}">
{{ if .has_faded_dex_background }}<span class='dex activate' aria-hidden='true'>{{ partial "image" (dict "path" "/img/dex-blurred.png" "size" "525x500" "fit" true "sizes" "(min-width: 768px) 70rem, 216vw" "lazy" false "non_drag" true) }}</span>{{ end }}
{{ partial "h1-huge" $ }}
<div class="prose">
{{ partial "description" . }}
</div>
{{ partial "btn" . }}
{{ partial "cursors" .cursors }}
</div>
</section>
{{ end }}
{{ with $section := .data }}
{{ $filter := $.page }}
{{ if and (ne $.page.Kind "term") .category }}{{ with site.GetPage (printf "/blog_categories/%s" .category) }}{{ $filter = . }}{{ end }}{{ end }}
{{ $paginator := $.page.Store.Get "paginator" }}
{{ $artikelen := slice }}
{{ if $paginator }}{{ $artikelen = $paginator.Pages }}{{ else }}{{ $artikelen = partial "blog-articles" $filter }}{{ end }}
{{ $uitgelicht := false }}
{{ if and (eq $.page.Kind "section") (eq $.page.Section "blog") (or (not $paginator) (eq $paginator.PageNumber 1)) }}{{ $uitgelicht = partial "blog-featured-article" $.page }}{{ end }}
<section class="overview">
<div class="container">
<div class="top">
{{ partial "h1-huge" $ }}
<div class="prose">{{ partial "description" . }}</div>
{{ partial "category-filter" (dict "page" $filter "taxonomy" "blog_categories") }}
</div>
<ul class="cards">
{{ range $i, $artikel := $artikelen }}
{{ if and $uitgelicht (eq $i 6) }}{{ partial "blog-card-featured" $uitgelicht }}{{ end }}
{{ partial "blog-card" $artikel }}
{{ end }}
{{ if and $uitgelicht (lt (len $artikelen) 7) }}{{ partial "blog-card-featured" $uitgelicht }}{{ end }}
</ul>
{{ if $paginator }}{{ partial "pagination" (dict "page" $.page "format" "terse") }}{{ end }}
</div>
</section>
{{ end }}
{{ with $section := .data }}
<section class="text-image">
<div class="container">
<article class='base'>
{{ if $.firstSection }}
{{ partial "breadcrumbs" $.page }}
{{ else }}
{{ partial "kicker" . }}
{{ end }}
{{ partial "h1-huge" $ }}
<div class="prose">
{{ partial "description" . }}
</div>
{{ partial "buttons" . }}
{{ with .tag_links }}
<div class="has-tags alt">
{{ range . }}
{{ if .url }}<a class="tag" href="{{ partial "lang-url" .url }}">{{ .title }}</a>{{ else }}<span class="tag">{{ .title }}</span>{{ end }}
{{ end }}
</div>
{{ end }}
</article>
{{ with .figure }}
<figure class="browser">
<span></span>
{{ with .tags }}<p class="has-tags">{{ range . }}<span>{{ . }}</span>{{ end }}</p>{{ end }}
{{ partial "media" (dict "media" .media "size" "2250x1800" "eager" $.firstSection) }}
</figure>
{{ end }}
</div>
</section>
{{ end }}
{{ with $section := .data }}
{{ $p := $.page }}
{{ $d := $p.Params.details }}
{{ $team := (index hugo.Data (site.Language.Label | lower)).team }}
<section class="blog-hero detail-hero">
<div class="container">
<p class="has-tags">
{{ range $p.GetTerms "blog_categories" }}<a class="tag uitgelicht" href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>{{ end }}
{{ if not $p.Date.IsZero }}<span><time datetime='{{ $p.Date.Format "2006-01-02" }}'>{{ $p.Date | time.Format ":date_medium" }}</time></span>{{ end }}
{{ if and (not $p.Date.IsZero) (not $p.Lastmod.IsZero) (gt ($p.Lastmod.Format "2006-01-02") ($p.Date.Format "2006-01-02")) }}<span>{{ T "Bijgewerkt" }} <time datetime='{{ $p.Lastmod.Format "2006-01-02" }}'>{{ $p.Lastmod | time.Format ":date_medium" }}</time></span>{{ end }}
<span>{{ T "Leestijd" (dict "minuten" $p.ReadingTime) }}</span>
</p>
{{ if gt (len $p.Title) 60 }}
{{ partial "h1" (dict "data" (dict "title" $p.Title) "firstSection" $.firstSection) }}
{{ else }}
{{ partial "h1-huge" (dict "data" (dict "title" $p.Title) "firstSection" $.firstSection) }}
{{ end }}
{{ with $d.authors }}
<ul class="authors">
{{ range . }}
{{ with index (where $team "title" .) 0 }}{{ partial "person" (dict "member" .title "sub" .role "tag" "li") }}{{ end }}
{{ end }}
</ul>
{{ end }}
{{ with $d.image }}
<figure class="browser">
<span></span>
{{ partial "image" (dict "path" . "size" "2000x1100" "alt" $d.alt_text "eager" true) }}
</figure>
{{ end }}
</div>
</section>
{{ end }}
Snel
Pagina’s worden statisch gegenereerd, dus je site laadt sneller. Goed voor je bezoeker, voor Google en voor de AI-modellen die je content lezen.
Veilig
Geen database om te hacken, een kleiner aanvalsoppervlak. Veiligheid zit in de bouw, niet achteraf erop geplakt.
Duurzaam
Geen constante updates nodig. Je site staat er over een paar jaar nog precies zo.
Creatieve vrijheid
Geen concessies aan plugins, alles op maat gebouwd. Het design dat je wilt, niet het design dat een template toelaat.
Gedaan. Ik heb de kop aangepast. Hier is een preview, kijk even voordat we publiceren.
Klaar om te publiceren?
AI-integratie via Dex
Na livegang wordt het beheren van je site een eitje. Via Dex, onze eigen AI-integratie, pas je content aan door simpelweg te vertellen wat je wilt veranderen. Jij schrijft, Dex doet het. Met live preview zie je de wijziging voordat hij live gaat.
Wat de SLA regelt
Je site blijft in topvorm, zonder dat jij erbij hoeft stil te staan. Van hosting en monitoring tot technische support en health checks: wij regelen het.
Wij hosten je site en houden hem in de gaten. We krijgen een signaal als er iets aandacht vraagt, voordat jij het merkt.
Grote kans dat het antwoord hieronder staat.
Drink een koffie met Max. Of bel hem, dat mag ook. Dan kijken we samen hoe je merk verder groeit.