Plain HTML/CSS/JS implementation of the NHS National Paediatric Early Warning System (NPEWS) observation chart. No framework, no build step, no dependencies.
This repository provides a bounded scoring and charting component for integration into a wider clinical system. It is not an EPR workflow or a complete SPOT/NPEWS solution and does not independently claim National PEWS compliance. See the product and compliance boundary.
Clinical safety status, known hazards, transferred controls, and release limitations are documented in SAFETY.md. No clinically approved release has been designated.
- Docker and Docker Compose (for running services)
# Start the demo app (http://localhost:8000)
s/up
# Equivalent - start only the demo app
s/up demo
# Start the SMART-on-FHIR sandbox (NPEWS app + local HAPI R4 FHIR server
# seeded with demo patient data + SMART launcher, patient browser,
# FHIR viewer, control panel). Opens the launcher with the NPEWS
# app prefilled - one click lands you on the chart.
# See smart/README.md for what SMART-on-FHIR is and how to use it.
s/up smartPress Ctrl+C to stop the services. Docker Compose will automatically clean up containers. s/down smart tears down the FHIR sandbox stack; s/down (no arg) tears down everything.
A live demo is deployed to GitHub Pages from main:
https://rcpch.github.io/digital-pews/
This is the full demo harness with the scenario sidebar. It updates automatically on every push to main.
- Serves at http://localhost:8000
- Uses live-server for automatic reload
- Volume-mounted from
chart/anddemo/ - Open http://localhost:8000/demo.html for the demonstration harness (left sidebar of example patients); http://localhost:8000/ renders a single chart.
The chart is a props-driven visualisation component: given a patient and a list of observations it renders into a fixed set of DOM hooks. Data is kept separate from presentation - the chart never carries a specific patient in its markup, and it computes all PEWS scores itself (see below).
The chart/ component is loaded as ES modules (no build step):
chart-shell.js- the shared chart DOM scaffold (mountChartShell(host)), used by both the standalone page and the demo harness so they can't drift.chart.js- the rendering engine. Importrenderand callrender({ patient, observations }). It imports its own dependencies:npews-scoring-config.js- age bands, scoring thresholds and age-band bounds.npews-scorer.js- computes the PEWS score, escalation level and applicable age band for every observation from the patient's date of birth. Scores are always computed, never hand-typed - any score in the input data is ignored.age-band.js- pure date maths for resolving the age band(s) an admission spans (supports seamless charting across a birthday boundary).
styles.css- all styling (colours, layout).
Two entry points render the chart (both in demo/):
demo/index.html- a single standalone chart. Loadsdemo-data.js(which publisheswindow.PATIENT/window.OBSERVATIONS), mounts the shell, then callsrender.demo/demo.html- the demonstration harness. A left sidebar lists example patients fromscenarios.js; selecting one mounts a fresh shell and passes the scenario torender({ patient, observations }). This replaced Storybook.
The chart is also packaged as a framework-neutral <npews-chart> custom element
so it drops into any host - a plain page, React/Angular/Vue, or a SMART-on-FHIR
app - with no framework dependency. The element self-provisions its stylesheet and
fonts, so a consumer only loads one module and feeds it a JSON object:
<script type="module" src="npews-chart.js"></script>
<npews-chart id="chart"></npews-chart>
<script type="module">
document.getElementById('chart').data = {
patient: {
name: 'Alex Thompson',
dob: '2017-03-14',
// ...nhsNumber, ward, bed, consultant
},
observations: [
// raw vitals only - no scores; the engine computes them
{ timestamp: '2025-01-10T08:00:00Z', respiratoryRate: 20, spo2: 99, /* ... */ },
// ...
],
};
</script>- Data is passed as a JS property (
.data), not an attribute, because it is a rich object. Convenience setters.patientand.observationsare also available. - Presentation stays separate from clinical data. Set
.options = { showDemographics: false }when the embedding host already provides patient identification; the standalone default istrue. - Scores are always computed from the patient's date of birth and the raw observations - any score in the input is ignored (single source of truth).
- See
chart/example.htmlfor a minimal, fully self-contained drop-in (no demo dependencies, inline JSON data) anddemo/embed-example.htmlfor a version that imports from the demo scenario catalogue.
ES modules require an HTTP server. Opening these files directly with
file://will fail with a CORS error. Runs/up(ors/up demo) and visithttp://localhost:8000/chart/example.htmlorhttp://localhost:8000/embed-example.html. Or use any web server you like (e.g.npx live-server --port=8000orpython3 -m http.server 8000).
Phase 1 limitation: the engine uses fixed DOM ids, so one chart per document is
supported today; embed additional charts in separate iframes. Shadow-DOM isolation,
multiple instances per page, and an NPM/UMD + Subresource-Integrity CDN bundle with
TypeScript prop types are Phase 2 (see spec/roadmap.md).
Three modes are supported, selectable via the toolbar toggle in the UI:
| Mode | Trigger | Chart height |
|---|---|---|
| Landscape | >1200px or manual | 140px |
| Portrait | 768-1199px or manual | 90px |
| Mobile | <768px or manual | 70px |
Auto-detection runs on load. The toolbar lets you override manually. To lock a layout in code, set data-lock-layout="landscape" (or portrait/mobile) on the <body> tag.
Four age bands are defined, each with different y-axis ranges, scoring thresholds, and header colours:
| Band | Label | Header colour |
|---|---|---|
0-11m |
0 to 11 months | Pink |
1-4y |
1-4 Years | Orange |
5-12y |
5-12 Years | Yellow |
13+y |
13+ Years | Grey |
All four age bands have demonstration scenarios in demo/scenarios.js
(shown in the demo.html sidebar), including a birthday-crossing scenario where a
child turns 5 mid-admission and the chart seamlessly joins the 1-4y and 5-12y
bands. demo-data.js holds the two full-day datasets reused by those scenarios.
The primary reference image is reference-sources/images/chart-5-12-years-1.png. Use test-output/VISUAL_COMPARISON.md to track which visual elements have been matched against the NHS paper chart.
When working on visual fidelity, keep the reference image and the running chart open side by side.
Lato (from Google Fonts) is the default fallback. If you have a Frutiger licence, override the CSS variable in your local stylesheet:
:root {
--font: 'Frutiger', 'Frutiger LT Std', Lato, sans-serif;
}Canvas text rendering uses the chartFont(size, weight) helper in chart.js, which reads --font at call time so the override takes effect automatically.
Demonstration scenarios (the example patients in the demo.html sidebar) live in
demo/scenarios.js. Add a plain object to the SCENARIOS array:
{
id: 'my-scenario', // unique, used in the URL hash (#my-scenario)
title: 'Patient Name', // sidebar label
ageBand: '5-12y', // shown as a chip
description: 'One line describing the clinical picture.',
patient: { name: 'Patient Name', dob: '2018-05-01', /* ... */ },
observations: [ /* raw vitals only - NO pewsTotal / escalationLevel */ ],
}Observations carry raw vital signs only; the chart computes the PEWS score, escalation level and applicable age band from the patient's date of birth. The sidebar hot-reloads on save (live-server).
Start at spec/README.md — the spec index and landing page.
spec/spot-npews-ui-spec.md/spec/spot-npews-spec.md- NHS SPOT/NPEWS specification (transcribed from the reference.xlsx)spec/npews-scoring-spec.json- canonical scoring spec (source of truth);spec/npews-scoring-tables.generated.md- generated reference table;spec/npews-scoring.md- clinical narrativespec/data-model.md- the Patient + Observation input shapes and data-entry codesspec/escalation.md- escalation levels, triggers, sepsis and ISBARspec/fhir.md- FHIR adapter + conformance testingspec/decisions.md- durable decision log;spec/implementation-notes.md- implementation notes + QA checklist;spec/roadmap.md- roadmapreference-sources/images/- PNG exports of the NHS NPEWS paper charts for each age bandreference-sources/*.pdf- source PDFs and specifications
Source code is licensed under LGPL-3.0-or-later. Documentation and clinical content are licensed under CC-BY-SA-4.0. The NHS SPOT/NPEWS specification and reference chart images remain the property of NHS England.