Sorry, you need to enable JavaScript to visit this website.

PHP

CivicTheme, an open-source design system for government

Everyone builds the same components, separately: navigation, cards and accessible forms

Every public-sector website needs the same handful of things:

  • a navigation people can actually use
  • a card, a button, a form that works with a screen reader
  • pages that read unmistakably as official

Most organisations pay to build all of it again, from scratch, on every project.

The cost is not only the first build. It shows up again when a component changes and nobody can say which of your sites carries which version, and again when accessibility arrives late as a remediation ticket rather than early as a default. And the sites drift apart until services meant to feel like one government no longer do.

A shared design system converts that repeated spend into a single maintained asset. Designing one is the easy half. The hard half is maintaining it so that the teams downstream can take an update without fear.

What CivicTheme is: a UI Kit, a Drupal theme, modules and a Figma source

CivicTheme(Opens in a new tab/window) is an open-source, component-based design system published by Salsa Digital(Opens in a new tab/window). Its own documentation puts the purpose plainly: it was created "so governments and corporations can rapidly assemble modern, consistent and compliant digital experiences".

4 parts ship, and they stay in step:

  • a CMS-agnostic UI Kit of components built with HTML, CSS and JavaScript, browsable in Storybook
  • a Drupal(Opens in a new tab/window) theme that connects the content model to those components
  • supporting modules, including one that adapts the theme to the federal GovCMS(Opens in a new tab/window) platform
  • a Figma design source that carries the same version number as the code, so a designer and a developer are provably looking at the same release

Accessibility sits in the baseline rather than in each agency's budget. The documentation is explicit: "The components have been built and assessed to comply with WCAG accessibility standards 2.1 AA out-of-the-box."

Our part in the work: architecture, release engineering and maintenance since 2021

Salsa Digital publishes CivicTheme, hosts it and runs its community. We wrote its first commit in September 2021 and have provided the engineering behind it ever since:

  • architecture of the component system
  • development of the components, the theme and the supporting modules
  • release engineering, to drupal.org and to npm
  • ongoing maintenance as Drupal, PHP and GovCMS move forward

Everything is developed in one repository and published out to the separate ones people install from. Develop together, ship separately: that one decision lets a small team keep a theme, a component library, companion modules and a documentation site moving in step.

Underneath it, the build and continuous integration run on our own open-source project tooling, the same tooling we bring to client platforms. For a client, that reads as inheritance: the build and test discipline behind a public design system arrives with us on your own platform.

Making an update safe to take: visual regression and schema update tests

Components exist in 2 forms, Twig templates and Drupal Single Directory Components, so that Drupal consumers and non-Drupal consumers each get the implementation they need. Keeping 2 versions of every component honest by hand would be untenable, so parity is machine-enforced: tooling synchronises them, and a check command fails the build the moment they drift.

The primary defence is visual. A unit test cannot tell you that a button moved 4 pixels, and teams downstream notice.

So every pull request captures screenshots of the components, publishes a side-by-side comparison and posts the link back into the review conversation where someone will actually read it. The same pipeline does double duty: one comparison catches a visual regression against the released version, the other catches drift between the 2 component implementations.

Then there is the question every organisation actually asks: what happens to our site when we take the update? Because the system ships configuration for content types, fields and site settings, updates are rehearsed against committed database snapshots, both schema-only and schema-with-content, before they reach anyone.

For a team downstream, that is what makes an update routine rather than a project: the change has been rehearsed against a database shaped like yours before it reaches you.

Release rules follow the same instinct: on drupal.org, where a release cannot be deleted, only the development branch is ever pushed.

Where it is now: Drupal 10 and 11, GovCMS, and 4 content profiles

Live and in service. Published on drupal.org and on npm, documented at docs.civictheme.io(Opens in a new tab/window), with the component library and a live demonstration site online.

4 industry content profiles ship alongside a default:

  • Corporate
  • Government
  • Higher Education
  • Health

Each is a complete worked example rather than a starter shell, so a team can watch the system do the job before committing to it.

It tracks the platform forward: Drupal 10 and Drupal 11, current PHP, GovCMS support kept current. A steady release cadence has carried it across 4 years, with a pre-release channel so teams can test a release candidate before they take it.

A single maintained design system, 4 industry content profiles, and 4 years of steady releases.

4 years on, we are still the engineering behind it.

For teams rebuilding the same components on every site

Everything on this page is 4 of our services running on one public open-source design system: the architecture of the component system, the development of the components and modules, the DevOps and release engineering that ships them, and the ongoing support and maintenance that keeps all of it current. The same standard applies to our AI-assisted delivery: whatever writes a component, the visual comparison and the schema update tests are what decide it is safe to release.

If you run a government agency, a university, or any organisation with a family of sites that keep drifting apart, this is the shape the engagement takes: a component library that is genuinely shared, accessibility in the baseline rather than in a remediation ticket, and updates rehearsed before they reach your site.

If you are weighing up CivicTheme for your next site, or you already run it and want taking an update to be routine, tell us which sites you are trying to keep in step.

Vortex, the Drupal project template

The 2 costs nobody puts on the budget: setup and drift

Ask a team that runs more than 1 Drupal(Opens in a new tab/window) site where its time goes, and 2 answers come back.

The first is setup. Every new project spends weeks on the same groundwork before anyone builds a feature:

  • local environments
  • CI pipelines
  • hosting wiring
  • code quality tooling
  • a test harness

The second is drift. Once a project is running it grows its own arrangement of tooling, so a year later moving a developer between 2 of your own sites means learning 2 different machines.

Neither cost appears on a budget line, and both are paid again on every project.

Installed once and kept current: local environment, CI pipelines and hosting

Vortex(Opens in a new tab/window) is a Drupal project template: a tested foundation you install once, pick features from, and then keep current for the life of the project.

The template is the pre-configured Drupal project itself, wired together and working on day 1:

  • a containerised local environment
  • CI pipelines
  • hosting integrations
  • code quality tooling
  • a testing harness

Documentation ships with it, including an onboarding checklist for new team members. An installer adds only the features you chose, from hosting and CI provider through to theme and AI agent instructions. On the current line that tool is the installer; the next major moves the job into a dedicated Vortex CLI.

That upgrade path is what separates Vortex from a starter kit, which is a one-time copy frozen at whatever it got right in its year. Vortex keeps the path open, so a project set up in its first year can adopt improvements made in its third through the same tool that created it.

For a client, that reads as continuity: the foundation under your site keeps getting the improvements made after your project started.

Whichever features you pick, local, CI and hosting all run the same provisioning path. That single decision is where "works on my machine" stops being a recurring argument.

The quality gates arrive switched on: secret scanning, static analysis, 90 per cent coverage

Plenty of templates leave quality tooling as an exercise for the reader. Vortex ships the gates configured and running across the whole codebase:

  • secret scanning
  • dependency auditing
  • container linting
  • static analysis
  • coding standards

A 90 per cent code coverage threshold fails the build by default, with the result posted back onto the pull request. Visual regression waits behind an opt-in label, so an expensive check runs only on the changes that warrant it. Third-party CI actions are pinned by commit hash, a ready answer to a supply-chain question many teams are now being asked.

Every gate can be switched off with a single variable, and that matters more than it sounds. A team can adopt the pipeline on day 1 without being blocked by it, then tighten it as they go. It is the difference between a pipeline people keep and one they quietly delete.

The documentation is held to the same standard: linted, tested, and gated against drifting out of step with the code.

9 years in and busier than ever: 111 releases across Drupal 7 to 11

The usual failure mode of a project template is quiet abandonment: excellent for 18 months, then attention moves elsewhere and the repository goes still.

Vortex began in July 2017 and has shipped 111 releases since. It has tracked Drupal 7, 8, 9, 10 and 11 across their lives, and today it targets Drupal 11.

Releases go out on a stated monthly cadence, borne out across the recent run. A second major version is already published alongside the first, with the release plumbing built so that promoting a new major to the default is a single configuration change.

It is being developed more actively today than in any year of its life.

111 releases since 2017, across Drupal 7 to Drupal 11, and busier today than in any year of its life.

For a client, that history reads as low risk: the foundation under your project is maintained by the team you are already working with.

Open source under GPL-3.0, and used on the work we are paid for

Vortex is open source under GPL-3.0 and developed in public: repository, issue tracker, releases and documentation are all open. Community support runs through Drupal Slack and GitHub issues, and paid support is available for teams that want a commercial contact behind it.

We use it ourselves, which is the part that keeps it honest. Our own website runs on Vortex and is offered openly as a reference for what a long-lived Vortex project looks like, and the client platforms we support run on it too. The template meets real delivery pressure, not only its own test suite.

The flow runs both ways. Tools that began inside client work ship with Vortex, including our Behat step library(Opens in a new tab/window), CI runner image(Opens in a new tab/window) and artifact deployer(Opens in a new tab/window). Pieces that outgrow it are published in their own right.

For teams paying the setup cost on every Drupal project

Everything on this page is 4 of our services pointed at our own product: the architecture of the template, its development, the DevOps and hosting wiring it ships with, and the support and maintenance that has kept it current since 2017. The same template ships the AI agent instructions behind our AI-assisted delivery, and the gates decide what is allowed to merge.

If you run a portfolio of Drupal sites, or a team that starts a new project every few months, this is the shape the engagement takes: your sites build, test and deploy the same way, new developers onboard against a stack they have seen before, and improvements arrive through the same tool that set the project up.

If your Drupal projects have each grown their own build and nobody can move between them, tell us what your setup looks like today.

A parts retailer's store that knows what fits

The question every parts buyer asks first: does this part fit?

Someone whose appliance has stopped working does not want to learn about part numbers. They want the right filter, the right seal, the right battery, and to be certain before they pay. Get it wrong and the part comes back, the refund goes out, the sale is gone.

Behind that sit 2 more doubts, so the whole purchase turns on 3 questions:

  • will this part fit my machine
  • when will it arrive
  • is it genuine

Answering all 3 plainly is how a parts store earns its living.

One engagement, bought in pieces: identity, data, search, storefront

The business is an owner-operated Australian retailer selling parts nationally through its own BigCommerce storefront and the major marketplace channels, and it is growing into new appliance categories. We were engaged directly, and the first thing we settled together was the order of work, not a design or a technology.

The engagement runs as 5 independent streams:

  • the visual identity
  • the compatibility data
  • the fitment search on top of it
  • improvements to the current store
  • the rebuild

Only the rebuild has a real predecessor: it implements the identity, so it waits, and we said so in the quotation instead of starting it at low intensity. For a client, that reads as control: each stream starts when it is worth starting, and nothing sits half-built while it waits.

Across the streams we have provided discovery, brand and design, architecture, data engineering and development.

A brand decided by looking: personas, logo systems, complete directions

The business already had the verbal half of its brand, commissioned separately and approved. Ours is the visual half.

We started with the customers rather than a mood board:

  • personas built from the store's own order data
  • a brand brief
  • a visual audit of the category

Then, rather than argue for a single direction, we built several complete ones and set them side by side, each with its own logo system, colour, typography and mockups, so the choice could be made by looking rather than by imagining.

2 moments matter. Our first persona analysis leaned on a sample that under-counted parts of the catalogue; the client caught it, we rebuilt it from the authoritative export with numbered corrections, and the recommendation held. Correcting ourselves in public bought more trust than being right would have.

Later the owner drew his own mark, so we vectorised it and rebuilt the identity around it.

Turning fitment into structured data and a live search widget

Which machines a part fits is the most important fact about that part, and in a catalogue grown over years that knowledge lives in the product description, written by whoever put it there.

Our job was to turn it into data the store can use. We built a scripted, unit-tested, rerunnable pipeline that reads the catalogue, extracts every compatible-model mention, and resolves the ambiguous ones into clean brand, series and model records mapped to the products that fit them.

AI does a single job in that chain, the judgement call; the rest is deterministic code that can be tested and re-run. Every mapped row keeps the sentence it came from, and because an AI pass always discards something, the discards are reviewed as a deliverable in their own right.

On top of that data sits the finder. A firm rule against recurring app fees ruled out every paid fitment app, and the constraint produced better engineering.

We used the platform's own category system as the managed vocabulary of machine models, native and free, then built the search as a small widget reading live from the storefront's GraphQL API. No app, no monthly fee. A customer types their model and sees only the parts that suit it, whether that part fits a single machine or hundreds.

Changing a live catalogue: a staged, reversible deployment

A catalogue-wide change on a store that is taking orders today is what keeps an owner awake, so the deployment is written down before it runs, as a document for the client. It is staged rather than a single switch, with the safeguards written into the plan:

  • every stage individually reversible
  • a full store export as the restore point
  • idempotent scripts
  • a rehearsal on a sandbox before production
  • the original descriptions archived rather than replaced
  • rollback triggers agreed in advance on business measures rather than error logs

For a store owner, that reads as a decision they can reverse: any stage can be stopped and undone on measures the business already watches.

The rebuild, meanwhile, has its discovery banked, including an information architecture organised around compatibility rather than product categories and built from the start to carry additional product ranges.

And since a brand cycle runs for months, we sorted the current store's improvement work by whether an item survives the rebuild, so what holds its value can go ahead now.

Where it stands: identity delivered, fitment search proven in a sandbox

  • the identity is locked and the brand book delivered
  • the fitment search is proven in a sandbox and waiting on the production data load
  • the migration is planned but not yet run
A fitment search built on the storefront's own category system and GraphQL API: no app, no monthly fee.

What is already paying off is the order the work is being done in.

For stores where the wrong part comes back

Everything on this page is 5 of our services running on one live retail catalogue: discovery and sequencing, brand and design, architecture, migration of the product data, and custom development on the storefront. The extraction pipeline is AI-assisted delivery in its plainest form: AI makes the judgement call, deterministic code does the rest, and what the AI discards is reviewed by a person.

If you sell parts, spares or consumables where the buyer has to be sure a product fits before they pay, this is the shape the engagement takes: compatibility pulled out of the descriptions it is buried in, a search built from what the platform already gives you, and a catalogue-wide change staged so it can be stopped and undone.

If your product descriptions know what fits but your storefront does not, tell us what your catalogue looks like.

RET Conveyancing website

The website a Melbourne conveyancing practice has to stand behind

Choosing a conveyancer happens fast. Someone has a contract in hand, a settlement date already moving toward them, and perhaps 10 minutes to decide who to call.

In that window a website has to explain a process most people go through a handful of times in their lives. It also has to show, by how carefully it is built, that the firm behind it is careful too.

RET Conveyancing is a licensed conveyancing practice in Melbourne. Conveyancing is the legal transfer of property, and the firm covers the full range of it, including the work many firms would rather pass on:

  • self-managed super fund work
  • trust work
  • subdivisions
  • developer work

It is a small, director-led team whose whole positioning is precision and low risk, which is a high bar for a website to meet.

What we built: Drupal 11, a component library and client intake forms

The site runs on Drupal(Opens in a new tab/window) 11 with a single page type and a library of components the team stacks to build any page:

  • heroes, media and text bands
  • service grids and icon cards
  • statistics and explainer steps
  • team profiles and testimonials
  • FAQ accordions
  • an office map with opening times

Each editorial component has a matching front-end component in the theme, one to one, so what an editor assembles is exactly what a visitor gets. For the firm, that means a new page is something their own team assembles, not a job that comes back to us.

The firm also takes work in through the site: an enquiry form, and 4 client intake forms covering a purchase, a sale and the matched pair either side of a related-party transfer. We built them natively in Drupal rather than on a separate form product, which means:

  • one interface for the team
  • one place where uploaded documents live
  • every file scanned before it is stored

That last part matters when the uploads are identity documents and contracts.

How the build stayed predictable: one blueprint, a generated backlog

A small site is no reason to improvise. We started from one versioned blueprint describing the whole content model and generated the delivery backlog from it, each story naming what it depends on.

Components were built before the pages that assemble them, so by the time page building began, every part a page needed already existed. We were just as deliberate about what not to build: established Drupal modules carry the forms, search, maps and opening hours, and only the presentation is custom.

The design reference lived in the repository as a static prototype beside the code, which turns "does it match the design" from an opinion into a difference anyone can point at. And when the finish date moved by a day, we sent that as its own note before anyone had to ask. For a client who chose us on precision, that is the only shape bad news should take.

Vortex: the same machinery a government platform runs on

This is the part we would most like people to notice. The site is built on Vortex(Opens in a new tab/window), our open-source Drupal project template, and inherits everything that comes with it:

  • a build, test and deploy pipeline that runs on every change
  • separate development, staging and production environments
  • dependency updates that arrive automatically as pull requests
  • coding standards and static analysis enforced automatically
  • automated browser tests over the journeys that matter

None of that is remarkable on a government platform. It is remarkable on a small practice's website, and it should not be.

The investment in that tooling was made years ago, so the marginal cost of bringing it here was close to nothing. What the firm gets for it is a site that takes updates through automated tests, deploys through the same pipeline on every change, and could be handed to any competent Drupal team tomorrow.

What that buys a small firm is time: updates arrive as pull requests to review rather than a project to schedule, so the team only touches the site when they have something to say.

Where it stands: built, handed over, kept current

The build ran through June 2026 and went to the firm for review with a written guide to building pages, adding media, managing menus and reviewing enquiries. We have kept the platform current since: core and the project template were last updated in late July 2026.

A small practice's website, running the same build, test and deploy pipeline a government platform runs on.

A website for a small practice does not need heroics. It needs to stay current, stay fast, and keep working quietly while the team gets on with settlements.

That is a maintenance question as much as a build question, and it is the part small sites most often go without.

For firms whose website is the first proof of care

Everything on this page is 4 of our services running on one small practice's website: UX and design, Drupal development, DevOps and hosting on an automated pipeline, and ongoing support and maintenance. The same engineering is available with AI-assisted delivery where it suits the work, at the same tested standard.

If you run a conveyancing practice, a law firm, or any small professional business where the website is the first evidence of how carefully you work, this is the shape the engagement takes: a component library your own team builds pages from, intake forms that accept client documents safely, and a platform kept current after handover.

If your client intake still arrives as email attachments, or your site is old enough that nobody wants to touch it, tell us how the work comes in.

A community radio station's website, built around its schedule

What a radio station asks of a website: recurrence, not a calendar

Ask a content management system for a schedule and it gives you a calendar. A radio schedule is a different animal.

Programs recur on their own patterns:

  • weekly
  • fortnightly
  • on the second Tuesday of the month
  • on the last Sunday of the month

The listener does not care about the pattern. They want to know whether their program is on this week, and what is playing right now.

The station broadcasts programming by and for communities that commercial and public radio do not reach. It is volunteer-run and listener-sponsored, and a supporter can name the program they are backing when they subscribe.

Almost everything about how a station like this works becomes a requirement on its website: a presenter publishing their own show, decades of broadcasting that has to stay reachable.

Our part in the work: an engineering partner on a Drupal 11 rebuild

Drupal(Opens in a new tab/window) 7 reached end of life, so the station's platform needed a new home. A partner agency brought us in as their engineering partner for the rebuild.

Since 2025 we have worked alongside their team on a Drupal 11 build, providing:

  • technical leadership
  • platform architecture
  • migration engineering
  • the delivery process

The brief was never only to move the site. It was to make sure the parts that run every day keep running on their own.

A schedule that tells the truth: weekly, fortnightly and monthly recurrence

We modelled recurrence the way broadcast actually works. Each program carries its own pattern, down to last and second-to-last of the month, the 2 cases most systems quietly decline to handle. Upcoming episodes are generated ahead of time and refreshed automatically, so the schedule a listener sees stays in step with what goes to air.

Then there is the part that only appears once a station is on air. A program booked for 9 o'clock does not begin at 9 o'clock exactly.

Recordings start a little early and run a little long, and the platform matches each recording back to the program that produced it within a tolerance. Without that, the audio archive would be a pile of unlabelled files, and that archive is a large part of what a station with decades behind it actually is.

Bringing the archive across: a Drupal 7 migration with its history intact

The migration from Drupal 7 carried across:

  • hundreds of programs with their history
  • the station's members and volunteers
  • the taxonomy that organises decades of broadcasting
  • more than 1,000 redirects, so links people saved years ago still land where they expect

For a client, that reads as continuity: the archive, the people and the saved links arrive with the content, rather than being rebuilt by hand after launch.

Getting there meant joining 2 live databases in a single query, which Drupal does not do out of the box, and knowing where Drupal 7 keeps content that looks absent until you check the right table. Migrations fail quietly on details like that, so the ones this one turned up are written into the codebase.

Built so the station can run it: roles, automation and a live player

A community station's day belongs to its programs, not to its website. The routine work happens by itself:

  • upcoming episodes appear ahead of time
  • recordings are attributed to the shows that made them
  • the schedule keeps itself current
  • the program catalogue comes straight from the station's podcast platform rather than being retyped

The live player was built for the audio format the station actually broadcasts, so listening works from the first page. For a client, that reads as time back: the hours that would go into keeping a website current go into programming instead.

Permissions follow how a volunteer-run station is really organised, with presenters, announcers, program makers and station managers each holding exactly the access their part of the station needs. A presenter can publish their own show, and only their own show.

The build runs on Vortex(Opens in a new tab/window), our open-source Drupal project template, with automated end-to-end tests over the journeys that matter, written on our published Behat step library(Opens in a new tab/window).

Where it stands: a Drupal 11 platform in active build through 2026

This is current work: a Drupal 11 platform, actively being built out through 2026. The schedule, the archive, the volunteer roster and the sponsorship model are one system, and each has edge cases only a real station produces.

Hundreds of programs, decades of archive and more than 1,000 saved links, carried from Drupal 7 onto Drupal 11.

Getting them right once is what lets a station's team spend their time on programming rather than on the website.

For platforms kept running by volunteers

Everything on this page is 4 of our services running on one live platform: platform architecture for a schedule broadcast actually produces, custom development for the parts no module covers, migration engineering off Drupal 7, and the DevOps and hosting that keeps releases repeatable.

If you run a community broadcaster, a member-funded not-for-profit, or any organisation whose website is maintained between shifts by people with another job to do, this is the shape the engagement takes: routine work built to happen on its own, roles that match how the organisation is really staffed, and an archive that survives the move. Where budget is the constraint, the same engineering is available with AI-assisted delivery, at the same tested standard.

If your Drupal 7 site is still carrying the schedule, the archive or the membership your organisation runs on, tell us what it has to keep doing.

A knowledge hub that puts research where practice happens

Scattered research: the evidence exists, finding it is the hard part

If your organisation holds knowledge other people need, the problem is rarely a shortage of material. In this centre's field the research is extensive, but it sits in departmental reports, evaluations, literature reviews and journal articles, spread across dozens of places.

A practitioner deciding what to try next rarely has an afternoon to go looking, and an employer trying to do the right thing for the first time often does not know the research exists at all.

Then there are the people the sector exists to serve, and their families, working out what good support should look like.

The centre set out to serve all 3 groups from one place: practical guidance on one side, the evidence underneath it on the other.

Our part in the work: technical leadership and Drupal delivery

A partner agency engaged us as their engineering partner on the build, while the relationship with the centre and the shape of the programme sat with them.

We provided technical leadership, architecture, platform engineering and ongoing maintenance. We also set the delivery process the build ran on:

  • automated code quality checks
  • automated tests over the journeys that matter
  • a release cadence the team could plan around

An evidence base that stays where it is curated: a feed, not a rebuild

The most consequential decision on this project was about where the evidence lives.

The straightforward way to build a research library is to build one: a content type, an editorial workflow, and someone keeping thousands of bibliographic records tidy forever. We took the other route.

The centre's evidence base is curated in an established open-access research archive, by a team that has specialised in exactly that for 2 decades, and the hub consumes it. Records keep their attribution and their permanent links, so the collection stays whole and useful outside the hub as well as inside it.

That meant being a good neighbour to another organisation's system. A nightly sync that asks a partner to serialise its whole catalogue and then discards almost all of it becomes someone else's problem.

We built the feed the other way around, so the hub says what it already holds and asks only for what has changed. The library stays current, and the cost to the source stays flat as the collection grows. For a client, that is the difference between an integration that stays cheap to run and one that becomes a standing cost.

What the audiences got: a filterable evidence base

On the practice side, the hub carries best-practice guides, tools and professional development material, organised around how practitioners approach the work rather than how the material was produced.

On the evidence side, every record is a complete bibliographic entry:

  • authorship and publisher
  • publication details
  • journal and peer-review status
  • licence and subject

That depth is what makes the library filterable rather than merely searchable, and faceted search is the part practitioners lean on most.

One property matters more than the rest. Records carry their accessible-format status as a first-class, filterable field rather than as a tag, because a real share of this audience depends on those formats to read at all. Someone can ask the library for material they can actually read.

Built on an open-source design system, accessibility checked on every build

The hub runs on an open-source, accessible, component-based design system whose engineering we had led before this project began. Custom component work went only where this product genuinely differs from any other site: the filters that drive the resource library. Everything else came from the design system as it ships, which is the whole economic argument for using one.

Accessibility is checked continuously rather than at the end. On every build, an automated test reads the site's own sitemap, visits each landing page and records what it finds.

Because it discovers pages instead of working from a fixed list, a new section is covered the day it goes live, not the day someone remembers to add it to a test.

Both dates held: the first release on its promised day, the full hub early

The first release, for practitioners, went live on the date promised at the start. The full hub, serving every audience, arrived ahead of its date at the end of 2025.

2 delivery dates named at the start. Both held, and the second landed early.

Saying it plainly is the point: described, then built, then shipped, on the days that were named. For a client, a date that holds is the difference between planning a launch and hoping for one.

It has been live and in use ever since, and we are still the team on it:

  • a steady release cadence
  • design system upgrades
  • search improvements
  • accessibility fixes as they surface

For organisations whose evidence has to reach the people who use it

Everything on this page is 4 of our services running on one live knowledge platform: architecture that decided where the evidence should live, a migration that keeps it current without burdening the archive that curates it, accessibility checked automatically on every build, and ongoing support and maintenance since launch.

If you run a research centre, a peak body or a not-for-profit whose knowledge has to reach practitioners, this is the shape the engagement takes: one architectural decision about where the collection lives, a feed that keeps it current, and delivery dates named before the work starts and then met. The same engineering is available with AI-assisted delivery where it suits the work, at the same tested standard.

If your evidence base is scattered across reports, journals and someone else's archive, and you are weighing whether to rebuild it or feed from it, tell us where it sits today.

A community radio station's website, with its whole broadcast history intact

A community radio station's website is its second transmitter, and its archive

It carries the station's daily work:

  • listeners find shows through it
  • presenters publish through it
  • members subscribe through it

Behind the front page sits what makes a specialist music station what it is: the record of every track it has ever put to air.

Someone catches half a song on the drive home and searches for it 3 days later. That only works if the history is still there and still addressable, which makes the back catalogue both institutional memory and the long tail of how a station gets found.

The station is member-supported and largely volunteer-run, and its presenters are volunteers with their own shows. A platform that needs someone performing a manual step at 9pm on a Tuesday will quietly stop working, because the person who would perform it has a day job.

Our part in the work: architecture, engineering and archive migration

A partner agency holds the relationship with the station and brought us in as their engineering partner. We have worked alongside their team since 2024, across the strands that make up the engagement:

  • the solution architecture for a purpose-built radio platform
  • the platform engineering and custom Drupal development
  • the migration of the station's broadcast archive
  • the build and deployment tooling the platform runs on

At the centre of it is a purpose-built radio platform for Drupal(Opens in a new tab/window). It models programs, episodes, presenters and tracks as first-class content, keeps them in step with the station's podcast platform, and serves the schedule wherever it is needed.

Its architecture documentation sits beside the code, so understanding how the platform works does not depend on one person's memory.

The website drives the recording hardware: from timeslot to podcast

This is the part we like best. Drupal is the system of record for the broadcast schedule, and it pushes that schedule out to the station's podcast platform, which records the live broadcast off air, cuts it into an episode, and syncs it back into Drupal as a published podcast.

So a producer changes a show's timeslot on the website, the recording system reconfigures itself, the broadcast is captured, and the podcast appears on the site. Nobody logs into the recording platform. Nobody has to remember.

For a client, that closed loop reads as staff time you do not spend: the step between a schedule change and a published episode does not need a person standing over it. The pattern travels well beyond radio: wherever an operational process hangs off a content change, the same closed loop is worth building.

Keeping the whole archive findable: staged migration and 301 redirects

Well over a decade of broadcast history sat in the station's legacy system: what was played, on which show, on which night. Moving a history that deep into a CMS is not a routine content migration, and the tempting shortcut is always to keep the recent years and let the rest go.

We kept all of it, and built the migration so that keeping it all was the safe choice rather than the brave one:

  • the history lands in staging tables first, so the migration reads local data rather than a remote API
  • the harvest is resumable, picking up cleanly after an interruption
  • it runs newest first, so a partial run still leaves the site current
  • every legacy track URL gets a permanent redirect, so years of links from blogs, forums and search results still land where they should

That is the difference between preserving an archive and merely storing one. For a client, it reads as traffic kept rather than lost: the links people already have, and the results already indexed, still land on a page.

Well over a decade of broadcast history kept whole, with a permanent redirect on every legacy track URL.

What listeners and presenters get: a persistent player and a fortnight-ahead guide

The live stream plays in a player that keeps playing as you move around the site, so browsing does not cut the music. Around it sits what a station needs to publish:

  • a program guide that runs a fortnight ahead
  • a playlist on every episode of what actually went to air
  • a gig guide that turns submissions from the public into published events
  • news and artist interviews

Access is shaped around how a volunteer station really runs: each presenter manages their own program's episodes and playlists, and the people coordinating gigs, interviews and promotions get exactly the access they need.

Built to be handed on: automated tests, accessibility checks and Vortex

A platform is only as good as what happens after the build, so this one is made to be carried on by whoever works on it next:

  • automated tests covering the platform at unit, kernel and browser level
  • a mock of the podcast platform's API standing in for the live service in tests
  • accessibility checks running automatically on every build
  • the project sitting on Vortex(Opens in a new tab/window), our open-source Drupal project template, so its build and deployment tooling is maintained upstream

Known issues are written down with the reasoning for when each will be picked up, because technical debt handled out loud is a habit worth having. Nearly 2 years in, we are still building it.

For platforms where the archive is the asset

Everything on this page is 4 of our services running on one platform: solution architecture for a purpose-built content model, custom Drupal development, an archive migration built to be resumed rather than restarted, and the build and deployment tooling underneath it.

If you run a broadcaster, a publisher, or any member-supported organisation whose back catalogue is the reason people find you, this is the shape the engagement takes: an archive treated as first-class content rather than an attachment, a migration that keeps every legacy URL addressable, and the operational steps that would otherwise fall to a person driven from the CMS instead. The same engineering is available with AI-assisted delivery where it suits the work, at the same tested standard.

If you are moving a long archive onto a new platform and cannot afford to lose what is already indexed, tell us what the archive looks like.

Climate Change Authority CivicTheme migration

When the website is the public record: a federal publication estate

Some organisations have a website because they need one. Others have a website because it is the record. If your work is to give public advice, the site is where that advice gets published, cited, downloaded and checked again years later, long after the news cycle that produced it has moved on.

The Climate Change Authority is an independent statutory body that advises the Australian Government on climate policy: emissions reduction targets, the Emissions Reduction Fund, the Renewable Energy Target, the Carbon Farming Initiative.

Its website carries that record:

  • annual reports
  • corporate plans
  • strategic frameworks
  • the outcomes of Authority meetings
  • public submissions filed by industry and community organisations

More than 1,000 documents, and the people who come looking are usually after one specific thing.

That is a publication estate, not a brochure site. It sets a different bar: findable, readable, accessible, and stable for a very long time.

Our part: the CivicTheme sub-theme, build and configuration

We were brought in for one thing: to move the site onto CivicTheme(Opens in a new tab/window), the Australian government design system. We help build and maintain CivicTheme itself, so this was familiar ground. A migration onto a design system tends to go faster when the people doing it know why each component is shaped the way it is.

It was a short and precise engagement in 2023. We delivered:

  • the site's CivicTheme sub-theme and the configuration around it
  • the front-end build and the component workflow the theme depends on
  • the local tooling the next developer would reach for

Then we handed the platform back. The Authority's own team and its partners have carried the site forward since, which is how a migration should end. For a client, that shape reads as a bounded cost: a specialist engagement with an end date, not a dependency.

Building inside GovCMS SaaS, a platform that allows no custom code

The site runs on GovCMS(Opens in a new tab/window) SaaS, the Australian Government's shared Drupal(Opens in a new tab/window) platform. There are no custom modules on that platform, and there structurally cannot be. Core, the distribution and the contributed modules all arrive in the platform image, and an automated check refuses a deployment if anything unexpected turns up in it.

That sounds like a limit on what you can build. It is really a limit on how. Everything we delivered lives in the theme and in configuration, and that turns out to be a discipline rather than a compromise:

  • no bespoke module to maintain
  • no bespoke upgrade path to plan around
  • a platform free to keep the software underneath the site patched

Constraints like these reward knowing the platform well. The front-end build had to fit a host that runs no build step of its own, and the platform's own code checks had to pass cleanly on files that nobody writes by hand. Both are the kind of problem you solve once and then stop paying for.

What a design system actually buys you: no bespoke content types or fields

The site uses CivicTheme's content model exactly as it comes. Not one bespoke content type, not one bespoke field.

That is easy to skim past, and it is the whole cost argument for design systems. Every custom field is something a person has to understand, document, test and carry through the next upgrade, indefinitely. A federal publication estate needed none of them, because the design system already covered the job.

What is left to look after is a thin layer of styling on top of a library that other people keep improving. For a client, that reads as a smaller bill to carry: you maintain the styling, not the system underneath it.

Accessibility works the same way. In a design system it is a property of the shared components, maintained once for everyone who uses them, rather than a line item in each agency's budget.

Where it landed: live on CivicTheme, carried by the client's own team

The Climate Change Authority's website is live at climatechangeauthority.gov.au(Opens in a new tab/window) and runs CivicTheme.

More than 1,000 documents on a federal publication estate, and not one bespoke content type behind them.

Our part was brief and deliberately so. A migration done well ends with a client who no longer needs the specialists, and that is what happened here: development continued in the hands of the Authority's team and its partners.

The design system underneath is still maintained and still improving, and the site inherits that work without commissioning any of it.

For agencies whose website is the public record

Everything on this page is 2 of our services on one federal publication estate: a migration onto a government design system, and development delivered entirely in theme and configuration on a platform that allows no custom code.

If you run a GovCMS site, or any publication estate that has to stay findable and accessible for years, this is the shape the engagement takes: a short specialist migration onto CivicTheme, a stock content model wherever the design system already covers the job, and your own team carrying the site afterwards. The same work is available with AI-assisted delivery where it suits the scope.

If you are weighing a move onto CivicTheme, or wondering what GovCMS SaaS will and will not let you build, tell us what your site has to keep publishing.

Git Artifact: safe build deployments to git-based hosting

What you push is what production runs: deploying from a git branch

A good deal of hosting still works the simplest way there is. You push to a git branch, the platform notices, and it deploys whatever it finds there. It is a clean model, and it puts an awkward requirement on your repository.

Your repository is not what production needs. It carries the parts a team builds with:

  • development tooling
  • test suites
  • source assets
  • build configuration

What it does not carry is the compiled CSS, installed dependencies and vendor code the running site depends on. Something has to bridge that gap, and for most teams that something is a deployment script written once, under time pressure, and then trusted forever.

That step is worth thinking about carefully, because of everything in a pipeline it is the one that can fail quietly.

A build that breaks stops the pipeline and tells you. A deployment that publishes the wrong contents raises nothing at all. It simply goes live.

A deployment step designed to refuse: dry runs, no-change guards and tag collisions

git-artifact(Opens in a new tab/window) builds the deployable artifact inside CI and commits only that to the separate repository your host watches. Development tooling stays out of the deployed package, which keeps the running site lean and narrows what is exposed there.

The part we care about most is what it does when something is not right. It refuses in 3 independent ways, each covering a different way a deployment can go wrong without anyone noticing:

  • pushing is a dry run unless you explicitly ask for a deploy, so the default invocation prints what it would do and writes nothing to the remote
  • if the artifact it has just built is identical to what is already deployed and no new tags are involved, it stops rather than adding noise to the history
  • if a tag would collide with one already on the destination, it stops there too

For a client, that reads as a deployment that stops rather than guesses: when something does not look right, the pipeline says so instead of publishing quietly.

Underneath those refusals sits the ordinary work of getting an artifact right:

  • a manifest controls exactly what goes in
  • a watermark in each commit message lets the tool find where it left off on a repository it does not own, which is what makes incremental artifact builds possible at all
  • tags are carried across with collision detection
  • symlinks survive the copy instead of being flattened into broken files

All of it was built test first, against a suite that provisions real throwaway git repositories rather than mocking them. Testing a deployment tool that way is slow and awkward. It is also the only way to know the safety rails actually fire.

Where it came from: a 2017 client audit, extracted as open source

git-artifact started inside client work. In 2017 we were auditing a dual-sector university's TAFE website platform, and we rebuilt its deployment step from the ground up: written test first in PHP, with the refusals above and a test suite that exercised every one of them.

2 years later, we did the part that mattered more. We lifted it out of the client's codebase and published it as a standalone open-source package, so every project after it inherited the same tested deployment step at no cost to anyone.

That is a habit rather than a one-off. When client work produces a piece of engineering that is better than that one project strictly needed, we take it out, publish it and maintain it. git-artifact is the earliest example and still the clearest one.

For a client, that habit reads as inherited engineering: the deployment step on your platform arrives already built, already tested and already maintained, rather than written fresh under time pressure.

Still deploying, 9 years on: Vortex, Acquia Cloud and Lagoon

git-artifact is maintained and in service 9 years after the first version and 7 after it became a package. It ships with Vortex(Opens in a new tab/window), our open-source Drupal(Opens in a new tab/window) project template, and it is the deployment mechanism for Vortex-based projects on git-deployed hosting, including Acquia Cloud and Lagoon.

9 years in service, 7 of them as an open-source package other projects inherit.

For a tool whose whole job is to say no at the right moments, longevity is the measure that counts. It has been doing the same careful thing in other people's pipelines for the better part of a decade, and the teams using it rarely have to think about it.

For pipelines whose deploy step nobody has read

Everything on this page is 4 of our services pointed at our own tooling: the architecture of a deployment step, the development of the package that implements it, the DevOps engineering it exists to protect, and the ongoing support and maintenance that has kept it current since 2017. It is the same rule we apply to AI-assisted delivery: however quickly a change is written, it reaches production through a deployment step that has been tested.

If you run a site deployed from a git branch, on hosting like Acquia Cloud or Lagoon, and the step that gets your build there is a script somebody wrote once, this is the shape the engagement takes. The deploy step becomes a tested, maintained package: a dry run by default, a refusal when something does not look right, and only the built site in front of your visitors.

If nobody on your team can say what your pipeline actually pushes to production, show us your deploy step.

Behat Screenshot, a picture of the page your test failed on

The failure message that leaves out the page: a failed browser test

If your platform has automated browser tests, you have read this message. A check looked for something on the page and did not find it. That is the whole report.

What you actually want to know is what the page was showing at that moment:

  • whether it rendered at all
  • whether it sent the visitor somewhere unexpected
  • whether a form came back carrying an error nobody has seen before

Without that, fixing it is guesswork. The loop goes like this:

  • change something
  • push
  • wait for the pipeline
  • read a message that still does not describe what happened

Each of those loops costs a real slice of the day, and after 2 or 3 of them the temptation to write the test off as unreliable and move on gets harder to resist. That is the moment a test suite starts losing the team's trust, and a suite nobody trusts stops protecting the platform.

Capture the page, not just the verdict: HTML and an image on failure

behat-screenshot(Opens in a new tab/window) is a Behat extension we wrote for exactly that moment. Behat is the testing tool that drives a real browser through the journeys a platform cannot afford to break:

  • signing in
  • searching
  • submitting a form
  • completing a purchase

When one of those journeys fails, the extension captures the page as evidence.

It saves 2 things, because either one alone leaves a question open. The rendered HTML is what you need to see what the page really contained, down to the element that was missing. The image is what you need to see what a person would have seen, which is where layout and rendering problems show up and where markup alone tells you nothing.

Together they usually answer the question in seconds, rather than in another pipeline run.

There are 2 ways to trigger a capture. It happens automatically when a scenario fails, and a test author can also capture deliberately, part-way through a scenario, from a single step. That second mode earns its keep when a journey passes but something along the route looks wrong.

Turned on in configuration, not in your test code: adoption without a rewrite

How much a tool costs to adopt decides whether it spreads. This one is switched on in a project's Behat configuration file rather than composed into the test code, so a team can enable it without touching a line of its own suite, and turn it off just as easily.

Small decision, large consequence: it is why the extension ends up switched on across whole platforms rather than on the single project whose developer went looking for it.

The evidence has to leave the build: publishing screenshots as artifacts

A capture nobody can open is not evidence. The extension writes where a build pipeline collects and publishes artifacts, so whoever picks up a failed run opens the page straight from the build interface instead of trying to reproduce the failure locally and hoping it happens again. That closes the loop from "the test failed in CI" to "here is the page it failed on".

For a client, that reads as a shorter path from a red build to a working site: the team spends the afternoon fixing the fault rather than reproducing it.

We took the same idea a step further in Vortex(Opens in a new tab/window), our open-source Drupal(Opens in a new tab/window) project template, which publishes captured screenshots somewhere a reviewer will actually look. Evidence that arrives where the conversation is already happening gets used. Evidence sitting in a container does not.

In service since 2017: shipped with Vortex and used beyond our own work

The extension was already part of our testing stack by 2017, documented alongside the rest of our Behat tooling on a long-running clinical trial platform, and it has been maintained ever since. It ships with Vortex, and it is loaded by the test configuration of every repository in the Victorian Government's central content platform.

The part we value most is the part we did not write. Developers with no connection to our practice and no connection to our clients have contributed patches to it, which is a signal no self-authored package list can manufacture. People send fixes to tools they depend on.

In our testing stack since 2017, shipped with Vortex, and carrying patches from developers outside the practice.

For a client, that is the quiet part of the value: the tooling under your test suite is maintained by the same people who maintain your platform.

It sits in a small family we maintain and use daily:

Used together, they mean a failed run explains itself.

For teams whose test failures do not explain themselves

Everything on this page is 2 of our services pointed at our own tooling: the development of the extension, and the ongoing support and maintenance that has kept it current since 2017. The same standard sits behind our AI-assisted delivery: however a change gets written, a failing test still has to show you the page before anyone calls it safe.

If you run a platform whose browser tests the team has started ignoring, or a pipeline whose failures nobody can diagnose without re-running them locally, this is the shape the engagement takes: the critical journeys get covered by tests, every failure captures the page behind it, and the evidence lands where the person picking up the build will see it.

If your team has started skipping a red test because nobody can tell what it means, show us the failing run.