What's new Report an issue
GIG Cymru NHS Wales, Digital Health and Care Wales

Get the files

The actual stylesheet and script to put in an application. Reading a component's source on this site shows you what it is; these are the files that make it work.

The short version

Download single-record.css, put it in your project, and link it. That is the whole of the minimum.

<link rel="stylesheet" href="/css/single-record.css">

It contains the font, every design token, the typography utilities and all 26 component stylesheets, so nothing else has to be fetched or configured. Now any markup you copy from a component page on this site will look right.

No npm, no build step, no network. Roboto is embedded in the stylesheet itself, so there is no font request to be blocked by a firewall or proxy. If npm install is failing on your machine, this route works regardless — and it is worth knowing that the design system adds no third-party dependencies of its own, so whatever is failing is in the toolchain rather than in these packages.

One thing to watch: opening the page straight from disk with file:// works for everything except the sprite icons, where a cross-file <use> is blocked and fails silently. Serve the folder over HTTP, or use icons.js instead.

The files

FileWhat it isDo you need it?
single-record.css Font, tokens, typography utilities and every component, in one file. Yes. Start here.
foundations.css Font, tokens and typography utilities — no component styles. If you use the React package. Each component brings its own stylesheet, so this is the other half.
single-record-dark.css Dark-mode token overrides. Load it after the file above. Only if your product supports dark mode. These values are still provisional.
icons.js The icon set as an ES module: iconMarkup(name) returns the SVG. If you are building markup in JavaScript.
sprite.svg The same icons as one SVG sprite, referenced with <use>. If you are writing plain HTML or Razor, with no JavaScript.
downloads/components/<name>.css One component's stylesheet on its own. Only if you are adopting a single component and cannot take the whole file.

These files are generated from the same source this website renders, every time the site is built. There is no hand-maintained copy to fall out of date — but equally, they are a snapshot: re-download after a release rather than assuming what you have is current.

Using an icon

Icons are the only part of the HTML layer that is not plain markup, because the set is generated. Two ways to place one, and neither needs a build step:

<!-- With the sprite: no JavaScript at all. -->
<span class="sr-icon sr-icon--sm">
  <svg><use href="/assets/sprite.svg#icon-nav-search"></use></svg>
</span>

The sprite has to be served over HTTP from your own origin. Browsers refuse a cross-file <use> reference on a page opened straight from disk (file://) or from another domain, and it fails silently — the icon is simply absent. If your icons do not appear, check that first before checking the markup.

Where the JavaScript is

Mostly, there isn't any, and that is deliberate. The HTML layer is markup and CSS: a button is a <button>, a table is a <table>. Behaviour that genuinely needs code, such as opening a modal or a date picker, lives in the framework wrappers rather than in a loose script you have to wire up:

How to install it

Four routes. Which one you want depends on what you are building, not on preference — a MAUI app cannot use npm at all, and a plain HTML page has no bundler to assemble packages.

You are buildingRouteNeeds credentials?
React, or anything with a bundler1 — npmNo
Plain HTML, Razor, ASP.NET2 — download the filesNo
Any of the above, on a machine that cannot reach npmjs.org3 — release tarballsNo
.NET MAUI, or Blazor4 — NuGetYes

Installing this repository from GitHub does not work, and any guide telling you to do so is out of date. This is a monorepo: the thing at the repository root is a private workspace container, not a package. Both of these fail, and they fail in a confusing way rather than with a clear error:

# Installs ONE package called @dhcw/sr-design-system.
# @dhcw/sr-react is not in node_modules afterwards.
npm install github:DHCW-Digital-Health-and-Care-Wales/single-record-design-system#main

# Installs the same repository root under a different name. The package it
# fetches is private, has no entry point, and importing from it throws
# ERR_MODULE_NOT_FOUND.
"@dhcw/sr-react": "github:DHCW-Digital-Health-and-Care-Wales/single-record-design-system#main"

npm has no way to install a single workspace out of a git repository. Use Route 1 instead — and if you have resolve.alias entries in a Vite config pointing inside node_modules/@dhcw to work around this, you can delete them.

Route 1 — npm

The packages are on npmjs.org. No registry configuration, no .npmrc, no token — a plain npm install works on your machine and in CI alike.

Installing for the first time. All four together — the React package depends on the other three, so installing it alone fails:

npm install @dhcw/sr-tokens @dhcw/sr-icons @dhcw/sr-web @dhcw/sr-react

Updating later. Nothing to edit. Your package.json carries a version range, so:

npm update @dhcw/sr-tokens @dhcw/sr-icons @dhcw/sr-web @dhcw/sr-react

That is the whole difference between this route and the tarballs below: there, a new version means editing eight values by hand.

The changelog says whether a release needs anything from you. Most do not.

Route 2 — Download the files (no build step at all)

Take single-record.css and sprite.svg from the table above and link them directly. This is the route for plain HTML, Razor, ASP.NET, or a React app where you write the markup and take the styling from the design system.

Everything on this site's component pages is plain markup with sr- classes, so the stylesheet alone gets you a correct-looking component in any framework. What you do not get is the React component wrappers — those need Route 2.

Route 3 — npm, from a release tarball

Route 1 is easier — use this one only if npmjs.org is unreachable from your build machine, or if a policy pins you to artefacts fetched from a known URL. Each release attaches the packages as tarballs, which plain npm installs directly from a GitHub release rather than from a registry.

The cost is that a version is baked into eight URLs, so updating means editing all of them and re-running npm install. The one-liner in each release's notes does the rewrite for you; doing it by hand is where the 404s come from, because the version appears twice per dependency.

Add all four to package.json and run npm install:

"dependencies": {
  "@dhcw/sr-tokens":  "https://github.com/DHCW-Digital-Health-and-Care-Wales/single-record-design-system/releases/download/v0.3.0/dhcw-sr-tokens-0.3.0.tgz",
  "@dhcw/sr-icons":   "https://github.com/DHCW-Digital-Health-and-Care-Wales/single-record-design-system/releases/download/v0.3.0/dhcw-sr-icons-0.3.0.tgz",
  "@dhcw/sr-web":     "https://github.com/DHCW-Digital-Health-and-Care-Wales/single-record-design-system/releases/download/v0.3.0/dhcw-sr-web-0.3.0.tgz",
  "@dhcw/sr-react":   "https://github.com/DHCW-Digital-Health-and-Care-Wales/single-record-design-system/releases/download/v0.3.0/dhcw-sr-react-0.3.0.tgz"
}

All four are needed together. The React package depends on the other three, so installing it alone will fail. Take the tag from the releases page and keep it pinned; bump it deliberately rather than tracking a moving branch.

Then import what you need. These paths are verified against each package's exports on every release — a release that cannot be installed and imported does not publish:

Import foundations, not single-record.css, when you are using the React package. Every React component already imports its own stylesheet, so loading the complete file on top of that ships all 21 component stylesheets plus a second copy of each one you actually use. On a screen using seven components that is 238KB rather than 131KB — the same styling, 45% less of it.

You write the same one line either way; only the target changes. Plain HTML has no bundler to assemble the pieces, so it keeps using single-record.css.

<!-- Route 1 (download) is the equivalent for plain HTML — see above. -->

Route 4 — NuGet, for .NET

npm cannot serve a .NET app. npm is JavaScript-only and there is no npm in the .NET toolchain, so none of the packages above can be installed into a MAUI or Blazor project. .NET has its own two packages, versioned in lockstep with the npm ones so that one version number describes the whole design system:

PackageForShips
DHCW.SingleRecord.MauiNative MAUI XAMLThree ResourceDictionaries and the brand marks
DHCW.SingleRecord.BlazorBlazorThe stylesheets, as static web assets

Both are a token and style layer, not a component library. You write your own markup or XAML and take the styling from the design system, the same way a plain HTML consumer does.

This feed needs authentication, and that is deliberate. GitHub Packages requires a token for every install, including public packages. The MAUI package carries the NHS Wales and DHCW brand marks, which are trademarked artwork not covered by its MIT licence, so a feed that cannot be read anonymously is the right place for it. Reasoning in DDR‑024.

Both packages: the feed

1. Add a nuget.config beside your solution. A classic personal access token with read:packages is enough to install. The %GITHUB_PACKAGES_TOKEN% form reads an environment variable, so this file is safe to commit and your token is not in it — set the variable from a secret in Azure DevOps, or in your shell profile locally.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="dhcw" value="https://nuget.pkg.github.com/OWNER/index.json" />
  </packageSources>
  <packageSourceCredentials>
    <dhcw>
      <add key="Username" value="YOUR_GITHUB_USERNAME" />
      <add key="ClearTextPassword" value="%GITHUB_PACKAGES_TOKEN%" />
    </dhcw>
  </packageSourceCredentials>
</configuration>

2. Install whichever you need.

dotnet add package DHCW.SingleRecord.Maui
dotnet add package DHCW.SingleRecord.Blazor

Updating is dotnet add package again, or bumping the Version on the PackageReference. Both packages move together, so take the same version for both.

MAUI

Targets net10.0-android, net10.0-ios and net10.0-maccatalyst. net10.0 matches what this repository builds with; if your app targets an earlier .NET it can be lowered. It ships three ResourceDictionaries and the brand marks:

DictionaryContents
SrColorsEvery primitive and semantic colour token, the two elevation shadows, and a …Dark twin for the semantics that change with the theme
SrIconsThe icon set as XAML path geometry, one x:String per icon
SrStylesImplicit and keyed control styles, the StyleClass type scale, and VisualStateManager states

Merge them as shown in the MAUI tab above. Order matters: SrStyles references keys from the other two, so merging it first fails at runtime rather than at build. If your app already has its own Colors.xaml, merge yours after the Single Record ones so yours win where they overlap.

Icons are geometry, not images. MAUI rasterises an SVG at build time through MauiImage and bakes the colour into the PNG. These are outline icons that have to take their colour from a token and follow the theme, so they ship as Path data:

<Path Data="{StaticResource SrIconNavSearch}"
      Stroke="{AppThemeBinding Light={StaticResource SrColorTextPrimary},
                               Dark={StaticResource SrColorTextPrimaryDark}}"
      StrokeThickness="1" StrokeLineCap="Round" StrokeLineJoin="Round"
      Aspect="Uniform" HeightRequest="24" WidthRequest="24" />

Fill is deliberately unset — filling closes shapes meant to read as strokes. StrokeThickness="1" is correct at a 24px render; MAUI's StrokeThickness does not scale with Aspect the way SVG stroke-width scales inside a viewBox, so scale it down proportionally at smaller sizes.

Brand marks come with it, registered as MauiImage in your app automatically. Reference the rasterised output, not the source: <Image Source="srsymwcpblue.png" />. sym is the icon-only mark, logo the full lockup — different artwork, not a crop of one another. Each mark ships one file per ink because brand artwork must never be recoloured; pick the variant that suits the background. Opt out with <SrIncludeBrandImages>false</SrIncludeBrandImages>.

Copying the XAML by hand still works. Take Colors.xaml, Icons.xaml and Styles.xaml from packages/maui, add them as MauiXaml, and merge them by Source instead of by type — the copies are plain dictionaries with no x:Class, so the <sr:SrColors /> form will not compile against them. Same order rule applies.

Blazor

DHCW.SingleRecord.Blazor ships the stylesheets as static web assets, so a Blazor host references them by URL rather than importing anything. Add these to App.razor or _Host.cshtml:

<link rel="stylesheet" href="_content/DHCW.SingleRecord.Blazor/css/single-record.css" />

<!-- Dark mode is opt-in and must load SECOND: it overrides the tokens above. -->
<link rel="stylesheet" href="_content/DHCW.SingleRecord.Blazor/css/single-record-dark.css" />

Then set data-theme="dark" on <html> to switch, and write markup with the same sr- classes every other framework uses:

<button class="sr-button sr-button--primary" @onclick="Confirm">Confirm patient</button>

The package carries the same files as Route 2's download, from the same build, so Blazor and web cannot drift:

PathContents
css/single-record.cssEverything: font, tokens, type utilities, all components
css/single-record-dark.cssDark-mode overrides. Opt-in, loaded second
css/foundations.cssTokens and type only, no components
css/sprite.svgThe icon set, for <use> references
css/components/*.cssOne component at a time

There are no Razor components in the package, and that is deliberate. It is a style layer, matching what the MAUI package does. Shipping one wrapped component out of twenty-one would imply a set that does not exist — you would still be writing your own markup for the other twenty.

Working in a checkout of the repository itself (rather than as a dependency)? Clone the org repo, then:

npm install
npm run build:web    # writes packages/web/dist/

The package name and its contents are the same either way — installing from GitHub today and from npm once it is published are the same package at the same version. Only the install command changes.