Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
33d4692
feat: add nextjs package
Jul 23, 2026
a30767d
fix: revert components export .. from
Jul 23, 2026
320d44b
fix: remove unneeded linting packages from nextjs package
Jul 23, 2026
817625a
feat: add PageBody and PageFooter
Jul 24, 2026
f0e053b
Merge branch 'main' into feat/setup-nextjs-package
Jul 24, 2026
c4838de
fix: remove duplicate styleLint config
Jul 24, 2026
cb00301
feat: update homepage with content
Jul 27, 2026
d8738b8
chore: update @types/node to v24
Jul 27, 2026
7f276dd
chore: remove unneeded global css
Jul 27, 2026
463de29
Update packages/nextjs/package.json
eelcobosklopper Jul 27, 2026
8023116
Merge branch 'feat/setup-nextjs-package' into feat/add-nextjs-homepage
eelcobosklopper Jul 27, 2026
710e1c9
chore: remove next-env.d.ts
eelcobosklopper Jul 27, 2026
6a374d1
chore: add next-env.d.ts to gitignore
eelcobosklopper Jul 27, 2026
4d91244
feat: add footer and logo
eelcobosklopper Jul 28, 2026
c84c9b8
Merge branch 'feat/add-nextjs-homepage' into feat/setup-nextjs-package
eelcobosklopper Jul 30, 2026
dfd79e8
feat: add distDir nextjs config and cleanup old .next references
eelcobosklopper Jul 30, 2026
d6f8784
fix: remove unrs-resolver from allowBuilds pnpm workspace
eelcobosklopper Jul 30, 2026
5447ac1
fix: grid cell span all
eelcobosklopper Jul 30, 2026
ebc91c9
feat: replace ma-theme with start-theme design tokens
eelcobosklopper Jul 31, 2026
03fb9d1
feat: update content homepage
eelcobosklopper Aug 3, 2026
59e5592
fix: move codeblock out of paragraph
eelcobosklopper Aug 3, 2026
e716008
chore: remove comments from tsconfig
eelcobosklopper Aug 3, 2026
beadaf6
chore: remove eslint config for jsonc
eelcobosklopper Aug 3, 2026
6acae05
feat: add detailpage and add navbar
eelcobosklopper Aug 4, 2026
81f930b
feat: update detail page content
eelcobosklopper Aug 4, 2026
1d108ae
feat: move css vars utrecht-page-header to layout.css
eelcobosklopper Aug 4, 2026
3102942
feat: replace utrecht-nav-bar with rijkshuisstijl-community/navigatio…
eelcobosklopper Aug 5, 2026
2f5d84d
feat: add css vars for navbar homepage
eelcobosklopper Aug 5, 2026
a51e68b
feat: add unoderedList component
eelcobosklopper Aug 5, 2026
883f41f
feat: update content
eelcobosklopper Aug 5, 2026
d7780ce
feat: move nextjs project to apps folder and rename to next
eelcobosklopper Aug 5, 2026
2199d98
fix: replace ReactNode with PropsWithChildren
eelcobosklopper Aug 5, 2026
2d185c3
feat: update title
eelcobosklopper Aug 5, 2026
fe017b2
feat: add clean script and cleanup scripts
eelcobosklopper Aug 5, 2026
7c50751
Merge branch 'feat/setup-nextjs-package' into feat/nextjs-detail-page
eelcobosklopper Aug 5, 2026
26d8d1e
feat: restructure layouts
eelcobosklopper Aug 5, 2026
84ca3bf
fix: remove link from footer logo
eelcobosklopper Aug 5, 2026
445fca7
fix: remove inverse color from footer
eelcobosklopper Aug 5, 2026
920569b
fix: footer cells span all
eelcobosklopper Aug 5, 2026
8e382fe
feat: add detail page to next steps
eelcobosklopper Aug 5, 2026
89abe9c
feat: add next readme
eelcobosklopper Aug 6, 2026
d665555
feat: rename next package to @example/next
eelcobosklopper Aug 6, 2026
4960792
chore: removed comment
eelcobosklopper Aug 6, 2026
24f50dc
fix: rename DefaultLayout
eelcobosklopper Aug 6, 2026
62d38d2
feat: update content homepage
eelcobosklopper Aug 10, 2026
47133e4
feat: add Utrecht Root
eelcobosklopper Aug 12, 2026
2f5dfb0
feat: import css module utrecht lists
eelcobosklopper Aug 12, 2026
f9d4dbf
feat: remove use client by updating import path
eelcobosklopper Aug 12, 2026
b66e254
fix: remove aria-label from nav due to a11y issues
eelcobosklopper Aug 12, 2026
212b924
feat: add form page
eelcobosklopper Aug 12, 2026
9931a14
feat: add spacing to radiobuttons
eelcobosklopper Aug 12, 2026
74ba702
chore: lint fix
eelcobosklopper Aug 12, 2026
a859eeb
chore: update nextjs react and react-dom
eelcobosklopper Aug 12, 2026
dca49ec
docs: update readme with why nextjs
eelcobosklopper Aug 12, 2026
cb78e86
Apply suggestions from code review
eelcobosklopper Aug 17, 2026
04169b7
feat: stylelint disallow gap
eelcobosklopper Aug 17, 2026
97d1798
feat: replace gaps with more specific gaps
eelcobosklopper Aug 17, 2026
04a772c
Apply suggestion from @Robbert
eelcobosklopper Aug 18, 2026
57d6f74
feat: replace element code with component Code
eelcobosklopper Aug 17, 2026
9fe770c
feat: add method to form so data doesnt end up in url
eelcobosklopper Aug 17, 2026
a073131
fix: move error to be above field
eelcobosklopper Aug 17, 2026
5874a79
feat: add note component
eelcobosklopper Aug 17, 2026
01de269
fix: use basis tokens
eelcobosklopper Aug 18, 2026
1b30e08
feat: replace utrecht button with candidate button
eelcobosklopper Aug 18, 2026
49d3a69
fix: rename radio to radiobutton
eelcobosklopper Aug 18, 2026
ac0a7d3
docs: update link
eelcobosklopper Aug 18, 2026
09ad401
feat: add actionGroup
eelcobosklopper Aug 18, 2026
7852485
feat: add perfectionist group sorting config and apply it
eelcobosklopper Aug 19, 2026
2cc6725
feat: rename Hint to FormFieldDescription
eelcobosklopper Aug 19, 2026
c979040
fix: rename Label to FormLabel
eelcobosklopper Aug 19, 2026
50faf39
feat: move navItems outside of Navbar
eelcobosklopper Aug 19, 2026
aa8237a
feat: move layout specific implementations from components to layout …
eelcobosklopper Aug 19, 2026
f1c409f
feat: add utrecht-page-layout class to body
eelcobosklopper Aug 19, 2026
d41edbb
feat: replaced radio and fieldset with radioGroup component
eelcobosklopper Aug 19, 2026
bc9f0f2
fix: FormField naming fix
eelcobosklopper Aug 26, 2026
cdcf6f4
feat: update base readme and improve pnpm next setup
eelcobosklopper Aug 26, 2026
6a8c6ed
feat: add componentReference to form fields
eelcobosklopper Aug 26, 2026
ad9245d
feat: add developer notes pane, piloted on the detail page
eelcobosklopper Aug 27, 2026
311d758
feat: add devPane to homepage
eelcobosklopper Aug 31, 2026
397402e
fix: devPane bottom margin
eelcobosklopper Aug 31, 2026
faa6fda
feat: add devNotes to form page
eelcobosklopper Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,6 @@ pnpm-lock.yaml

# Ignore generated files from Stencil
components.d.ts

# Ignore generated files from Next.js
next-env.d.ts
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ This design system is based on the NL Design System architecture.

For more info about the NL Design System and learn about things happening in our open source community, join the `#nl-design-system` Slack via [praatmee.codefor.nl](https://praatmee.codefor.nl)!

## Projects in this repository

This repository contains two independent projects:

### Design system packages & Storybook

The `packages/*` and `proprietary/*` directories contain this design system's own component packages (CSS, React, web components) and their documentation, published to npm and browsable via Storybook.

- Install dependencies: `pnpm install`
- Run storybook: `npm run storybook`

### Next.js example app

`apps/next` is a Next.js application that shows how to discover and use components from the NL Design System in a real app. It is **not** built from this repository's own packages above. It consumes published packages from the wider NL Design System ecosystem (`@nl-design-system-candidate`, `@utrecht/component-library-react`, `@amsterdam/design-system-react`). The two projects live in the same repository but demonstrate different things.

See [apps/next/README.md](apps/next/README.md) for setup, available scripts, and how to use the example.

---

✂ In your own repository: **remove the "Getting started" section below!**
Expand Down
91 changes: 91 additions & 0 deletions apps/next/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<!-- @license CC0-1.0 -->

# NL Design System Next.js example

A Next.js application that shows how to discover and use components from the [NL Design System](https://nldesignsystem.nl)
in a real app. The homepage deliberately mixes components from multiple implementations
(`@nl-design-system-candidate`, `@utrecht/component-library-react`, and `@amsterdam/design-system-react`), and [the detail page](http://localhost:3000/detail) shows how to apply a different theme to just part of a site.

## Setup

### Prerequisites

- [Node.js](https://nodejs.org/)
- [pnpm](https://pnpm.io/)

### Install

Run the install from the root of the monorepo so all workspace packages are linked correctly:

```sh
pnpm install
```

### Run the dev server

From this directory (`apps/next`):

```sh
pnpm dev
```

Or from the monorepo root:

```sh
pnpm --filter @example/next dev
```

The app is served at [http://localhost:3000](http://localhost:3000).

## Available scripts

| Script | Description |
| -------------- | ------------------------------------------------ |
| `pnpm run dev` | Start the Next.js dev server |
| `pnpm build` | Build the app for production (output in `dist/`) |
| `pnpm start` | Start the production server (run `build` first) |
| `pnpm clean` | Remove the `dist/` output directory |

## How to use this project

- Start on the homepage (`app/(default)/page.tsx`). It explains the NL Design System's maturity model
(Hall of Fame, Candidate, Community, Help wanted, Discouraged) and walks through the steps to install and use a
component in a React app.
- Each component under `components/` (e.g. `components/Heading`, `components/Link`, `components/Alert`) wraps a
single NL Design System component and applies those install/import steps.
- Visit `/detail` to see how to theme part of a site differently: it lives in its own route group
Comment thread
eelcobosklopper marked this conversation as resolved.
(`app/(detail)/`) with its own layout that installs a different design tokens package and applies a different
theme class, instead of the app-wide `start-theme`.
- Browse [nldesignsystem.nl/componenten](https://nldesignsystem.nl/componenten/) to discover which components exist
and in which implementations before building your own.

## Adding a new component

1. Pick an implementation (e.g. `@nl-design-system-candidate`, `@utrecht/component-library-react`,
`@amsterdam/design-system-react`) from [nldesignsystem.nl/componenten](https://nldesignsystem.nl/componenten/).
2. Install the React component and its CSS package, e.g.:

```sh
pnpm add @nl-design-system-candidate/heading-react @nl-design-system-candidate/heading-css
```

3. Import the component's CSS once, and import/render the component — see any file in `components/` for a working
example.

## Changing the theme

A theme is a design tokens package plus a class name applied to an element (usually `<html>`). See
`app/(detail)/layout.tsx` for an example that scopes a theme to a single route group, and `app/(detail)/detail/page.tsx`
for the full explanation.

## Why Next.js?

This example uses [Next.js](https://nextjs.org/) because it's one of the most widely adopted React frameworks, so
its patterns will look familiar to most teams evaluating the NL Design System. The same NL Design System components work in any React setup, but it's a
convenient, widely-known host for demonstrating them.

## Related

- [Root repository README](../../README.md) — monorepo setup, contributing, and license information
- [Storybook for this project](https://nl-design-system.github.io/example/)
- [nldesignsystem.nl](https://nldesignsystem.nl)
49 changes: 49 additions & 0 deletions apps/next/app/(default)/default.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.start-theme {
/* Page Header */
--utrecht-page-header-background-color: var(--basis-color-accent-1-inverse-bg-default);
--utrecht-page-header-content-background-color: var(--basis-color-accent-1-inverse-bg-default);
--utrecht-page-header-content-color: var(--basis-color-accent-1-bg-default);

/* Navbar */
--rhc-nav-bar-background-color: var(--basis-color-default-bg-default);
--rhc-nav-bar-border-color: var(--basis-color-accent-1-color-document);
--rhc-nav-bar-color: var(--basis-color-accent-1-color-document);
--rhc-nav-bar-container-inline-size: 100%;
--rhc-nav-bar-content-column-gap: var(--basis-space-inline-xl);
--rhc-nav-bar-heading-font-weight: var(--basis-text-font-weight-default);
--rhc-nav-bar-icon-active-color: var(--basis-color-accent-1-color-active);
--rhc-nav-bar-icon-color: var(--basis-color-accent-1-color-document);
--rhc-nav-bar-link-active-background-color: var(--basis-color-accent-1-color-document);
--rhc-nav-bar-link-active-color: var(--basis-color-accent-1-color-active);
--rhc-nav-bar-link-background-color: var(--basis-color-accent-1-color-document);
--rhc-nav-bar-link-color: var(--basis-color-accent-1-color-document);
--rhc-nav-bar-link-column-gap: var(--basis-space-block-lg);
--rhc-nav-bar-link-focus-color: var(--basis-color-accent-1-color-document);
--rhc-nav-bar-link-font-size: var(--basis-text-font-size-md);
--rhc-nav-bar-link-hover-background-color: var(--basis-color-default-bg-hover);
--rhc-nav-bar-link-hover-color: var(--basis-color-accent-1-color-document);
--rhc-nav-bar-link-padding-block-end: var(--basis-space-block-lg);
--rhc-nav-bar-link-padding-block-start: var(--basis-space-block-lg);
--rhc-nav-bar-link-padding-inline-end: var(--basis-space-inline-xl);
--rhc-nav-bar-link-padding-inline-start: var(--basis-space-inline-xl);
--rhc-nav-bar-max-inline-size: var(--basis-page-max-inline-size);
--rhc-nav-bar-padding-inline: var(--basis-space-inline-xl);
--rhc-text-font-family-default: var(--basis-text-font-family-default);

/* Note */
--utrecht-note-info-color: var(--basis-color-info-color-document);
--utrecht-note-info-background-color: var(--basis-color-info-bg-default);
--utrecht-note-info-border-color: var(--basis-color-info-border-default);
--utrecht-note-info-border-width: var(--basis-border-width-sm);
--utrecht-space-around: var(--basis-space-inline-md);
--utrecht-note-border-radius: var(--basis-border-radius-sm);
--utrecht-note-border-width: var(--basis-border-width-md);
--utrecht-note-row-gap: var(--basis-space-inline-md);
--utrecht-note-padding-block-start: var(--basis-space-inline-xl);
--utrecht-note-padding-block-end: var(--basis-space-inline-xl);
--utrecht-note-padding-inline-start: var(--basis-space-inline-xl);
--utrecht-note-padding-inline-end: var(--basis-space-inline-xl);

/* RadioGroup */
--utrecht-form-field-label-margin-block-end: var(--basis-space-inline-md);
}
20 changes: 20 additions & 0 deletions apps/next/app/(default)/form/form.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.example-form-page {
display: flex;
flex-direction: column;
padding-block: var(--basis-space-block-2xl);
padding-inline: var(--basis-space-inline-xl);
row-gap: var(--basis-space-block-xl);
}

.example-form-page__fields {
display: flex;
flex-direction: column;
row-gap: var(--basis-space-block-2xl);
}

.example-form-page__option {
align-items: center;
column-gap: var(--basis-space-inline-sm);
display: flex;
flex-wrap: wrap;
}
171 changes: 171 additions & 0 deletions apps/next/app/(default)/form/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { ActionGroup } from '@/components/ActionGroup/ActionGroup';
import { Button } from '@/components/Button/Button';
import { Checkbox } from '@/components/Checkbox/Checkbox';
import { Code } from '@/components/Code/Code';
import { ComponentReference } from '@/components/ComponentReference/ComponentReference';
import { ErrorMessage } from '@/components/ErrorMessage/ErrorMessage';
import { FormField } from '@/components/FormField/FormField';
import { FormFieldDescription } from '@/components/FormFieldDescription/FormFieldDescription';
import { FormLabel } from '@/components/FormLabel/FormLabel';
import { Heading } from '@/components/Heading/Heading';
import { Paragraph } from '@/components/Paragraph/Paragraph';
import { RadioGroup } from '@/components/RadioGroup/RadioGroup';
import { Switch } from '@/components/Switch/Switch';
import { TextArea } from '@/components/TextArea/TextArea';
import { TextInput } from '@/components/TextInput/TextInput';
import { DevNote } from '@/dev-pane/DevNote';
import './form.css';

const FormPage = () => {
return (
<div className="example-form-page">
<DevNote
note={
<>
<ComponentReference label="Heading" npmPackage="@nl-design-system-candidate/heading-react" />
<ComponentReference label="Paragraph" npmPackage="@nl-design-system-candidate/paragraph-react" />
<ComponentReference label="Code" npmPackage="@nl-design-system-candidate/code-react" />
</>
}
title="Contact form overview"
>
<Heading level={1}>Contact us</Heading>
<Paragraph>
This page gathers the Utrecht Design System&apos;s form elements into a single contact form: text inputs, a
radio group, a select, a text area, a checkbox, and a toggle. Every component here can render as a Server
Component &mdash; only <Code>Checkbox</Code> needs to run on the client, since it uses React hooks internally
to support an indeterminate state. The page itself is laid out with a plain flex column and token-based
spacing, not the <Code>Grid</Code> component used elsewhere on this site.
</Paragraph>
</DevNote>

<form className="example-form-page__fields" method="POST">
<DevNote
note={
<>
<ComponentReference label="FormField" npmPackage="@utrecht/component-library-react" />
<ComponentReference label="FormLabel" npmPackage="@utrecht/component-library-react" />
<ComponentReference label="Text Input" npmPackage="@utrecht/textbox-css" />
</>
}
title="Name field"
>
<FormField>
<FormLabel htmlFor="name">Name</FormLabel>
<TextInput autoComplete="name" id="name" inputRequired name="name" required />
</FormField>
</DevNote>

<DevNote
note={
<>
<ComponentReference label="FormField" npmPackage="@utrecht/component-library-react" />
<ComponentReference label="FormLabel" npmPackage="@utrecht/component-library-react" />
<ComponentReference label="ErrorMessage" npmPackage="@utrecht/component-library-react" />
<ComponentReference label="Text Input" npmPackage="@utrecht/textbox-css" />
</>
}
title="Email field"
>
<FormField invalid>
<FormLabel htmlFor="email">Email address</FormLabel>
<ErrorMessage id="email-error">Enter a valid email address, such as name@example.com.</ErrorMessage>
<TextInput
aria-describedby="email-error"
defaultValue="not-an-email"
id="email"
inputRequired
invalid
name="email"
required
type="email"
/>
</FormField>
</DevNote>

<DevNote
note={<ComponentReference label="Radio Group" npmPackage="@utrecht/radio-group-react" />}
title="Preferred contact time"
>
<RadioGroup
label="Preferred contact time"
name="contact-time"
options={[
{ label: 'Morning', value: 'morning' },
{ defaultChecked: true, label: 'Afternoon', value: 'afternoon' },
{ label: 'Evening', value: 'evening' },
]}
/>
</DevNote>

<DevNote
note={
<>
<ComponentReference label="FormField" npmPackage="@utrecht/component-library-react" />
<ComponentReference label="FormLabel" npmPackage="@utrecht/component-library-react" />
<ComponentReference label="FormFieldDescription" npmPackage="@utrecht/component-library-react" />
<ComponentReference label="Text Area" npmPackage="@utrecht/textarea-css" />
</>
}
title="Message field"
>
<FormField>
<FormLabel htmlFor="message">Message</FormLabel>
<FormFieldDescription id="message-hint">Optional</FormFieldDescription>
<TextArea aria-describedby="message-hint" id="message" name="message" rows={4} />
</FormField>
</DevNote>

<DevNote
note={
<>
<ComponentReference label="FormField" npmPackage="@utrecht/component-library-react" />
<ComponentReference label="Checkbox" npmPackage="@utrecht/checkbox-css" />
<ComponentReference label="FormLabel" npmPackage="@utrecht/component-library-react" />
</>
}
title="Contact consent field"
>
<FormField>
<div className="example-form-page__option">
<Checkbox id="agree-to-contact" inputRequired name="agree-to-contact" required value="yes" />
<FormLabel htmlFor="agree-to-contact">I agree to be contacted about this request</FormLabel>
</div>
</FormField>
</DevNote>

<DevNote
note={
<>
<ComponentReference label="FormField" npmPackage="@utrecht/component-library-react" />
<ComponentReference label="FormLabel" npmPackage="@utrecht/component-library-react" />
<ComponentReference label="Switch" npmPackage="@utrecht/form-toggle-css" />
</>
}
title="Newsletter field"
>
<FormField>
<FormLabel htmlFor="newsletter">Subscribe to the newsletter</FormLabel>
<Switch id="newsletter" name="newsletter" value="yes" />
</FormField>
</DevNote>

<DevNote
note={
<>
<ComponentReference label="ActionGroup" npmPackage="@utrecht/action-group-react" />
<ComponentReference label="Button" npmPackage="@nl-design-system-candidate/button-react" />
</>
}
title="Submit action"
>
<ActionGroup>
<Button type="submit">Send message</Button>
</ActionGroup>
</DevNote>
</form>
</div>
);
};

export default FormPage;
13 changes: 13 additions & 0 deletions apps/next/app/(default)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { PropsWithChildren } from 'react';
import { Layout } from '@/layout/Layout';
import '@amsterdam/design-system-tokens/dist/index.css';
import '@nl-design-system-unstable/start-design-tokens/dist/theme.css';
import './default.css';

const DefaultLayout = ({ children }: PropsWithChildren) => (
<Layout navbarCurrent="home" theme="start-theme">
{children}
</Layout>
);

export default DefaultLayout;
Loading
Loading