Shared UI infrastructure
Shared design system for public services
A library of reusable interface components that gives public-service applications consistent navigation, forms, feedback and accessible interaction across frameworks.

The library builds on an existing public-sector design system and web kit. Lit-based Web Components provide shared controls, while dedicated Angular libraries support the framework versions used by different teams.
01 / The library
Shared building blocks for everyday services
The library includes components such as notifications, pagination, dropdowns, expandable panels, cards, progress indicators, dialogs, autocomplete and shared headers and footers. Applications compose these building blocks into their own service workflows.
The starting point was the existing design system: its citizen-centred design guidance and public web kit supplied the visual language and interface patterns. On top of that foundation, the team built a Lit-based Web Component library and dedicated Angular libraries for the different Angular versions used by consuming applications.
02 / Framework support
Shared controls, version-specific Angular libraries
I needed to make shared controls work across applications using different Angular versions. The existing web kit supplied the design rules, but the applications still needed reusable behavior and framework integration.
I helped build the Lit-based Web Component library and its Angular integrations on top of the existing design system. I worked on properties, events and reactive updates, with dedicated Angular libraries supporting the versions used by consuming applications.
Sharing the underlying control reduces duplication. Each Angular integration still needs to handle reactive properties, event payloads, forms and lifecycle behavior for its supported version.
03 / Accessibility
Build keyboard and screen-reader behavior into the controls
A dialog or autocomplete can look correct and still be unusable with a keyboard or screen reader. Repeating that component across applications repeats the defect and makes each consuming team responsible for finding it.
I treated keyboard interaction, semantics, accessible names and focus behavior as requirements from the start. I used native semantics where possible and made custom interaction behavior part of the component contract, rather than leaving accessibility to each consuming application.
This requires more work when a component is first built, but avoids repeating it in every application. Focus order and keyboard behavior are part of compatibility: changing them can break an existing workflow even when the component looks the same.
04 / TypeScript APIs
Use TypeScript to define supported component usage
Reusable components have inputs, outputs and supported configurations. Weakly typed values push mistakes into runtime behavior, where an invalid option or misunderstood event can fail differently in every application.
I used TypeScript to define component properties, event payloads and supported configurations. Exposing those types through the Angular libraries let consumers catch incorrect usage during development instead of discovering it through broken interactions.
These contracts catch incorrect values and event handling in typed applications. Data arriving from JSON or JavaScript still needs runtime validation; compile-time types cannot check it.
05 / HTTP requests
Bypass host interceptors for external resources
An Angular application can apply authentication, URL rewriting or other policies through HttpClient interceptors. A library request for an external resource can be unintentionally changed by policies intended for the host application’s APIs.
I used an HttpClient constructed with HttpBackend for external-resource requests that needed to bypass the host’s interceptors. This kept API-specific authentication and URL transformations from being applied to those library requests.
This also skips logging, retries, error handling and any other policy supplied by interceptors. Requests using this path therefore need an explicit decision about which of those behaviors to provide separately.
06 / Customization
Limit customization to supported design-system patterns
Unlimited styling and behavioral overrides let each application create its own version of a shared control. That undermines consistency and can invalidate assumptions about focus, contrast, layout and interaction.
I limited extension points to variants and composition rules supported by the design system. Consumers could use public properties, events and documented styling options without depending on internal markup or introducing unsupported behavior.
A new interaction pattern needs to be agreed and maintained in the shared library. This takes longer than a local override, but keeps applications aligned and prevents consumers from depending on internal markup.
Evidence & further reading