Back to portfolio

Modern AI Solutions · Pathfinder Suite · 2026

UI Patterns Framework

A component framework built to stop ten services from solving the same interaction problems differently. Started as documentation. Shipped as code.

My role UX and front-end lead, small team
Product Pathfinder, enterprise suite
Timeline Q1 through Q3 2026 (Phase 2 complete)
Status Phase 3 active (8+ services remaining)

Problem

Same questions, different answers every time

Pathfinder is a multi-product suite: Tasks, Playbooks, Contacts, Documents, Files, and about a dozen more services sharing one React codebase. We had a component library: buttons, dropdowns, cards, tables. What we did not have was any guidance on how to put them together.

Every service team solved the same interaction problems from scratch. One put Edit in the overflow menu. Another had it inline. A third had no consistent overflow structure at all. Action ordering was arbitrary. Status icons varied. Filter bars were reinvented on every ticket.

The real cost was not inconsistency visible to users. It was the compounding maintenance burden of ten different answers to the same question, each drifting further from the others over time. When our Playbooks list component hit 2,340 lines, it was clear the status quo was not scaling.

The design system had grown the atoms. What was missing was the sentence structure: documented, enforceable patterns for how to combine them, with clear rules for the edge cases.

Users

The framework's users were the people building the product

Pathfinder's end users never saw the framework. The people it served were the designers, developers, and cross-functional contributors building the product. Each group had different friction with the status quo.

Those gaps shaped what the framework had to do: not just document conventions, but enforce them at the component level so the right choice was also the easy one.

New engineers joining the team had no way to understand conventions except by reading the largest, most complex component and making inferences. The framework was meant to make that unnecessary.

Decisions

Three forks in the road

Decision 1

Phased rollout vs. big-bang migration

Once the framework existed, we had to decide how to migrate ten-plus services that were all doing things differently. Fix everything at once, or sequence it?

Option A Big-bang refactor: pause feature work, migrate all services simultaneously.
Option B Opportunistic: publish the framework; teams adopt it when they have capacity.
Chosen Phased rollout: validate on two services first (Tasks and Playbooks), then migrate remaining services in prioritized tiers through Q4 2026.

Why: Option A was too risky. We did not yet know where the framework would break against service-specific data models. Option B was not a migration, it was a hope. By running Tasks first, we caught a real gap before scaling: filter dropdowns needed context-aware options the initial standardized component did not support.

Decision 2

Centralized components vs. documentation only

Most design systems stop at documentation. We had to decide whether the framework would be a style guide or a set of importable, opinionated components that enforce consistency at the code level.

Option A Documentation only: publish design rules and code examples; teams implement per service.
Option B Copy-paste patterns: provide reference code to copy and adapt.
Chosen Centralized components: build StandardActionMenu, ViewModeToggle, useViewMode, and uiPatterns.ts as importable, single sources of truth.

Why: With copy-paste, every copy diverges. An accessibility fix to the action menu would need to be manually applied across ten services. With a centralized StandardActionMenu, one fix propagates everywhere. The tradeoff was developer flexibility, but for patterns that needed to be identical, that flexibility was the problem we were solving.

Decision 3

Which actions to surface inline vs. hide in overflow

Every service had its own answer: some put Edit in the overflow menu, some had it inline, some had no consistent rule. Users were finding core actions in different places depending on which part of the product they were in.

Option A Everything in overflow: cleaner surface, lower discoverability, simpler to implement uniformly.
Option B Everything inline: maximum discoverability, visual noise that scales badly as action sets grow.
Chosen Frequency-based hybrid: Favorite, Edit, and primary action (Start/Run) always inline. Low-frequency and destructive actions always in overflow.

Why: The overflow menu's job is to hold actions you do not use every session. When Edit ends up there, you have hidden the most common thing a user wants to do on a given item. The rule we codified: if you would expect to use it multiple times per session, it lives inline. If it is rare or irreversible, it goes in overflow. Destructive actions always anchor the bottom tier with visual separation.

Tradeoffs

What we gained and gave up

The tradeoff we kept running into was consistency against flexibility. The framework's value came from consistency, but the moment it became too rigid, teams would work around it and the inconsistency would return through the back door.

Real moment of pushback

The filter dropdown problem

During the Tasks migration, the initial standardized filter component failed. Tasks needed context-aware filter options that changed based on item state, something the generic component did not support.

This was the scenario we had been most worried about. If the framework could not accommodate service-specific needs, teams would reject it and build their own thing, and we would end up with more divergence than we started with.

Resolution We updated the framework to support service-specific variants alongside the standard implementation. The rule became: standardize structure and positioning; allow customization of content. The 90% that needs to be identical stays locked. The 10% that is service-specific is documented as a legitimate extension point, not a workaround.

Gained

  • Bug fixes and accessibility improvements propagate to all services automatically
  • 68% code reduction on first full migration (2,340 to 751 lines)
  • New services start from a solved problem, not a blank page
  • Design reviews focus on product decisions, not relitigating conventions
  • Onboarding ramp shortened: the framework is the documentation

Given up

  • Short-term flexibility for individual service teams
  • Upfront investment before any migration could begin
  • Framework team becomes a dependency for significant pattern changes
  • Edge cases must be formally documented as variants, not silently handled

Adoption and Outcomes

What two phases proved

Phase 1 validated the framework on two services before committing to a broader rollout. Phase 2 extended it to Contacts, Documents, and Files, with no significant rework to the core components. Both phases came in without regressions.

68%
code reduction on Tasks vs. Playbooks. Same feature surface, fraction of the maintenance footprint.
5
reusable components shipped and in production: StandardActionMenu, ViewModeToggle, useViewMode, uiPatterns.ts, SortableColumnHeader.
23
services registered in VIEW_MODE_KEYS, covering the full migration target through Q4 2026.
0
regressions introduced. TypeScript-first implementation caught interface mismatches at build time.

Developers noticed the difference. Tasks took a fraction of the usual implementation time, and when bugs surface in shared components, the fix goes out once and reaches every service automatically.

Phase 3 is active now. The question I am watching is whether the variant strategy holds as data models get more complex. Agreements and InfoVaults have edge cases the first migrations did not. Those will tell us whether the framework needs a second generation or just new documentation.

What I'd carry forward

The hardest part of this project was not designing the patterns. It was building the framework before the demand was obvious. It paid off because we proved it on a real service before publishing it, and enforced it through code rather than hoping documentation would be enough.

The lesson from the filter dropdown friction is the one I think about most. A design system that cannot flex will be abandoned. The patterns that stick are the ones that make it easier to do the right thing than the wrong thing, not the ones that make the wrong thing impossible.