diff --git a/.gitignore b/.gitignore index f79288d9..96f6b154 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/README.md b/README.md index ea54962c..337acaa3 100644 --- a/README.md +++ b/README.md @@ -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!** diff --git a/apps/next/README.md b/apps/next/README.md new file mode 100644 index 00000000..07c52b95 --- /dev/null +++ b/apps/next/README.md @@ -0,0 +1,91 @@ + + +# 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 + (`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 ``). 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) diff --git a/apps/next/app/(default)/default.css b/apps/next/app/(default)/default.css new file mode 100644 index 00000000..783bbe73 --- /dev/null +++ b/apps/next/app/(default)/default.css @@ -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); +} diff --git a/apps/next/app/(default)/form/form.css b/apps/next/app/(default)/form/form.css new file mode 100644 index 00000000..aebb14f9 --- /dev/null +++ b/apps/next/app/(default)/form/form.css @@ -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; +} diff --git a/apps/next/app/(default)/form/page.tsx b/apps/next/app/(default)/form/page.tsx new file mode 100644 index 00000000..800556f1 --- /dev/null +++ b/apps/next/app/(default)/form/page.tsx @@ -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 ( +
+ + + + + + } + title="Contact form overview" + > + Contact us + + This page gathers the Utrecht Design System'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 — only Checkbox 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 Grid component used elsewhere on this site. + + + +
+ + + + + + } + title="Name field" + > + + Name + + + + + + + + + + + } + title="Email field" + > + + Email address + Enter a valid email address, such as name@example.com. + + + + + } + title="Preferred contact time" + > + + + + + + + + + + } + title="Message field" + > + + Message + Optional +