Yusra Benhaddou

UI designer · Chicago, IL · Seven years · product UI and design systems

A screen is nine screens.

Every portfolio shows the happy path: a list with six well-named items and a form with nothing wrong in it. That screen is about a ninth of the work. The other eight states are below, built as live markup rather than shown as pictures, with how often each one happens and what it costs when nobody draws it.

Saved addresses · Checkout, account settings, and the delivery step of three different flows

One component, 9 states, all of them rendered.

One component, nine states, all of them live markup on this page. None of these is a screenshot. If a portfolio shows you a picture of an empty state, it has shown you that somebody drew an empty state, which is a different claim.

Saved addresses 3 saved
Home 2119 W Division St, Chicago, IL 60622
Work 540 N Michigan Ave, Suite 1120, Chicago, IL 60611
Mum 84 Rue Ibn Batouta, Casablanca 20250
Ideal 61% of sessions It is the only one in the mockup, and it is the one nobody needs help with.
Saved addresses
No addresses yet Nowhere to send anything.

Add one now, or skip and enter it at checkout. Both take about the same time.

Add an address
Empty Every new account, once Left to the engineer it becomes a blank box. It is the first thing a new user ever sees and it is the cheapest state to get right.
Saved addresses  

Loading saved addresses.Skeletons at the real row height, so nothing moves when the data lands.

Loading 100% of sessions, briefly A spinner where a skeleton belongs. The layout jumps when data lands, which reads as slower even when it is not.
Saved addresses
Could not load Your addresses did not load.

The address service timed out. Nothing has been lost — this is a read, not a save.

Try again
Error 2.4% of loads Needs a real error string, which means asking an engineer what can actually fail. Skipping that conversation is how “Something went wrong” gets shipped.
Saved addresses
Offline You are not connected.

These three are the ones your device had already. You can pick one; saving a new one has to wait.

Home 2119 W Division St, Chicago, IL 60622
Offline 0.9% of sessions Assumed to be the browser’s problem. The browser shows a dinosaur; your form still says Save.
Saved addresses
No access This account cannot see saved addresses.

Shared accounts hide them by default. Whoever owns the account can turn it on — your data is there.

No permission 3.1% of sessions Requires knowing the permission model. Undesigned, it is usually an empty state, which tells a user their data is gone.
Saved addresses 4,182 saved
Home 1 2119 W Division St, Chicago, IL 60622
Work 2 540 N Michigan Ave, Suite 1120, Chicago, IL 60611
Mum 3 84 Rue Ibn Batouta, Casablanca 20250
Home 4 2119 W Division St, Chicago, IL 60622
Work 5 540 N Michigan Ave, Suite 1120, Chicago, IL 60611
Mum 6 84 Rue Ibn Batouta, Casablanca 20250
Home 7 2119 W Division St, Chicago, IL 60622
Work 8 540 N Michigan Ave, Suite 1120, Chicago, IL 60611
Mum 9 84 Rue Ibn Batouta, Casablanca 20250
Too much 4% of accounts Designed with three rows, shipped to somebody with 4,000. The scroll container, the search and the count all have to exist before that happens.
Saved addresses
Work Benhaddou-Vandermolen Facilities Management Cooperative of Greater Cook County (Northside Br.), 540 N Michigan Ave, Suite 1120, Chicago, IL 60611

94 characters, in a slot drawn for 30. It wraps rather than truncating, because an address you cannot read in full is not an address.

Too long 11% of records A 94-character name in a slot drawn for 30. Either it truncates on purpose or it breaks the layout by accident, and one of those is a decision.
Saved addresses
Home 2119 W Division St

The same component at 200%. Nothing is clipped and the row action is still one target.

At 200% zoom WCAG 1.4.4, always A conformance requirement, not a nicety. Most component libraries fail it at the first fixed pixel height.

The frequencies are from one product over one year, and the point is not the exact numbers — it is that eight of the nine happen often enough to have a number at all. A state reached by 2.4% of loads is reached by thousands of people a month, and it is the state they will tell somebody about.

6 shipped products · 54 cells · 8 missing

Where the states actually are.

Fifty-four cells. Browser default is not a failure — sometimes the browser’s behaviour is genuinely correct and drawing over it is worse. Missing is a failure, and there are enough of them here that leaving them off would have made the table decorative.

Designed

Drawn, specified, and in the handover file.

Browser default

Left to the browser on purpose. Sometimes that is the right answer.

Missing

Nobody decided. This is the only one of the three that is a failure.

Every cell says a word — the colour is only a shortcut
Product IdealEmptyLoadingErrorOfflineNo permissionToo muchToo longAt 200% zoom
Checkout, retailThe one that was resourced properly. Offline is still the browser’s, and that was a decision rather than an oversight. DesignedDesignedDesignedDesignedBrowser defaultDesignedDesignedDesignedDesigned
Internal admin consoleInternal tools get the least state work and have the most permission states. This one had eleven roles and three designed permission screens. DesignedDesignedDesignedBrowser defaultMissingDesignedDesignedBrowser defaultMissing
Booking flow, clinicHealthcare, so zoom and error strings were audited by somebody outside the team. Too-much-data was left because a patient cannot have 4,000 appointments. DesignedDesignedDesignedDesignedDesignedDesignedBrowser defaultDesignedDesigned
Reporting dashboardThe worst on this list and the best-looking. Every screenshot of it is full of data, which is exactly the problem the page is about. DesignedMissingDesignedBrowser defaultMissingBrowser defaultDesignedMissingBrowser default
Design system, v2A system is where states are cheapest to fix, because you fix them once. Eight of nine designed, and the ninth is deliberate. DesignedDesignedDesignedDesignedBrowser defaultDesignedDesignedDesignedDesigned
Marketing site CMSTwo weeks and no budget for states. I am including it because a portfolio of only the well-funded work is a portfolio of budgets. DesignedBrowser defaultBrowser defaultBrowser defaultMissingMissingBrowser defaultMissingBrowser default

“The handover had the empty state, the permission state and a 94-character company name already in it. I have built maybe forty of these and it is the first time I have not had to invent half of it myself on the Thursday.”

Front-end engineer, retail checkout — 2025

Under the component

Eight numbers that decide how it feels.

None of these are visible in a screenshot and all of them are visible in use. Every one is a decision that somebody makes, and when a designer does not make it an engineer does.

All eight are live on this page, not just described on it
DecisionHereWhy that number
Minimum hit target 44 × 44 px WCAG 2.5.8 asks for 24; Apple and the actual behaviour of thumbs ask for 44. The row action here is 44 and the visible icon inside it is 20.
Type scale ratio 1.2 Minor third, six steps, 14 to 34 px. A 1.25 or 1.333 scale looks better on a specimen sheet and produces a heading that is too big three levels down.
Base line height 1.55 On body copy. WCAG 1.4.12 requires that 1.5 does not break the layout, which is a different and stricter thing than choosing 1.5.
Table collapses at 640 px The width at which this row stops being a row and becomes a stacked block. Chosen from the longest realistic address, not from a device size.
Longest content tested 94 chars The longest organisation name in the real database. Every text slot on the page was checked against it, which took an afternoon and found four breaks.
Focus ring 3 px, 2 px offset Same ring everywhere, never removed, and checked at 3:1 against every background it can land on. It is the single most commonly deleted line of CSS in this trade.
Motion 120–180 ms Long enough to be seen, short enough not to be waited for, and all of it inside a prefers-reduced-motion query that turns it off completely.
Colours carrying meaning 0 No state on this page is communicated by hue alone. Every one prints a word, and the audit cells carry a glyph as well.

Open the State Checklist →

What arrives at the end

Five things, and the last one matters most.

Every state, drawn
All nine, per component, in the same file. Not a happy-path screen with a note saying “also empty state”. The note is where the state goes to not get built.
The real strings
Error copy agreed with whoever knows what can fail, before handover. “Something went wrong” is what gets written when that conversation did not happen.
The longest content in the database
An actual query result, not filler text. Half the layout breaks in a product are one record somebody typed a full legal entity name into.
Tokens, not hex codes
Named to their role rather than their colour. A token called `--danger` survives a rebrand; one called `--red-600` becomes a lie the first time the red changes.
Two hours with the engineer
Booked before the last week. Everything above is worth less than this, and it is the first thing cut from a schedule.

Common questions

Mostly about the other eight.

Why not just show the finished screens?

Because the finished screen is the part of the job everybody can already do. The reason a product feels unfinished is almost never the ideal state — it is the empty one that is a blank box, the error that says “Something went wrong”, and the table that breaks when somebody has four thousand rows.

Are these really rendered, or are they images?

Rendered. Everything in the nine-state section is live markup in this page, which you can confirm by resizing the window or by opening the inspector. A picture of an empty state proves somebody drew one; it does not prove it works.

Is “browser default” a failure?

No, and that is why it is its own verdict rather than being folded into Missing. Sometimes the browser’s behaviour is genuinely the right answer and drawing over it is worse — the offline page is the usual example. It is a failure when nobody decided, which is most of the time.

Do you work in Figma or in code?

Both, and the states are the reason. Nine states across a component set is where a design file becomes unmaintainable and where a coded prototype starts paying for itself. I hand over both and the code is the one that is correct.

What about the dashboard on this page that scores badly?

It is the best-looking thing I have made and it has three missing states. Leaving it off would have made the audit decorative, which is the exact failure mode of every portfolio that only shows work that went well.

Can you work with our existing design system?

Usually the fastest route, yes. The first thing I would do is audit it against these nine states, which takes about a day per component set and generally finds that the ideal state is beautiful and the other eight do not exist.

Hire me

Which component, and which state is broken?

Two days a week available from April. Fields marked with an asterisk are required.

This demo form posts nowhere. Point site.formAction at your own endpoint.