52 scored dimensions across 29 products, from 5,817 lines of teardown research and 434 screenshots.
Machine readable version: FEATURE-MATRIX.json.
3 SPOTS LEFT, which in a Pilates room cannot distinguish a six machine studio from a twelve machine one.yes or no only from an explicit statement in a findings file, or from a screenshot opened and read during this pass. Everything else is unknown. Two claims were resolved this way: Kimai's landing screen and Cloudscape's in product topbar.not_scored. A small honest denominator was preferred over a padded one.Denominator bases. Admin products evaluable: 14. Plus two component galleries where metric dimensions apply: 16. Products with an observed table: 10. Products with an observed record detail: 6. Booking platforms walked live: 5, of which class booking products: 4. Published design systems surveyed: 8, of which two could not be read.
| Feature | Count | Who has it | So what |
|---|---|---|---|
| Opens with a row of equal KPI cards | 5 of 14 | Ant Design Pro, Tabler, shadcn admin, Umami, Refine | All five are ranked among the least distinctive screens in the set. The correlation is stated outright in the research: a KPI row is what you reach for when you have not decided what the user's first action is |
| Sidebar is collapsible | 6 of 10 | Grafana, Cloudscape, Refine, Ant Pro, shadcn, Umami | Table stakes. Its absence would read as an omission, not as restraint. Ant Pro's handle straddling the sidebar edge is the one worth copying |
| Sidebar uses text group headings | 2 of 10 | Umami, shadcn admin | The field is against them. Refine states the threshold: eight items do not need headings. Our studio app crosses it, the portal does not |
| Global search in the topbar | 3 of 14 | Grafana, shadcn admin, MUI template | A differentiator, not a catch up. Member lookup by name is the most repeated front desk action in our product and 11 of 14 admin products do not offer a global search at all |
| Command palette surfaced | 2 of 14 | Grafana, shadcn admin | Low value as built. Plausible instead prints a one letter key cap beside all fourteen options in its date menu, which is the only keyboard affordance surfaced in the UI anywhere in the teardown |
| Breadcrumb | 4 of 14 | Grafana, Cloudscape, MUI template, Evidence | Three of the four products that skip it are the three ranked most distinctive. Atomic CRM, Ant Pro and Plausible all independently ship "no page title, no breadcrumb, no greeting" |
| Page or record level tabs | 5 of 14 | Cloudscape, Ant Pro, Refine, shadcn, Plausible | Low confidence, 7 unknowns. Cloudscape's variant is the one to take: a summary card of four identity facts pinned above the tab strip so you never lose which record you are in |
| Density or compact toggle | 3 of 14 | Cloudscape, Kimai, Ant Pro | Rare. Follow Cloudscape's published rule rather than the count: comfortable by default, compact in the HQ console only, never in the studio app where it would shrink touch targets |
| Ships both light and dark | 8 of 16 | Cloudscape, Atomic CRM, Tabler, shadcn, Mantine, Tremor, Umami, MUI | Exactly split. Grafana ships dark only and Plausible light only, and both are ranked at the top of the distinctiveness list, so one committed theme per surface is defensible |
So what for the shell. The distinctive products delete chrome rather than arrange it. Plausible has no navigation at all, Atomic CRM has four top tabs and no sidebar, Evidence has a two item text rail. shadcn admin stacks three navigation levels before any data and ships two visibly disabled tabs, and it is the one the client called average.
| Feature | Count | Who has it | So what |
|---|---|---|---|
| Whole row click opens the detail | 3 of 10 | react-admin, Atomic CRM, Refine | The majority put navigation in the first cell or in an overflow menu. Cloudscape's published rule says first cell link plus whole row hover, and only its own product follows it. This contradicts our current spec, which says the row is the hit target |
| Detail opens as a drawer or side sheet | 0 of 6 | nobody | The single most decision changing count in the admin set. Every one navigates to a full page, and Cloudscape's own decision table uses our exact case ("who is booked into a class, seen from the schedule grid") as the worked example for a side sheet |
| Checkbox selection with batch actions | 6 of 10 | Cloudscape, Kimai, react-admin, Refine, Ant Pro, shadcn | Expected where batch actions exist. Cloudscape's disabled but visible header buttons beat a bar that appears on selection, because you can see what selection would enable before selecting |
| Filter state persists in URL or session | 3 of 14 | Grafana, Kimai, Plausible | Nearly free and nearly nobody does it. 0 of 14 ship a named saved views feature; the closest anything comes is Plausible's shareable filtered URL |
| Column chooser | 3 of 10 | Cloudscape, react-admin, shadcn admin | HQ console only. Every column on a roster is load bearing, so a chooser there is a way to break the screen |
| Multi column sort with visible precedence | 1 of 10 | Cloudscape | Cloudscape's numbered 1▲ 2▲ indicators appear nowhere else in the teardown. Its own rule says single column by default and multi only for cross dimensional analysis, which is exactly the HQ console |
| Detail is the edit form, no read mode | 1 of 6 | react-admin | Removes an entire screen, an entire mode and an Edit button from the build. Keep its maximum separation of Save and Delete, drop its disabled until dirty Save per Cloudscape's rule |
| Designed empty or null state observed | 5 of 14 | Grafana, Cloudscape, Kimai, Atomic CRM, Refine | Grafana's is the best in the set: No data rendered as the stat's value in the same 40px type a real number would use, so the layout never reflows between loading, empty and populated. Tabler ships a route called "empty" that is literally blank |
So what for tables. Two rules from the same source resolve the recurring conflict: hover the whole row so the eye can scan it, navigate from the first cell so checkboxes, links and inline buttons can coexist in the row. And the drawer count is the opening: nobody in either product set keeps the list visible behind the record.
| Feature | Count | Who has it | So what |
|---|---|---|---|
| The metric row is also the chart's control | 1 of 12 | Plausible | Six metrics in 70px that double as the chart's tab bar, against shadcn's 130px for four that do nothing. This is the direct answer to the client's rejection of the KPI row and it is essentially unique |
| Comparison baseline printed as a number | 1 of 9 | Metabase | up 0.9% vs. previous month: 1,217. Everyone else ships +20.1% from last month and makes you work out what it is 20.1% of. One extra field, and it is the difference between a number and a fact |
| Heat strip or segment small multiple | 1 of 16 | Tremor | Ninety segments, 7px wide, 24px tall, one per day, with 90 days ago and Today as the only axis. Ninety data points, one line of type, zero chart chrome |
| Metrics live in the chart's legend slot | 1 of 16 | Tabler | Removes the metric row instead of redesigning it. The numbers move into the legend slot the chart needed anyway, colour bound to the lines |
| Metrics set into the page header band | 1 of 16 | Ant Design Pro | Zero vertical space spent on a metric row. Includes a rank rendered as a fraction, 8 / 24 |
| Annotations on a time series | 2 of 12 | Grafana, Plausible | Only Grafana pairs the marked chart with a bound event log beneath it, so you read the shape above and the reason below without a tooltip |
| Sparkline inside a summary metric | 5 of 16 | Refine, Ant Pro, Tabler, Tremor, MUI | Three separate good ideas across five products and no product combines them: Refine's shared baseline, MUI's direction coloured fill, Tabler's dashed previous period ghost line |
| Sparkline or bar inside a table cell | 2 of 10 | MUI template, Tabler | A whole trend per row without leaving the table. Plausible's related trick is cheaper still: the proportional bar is a tinted background on the row itself, so the encoding costs zero vertical space and the row stays clickable |
| Metric written as a sentence | 8 of 16 | Grafana, Cloudscape, Ant Pro, Tabler, shadcn, Mantine, Plausible, Evidence | Exactly half. Proves the pattern is normal, not eccentric, which matters because it is one of our two signature candidates |
| Gauge, dial, half donut or ring | 4 of 16 | Refine, Ant Pro, Tabler, Mantine | The research is emphatic against all four for this product. Refine's speedometer spends a 380 by 360 panel on one percentage and its needle implies precision the data does not have |
| Chart tooltips | 0 yes, 0 no, 16 unknown | not answerable | A screenshot harness cannot capture hover. This is a method limitation, not a finding, and it is the one thing on this list still to verify with a live pass |
So what for metrics. The three best answers to the four card row all cost less space than the row they replace: Plausible's control band (70px for six), Tabler's legend metrics (zero added), Ant Pro's header band metrics (zero added). None of them is a better looking card.
| Feature | Count | Who has it | So what |
|---|---|---|---|
| Skeleton loading rather than a spinner | 1 yes, 1 no, 14 unknown of 16 | Grafana skeletons, Plausible spinners | Mostly unanswerable from static captures. Material is the only design system publishing numeric thresholds and they are testable, so use those instead: nothing under 200ms, skeleton to 5s, determinate progress beyond |
| Any keyboard shortcut surfaced in the UI | 3 of 14 | Grafana, shadcn admin, Plausible | Plausible's date menu prints a key cap beside all fourteen options, teaching the shortcut at the moment of use rather than hiding it in a help modal. The other two just show a chip in a search box |
So what for craft. Motion, focus states, route transitions and toast versus inline confirmation are not observable from a screenshot harness and are not scored here. They are covered instead by findings/03-motion-craft.md, which reads real source constants (Sonner, Vaul, Motion) rather than inferring from images, and by the prescriptions below.
| Feature | Count | Who has it | So what |
|---|---|---|---|
| Capacity denominator shown | 0 of 4 | nobody | The single biggest opening in the category. Two studios were so short of it that they typed "Limited spots available per class" into their own hero band by hand, where it is unverifiable and never updates |
| Waitlist position or depth shown | 0 of 4 | nobody | Join waitlist and Waitlist are the whole story anywhere in the set. It is the difference between joining and giving up |
| All three free before commitment | 0 of 4 | nobody | See fullness, see price, book, without an account. The compound test nobody passes |
| Apparatus or equipment modelled | 0 of 4 | nobody | Walla sells "bike spots" for cycling on its own modality card and offers no equivalent on its Pilates card. The specialist has said out loud that this does not exist for reformers |
| Spot or machine pickable | 0 of 4 | nobody | Same source. Members have a preferred machine and no platform lets them say so |
| Level is a real field | 0 of 4 | nobody | Proven three times on three platforms: CultReformed: Reformer Level 1.5, Group Apparatus 1, 37th St -New Client Orientation Class $20. Level, price and location all get smuggled into a text field |
| Teacher is browsable | 0 of 4 | nobody | Every product renders the teacher as a 20px circle and a first name. The consumer app members arrive from, Alo, puts INSTRUCTORS in its primary navigation |
| Studio controls typography inside the widget | 0 of 3 | nobody | Measured on Arketa: the studio owns everything down to a 1px border it drew itself and nothing one pixel inside it. The only studio specific token in the whole widget is a utm_source parameter |
| Cancellation policy is its own field | 0 of 4 (3 unknown) | nobody | Punchpass concatenates the class description, the parking directions and the refund policy into one free text blob |
| Fullness visible before login | 1 of 4 | Punchpass | And it paints it backwards: CLASS FULL is white on solid teal, 1 SPOT LEFT is a quiet grey chip. The urgent state is loud and the scarce state is quiet |
| Price on the schedule row | 1 of 4 | Momence | And it puts the price in the top right corner and the button in the bottom right of the same panel, roughly 480px apart with a rule between them |
| Guest booking, no account demanded | 1 of 4 | Momence | The only genuine guest checkout in the set. Punchpass walls before availability, which is the worst possible position; Arketa walls after a $473.02 total |
| Default view is a seven column week grid | 1 of 5 | Arketa | Right shape, wrong content. The other four are two agendas, one single day view and one month grid. Nothing in the category treats a weekly slot as a relationship a member has |
| Studio timezone stated on the row | 1 of 4 | Arketa, mobile only | Its desktop grid is the degraded one. Momence's schedule and its own checkout, one click apart, disagree by five hours for the same class |
| Substitution shown inline on the row | 1 of 4 (3 unknown) | Punchpass | Called the best single detail in the category, and it costs nothing. It exists because members care who is teaching |
| Reviews or ratings anywhere | 1 of 4 | ClassPass | 12,500 ratings, a histogram, and every review tied to a named class. No studio software has any review concept at all |
| Empty state names a recovery action | 1 of 4 | ClassPass | A live Arketa studio returns No Classes for all seven days with no explanation and no next available date |
| Paginates inside a single week | 1 of 3 | Momence, as a defect | Caused by a 583px phone layout pinned into a 1280px viewport |
| Price visible without an account | 3 of 4 (1 unknown) | Punchpass, Momence, Arketa | The category does disclose price. It is availability it hides |
| Waitlist offered at all | 3 of 4 (1 unknown) | Punchpass, Momence, Arketa | Table stakes. The gap is not the waitlist, it is everything the waitlist refuses to say |
| Platform stamps itself on the member screen | 3 of 3 | Punchpass, Momence, Arketa | In the page title, in the footer, and in Arketa's case as a badge directly under the payment form |
| Named layout defect at 390px | 5 of 5 | all of them | Every member arrives on a phone and every product breaks something there. Momence spends roughly 470px of an 844px screen on filters before a single class appears |
So what for booking. The category hides the one number that decides the booking and shows the one number that decides nothing. Fullness is communicated by the verb on a button in three of four products, while the studio side of the same platform is sold a "Capacity filled 20.4%" tile. The number exists, the studio sees it, the member never does.
Counts across the eight systems surveyed. Two of the ten intended sources could not be read (Atlassian is client rendered and returned only navigation chrome; legacy Polaris usage guidance no longer resolves), which is why some denominators are five rather than eight.
| Prescription | Count | So what |
|---|---|---|
| Publish a KPI card or stat card component | 0 of 8 | The component the client rejected does not exist in any mature system. What they offer instead is Cloudscape's key value pairs, a one to four column label over value grid inside one container with no per metric chrome |
| Publish numeric loading thresholds | 1 of 8 | Only Material: nothing under 200ms, loading indicator 200ms to 5s, progress indicator beyond 5s. They are the only testable numbers available, so they become our acceptance criteria |
| State one primary button per page or section | 3 of 8 | Nord's per section scope is the version that survives a real booking page, which has several independent commit points |
| Agree on empty state anatomy | 4 of 4 that document it | Visual, heading, description, primary action, secondary action. They split on alignment (Primer centres, Carbon left aligns and publishes why) and on button weight (Cloudscape says always secondary) |
| Call the family "toast" | 0 of 5 | The client was right. Carbon calls it Notification with toast as one of seven types; Cloudscape calls it Flashbar and has no transient variant at all |
| Auto dismiss their transient notification | 2 of 3 that publish a timing | Material publishes the argument against: auto dismissing messages are inaccessible to people who need more time. Our front desk is the exact at risk case, since they are looking at a member, not the screen |
| Scale icons from one master rather than redrawing | 1 of 5 | Nord scales from a 20px grid, which is the domain match: its icons exist to sit inline with small text, exactly like ours |
So what. Two of these override the observed counts outright. Nobody ships a KPI card component, and nobody in the systems literature thinks a toast should be the record of what happened.
From findings/04-distinctiveness.md, which audits design/style/ against the same evidence base.
| Measure | Value | So what |
|---|---|---|
| Tells found in our own system | 14 of 25 catalogued | We avoid every tell on our own anti pattern list and are still anonymous, because avoiding tells is not the same as making decisions |
| Component rules using the display serif | 2, across roughly 10,000 lines, both at or below our own 20px floor | The brand's voice is declared in the tokens and absent from the product |
| Type scale range in the product band | 2.08x | Against Linear at 4.4x in one block and ClassPass at 5x inside one sentence. No size in our system has the job of being looked at first |
Occurrences of --accent |
102, plus 25 of --accent-soft |
A colour used 102 times is a background. No colour in our system appears exactly once, which is the single mechanism that makes Linear's amber work |
| Prescriptions in section 7 of the design system | 0, against 13 prohibitions | A system defined only by what it refuses converges where every careful system converges, which is "good but average" |
Symmetric auto-fit grids |
6 | Every collection in the product is an evenly divided row that cannot have a focal point by construction |
| Motion tokens in genuine use | 61 micro, 17 component, 9 overlay, 4 springs | A real hierarchy, and it beats shadcn admin and MUI outright. Do not touch it |
So what. The fix is additive, not corrective. Nine things in the system are genuinely designed and should not be touched. What is missing is a signature, and the recommendation is the arc meter: Jessica's concentric arc logo geometry used as the product's capacity primitive, on the single most repeated data point in the product.
Nine conflicts. Each is resolved with a reason rather than averaged.
deep/jpg/p02-cloudscape-02-dashboard.jpg: the in product topbar has no search field at all.no on a dash, always present behaviour rows score unknown.Ranked by decision weight. Each is backed by a count above and specified tightly enough to build from.
1. The spot meter, with a denominator. Backed by 0 of 4.
A segmented bar of N cells where N is the studio's real reformer count, plus the literal string to its right. Open: 4 of 12 open in teal. Two or fewer: same meter, string in plum, because colour marks scarcity and not fullness. Full: meter filled at 40% ink, string Full. A reformer out of service drops the denominator and the row reads 4 of 11 open. The same component renders at the same weight on the schedule row, in the class sheet, on the staff roster and in the HQ console, so a member and an owner looking at Thursday 9:30 read the identical fact. This is the product's signature data component and it is drawn as the arc meter wherever there is one value and the eye lands, as a tabular number wherever many are scanned down a column.
2. Waitlist position, everywhere the waitlist appears. Backed by 0 of 4.
The action label carries it: Join waitlist, 3 ahead. The portal carries it: You are 3rd of 5, in a section never mixed with confirmed bookings, plus an Auto book me if a spot opens toggle defaulting on.
3. Apparatus and level as real fields with their own chips. Backed by 0 of 4, proven on three platforms. Level chip and apparatus chip side by side on line 3 of the row, both filterable, Inter 600 at 11px, teal on 12% teal. The studio never types a level into a class name again. The same chips become the filter row, which is a single horizontal row of the studio's real class types, not five stacked rows of dropdowns.
4. Teacher pages, plus a Sub badge. Backed by 0 of 4 browsable, 1 of 4 substitution.
The teacher's name on every row links to a real page with a credential line and their upcoming classes. Substitution is a first class Sub badge on the row, not a parenthetical inside the name. Follow a teacher is a portal feature: their classes sort first and a new slot notifies.
5. Guest booking, with the wall in the right place. Backed by 1 of 4 and 0 of 4 on the compound test. Order is availability, price, class selection, account, payment. Availability at zero clicks, drop in price at zero clicks, the full option set at one tap, then email only with a magic link and no password field. Collect email plus first name, create the account silently, send the link with the confirmation.
6. The metric band is the chart's control. Backed by 1 of 12.
Studio app home. One cream container, plum hairline dividers, six cells: CLASSES TODAY, BOOKED, WAITLIST, NO SHOW RATE, NEW CLIENTS, AVG FILL. Labels Inter 11px uppercase, values 22px, delta inline on the value's baseline. The selected cell gets a cream fill and a 2px plum top accent and drives the one chart beneath it. Cells are fixed width so a range change never shifts layout. This replaces the rejected four card row, adds two metrics and costs half the height.
7. Class detail opens as a side sheet, never a page. Backed by 0 of 6 and 0 of 4. 480px from the right on desktop, bottom sheet on mobile, schedule visible behind it. The same container carries the roster and the quick edit. The full class definition, and only that, gets its own page, per Cloudscape's rule that a split panel must not replace the details page.
8. Print the baseline with every delta. Backed by 1 of 9.
A hard system rule: no percentage delta ships without the number it is a delta of. Retention 83 percent, down from 87 percent in June, never 83% (-4%).
9. Class fill as a tinted row background. Backed by 2 of 10.
The next seven days as one row per class: day, time and teacher left, 9/12 right aligned, and the row background filled to 75% at roughly 8% teal. Under filled classes read as visibly emptier rows with no chart and no added height, and the row is already the click target. Highest value single steal from the admin set.
10. The term heat strip on class and instructor detail. Backed by 1 of 16.
One segment per session across a term: teal ran full, amber ran under filled, muted cancelled. 12 weeks ago left, Today right. It answers "is this slot working", which a line chart cannot.
11. Metrics in the legend slot for the HQ console. Backed by 1 of 16. Studio names as the legend, each with its current figure at 22px under its name, hairline separated, right of the plot. Fourteen studios' current numbers at zero added vertical cost.
12. Key caps in the date range menu. Backed by 3 of 14 surfacing any shortcut.
Today D, This week W, This month M, This term T, Compare X, right aligned in a key cap on every option. A studio manager opens that control twenty times a day. Per the motion research, keyboard initiated actions get no entrance animation at all.
13. Filter and scope state lives in the URL, from day one. Backed by 3 of 14, and 0 of 14 saved views. Every filter, date range and studio scope. It gives us shareable views, a correct back button and deep links for free.
14. Never render a past day as No Classes, never paginate a week. Backed by 1 of 3 paginating and a live studio showing seven blank cells.
Past days collapse to a 40px date stub, elapsed classes collapse to a 24px line reading 9:30 Reformer L1 · done, and the grid opens on today. An empty week names the next real class and renders it inline and bookable.
15. Studio local time is primary, labelled, and unchanged through every screen. Backed by 1 of 4. Viewer's zone appears as a secondary line only when it differs, on the row and identically at checkout. The five hour contradiction between one product's schedule and its own checkout is the failure mode to test against.
16. Price and action are one stack at the bottom of the cell. Backed by 1 of 4 printing a price at all, and separating it from its button by roughly 480px.
Drop in price, then the saving prompt only when true, then the primary button. For a signed in member the price line becomes Free with your 8-class pack, 3 left.
17. Never pre select a subscription, never print a sentinel. From the checkout that ranks a $459 recurring charge above a $15 intro class and ships expires after 999999 days to paying customers.
At most four options after Book, cheapest entry first: what you already own, the intro offer, the single class, then See all 9 options as a disclosure. If a pass does not expire the string is No expiry. Compute the per class rate, never store it, because the platform that stores it ships titles and bodies that contradict each other.
18. No GetEYT mark anywhere a member can see it. Backed by 3 of 3. It belongs in the HQ console. Per studio theming goes three levels deeper than the category ceiling: display typeface, accent, photography and copy, all the way through checkout, with no visual seam at the money moment.
19. Design 390px first. Backed by 5 of 5 having a named defect there. Every failure in the set is a desktop layout surviving badly on a phone. A class row must be visible in the first viewport. Filters are one chip row.
20. Four named empty states, not one component with a prop. Backed by 5 of 14 having anything designed at all.
Nothing yet (positive imperative heading, secondary button), No matches (action text exactly Clear filters), No access (explanation plus a route), and errors routed to the notification system instead. Left aligned per Carbon. Plus starter content for a brand new studio: two example class types and one schedule template, labelled as examples, one click to delete.
21. Give the display serif a real job. It appears in two component rules across roughly 10,000 lines, both below its own stated floor. One display step at 32px or above, spent on the class name, the day heading, and the single primary number per screen. Never in a table cell, never in an element repeating more than five times on a screen.
22. Break the accent's monopoly and add one colour used exactly once. 102 occurrences is a background. Reduce --accent to interaction only, then spend a single warm ochre on one semantic in the entire product: the class in progress right now. Written as an absolute rule, because a second use kills it.
23. Write prescriptions, not prohibitions. Section 7 is 13 bans and 0 rules that say do this distinctive thing. Every decision in this document that survives review becomes a prescription in that file, with its count attached as the reason.
Sources: findings/01 through findings/07, plus direct reads of deep/jpg/p03-kimai-01-home.jpg and deep/jpg/p02-cloudscape-02-dashboard.jpg to resolve two ambiguities. Every count in this document is reproducible from FEATURE-MATRIX.json, where each dimension carries its per product scoring and its file and line citations.