Reusable structure
Feature cards keep icon, heading, and copy spacing consistent across marketing pages.
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.
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
Pair concise copy with product visuals, venue photography, or campaign artwork.
Background
Background image heroes work well when the visual is decorative and the copy should lead.
Announcement
Text-only heroes keep the first viewport calm for documentation, changelogs, and product updates.
| 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. |
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
Drop tokens at any scope: globally, per section, or inline.
Combat UI
Display-scale heading and a roomier content column. Use this on the marketing or product home page.
Case study
Two-column layout: content on the left, aside on the right at ≥ 56rem, stacked
below.
Promo
Flips surface and text tokens for emphasis. Pair with
data-variant="full-bleed" for a hero band.
Status
Title and copy align to the center axis.
| 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.
Use the class="cui-cta cui-surface cui-stack" block for conversion sections that need simple, split, full-bleed, or
sticky placement.
Next step
Simple CTAs center the message and action for focused sections.
Build faster
Split CTAs keep action groups aligned beside the message on wider screens.
Launch
Full-bleed CTAs create a section-level break without adding a separate wrapper.
Inverse full bleed
The data-tone="inverse" treatment can be combined with any
data-variant.
Use them sparingly for application flows, demos, and campaign signup pages.
| 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 |
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.
Feature cards keep icon, heading, and copy spacing consistent across marketing pages.
The grid adapts from single-column mobile layouts to dense desktop sections.
Grid width, gap, and card padding can be tuned with `--cui-*` variables.
A reusable content model for event, product, and recruitment campaigns.
Composable sections for service pages, proof blocks, and conversion flows.
Use general cards for resource lists, article collections, and compact page modules.
The classes do not depend on shadow DOM, so generated markup stays readable.
Surface, border, text, and shadow values follow the active theme tokens.
| 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 |
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>.
A media-led card pairs a leading visual with eyebrow, title, copy, and an action group.
Editorial
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
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.
Campaign
Overlay cards layer copy on top of a media background with a configurable scrim.
Centered
Use data-align="center" and data-scrim="solid" for poster-style blocks.
| 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 |
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.
Staff engineer
Design lead
PM
QA engineer
Chief technology officer
Two-sentence placeholder bio. The second sentence is here to fill a second line for the balanced grid demo.
Founder & CEO
Technology
Staff engineer
QA engineer
Marketing
PM · Growth
Use data-layout="horizontal" for deep hierarchies that read better left-to-right,
or for narrow viewports where vertical trees overflow.
CEO
CTO
CMO
PM · Growth
| 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-person — flat 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.
|
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.
Two-sentence excerpt placeholder. The featured variant uses a wider media frame and a larger title so it can lead a section.
Category placeholder
The row orientation pairs media beside body. The flex-wrap basis collapses to a stack on narrow containers.
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.
Category placeholder
Category placeholder
Category placeholder
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.
Design
Engineering
Product
Design
Engineering
Product
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 }.
| 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.
|
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.
Two-hour workshop on modern layout primitives — grid, flex, container queries — with a take-home exercise set and feedback on submissions.
Conference
Talks from product, design, and engineering teams shipping component libraries at scale. Featured variant uses a larger date block and title.
Meetup
Cancelled — venue lost power the morning of the event. Refunds processed via the original payment method within five business days.
You talk through what's happening in the city. Reports come in from residents and businesses, and the team splits them up between you.
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.
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.
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.
Feedback loops, reporting, and admin. It takes about half an hour at the end of each day.
Off home, with enough energy left for the rest of your life.
| 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 7–22. |
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 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
Questions about your account or a live project? We reply within one business day.
Press
For interviews, brand assets, and partnership enquiries.
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.
| 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) |
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.
Houd toezicht in de wijk, ga in gesprek met bewoners en draag bij aan een veilige, leefbare Westwijk.
Je kent de wijk en maakt het verschil.
Als wijkBOA werk je contextgericht: je ziet wat er speelt, spreekt mensen aan en pakt door waar het nodig is.
Die bijdraagt aan een veilig Molenlanden.
Je bent zichtbaar in de kernen, werkt samen met politie en partners en houdt de buurt veilig en leefbaar.
Aanspreekpunt voor de wijk, dichtbij de mensen.
Een afwisselende functie waarin je veel buiten bent en echt het verschil maakt voor bewoners en ondernemers.
Openbare Orde en Veiligheid
Houd toezicht in de wijk, ga in gesprek met bewoners en maak het verschil voor een veilige, leefbare Westwijk.
Je loopt dagelijks je ronde door de Westwijk. Je bent zichtbaar, aanspreekbaar en signaleert wat er speelt — van leefbaarheid tot verkeersveiligheid.
Je bent benaderbaar, luistert actief en blijft kalm onder druk. Een ronde lopen schrikt je niet af — je bent graag buiten en in beweging.
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.
| 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 |
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.
| 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) |