Specification
We are performing a technology spike to iterate on key design decisions needed to be explored before committing to a future development process. Specifically we are:
- Creating a static site which will host health data connect data
- We will use static site generators (SSG) to do this
- We are working towards removing WordPress with a SSG
Because you are working within a technology spike you will be generating multiple iterations of the same solution switching to other technologies and then listing their pros/cons of each one to help with final evaluation.
We will create all iterations of the site and then create a landing page which will allow us to navigate these different static sites.
Aims
- Create a static site which hosts all data currently available within HDC and allows for search and filtering of HDC entries
- Develop a better data model for HDC to integrate into their data capture
- Have a data flow which allows for easy deposition by Alliance members into the new portal
- Automated deployment of site on update
- Close out GAT-7730: [TECHSPIKE] Explore replacing WordPress with markdown+Git with decided outcomes to develop the replacement WordPress deployment
Progress so far, and requirements for subsequent spikes
All three planned SSG iterations are now built: spikes/astro/, spikes/nextjs/ (static export), spikes/eleventy/ — each a full vertical slice (SSG + data model + search + filters + CMS + styling + markdown-page rendering), each with its own EVALUATION.md. See /REPORT.md at the repo root for the formal cross-spike conclusion and recommendation — that document, not this one, is the place to look for "which SSG should we pick and why."
Several artifacts are shared infrastructure across all three iterations, not duplicated per SSG:
/schema/hdc-entry.schema.json+/schema/valuesets/*.md— the data model and identified value sets./data/entries/*.yaml— every converted HDC entry, validated against that schema./data/site-settings.yaml+/schema/site-settings.schema.json— the shared banner singleton, testing the CMS's "turn on/off and edit text + link for a banner" requirement identically in all three spikes.
Every SSG iteration reads directly from /data and /schema rather than keeping its own copy.
The Astro spike also surfaced requirements that weren't explicit in earlier drafts of this document but apply to every iteration, not just Astro:
- One search implementation per site, used everywhere search is offered. The Astro spike initially had two: a full one on a dedicated
/search/page, and a weaker name/description-only filter on the homepage — which meant the "search MUST reach non-displayed attributes" requirement below silently failed on the homepage while passing on/search/. Whatever search library a spike uses, every surface that looks like a search box (a listing page's quick filter included) must be backed by the same index/query, not a separate hand-rolled substring match. - Filters must cover Categories, Readiness tier, and License, and must offer a way to clear all active filters in one action (see "Filters" below — this was tightened after the first Astro pass only had Categories + Readiness tier).
- Styling must be sourced from the live site, with the sourcing documented. Colours, logo, and any per-value visual treatment (e.g. category chip colours, the readiness-tier icon) should be extracted from
ukhealthdata.org's actual stylesheets/assets rather than designed fresh, and the spike's evaluation should record exactly where each token came from (seespikes/astro/src/styles/hdc-theme.css's header comment for the expected level of detail). Where a live-site asset can't be reused for licensing reasons (e.g. a commercial webfont), that must be called out explicitly rather than silently substituted. - HDC's source data is a subset of a full record, permanently — design for that, don't chase completeness. Even HDC's own richest per-entry export (~30 fields) is missing things a full record could have (e.g. almost no entry carries a publication/DOI link, even where one plausibly exists) — this is a limitation of the process that populates the live site, not a temporary gap. Every spike's data handling (schema, UI, search/filter facets) should treat most optional fields as normally absent, not as missing data to backfill or fetch from elsewhere. See
/schema/README.mdand/schema/GAPS.md.
Selecting a suitable CMS/static site engine
Possible CMS/static site options. This must be able to generate content based on submitted data e.g. YAML, can be easily modified by a non-technical individual, a trivial context switch to managing, scale to the needs of HDC (portal, search) and our get rid of WordPress goal (tables, images, videos)
- Astro (no prior experience but a true SSG)
- Next.js using static export (inline with HDR tech stack)
- Eleventy (SSG with experience from previous role at EMBL)
Integrate search
Search engine (in-process JavaScript) to support HDC. Solution needs to offer filters, search over non-displayed attributes, free text, scale to 100s easily within the browser.
- Lunr.js
- FlexSearch
- MiniSearch
- Orama
- Pagefind (can use metatags in a page to hardcode filters - default search engine)
You will need to review the current offering from HDC for the filters and review what has been said below. Ensure the design of the system allows us to iterate on each of these as individual loaders for the search. Potentially have a separate instance of the SSG to provide this functionality.
Search MUST be able to search within a document NOT limited to what is provided in the first page of the interface e.g. searching for "Sangya" should bring back the "Cohort Discovery Service" due to the contactTeamPerson is "Sangya Pundir".
We want a go button on the main page next to the search box and a reset so we can reset all options.
Each introduction page (landing page) is effectively an implementation of a search engine which will then find hits on the other pages. So produce a page for each implementation and integrate as best you can. Write a short summary per engine about how easy/hard it was to integrate. Each page for search MUST include the filters below since this is attempting to evaluate how well the products work.
Filters
Review https://ukhealthdata.org/health-data-connect/ and the screenshot for details of the filters. We should support filters and data that do not appear in the summary listings. This means we support
- Categories
- Readiness tier
- License
- Intended users
- Primary purpose
- Cost model
- Support level
- Number adopting (which is a range filter)
Provide a way to clear the active filters too
CMS gui like options
To support the removal of WordPress we need to support non-technical people to develop pages and deposit custom formats and data including images and videos. We have identified the following two example CMS solutions providing low cost editors over a GitHub repo
- Sveltia
- Decap CMS
Goals for this spike are
- Install over each of the SSGs
- Create and deposit a page within the repository
- Engage with a non-technical contributor to see if they can add and remove content
Note additional constraints defined by the CMS issue (you are free to let this be a human interaction)
- Ability for non-Developer to navigate and update content (wording, images)
- Ability to turn on/off and edit text + link for a banner on an environment (e.g. gateway)
- Allow for separate instances for each Gateway environment
- Ability to add images, add/embed videos
- Create unique page layouts
We should provide a link to which ever CMS is used on the home page.
Styling
Reuse the existing styling developed for HDC. Take it from their website and integrate.
Release method
We want an automated release pattern with no devops involvement required. Already we assume this will be deployed via GCP's bucket HTML system with a small CDN over the top. The ingest of new records should take no additional effort to perform. Consider how we might support pre-scanning of entries.
Converting the HDC CSV data into something more useful
Take the example data and convert this into something better. Design a schema and look for the enums present. Define what they are.
Demonstrate Markdown rendering
Introduce evaluation.md as a page into the site alongside a summary markdown page which briefly describes the aims of this project. Render these as two separate pages reusing the correctly styling. You are testing the CMS' ability to allow a user to drop a markdown file and have it render sensibly without your input. Otherwise if you need to custom create pages for each file this is not easy.
Demonstrate alternative layouts and images
One key factor was altering layouts and bringing in images. We have the images folder and the markdown folder offering an outline.md which refers to images. You need to create a new layout which is different from the HDC portal and then demonstrate how we can alter the layout/rendering. This might include sharing the overall HEAD and structure of the site or creating a fresh stack again. Either way you MUST work with the SSG's tooling to ensure you are not cheating the system. In the case of one where there is no default markdown rendering you are responsible for creating a sensible infrastructure to support that automatic drop in rendering (as done above).
Individual environments/rendering
Explore how each SSG could offer a way to provide alternative environments based on the target deployment infrastructure. Almost like feature flag based rendering e.g. displaying something is on a dev/preprod/prod or a rebadged site.
Render the whole record
Entry detail pages MUST render every field present in the entry record — nothing in the data file may be omitted from the page, in each and every SSG implementation. Earlier iterations deliberately left a handful of fields unrendered (contactTeamPerson, originatorOrgs, adoptionEvidence, contact/adoption-pack/maturity fields) to demonstrate the "search over non-displayed attributes" requirement — that demonstration served its purpose, but a real portal shows the whole record. The "Sangya" search test remains in force, now as a regression check that search covers the full record.
Search-index-driven listings with pagination
The homepage listing must be rendered from the search index/corpus, not rendered in full and then hidden. The first iterations of every spike server-rendered every entry card into the homepage and used client JS to hide non-matches — acceptable at 13 entries, but the wrong architecture at the 100s of entries this system must scale to, and it quietly makes the DOM (not the search index) the real data source for filtering.
Requirements, for each and every SSG implementation:
- The listing page queries the search corpus/index and renders only the entries for the current page of results. No hidden pre-rendered cards.
- Pagination over the result set, with a page-size selector offering 5, 10, 20, 50, 100 and defaulting to 20.
- Pager controls must not render actions that can't be taken — no Previous button on page 1, no Next button on the last page. A disabled button is not acceptable; don't render it at all.
- Every surface that renders a result list gets the same paginated treatment, not just the homepage — in the Astro spike that includes all four
/search/<engine>/comparison pages, which must share one results-view implementation with the homepage rather than each rolling their own. - Free-text search, all 8 filters, Go/Reset, and the result count all continue to work exactly as before, driving the same paginated result set — one search implementation per site still applies.
- The "search MUST reach non-displayed attributes" requirement (the "Sangya" test) must still hold on the paginated listing.
Future planning
Data modelling and ValueSet usage
Spike one: Data schema definition
Review the data model from HDC (see web site for example). Identify possible gaps in the model compared to our own data model in gateway. Also vice versa. Identify value sets. Look at the data model we want to use to enforce this (can we use schemata and traser). Enforce using JSONSchema but be aware we might need to allow other data formats to be compared against the schema.
Identify possible links back to schema.org or similar entities for this work and we might want to embed that into the pages (semantic web support).
Spike two: ValueSets
A value set is a defined list of authorised values, codes, or terms used to validate data entry and ensure consistency. They are a lot like enums and in fact enums do the same but the key component here is they are publicly available, can be reused by others and almost stand alone as a standard. The can also semantically link out to other controlled vocabularies.
Goals for this are
- Identify the value sets from the HDC schema
- Identify alternative valuesets externally
- If there is a gap, create the value set using enum/similar semantics
- No need to develop full valuesets now (I think that will come in the future and maybe traser expands)