Skip to content

Page sections

Composable, slot-based blocks for the moments every marketing or editorial page needs: a hero, a page intro, a call to action, content grids, media blocks, team / people blocks, a map, and blog / news blocks. Each is a custom element with a CSS-class fallback so it works from CMS-generated HTML.

Hero

Use the .cui-hero block for text-only, split-media, background-image, and overlay introductions. Image and video media live in light DOM inside a .cui-hero-media wrapper.

Campaign

Launch pages with rich media.

Pair concise copy with product visuals, venue photography, or campaign artwork.

Start Preview
Abstract product interface preview

Background

Put imagery behind the message.

Background image heroes work well when the visual is decorative and the copy should lead.

Explore

Editorial

Video-backed stories with readable copy.

Use an overlay hero when motion supports the message and the headline still needs priority.

Watch

Announcement

A focused text hero for direct messages.

Text-only heroes keep the first viewport calm for documentation, changelogs, and product updates.

Read more
API reference
Class Purpose
.cui-hero Hero surface. Pair with a variant modifier (below).
.cui-hero-inner Centered, width-capped grid wrapping the content column and optional media.
.cui-stack Content column — holds the eyebrow, title, lead copy, and actions. The band caps its width and tightens the rhythm in place.
.cui-eyebrow Small kicker text rendered above the title.
.cui-display The headline, at hero (display) scale. Sized via the preserved --cui-hero-title-size.
.cui-cluster CTA button group — a flex row that wraps on narrow viewports.
bare <p> Lead copy in the content column — rendered larger and muted at reading measure.
.cui-hero-media Wraps the image or video for the split / overlay variants; the inner media covers the cell.
.cui-hero-bg Background-image layer for the background variant. Set the image with an inline --cui-hero-background-image custom property.
Modifier class Purpose
data-variant="split" Shows the .cui-hero-media beside the copy.
data-variant="background" Renders the .cui-hero-bg image behind the content.
data-variant="overlay" Layers the content on top of a .cui-hero-media element with a darkening gradient.
data-variant="text" Copy-only hero with tighter rhythm and a narrower container.
data-media="start" Places the media before the copy in the split layout.
data-align="center" Centers the content column (pair with data-variant="text").
data-width="narrow · default · wide" Caps the inner content's inline-size to a site container scale while the hero surface stays full-bleed. Defaults to wide (96rem); default aligns with the rest of the site content (72rem), narrow suits editorial copy (48rem). Drives --cui-hero-container-size.
Variable Default
--cui-hero-min-block-size min(86vb, 48rem)
--cui-hero-padding-block --cui-section-space-hero
--cui-hero-gap --cui-space-7
--cui-hero-content-size 42rem
--cui-hero-container-size --cui-container-wide (96rem); switched by data-width="narrow · default · wide".
--cui-hero-media-size 38rem
--cui-hero-media-min-block-size clamp(18rem, 44vi, 34rem)
--cui-hero-media-radius --cui-radius-2
--cui-hero-title-size --cui-font-size-display
--cui-hero-background-image Set inline on .cui-hero-bg for the background variant.
--cui-hero-overlay Linear gradient — used by the overlay variant.
--cui-hero-bg · --cui-hero-color Surface and text overrides.

Page intro

Use the .cui-page-intro block as a page header strip: eyebrow, display title, lead copy, optional meta line, action row, and an optional aside column. The band styles its own descendants, so the content carries only canonical primitives — .cui-stack, .cui-display, .cui-meta, .cui-cluster, and .cui-surface.cui-aside. Modifier attributes tune scale and tone for landing, case, vacancy, and campaign pages.

Theming

Composable design tokens for product surfaces.

Drop tokens at any scope: globally, per section, or inline.

Read tokens Components

Combat UI

Progressive components for the modern web.

Display-scale heading and a roomier content column. Use this on the marketing or product home page.

Get started

Case study

Reshaping operational reporting at Atlas.

  • Sector — Logistics
  • Year — 2026
  • Stack — Combat UI

Two-column layout: content on the left, aside on the right at ≥ 56rem, stacked below.

Promo

Inverse surface for the headline moment.

Flips surface and text tokens for emphasis. Pair with data-variant="full-bleed" for a hero band.

Try it

Status

Centered intro for short-form pages.

Title and copy align to the center axis.

API reference
Modifier class Effect
data-variant="landing" Display-scale title, wider content column
data-variant="case" Muted background, two-column with aside at ≥ 56rem
data-variant="vacancy" Two-column with aside; tighter content measure
data-variant="campaign" Muted background, two-column with aside
data-tone="inverse" Inverse surface and foreground tokens
data-variant="full-bleed" Span the full inline-size of the parent
data-align="center" Center-align title, copy, and actions
Class / element Purpose
.cui-page-intro-layout Grid wrapper for the content column and optional aside.
.cui-stack Content column — holds the eyebrow, title, meta, lead copy, and actions.
.cui-eyebrow Kicker text rendered above the title.
.cui-display Display heading, retuned to page-intro scale by the band.
.cui-meta Meta strip — a <ul> of labels (sector, year, reading time).
.cui-cluster Action row — primary and secondary CTA buttons.
.cui-surface.cui-aside Optional summary rail, shown to the right of the content at ≥ 56rem. Used by the case, vacancy, and campaign variants.
Variable Default
--cui-page-intro-padding-block --cui-section-space-compact
--cui-page-intro-gap --cui-space-5
--cui-page-intro-content-size 52rem
--cui-page-intro-aside-size 20rem
--cui-page-intro-title-size --cui-font-size-5
--cui-page-intro-bg · --cui-page-intro-color Surface and text overrides.
--cui-page-intro-inverse-bg · --cui-page-intro-inverse-color · --cui-page-intro-inverse-muted-color · --cui-page-intro-inverse-aside-bg Inverse-mode overrides (applied by data-tone="inverse").

See Theming → Component tokens for the full --cui-page-intro-* token reference.

CTA

Use the class="cui-cta cui-surface cui-stack" block for conversion sections that need simple, split, full-bleed, or sticky placement.

Next step

Start with the component docs.

Simple CTAs center the message and action for focused sections.

Browse components

Build faster

Pair supporting copy with actions.

Split CTAs keep action groups aligned beside the message on wider screens.

Open demo Foundations

Launch

Create a strong campaign close.

Full-bleed CTAs create a section-level break without adding a separate wrapper.

View patterns

Inverse full bleed

Make contrast explicit.

The data-tone="inverse" treatment can be combined with any data-variant.

View inverse

Sticky CTAs stay available during long pages.

Use them sparingly for application flows, demos, and campaign signup pages.

Continue
API reference
Class Purpose
.cui-cta-content Content column - composes .cui-stack; tightens the gap and caps the measure.
.cui-eyebrow Small kicker line above the title.
.cui-cta-title The call-to-action heading.
.cui-cluster The actions row - one or more CTA buttons that flex-wrap automatically.
Modifier class Purpose
data-variant="simple" Centers the message and action for focused sections.
data-variant="split" Places actions in a side column at ≥ 48rem.
data-variant="full-bleed" Edge-to-edge muted band with section-scale padding and no card chrome.
data-variant="sticky" Pins the CTA to the bottom of the viewport.
data-tone="inverse" Inverse surface and text colors for high-contrast moments.
Variable Default
--cui-cta-padding --cui-space-6
--cui-cta-gap --cui-space-5
--cui-cta-content-size 48rem
--cui-cta-title-size --cui-font-size-4
--cui-cta-bg · --cui-cta-color Surface and text overrides.
--cui-cta-inverse-bg · --cui-cta-inverse-color · --cui-cta-inverse-muted-color · --cui-cta-inverse-eyebrow-color Inverse-mode overrides (applied by data-tone="inverse").
--cui-cta-sticky-z-index 20
--cui-cta-sticky-offset --cui-space-4

Content grids

Use the one .cui-grid primitive for feature lists, case cards, general card groups, logo strips, and stats — set --cui-grid-min per content type (18rem features, 20rem cases, 10rem stats, 9rem logos). It is CSS-only, configurable with `data-*` modifiers and `--cui-*` variables, and works from CMS or static HTML.

Reusable structure

Feature cards keep icon, heading, and copy spacing consistent across marketing pages.

Responsive by default

The grid adapts from single-column mobile layouts to dense desktop sections.

Token-driven

Grid width, gap, and card padding can be tuned with `--cui-*` variables.

Abstract interface preview

Case study

Campaign launch system

A reusable content model for event, product, and recruitment campaigns.

Abstract interface preview

Website

Agency page toolkit

Composable sections for service pages, proof blocks, and conversion flows.

Editorial card

Use general cards for resource lists, article collections, and compact page modules.

CMS friendly

The classes do not depend on shadow DOM, so generated markup stays readable.

Theme aware

Surface, border, text, and shadow values follow the active theme tokens.

Northstar
Signal Labs
Atlas Works
Field Co.
42% Higher page reuse
18 Reusable sections
3x Faster campaign assembly
API reference
Class Purpose
.cui-grid The one auto-fit grid wrapper for every card type below — set --cui-grid-min per content type and pin columns with data-columns.
.cui-feature-card / .cui-feature-icon Feature cards with consistent icon, heading, and copy spacing.
.cui-case-card / .cui-case-card-media / .cui-case-card-body / .cui-case-card-meta Case-study cards with optional media, body, and meta line.
.cui-content-card General-purpose cards for resource lists and article tiles.
.cui-logo-item Logo / partner cells with consistent block-size.
.cui-stat / .cui-stat-value / .cui-stat-label Number-focused cards for metrics, proof points, and KPIs.
Modifier Purpose
data-columns="2 | 3 | 4" On .cui-grid — pins the column count at ≥48rem.
data-gap="compact | spacious" On .cui-grid — tightens or relaxes the grid gap.
data-align="start | center" On a feature card or stat block — aligns its inner content.
data-variant="flat" On a case card — drops the elevation shadow.
data-variant="borderless" On a content card — drops the surrounding border for editorial layouts.
Variable Default
--cui-grid-min 16rem (set per content type: 18rem features, 20rem cases, 10rem stats, 9rem logos)
--cui-feature-icon-size --cui-size-control
--cui-case-card-media-ratio Per-instance aspect ratio override (e.g. 4 / 3).
--cui-grid-gap --cui-space-4
--cui-logo-item-min-block-size 4rem
--cui-stat-value-size --cui-font-size-5

Media blocks

Use media blocks for editorial imagery, figure callouts, media-led cards, and overlay cards. They are CSS-only, work from CMS-generated HTML, and accept any of <img>, <picture>, <video>, <iframe>, or inline <svg>.

Abstract Combat UI banner
Schematic of the page composition system
Figure 1. Page composition primitives combine to form section-level patterns.
Composition pattern preview

Editorial spread

A media-led card pairs a leading visual with eyebrow, title, copy, and an action group.

Read more
Composition pattern preview

Editorial

Surface presets

The card composes .cui-surface, so the shared data-elevation and data-radius presets retune its chrome — here, flat with a larger radius.

Side-by-side media card

Side by side

Row orientation

Set data-orient="row" to place the media beside the body. The layout uses flex-wrap, so the media stacks above the body when the card is narrower than the combined flex basis.

Continue reading
Campaign visual

Campaign

Tell the story above the fold.

Overlay cards layer copy on top of a media background with a configurable scrim.

Read campaign
Centered overlay card

Centered

Solid scrim, centered content.

Use data-align="center" and data-scrim="solid" for poster-style blocks.

API reference
Class Purpose
.cui-media-full Edge-to-edge media wrapper. Constrains aspect ratio and rounds corners; pair with data-bleed="full" to escape the container gutter for true full-bleed sections.
.cui-figure / .cui-figure-caption Native <figure> wrapper with consistent caption styling. Works with a <figcaption> child or a sibling element using .cui-figure-caption.
.cui-surface.cui-media-card / .cui-media-card-media Card with a leading media block above (or beside) a .cui-stack body. Composes .cui-surface for chrome and styles its own descendants; the body carries a .cui-eyebrow, a bare heading, copy, a .cui-metadata line, and a .cui-cluster. Drop inside .cui-grid for responsive grids.
.cui-media-overlay / .cui-media-overlay-media Card with copy overlaid on a media background and a configurable scrim. The body is a .cui-stack carrying a .cui-eyebrow, a .cui-display title, copy, and a .cui-cluster — the overlay styles them in place (light foreground, near-white eyebrow).
Modifier Purpose
data-ratio="square | portrait | wide | auto" Sets the aspect ratio on .cui-media-full, .cui-figure, and .cui-media-overlay. Defaults to 16 / 9; auto falls back to the asset's intrinsic ratio.
data-bleed="full" On .cui-media-full or .cui-figure — escapes the container gutter to span the viewport. Place inside a .cui-section, not inside a .cui-container.
data-radius="none" On .cui-media-full — removes the rounded corners.
data-orient="row" On .cui-media-card — places the media beside the body using flex-wrap. The media wraps above the body when the card is narrower than the combined flex bases of media (--cui-media-card-media-size) and body (--cui-media-card-body-min).
data-variant="flat | borderless" On .cui-media-card — drops the elevation shadow, or both the border and shadow for editorial layouts.
data-scrim="solid | top | none" On .cui-media-overlay — replaces the default bottom-up gradient with a flat solid scrim, a top-down gradient, or no scrim at all.
data-align="center | end" On .cui-media-overlay — centers or end-aligns the overlay copy. Defaults to bottom-start.
data-align="center" On .cui-figure — centers the media and caption.
Variable Default
--cui-media-ratio 16 / 9
--cui-media-fit cover
--cui-media-position center
--cui-media-radius --cui-radius-2
--cui-media-bg --cui-color-surface-muted
--cui-media-card-media-ratio 16 / 10
--cui-media-card-media-size 18rem — flex basis for the media column when data-orient="row" is set.
--cui-media-card-media-min-block-size 12rem — minimum block size of the media column in row orientation.
--cui-media-card-body-min 18rem — flex basis for the body column in row orientation. The body wraps below the media when the card is narrower than this plus the media size.
--cui-media-overlay-color oklch(98% 0 0deg) — overlay text color (independent of theme inversion).
--cui-media-overlay-min-block-size 22rem
--cui-media-overlay-content-size 32rem
--cui-media-overlay-padding --cui-space-5 --cui-space-6
--cui-media-overlay-scrim Bottom-up gradient — override for custom scrim colors or directions.
--cui-media-overlay-title-size --cui-font-size-3

Team & people

Person cards, team grids, leadership grids, and org charts. Markup stays semantic — an <article> per person, nested <ol> for the org chart — so the content is accessible to screen readers and search engines. All four blocks are CSS-only and work from CMS-generated HTML; no JavaScript is required.

The org chart uses CSS connector lines drawn from pseudo-elements; no SVG is required. For richer vector diagramming (curved connectors, custom routing) you can layer an <svg aria-hidden="true"> overlay above the chart and position it absolutely — the underlying <ol> remains the source of truth for SEO and assistive tech.

Portrait of First Last

Engineering

First Last

Staff engineer · Platform

Portrait of First Last

First Last

Design lead · Marketing

Short placeholder bio describing this person's role and background.

Portrait of First Last

First Last

PM · Growth

Placeholder bio line — keeps the layout truthful at one paragraph.

Portrait of First Last

First Last

Staff engineer

Portrait of First Last

First Last

Design lead

Portrait of First Last

First Last

PM

Portrait of First Last

First Last

QA engineer

Portrait of First Last

CEO

First Last

Founder & CEO

Two-sentence placeholder bio. Long enough to wrap onto a second line and exercise the leadership layout's vertical rhythm.

Portrait of First Last

CTO

First Last

Chief technology officer

Two-sentence placeholder bio. The second sentence is here to fill a second line for the balanced grid demo.

  1. CEO

    First Last

    Founder & CEO

    1. CTO

      First Last

      Technology

      1. First Last

        Staff engineer

      2. First Last

        QA engineer

    2. CMO

      First Last

      Marketing

      1. First Last

        PM · Growth

Horizontal layout

Use data-layout="horizontal" for deep hierarchies that read better left-to-right, or for narrow viewports where vertical trees overflow.

  1. First Last

    CEO

    1. First Last

      CTO

    2. First Last

      CMO

    3. First Last

      PM · Growth

API reference
Class Purpose
.cui-person Person card container. Apply to an <article> or <a>; link variant gets an accent border on hover / focus.
.cui-person-photo Portrait. Defaults to a circular crop; use data-photo="square | rounded" on the parent .cui-person to override.
.cui-stack Body column for the name, role, bio, meta, and links. Compose .cui-stack on the wrapping <div>; the card sets its gap from --cui-person-body-gap.
.cui-person-name / .cui-person-role / .cui-person-bio / .cui-person-meta Heading, role (small subtitle), longer bio paragraph, and an uppercase eyebrow line (department or pronouns).
.cui-cluster <ul> wrapper for contact / social links. Compose .cui-cluster; the card flattens the list and adds the gap above it, and .cui-person a styles the links. List semantics retained for assistive tech.
.cui-grid Responsive grid wrapper around .cui-person children. Supports data-columns and data-gap; set --cui-grid-min: 20rem with data-gap="spacious" for the leadership layout.
.cui-org-chart / .cui-org-node / .cui-org-children / .cui-org-card Semantic org chart. .cui-org-chart is the root <ol>; .cui-org-node is each <li> containing one card and an optional nested .cui-org-children list. Add .cui-org-card alongside .cui-person for tighter org-card sizing.
Modifier Purpose
data-orient="row" On .cui-person — photo beside the body instead of above. Useful for leadership cards or single-column lists.
data-align="center" On .cui-person — centers photo, name, role, and links.
data-photo="square | rounded" On .cui-person — swaps the circular crop for a square or slightly-rounded crop.
data-variant="flat | borderless | leadership" On .cui-personflat drops the shadow; borderless drops border and shadow (for editorial layouts); leadership scales photo, padding, and type sizes.
data-columns="2 | 3 | 4" On .cui-grid — fixed column count from 48rem and up.
data-gap="compact | spacious" On .cui-grid — tightens or relaxes the grid gap.
Leadership grid On .cui-grid — set --cui-grid-min: 20rem with data-gap="spacious", and give each card data-variant="leadership" for the scaled-up preset.
data-layout="vertical | horizontal" On .cui-org-chart — top-down (default) or left-to-right hierarchy. Horizontal works well for deep trees on narrow viewports.
data-line="dashed" On .cui-org-chart — dashed connector lines instead of solid.
Variable Default
--cui-person-photo-size --cui-space-7 (column orient) · --cui-person-photo-row-size (7rem) in row orient · --cui-person-photo-leadership-size (11rem) for leadership.
--cui-person-photo-ratio 1 / 1
--cui-person-photo-radius 50% (overridden by data-photo modifiers).
--cui-person-padding --cui-space-5
--cui-person-gap --cui-space-4 — gap between photo and body inside the card.
--cui-person-body-gap --cui-space-1 — gap between body children (name, role, bio).
--cui-person-name-size / --cui-person-role-size --cui-font-size-1 / --cui-font-size-0 (leadership scales up to --cui-font-size-2 / --cui-font-size-1).
--cui-person-meta-color --cui-color-accent
--cui-grid-min 16rem default for the team grid · set 20rem for the leadership layout.
--cui-org-gap --cui-space-5 — gap between sibling nodes.
--cui-org-link-size --cui-space-5 — connector arm length.
--cui-org-line-width 2px
--cui-org-line-color --cui-color-border
--cui-org-card-min-inline-size 14rem — minimum node width so cards stay legible.
--cui-org-v-middle / --cui-org-v-first / --cui-org-v-last / --cui-org-v-only SVG mask images used to render the vertical-layout connectors. Each is a url("data:image/svg+xml;…") path. Override to swap in your own SVG — curved corners, dotted strokes, brand-coloured paths, animated dashes, anything an SVG <path> can express. The mask is filled with --cui-org-line-color, so you only need to author the stroke geometry.
--cui-org-h-middle / --cui-org-h-first / --cui-org-h-last / --cui-org-h-only Same as the vertical set, for data-layout="horizontal". Each draws the same geometry rotated 90°: vertical bus on the left, horizontal arm to the child on the right.

Connectors are rendered as SVG via CSS mask-image. The fill color comes from --cui-org-line-color; the geometry comes from the SVG. Both can be overridden independently — re-skin the chart without touching its markup.

Markup contract Notes
<article class="cui-surface cui-person"> Use <article> for standalone person cards (each is independently meaningful). Heading levels inside should follow your page outline.
<ol class="cui-org-chart"> Ordered list reflects reporting hierarchy. Always provide an aria-label or aria-labelledby describing what the chart represents.
<ol class="cui-org-children"> Nested ordered list of direct reports. Nest as deep as the org demands; CSS connectors keep working at any depth.
Optional SVG overlay For curved or animated connectors, layer an <svg aria-hidden="true"> on top of the chart using position: absolute. The list remains the SEO source; the SVG is purely decorative.
Schema.org / JSON-LD Pair the chart with Organization and Person JSON-LD for richer search results. The block does not emit JSON-LD itself — add it once per page based on the same data you use to render the cards.

Blog & news

Article cards, grids, lists, and a filter chip group. All blocks are semantic <article> elements with real headings, links, and <time> elements, so search engines and feed readers pick them up before any JavaScript loads. The optional cui-article-filter enhances a stock form into an instant client-side filter.

Category placeholder

Row-oriented article placeholder

The row orientation pairs media beside body. The flex-wrap basis collapses to a stack on narrow containers.

  • First Last

Stacked variant

data-variant="stacked" on the list strips the card chrome and renders hairline separators between entries — useful for changelogs, release notes, or compact sidebar lists.

cui-article-filter wraps a stock <form> of radio chips or checkboxes. On change, it toggles hidden on .cui-article-card elements in its target grid / list based on data-category. With no JavaScript the form still posts (server-side filtering); with JavaScript you get instant client-side updates.

Filter

Swap the type="radio" inputs for type="checkbox" to allow multi-select. Listen for cui-article-filter-change on the element to react — its detail is { values, multi, visibleCount }.

API reference
Class Purpose
.cui-surface.cui-article-card Article container — composes .cui-surface for the chrome and styles its own descendants. Use an <article> for standalone entries. Optional data-category drives filter visibility.
.cui-article-card-media Media container (bespoke; bleeds edge-to-edge). Apply to an <a> wrapping <img> / <picture> / <video> for a clickable thumbnail with hover zoom — or drop a bare first-child media element and skip the class.
.cui-stack body with .cui-eyebrow / .cui-display / a bare <p> / .cui-meta / .cui-cluster Contextual content column. The card styles these canonical primitives directly — the eyebrow is the category, the display the title (wrap a child <a>), the bare paragraph the excerpt, the meta row the byline / date / reading-time (first entry reads at full strength), and the cluster the actions row. No per-slot classes.
.cui-grid of cards Responsive grid wrapper (set --cui-grid-min: 18rem). Supports data-columns="2 | 3 | 4" and data-gap="compact | spacious".
.cui-stack of cards Single-column list. Pair with .cui-article-card[data-orient="row"] for media-beside-body. data-variant="stacked" on the stack strips card chrome and renders hairline separators between cards.
.cui-article-filter / .cui-article-filter-chip / .cui-article-filter-label / .cui-article-filter-count Pill-shaped filter chip group. Each chip wraps a hidden radio / checkbox so the form posts as native HTML when JavaScript is disabled.
Modifier Purpose
data-orient="row" On .cui-article-card — media beside body with flex-wrap collapse.
data-variant="featured" On a card — wider media (21:9), larger title, more padding.
data-variant="compact" On a card — hides media, smaller title, tighter padding.
data-variant="flat | borderless" On a card — drops the shadow / drops border and shadow.
data-variant="stacked" On the .cui-stack list — hairline separators instead of card chrome.
data-columns="2 | 3 | 4" On the .cui-grid — fixed column count from 48rem and up.
data-gap="compact | spacious" On the .cui-grid — tightens or relaxes spacing.
data-category="<value>" On a card — categorisation hook for cui-article-filter.
Variable Default
--cui-article-card-radius --cui-radius-2
--cui-article-card-padding --cui-space-5 · --cui-space-6 when featured
--cui-article-card-gap --cui-space-2 — gap between body children.
--cui-article-card-media-ratio 16 / 10 · 21 / 9 when featured.
--cui-article-card-media-size 18rem — flex basis for the media column in row orientation.
--cui-article-card-body-min 20rem — flex basis for the body column in row orientation.
--cui-article-card-title-size --cui-font-size-2 default · --cui-font-size-3 when featured · --cui-font-size-1 when compact.
--cui-article-card-category-color --cui-color-accent — category eyebrow tint.
Grid / list spacing Use the primitive knobs — --cui-grid-min (18rem) on the .cui-grid, --cui-stack-gap on the .cui-stack.
cui-article-filter API Purpose
target="<selector>" Required CSS selector pointing at the .cui-grid / .cui-stack of cards to filter. If omitted (or it matches no element) the filter no-ops and warns in the console.
filter.selectedValues Read-only array of checked input values inside the filter.
filter.refresh() Recompute visibility — call after dynamically adding or removing cards.
cui-article-filter-change event Bubbles from the element with { values, multi, visibleCount } after each change.
Markup contract Inputs of type="radio" or type="checkbox" inside the element. An empty value="" means "all categories". Mix checkboxes for multi-select.

Events & calendar

Event cards, lists, and a month-view calendar component. Each card is a semantic <article> with a real <time> element, status pill, and link, so search engines and feed readers index the listing before the calendar JS loads. The cui-calendar component enhances a slotted list of event cards into an interactive month grid; cui-day-planner renders the same card list as a vertical day timeline with column-packed overlap, hour rules, and a now-line. The source cards stay in the DOM (visible by default, or visually hidden via events-hidden) so the page degrades cleanly.

Workshop

CSS fundamentals · hands-on workshop

  • Studio · Amsterdam
  • 24 seats

Two-hour workshop on modern layout primitives — grid, flex, container queries — with a take-home exercise set and feedback on submissions.

Register Add to calendar
Upcoming

Conference

Combat UI Summit 2026

  • All day
  • Felix Meritis · Amsterdam

Talks from product, design, and engineering teams shipping component libraries at scale. Featured variant uses a larger date block and title.

Live stream
Live now

Meetup

Component patterns dinner

  • Café Bouwman

Cancelled — venue lost power the morning of the event. Refunds processed via the original payment method within five business days.

Cancelled

  1. Coffee with the team

    You talk through what's happening in the city. Reports come in from residents and businesses, and the team splits them up between you.

  2. Into the neighbourhood

    You head out with a colleague to follow up on reports. Along the way you spot a wrongly parked car, rubbish next to a container, and a stray dog. You talk to people, resolve what you can, and refer the rest.

  3. Lunch

    Back at the city office for lunch — a chance to recharge, catch up, and laugh with the team. You work hard and have fun doing it.

  4. Partner meeting

    Sync with police, public-safety colleagues, and a handful of partners. There's an integrated operation tomorrow and everyone wants to be prepared — safety first.

  5. Debrief

    Feedback loops, reporting, and admin. It takes about half an hour at the end of each day.

  6. Home

    Off home, with enough energy left for the rest of your life.

API reference
Class Purpose
.cui-surface.cui-event-card Container for one event — composes .cui-surface for chrome and styles its own descendants (body .cui-stack, category .cui-eyebrow, title .cui-display, meta .cui-meta, actions .cui-cluster). Carry data-status to tone the date block and corner pill; data-variant="featured | compact | flat | borderless" to swap chrome; data-orient="stacked" for poster-style layouts.
.cui-event-card-date Render as <time datetime> with month / day / year spans. The cui-calendar / cui-day-planner read the first <time> inside the card to plot the event.
.cui-display[data-cui-event-title] The title — .cui-display for styling, data-cui-event-title as the hook the calendar / day-planner read for the label and link.
.cui-grid (layout) Lay cards out with the grid primitive — set --cui-grid-min: 22rem for the roomier event column. Pin columns with data-columns="2 | 3".
.cui-stack (layout) Single-column list. data-variant="stacked" drops card chrome to hairline separators; data-variant="agenda" renders author-supplied <h3> day headers between groups.
.cui-surface.cui-itinerary-item Narrative day-in-the-life / programme rows. Lay them out in a .cui-stack (render as <ol> when order matters); each row composes .cui-surface and styles its own descendants (body .cui-stack, a bare heading, bare <p> copy). Only the .cui-itinerary-marker keeps a class — a <time datetime> or free-form text (Day 1, Step 2). Set data-variant="compact" on the stack to tighten spacing, or data-variant="bare" to drop card chrome.
cui-calendar API Purpose
year / month Initial visible month. Defaults to today.
weekday-start="monday | sunday" First column of the grid. Defaults to monday.
locale BCP-47 locale used for month and weekday labels. Falls back to document.documentElement.lang, then navigator.language.
events-hidden Visually hide the slotted event list while keeping it in the DOM for screen readers and search engines.
calendar.next() / previous() / goTo(year, month) Programmatic navigation. month is 1-indexed.
calendar.refresh() Re-read slotted event cards. Call after dynamically adding cards.
cui-calendar-day-select event Bubbles with { date, iso, events } when a day cell is activated.
cui-calendar-event-select event Cancelable; bubbles with { date, iso, event } when an event pill is activated. Call preventDefault() to stop the link from following.
cui-calendar-navigate event Bubbles with { year, month } after the visible month changes.
cui-day-planner API Purpose
date="YYYY-MM-DD" Visible day. Defaults to today.
start-hour / end-hour Visible window. Defaults to 722.
slot-minutes="15 | 30 | 60" Minor-gridline cadence. Defaults to 60.
now-line Set to false to hide the today now-line. Defaults to on.
Event end times Read from a second <time datetime> on the card, or from a data-end attribute (full ISO or HH:MM). Cards without an end default to a 60-minute block.
planner.next() / previous() / goTo(iso | Date) Programmatic navigation.
cui-day-planner-event-select event Cancelable; bubbles with { iso, date, event } when an event block is activated.
cui-day-planner-slot-select event Bubbles with { iso, date, minutes } (minutes from midnight, rounded to slot-minutes) when an empty slot is clicked.
cui-day-planner-navigate event Bubbles with { iso } after the visible day changes.
Variable Default
--cui-event-card-date-size 7rem (featured: 9rem; compact: 5.5rem)
--cui-event-card-title-size --cui-font-size-2
--cui-event-card-category-color --cui-color-accent
--cui-grid-min (grid) 22rem for the event column
--cui-stack-gap (list) --cui-space-3 for the compact list rhythm
--cui-calendar-cell-min-size 4.5rem (small screens: 3.25rem)
--cui-calendar-accent --cui-color-accent
--cui-day-planner-row-block-size 3rem (small screens: 2.5rem)
--cui-day-planner-label-inline-size 4.5rem
--cui-day-planner-event-bg Accent-tinted surface; status attribute swaps tone.

Contact

Contact cards, location cards, and a contact form layout. Each is CSS-only and works from CMS-generated HTML — no JavaScript required. Both cards compose .cui-surface and style their own descendants, and share the .cui-contact-methods <dl>, so a phone number reads the same whether it sits on a support card or beside an office address. Use real <a href="mailto:"> / <a href="tel:"> links and an <address> element so search engines and assistive tech pick the details up directly.

Drop several contact cards into a .cui-grid for a contacts directory, and put a cui-map, <iframe>, or photo in a location card's media panel. The contact form styles layout only — its controls reuse the shared .cui-input / .cui-textarea / .cui-field-label classes, so it works as a plain <form> or wrapped in a cui-form for validation.

Support

Customer success

Questions about your account or a live project? We reply within one business day.

Email
hello@example.com
Phone
+31 20 123 4567
Send a message

Press

Media & partnerships

For interviews, brand assets, and partnership enquiries.

Email
press@example.com
Hours
Mon–Fri · 9:00–17:00
Map of the Amsterdam office

Amsterdam HQ

Keizersgracht 123
1015 CJ Amsterdam
Netherlands
Hours
Mon–Fri · 9:00–18:00
Phone
+31 20 123 4567
Get directions

A contact form is a composition, not a dedicated block: cui-form for validation and submit, cui-field for labels / help / errors, the .cui-input family for the controls, and the layout primitives (.cui-stack, .cui-grid, .cui-cluster) for arrangement. See the Forms page for the full validation and i18n story.

Please enter your name. Please enter your email. That doesn't look like a valid email address.
Please add a short message. Please confirm we may contact you.
Cancel
API reference
Class Purpose
.cui-contact-card Single point of contact; compose class="cui-surface cui-stack cui-contact-card". Carry data-variant="flat | borderless | inverse" for chrome, data-align="center" to center content. The card styles its own descendants: a .cui-eyebrow label, a .cui-display title, a bare <p> lead, a .cui-contact-methods list, and a .cui-cluster actions row.
.cui-contact-methods Label / value list shared by both cards. Render as a <dl> of <dt> labels and <dd> values — no per-row slot classes. data-layout="inline" lays the label beside the value.
.cui-location-card Office / venue card: media panel plus body; compose class="cui-surface cui-location-card" with a .cui-stack body. data-orient="stacked" pins the media on top; data-variant="flat | borderless" for chrome. The body styles its own descendants: a .cui-display name, an <address>, a .cui-contact-methods list, and a .cui-cluster actions row. The .cui-location-card-media panel is kept.
Contact form No dedicated class — compose cui-form (validation / submit), cui-field (labels / help / errors), and the .cui-input family, laid out with .cui-stack, .cui-grid (data-columns for side-by-side fields), and .cui-cluster (actions). See the Forms page.
Variable Default
--cui-contact-card-padding --cui-space-5
--cui-contact-card-title-size --cui-font-size-2
--cui-contact-methods-gap --cui-space-3
--cui-location-card-media-size 16rem (row orient)
--cui-location-card-media-ratio 16 / 10 (when stacked)

Recruitment

Vacancy cards, grids, lists, and a two-column detail layout for job boards. Each card is a semantic <article> whose title and apply action are real <a href> links, so listings stay crawlable and work without JS. The same card flexes from a minimal "title + read more" listing to an image-led row, and the detail layout pairs a narrative body with a sticky "Snel overzicht" summary, apply CTA, and contact block — enough to cover the range of formats real municipal recruitment sites publish.

WijkBOA

Je kent de wijk en maakt het verschil.

  • 36 uur
  • Gemeente Vlaardingen
  • Schaal 8

Als wijkBOA werk je contextgericht: je ziet wat er speelt, spreekt mensen aan en pakt door waar het nodig is.

Nieuw

Veiligheid

WijkBOA

  • 36 uur
  • Schaal 8

Contextgericht handhaven in de buurt waar jij thuis bent.

Nieuw

Wijkboa

Aanspreekpunt voor de wijk, dichtbij de mensen.

  • 36 uur
  • Molenlanden

Een afwisselende functie waarin je veel buiten bent en echt het verschil maakt voor bewoners en ondernemers.

Nieuw

Openbare Orde en Veiligheid

Toezichthouder Westwijk

Houd toezicht in de wijk, ga in gesprek met bewoners en maak het verschil voor een veilige, leefbare Westwijk.

  • 32–36 uur
  • Vlaardingen
  • Schaal 5
  • Contract

Wat ga je doen?

Je loopt dagelijks je ronde door de Westwijk. Je bent zichtbaar, aanspreekbaar en signaleert wat er speelt — van leefbaarheid tot verkeersveiligheid.

  • Toezicht op leefbaarheid, verkeersveiligheid en overlast
  • Aanspreekpunt voor bewoners en ondernemers
  • Samenwerken met handhavers en politie in het kernteam

Wanneer hebben wij een match?

Je bent benaderbaar, luistert actief en blijft kalm onder druk. Een ronde lopen schrikt je niet af — je bent graag buiten en in beweging.

Wat wij jou bieden

Een salaris van minimaal € 2.495 en maximaal € 4.746 bruto per maand (schaal 5), ABP-pensioen, een Individueel Keuzebudget van 17,05% en een reiskostenvergoeding.

API reference
Class Purpose
.cui-surface.cui-vacancy-card One job opening. Compose .cui-surface for the card chrome (the card zeroes its padding so media bleeds; the body carries the inset). Default is a lean single column; add a first-child media element and data-orient="row" for an image-led card. data-variant="featured | compact | flat | borderless" swaps chrome; data-status="new | closing | closed" tones the corner pill. Carry data-team / data-location / data-contract for filtering.
Card descendants (contextual) The card styles its own descendants — no bespoke slot classes. Inside the .cui-stack body: .cui-eyebrow is the team label (retinted, linked-label styling), .cui-display the linked title and .cui-lead the tagline (both retuned to card scale via the type primitives' override vars), a bare <p> the excerpt, and .cui-cluster the bottom-aligned action row. The first-child <img> / <picture> / <a> is the media (only shown in data-orient="row"); a direct-child <span> is the status pill (toned by data-status).
.cui-meta data-variant="chips" The shared .cui-meta primitive carries the fact row used in the card and the detail header. data-variant="chips" renders each <li> as a rounded pill (hours, location, salary scale, contract); drop it for the inline ·-separated row.
.cui-grid Lay cards out with the .cui-grid primitive; set --cui-grid-min: 20rem for the roomier vacancy column. data-columns="2 | 3 | 4" pins fixed columns ≥48rem; data-gap="compact | spacious" adjusts spacing.
.cui-stack Single-column overview is the .cui-stack primitive (set --cui-stack-gap for compact / spacious rhythm). data-variant="stacked" on the stack collapses vacancy-card chrome to hairline separators for a dense index.
.cui-vacancy-detail Main + aside grid for a detail page; container-query driven (flips at 52rem inside a .cui-page ancestor), collapses to one column below it. data-aside="start" moves the aside before the body.
Detail descendants (contextual) The detail styles its own descendants — no bespoke slot classes. The full-width title band is the direct-child <header> (compose .cui-stack), carrying only .cui-eyebrow / .cui-display / .cui-lead, retuned to page scale in place. The narrative body column is a .cui-flow / .cui-prose child (capped measure); group its copy into plain <section>s — heading plus paragraphs / lists — which render cleanly whatever headings a posting uses.
.cui-vacancy-aside + .cui-surface.cui-stack Sticky aside rail — composes .cui-surface (muted background, no shadow) and .cui-stack — and styles its own descendants: a .cui-eyebrow section label ("Snel overzicht"), a quick-facts <dl>, a .cui-stack holding the full-width apply .cui-button (plus an optional muted note), and a buttonless .cui-stack for the hairline-separated contact block (a <strong> name and tel: / mailto: links).
Variable Default
--cui-vacancy-card-title-size --cui-font-size-2 (featured: --cui-font-size-3)
--cui-vacancy-card-media-size 14rem (row orient flex-basis)
--cui-grid-min (on .cui-grid) 20rem recommended for vacancy cards
--cui-stack-gap (on the list .cui-stack) --cui-space-4
--cui-vacancy-detail-aside-size 19rem

Pagination

A navigation block for paging through long lists. Built for server-rendered, crawlable paging: each page is a real <a href>, so the control works without JS and search engines follow it. Mark the current page with aria-current="page" and an unavailable prev / next direction with aria-disabled="true" on a <span>. The compact variant trades numbered pages for a bare prev / next pager with a "Pagina X van Y" status, for narrow viewports.

API reference
Class Purpose
.cui-pagination <nav aria-label> wrapper. data-align="start | end" aligns the row; data-variant="compact" hides numbered pages.
<ul> / <li> Flex-wrap list and its item wrappers — styled contextually under .cui-pagination; no class needed.
.cui-pagination-link Page number or prev / next control. aria-current="page" paints the active page in the accent fill; aria-disabled="true" / :disabled mutes it; data-control="prev | next" widens the target for a label.
.cui-pagination-ellipsis Decorative "…" for skipped ranges — hide its item from assistive tech.
.cui-pagination-status Muted "Pagina X van Y" text; the readable alternative in the compact variant.
Variable Default
--cui-pagination-gap --cui-space-1
--cui-pagination-size 2.5rem (min link target)