Highwire: Global Component System Overhaul

When I joined Highwire, I saw an opportunity to simplify development, unify design, and clean out years of CSS debt across a sprawling Angular app.


Overview

Highwire’s frontend spanned several microservices, but the visual experience was fractured and inconsistent. Styles conflicted. Components were duplicated. CSS overrides piled up across services.

This wasn't just a technical inconvenience — it impacted our ability to scale, slowed delivery, and created a disjointed experience for users.

My goal: Build a unified, scalable component system that aligned design and development while removing legacy styling at the root.

Role

Lead UX Engineer

Team

Design + Platform

Timeline

Ongoing over 6 months


The Problem

Across the app:

  • PrimeNG, Bootstrap, and legacy styles constantly conflicted
  • !important was everywhere
  • Shared components were inconsistently styled
  • No reliable system for implementing designs

The solution needed to do more than just modernize styles — it needed to scale across teams and services without introducing new tech debt.


1. Starting with Cleanup

I started with subtraction. Using an AI-assisted script, I scanned for unused global CSS classes across our codebase.

CSS usage audit results

The script flagged thousands of dead classes. But many deeply embedded styles — especially from Bootstrap and PrimeNG — had to be removed manually.

One by one, I replaced legacy classes with Tailwind utilities, simplifying the styling model and removing bloat from every layer of the stack.

0
Global CSS lines removed
0
CSS files removed
0
Reduction in file size

2. Tailwind as the Foundation

Next, I introduced Tailwind as our styling foundation — not just as a utility layer, but as the source of truth for component structure and visual language.

To do this, I reorganized our style hierarchy in angular.json. Tailwind now sat at the base, with Bootstrap and PrimeNG layered above it in a way that respected the design tokens and avoided collisions.

angular.json
"styles": [
          "node_modules/bootstrap/dist/css/bootstrap.min.css", // Bootstrap
          "projects/primeng/css/hw-theme.css", // Primeng Theme
          "projects/frontend/src/styles.css", // Global Styles
          "projects/frontend/src/tailwind-styles.css", // Tailwind Styles
          "node_modules/@fortawesome/fontawesome-pro/css/fontawesome.css", // Font Awesome
          "node_modules/primeng/resources/primeng.min.css", // More Primeng
          ...
  ],

Before: global styles loaded in unpredictable order, leading to constant override battles.

angular.json
"styles": [
      "projects/app-frontend/src/styles.css", // Tailwind + Global css
      "projects/app-frontend/src/bootstrap.scss", // Bootstrap
      "projects/ui-theme/src/primeng.scss", // Primeng
      "node_modules/@fortawesome/fontawesome-pro/css/fontawesome.css", // Font Awesome
      ...
  ],

After: Tailwind-first architecture with isolated, scoped overrides from third-party libraries.

Result: Visual consistency, reduced specificity wars, and a foundation for building clean, reusable components.


3. Component Design Strategy

Once Tailwind was in place, I turned my focus to the components themselves.

I worked with designers to identify recurring patterns in Figma — such as cards, buttons, pills — and documented how they appeared across the product. My rule of thumb: a pattern had to appear in 2–3 places before it became a shared component.

UI pattern audit in Figma

Out of that audit came a standardized set of inputs, variants, and visual rules.

A Figma walkthrough of one of the various components I created.

Result: Reusable, composable components with inputs mapped to Figma. Design and dev spoke the same language.


4. Building the Components

By leveraging Angular's signals, I was able to create small, effecient components that could be used across the app.

button.component.ts
export class ButtonComponent {
size = input<'xs' | 'sm' | 'base' | 'lg'>('base');
variant = input<'primary' | 'text' | 'light'>('primary');
color = input<'blue' | 'gray' | 'red'>('blue');
label = input<string>();
roundedFull = input<boolean>(false);

classes = computed(() => {
  const sizeClass = {
    xs: 'px-1.5 py-1 text-xs',
    sm: 'px-2 py-[0.3125rem] text-sm',
    base: 'px-3 py-[0.4375rem] text-base',
    lg: 'px-3 py-[0.5625rem] text-xl',
  }[this.size() || 'base'];

  const colorClass = {
    primary: {
      blue: 'bg-blue hover:bg-blue-700 border-blue text-white',
      gray: 'bg-gray-600 hover:bg-gray-700 border-gray-600 text-white',
      red: 'bg-red hover:bg-red-700 border-red text-white',
    },
    text: {
      blue: 'text-blue hover:text-blue-700',
      gray: 'text-gray-600 hover:text-gray-700',
      red: 'text-red-600 hover:text-red-700',
    },
    light: {
      blue: 'text-blue',
      gray: 'text-gray-500',
      red: 'text-red-600',
    },
  }[this.variant() || 'primary'][this.color() || 'blue'];

  const variantClass = {
    primary: '',
    text: 'bg-transparent hover:bg-gray-50 border-transparent',
    light: 'bg-gray-50 hover:bg-gray-100 border-gray-100',
  }[this.variant() || 'primary'];

  const roundedClass = this.roundedFull() ? 'rounded-full' : 'rounded-md';

  return `${sizeClass} ${colorClass} ${variantClass} ${roundedClass}`;
});
}

6. Storybook + Documentation

Realizing that developers weren't referencing the design docs, I documented every shared component in Storybook, from buttons to pills to cards — with visual examples and usage rules.

Card component variants Button variants in Storybook

Being involved in PR reviews also allowed me to catch any issues with the components before they were merged.

Result: Storybook became the single source of truth for implementation and visual QA.


7. Developer Enablement

I didn't want to be the sole enforcer of the new component system. To drive adoption across the org, I focused on internal education and tooling.

In addition to doing demos and writing docs on best practices, I created ESLint rules to guide developers.

Confluence Docs

Since we hadn't taken time to remove old components, I made it my mission to consolidate them into the new system.

Result: Better dev velocity, less duplication, and shared language across design and engineering.


Outcome

Component usage in Highwire

New Components New Components New Components New Components

Easier to maintain

Tailwind-first system

More consistent

Storybook established

Faster to develop

Design to code speed increased


Takeaways

  • Start with deletion. Cleanup made everything that followed easier to maintain and scale.
  • Don't bite off more than you can chew. I should have started with a smaller scope and worked my way up.
  • Figma and code must match. Keeping design and dev aligned reduces bugs, questions, and wasted effort.
  • Thin overrides > heavy theming. Tailwind let us stay flexible without creating another rigid system.
  • Developer education is infrastructure. The work only matters if other people can use it well.

TL;DR

“I led a full revamp of Highwire’s component styling system — removing 4,000+ lines of legacy CSS, aligning design and dev in Figma and Storybook, and enabling faster, more consistent frontend work across our Angular platform.”