Details Page Framework
Cut recurring custom-page requests to a quarter of their rate by giving a schema-driven platform the vocabulary to express meaning.

Company
Locus
Timeline
3 weeks design, then a phased rollout
5-warehouse pilot → 5-month live rollout across all clients
Role
Senior Product Designer
Team
1 Product Manager 4 Engineers (FE+BE)
At a glance
Outcome
Detail pages became a governed, reusable platform capability instead of a recurring per-client design problem.
~1
Custom-page requests down from ~1/month
7
Detail types reused across multiple entities
~3x
Time to first action on the page
Context
Locus is a logistics orchestration platform. Operations teams use it to plan routes, manage pickups and deliveries, run warehouses, and coordinate fleet and workforce. Every object they touch, an order, a shipment, a driver, a route, has its own detail page.
We had re-architected those pages around a schema-driven model: every detail page renders from a JSON schema through one shared renderer, with no per-entity code. That is what lets Locus introduce a new entity without building a new screen.

Each entity is a JSON schema. One renderer turns them all into detail pages.
Before the re-platform, each entity had handcrafted pages with character. A route felt like a route, a contact felt like a person. Schema-driven UI solved the engineering problem and stripped that character out. Architecturally the right call, but it left every entity looking identical.
Scale at the time
14 entities
Live in the platform, with 20+ planned
~3M
Across top 3 detail pages over 180 days
One shared renderer carried the entire platform. That was the bet, and the constraint.
Detail pages were the most-used surface on it. Order details alone drew 2.57M views over 180 days; Tour and Quote details add another 0.51M. Coordinators return to these pages dozens of times a shift, and every second spent searching compounds across hundreds of time-sensitive decisions.
The problem
V1 shipped. It technically worked. It just did not communicate.
This surfaced before V2 even started. On the first day of rollout to a couple of client warehouses, the feedback was mostly bad: every page looked the same, and anything urgent was hard to find. I watched two TAMs press Ctrl+F, right in front of me, to find something on a page they had seen hundreds of times.
"All the detail pages look the same."
"I can't tell what I'm looking at."
"It is very difficult to find what I am looking for."

Ctrl+F on a page you've seen hundreds of times. That's a design failure, not a user one.
The middle quote is the one that mattered. Not "I can't find it" but "I can't tell what I'm looking at."
This was a recognition problem, not a navigation one.
Take a simple question: "Is this shipment late?" The answer was on the page. To get it, a user had to find the planned pickup time, find the actual pickup time, and calculate the difference themselves. Two fields. No logic. No answer.
The schema described what fields exist, never what they mean, so the renderer listed every entity the same way: same headings, same columns, same everything.

Routes, contacts, timestamps, all flattened into the same label-value grid.
The platform was scaling. The framework wasn't.
Why this was a platform problem, not a page problem
What clients asked for was simpler than customization. They wanted to consume the same data better: tell entities apart, find the urgent thing, stop scanning. No one asked for bespoke pages.
Both naive fixes were traps. Restyling was cosmetic. Building custom pages per client would have read better, but recreated exactly the fragmentation the schema-driven platform was built to eliminate: duplicated effort, inconsistent patterns, higher maintenance, slower onboarding.
So I reframed the ask. Delivering comprehension meant the platform had to express the same data differently per entity, because a route is a journey and a contact is a person, and the flat schema flattened that distinction.
That turned a findability complaint into three governance questions: what can flex per entity, what must stay consistent, and who decides when the system needs a new pattern.
What I ruled out
My first instinct was that this was a visual problem. The pages looked flat. Maybe better structure would fix it. So I explored nested containers, hierarchy lines, indentation, more whitespace. Each a genuine attempt to add visual order to the flat grid.

I brought these to the design team. The lead designer looked at them and said:
"Why are we even working on beautification?"
He was right. Every exploration was cosmetic, rearranging the surface without touching the structure underneath. Same problem, more decoration on top.
So I took the most complex entity, shipment details, and did the opposite of what schema-driven development demanded. I handcrafted the page. No schema constraints, no renderer rules. Not to ship it, but to diagnose what the framework actually needed to support.

Two gaps surfaced immediately.
There was no way to group related information without adding a heading, so data that belonged together sat in the same flat grid as everything else.
And there was no way to represent data meaningfully: a route wasn't a journey, a contact wasn't a person, every field looked identical, so users had to hold the whole page in memory.

Design principles
I worked from four principles.
Express, don't decorate
The fix had to live in what the schema could say, not in surface styling.
Flexibility, but bounded
Teams compose freely from primitives, never invent unbounded one-offs.
Reuse over bespoke
A new pattern earns its place by working across entities, not by serving one.
Govern the edges, not the center
Keep structure predictable; let meaning and grouping flex.
The solution: teaching the schema to express meaning
Slots
Slots are containers within a heading level that group related fields intentionally, independent of column layout. Origin node and pickup time in one slot, destination and delivery time in another. Each group stays together and sizes to its content.
Slots
Detail types
Detail types define what a group of fields represents, giving the system vocabulary to present data by meaning, not just structure. Any movement renders as a visual path: origin, stops, destination, with on-time status inline.
Detail types
Why two primitives, not one?
Keeping the two separate was deliberate: a new entity can reuse any existing detail type in any slot configuration without either primitive changing. Before, the schema could only say "here are four fields." Together, the primitives let it say "this is a journey, show it like one."

Detail types on the left. Slot-based layout on the right. Any detail type plugs into any slot.
The same principle applied one level down. I redesigned the label-value pair to carry meaning: a status tag next to a date answers "on time or late" without mental calculation; supporting text adds context without adding fields.

Before: two fields, no logic. After: one answer.
Semantic systems are harder to govern than visual ones. The risk: teams treating slots as layout hacks, or requesting one-off detail types, turning governed customization back into custom pages. So the gate. A new detail type needs joint design-engineering review, must prove reuse across at least two entities, and can't duplicate an existing one. The two-entity rule is a guardrail, not a law, but the bar for exceptions is high.
The pushback
The primitives only mattered if engineering would build them. Backend team's first response was no.
"You're asking us to change the schema for every entity. With these deadlines, that's not happening."
It was a fair objection. Adding semantic properties across all entities at once meant a simultaneous schema migration, exactly the risky, all-at-once change that slips deadlines and breaks live pages. If I'd insisted on the full vision up front, the framework would have died here.
So I changed the shape of the ask. The new properties would be optional and backward-compatible: existing pages keep rendering through the default model, untouched, while only the three highest-traffic entities adopt slots and detail types first. That reframed it from "rewrite the schema contract" to "extend it, incrementally, starting where the traffic is." Engineering could say yes, because it de-risked their side without compromising the design.
The lesson
A platform design is only as real as engineering's willingness to build it. The win wasn't the primitives. It was finding the version engineering could ship without betting the quarter on it.
What shipped
I created reference outputs for all 14 entities to stress-test whether the same primitives could handle 14 different data shapes, then we implemented Order details first, iterating until the renderer output matched intent. Once the schema-to-renderer contract was clear, engineering implemented the rest with lighter involvement from me.
The framework spans all 14 entities, but we customized only where it paid off. Semantic treatment is expensive, so we invested it by traffic: orders and shipments got the full treatment; the long tail keeps the default layout. A few entities, like Team, had no data worth grouping, and there the default is the right answer, not a gap.
The goal wasn't richness everywhere. It was to add meaning where it exists.
Before & after
Before
The schema rendered data
Every entity looked identical, because to the system, every entity was identical. Finding anything meant scanning a flat grid top to bottom.
After
The schema communicated meaning
Related fields group into slots. A route renders as a journey. On-time status reads inline. The same data, recognized instead of searched.

Same data. Different comprehension speed.
Result
The framework shipped in two stages: a small pilot first, then a measured live rollout across all clients.
Pilot
5 warehouses, 3 clients, 2-week field follow-up.
I went to all five warehouses to hear directly from dispatchers, who the TAM tests couldn't stand in for. They disliked the orders and trips pages in the first week, then found them easier as they kept using them. The new representation "made more sense" but they couldn't always say why.
When structure does its job, comprehension registers as a feeling, not an observation.
Users don't see the slots. They just stop searching.
Live rollout
5 months, all clients
Once the pilot held, we rolled out everywhere and measured under real load.
Impact measured
~1
Custom-page requests down from ~1/month
7
Detail types reused across multiple entities
~3x
Time to first action on the page
4.1
Ease of finding information up from 2.7
A note on the data:
Time-to-action and survey figures are from the live rollout; deflection is from request logs. The "~1 month → ~1 week" entity stand-up time is an estimate from the entities we built, not instrumented.
What I'd do differently
Instrument the behavioral proxies properly
I tracked repeat-visit intervals and scroll-then-search patterns as signals, but didn't lock down a clean before/after baseline. Directionally useful; a real delta would have made them defensible.
Pull dispatchers in earlier
I validated with TAMs first because they were fast and available, and only reached dispatchers at the pilot stage. The people making time-pressured decisions on these pages all day might have surfaced problems sooner.
The takeaway
A new comprehension need is now met by configuring the schema with existing primitives, not by building a custom page. Detail pages stopped being a recurring design problem and became a governed, reusable platform capability.
The same structure that helped experienced users find information faster also helped new users interpret the system with less prior knowledge.
We optimized for recognition. We got learnability for free.