Scan complete · 1986ms · 4,065 code files

Design System Diagnosis

dubinc/dub · scanned 2026-08-22
Health score
25/100
nextTypeScriptTailwind CSSCVAmonorepo
Summary
632 arbitrary values like [400px] punched through the Tailwind scale, each one a precedent your agent will follow. 22 components implemented more than once, so an agent asked for a Button has several random options. 60 off-scale spacing values where a dozen would do.

Where to start

Three tweaks · 25 → 55

01
Collapse the 14 near-identical colours
+10
#ffffff and #fafafa are the same colour to any eye, and they are used 9 times between them. Nobody chose to have both. Pick the one that is already a token, point the other at it, and the pair stops multiplying.
02
Decide about the 16 components nobody imports
+10
<Alert>, <AlertDescription>, <AlertTitle> and others sit in the system with no callers. Adopt them or delete them: either answer is better than a system with rooms nobody enters.
03
Fold 48 inline style blocks back into the system
+10
These are static values written as style attributes; start with apps/web/ui/partners/groups/design/lander/modals/accordion-block-modal.tsx (4 blocks). Dynamic positioning was already excluded, so all of these could be classes or tokens today.

You sat through the roast

so you get a present.

Agent trap. <PayoutStats> alone has 3 implementations. An agent asked for one cannot tell which is canonical, so it picks at random or writes another, and every wrong pick becomes the example the next agent copies. This is the finding that multiplies itself.
Agent trap. [400px] appears 24 times. An agent looking for how this repo does spacing reads repetition as intent, so it will write occurrence 25. The most copied pattern here is the one you least want copied.
104
distinct colours
Ideal Design System~24
Avg Design System130
Reputable systems24
15
shades of grey
Ideal Design System~13
Avg Design System17
Reputable systems5
60
off-scale spacing values
Ideal Design System~12
Avg Design System34 · messier than 74%
Reputable systems6
22
duplicated components
Ideal Design System0
Avg Design System20
Reputable systems12
48
inline style blocks
Ideal Design System0
Avg Design System49
Reputable systems12
14
near-identical colour pairs
Ideal Design System0
Avg Design System13
Reputable systems1
1
!important declarations
Ideal Design System0
Avg Design System7 · cleaner than 62%
Reputable systems3
16
components never imported
Ideal Design System0
Avg Design System14 · messier than 74%
Reputable systems0
632
arbitrary bracket values
Ideal Design System~20
Avg Design System70 · messier than 91%
Reputable systems0

Package by package

2 packages with enough UI to judge · the repo score above is the whole thing blended, and 7 packages were too small or too backend to score

packagescoreworst findingsize
apps/web 40 39 inline style blocks 3,335 files
packages/ui 80 16 components nobody imports 478 files

A repo scores below its own packages by arithmetic, not by accident: distinct values add up across packages, so the whole always carries more than any part. Read the package scores for where each team stands, and the repo score for what your agent sees when it looks at everything at once.

What your AI agent sees today

No CLAUDE.md, no AGENTS.md, no .cursorrules. Every time your AI builds UI here, it guesses, from everything below. This is why its output looks almost-but-not-quite right.

Palette
104colours

A healthy product palette is up to ~24 colours: one brand hue with a few tints, one accent, up to 13 greys, and status colours.

49 tokens55 strays
Colour usage
Sized by how often each is used
token stray
+44
Grey ramp · sorted dark → light · how many can you tell apart?
14 nearly identical pairs · copy-paste, not decisions
#ffffff ×7 #fafafa ×2#d4d4d4 ×3 #d1d5db ×2#f5f5f5 ×3 #f0f0f0 ×2#eab308 ×2 #efb100 ×1#db2777 ×2 #da2778 ×1#ede8fd ×1 #f5edff ×1#ede8fd ×1 #ede9fe ×1#ede8fd ×1 #f3e8ff ×1+6 more

60 off-scale spacing values

a disciplined repo keeps these around a dozen · on-scale Tailwind steps (·) shown for context · off-scale in coral

·2
2,711
·4
1,966
·3
1,506
·1
1,127
·6
1,047
·0
800
·1.5
595
·2.5
417
·5
373
·0.5
285
·8
250
·10
217
·px
176
·16
89
·12
86
·3.5
35
·7
34
·20
22
632 arbitrary values punched through the scale, one bracket at a time
[400px] ×24[0.625rem] ×22[300px] ×19[200px] ×18[10px] ×17[180px] ×16[1200px] ×14[18px] ×11[50px] ×11[360px] ×11+169 more

Typography & shape

3
typefaces
Ideal Design System~3 =
Avg Design System3 =
Reputable systems1
41
font sizes
Ideal Design System~10
Avg Design System20 · messier than 88%
Reputable systems6
27
border radii
Ideal Design System~10
Avg Design System13 · messier than 88%
Reputable systems2
the font sizes, by use
·sm ×2263·xs ×812·lg ×270·base ×230·xl ×69·3xl ×14·2xl ×14·[0.625rem] ×14·[13px] ×11·4xl ×10·5xl ×8·[0.8125rem] ×7·[0.7rem] ×6·[11px] ×6
the corners, drawn · 27 radii in one interface
999px ×3
50% ×2
10px ×1

3 typefaces, declared 5 different ways

every distinct declaration is a chance for the next one to be wrong

system-ui×2
Inter×1
satoshi×1
Geist×1
ui-monospace×1

Where it hurts most

the 5 files carrying the most off-system styling

Duplicated components

an agent asking which one is canonical gets several plausible answers. Paths open in VS Code

two icon sets collide on 11 names · one problem, not 11
<Lock><Download><Users><CursorRays><Hyperlink><User><Gear><Eye><LinesY><ConnectedDots4><Heart>

apps/web/ui/shared/icons/lock.tsx vs packages/ui/src/icons/nucleo/lock.tsx

<EmptyState> · defined twice, one wraps the other
dropdown family · 8 variants
<FolderDropdown>
apps/web/ui/folders/folder-dropdown.tsx
used 3×
<PartnerProgramDropdown>
apps/web/ui/layout/sidebar/partner-program-dropdown.tsx
used 3×
<UserDropdown>
apps/web/ui/layout/sidebar/user-dropdown.tsx
used 2×
<MoreDropdown>
apps/web/ui/links/link-builder/more-dropdown.tsx
used 1×
<PayoutMethodDropdown>
apps/web/ui/partners/payouts/payout-method-dropdown.tsx
used 1×
<SubmittedLeadStatusDropdown>
apps/web/ui/submitted-leads/submitted-lead-status-dropdown.tsx
used 1×
…and 2 more
header family · 8 variants
<InviteSheetHeader>
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/invite-sheet-ui.tsx
used 2×
<LinkBuilderHeader>
apps/web/ui/links/link-builder/link-builder-header.tsx
used 2×
<PageContentHeader>
apps/web/ui/layout/page-content/page-content-header.tsx
used 2×
<BountyHeaderTitle>
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/[bountyId]/bounty-header.tsx
used 1×
<GroupHeaderTabs>
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/groups/[groupSlug]/group-header.tsx
used 1×
<GroupHeaderTitle>
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/groups/[groupSlug]/group-header.tsx
used 1×
…and 2 more
avatar family · 7 variants
<PartnerAvatar>
apps/web/ui/partners/partner-avatar.tsx
used 74×
<UserAvatar>
apps/web/ui/users/user-avatar.tsx
used 34×
<CustomerAvatar>
apps/web/ui/customers/customer-avatar.tsx
used 9×
<TokenAvatar>
apps/web/ui/token-avatar.tsx
used 9×
<Avatar>
packages/ui/src/avatar.tsx
used 3×
<ActivityLogUserAvatar>
apps/web/ui/activity-logs/activity-entry-chips.tsx
used 2×
…and 1 more
alert family · 7 variants
<AlertCircleFill>
apps/web/ui/shared/icons/alert-circle-fill.tsx
used 12×
<ShieldAlert>
packages/ui/src/icons/nucleo/shield-alert.tsx
used 2×
<ReviewFraudAlertSheet>
apps/web/app/(ee)/admin.dub.co/(dashboard)/partners/fraud/review-fraud-alert-sheet.tsx
used 1×
<Alert>
packages/ui/src/alert.tsx
<AlertDescription>
packages/ui/src/alert.tsx
<AlertTitle>
packages/ui/src/alert.tsx
…and 1 more
progress family · 6 variants
<ProgressCircle>
packages/ui/src/progress-circle.tsx
used 6×
<BountyProgressBarRow>
apps/web/ui/partners/bounties/bounty-progress-bar-row.tsx
used 4×
<StepProgressBar>
apps/web/ui/partners/merge-accounts/step-progress-bar.tsx
used 4×
<PerformanceBountyProgress>
apps/web/ui/partners/bounties/bounty-performance.tsx
used 3×
<SubmissionBountyProgress>
apps/web/ui/partners/bounties/bounty-performance.tsx
used 3×
<ProgressBar>
packages/ui/src/progress-bar.tsx
sidebar family · 6 variants
<LayoutSidebar>
packages/ui/src/icons/nucleo/layout-sidebar.tsx
used 1×
<MarketplaceSidebarFilters>
apps/web/ui/program-marketplace/marketplace-sidebar-filters.tsx
used 1×
<ProgramSidebar>
apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/[programSlug]/apply/program-sidebar.tsx
used 1×
<SidebarProvider>
apps/web/app/(ee)/app.dub.co/(new-program)/sidebar-context.tsx
used 1×
<SidebarUsage>
apps/web/ui/layout/sidebar/sidebar-usage.tsx
used 1×
<FilterSidebar>
packages/ui/src/filter/filter-sidebar.tsx

What you actually use

1,570 components defined · top by adoption · the real system, buried in here

componentuseddefined inprops
<Button> 988× packages/ui/src/button.tsx text variant textWrapperClassName shortcutClassName loading
<Link> 616× apps/web/ui/shared/icons/link.tsx none
<Modal> 185× packages/ui/src/modal.tsx showModal setShowModal onClose desktopOnly preventDefaultClose
<Tooltip> 144× packages/ui/src/tooltip.tsx content contentClassName disabled side disableHoverableContent
<LoadingSpinner> 113× packages/ui/src/icons/loading-spinner.tsx none
<Footer> 107× packages/email/src/components/footer.tsx email marketing unsubscribeUrl https notificationSettingsUrl
<Footer> 107× packages/ui/src/footer.tsx staticDomain
<Popover> 100× packages/ui/src/popover.tsx content align side openPopover setOpenPopover
<InfoTooltip> 97× packages/ui/src/tooltip.tsx none
<X> 90× apps/web/ui/shared/icons/x.tsx none
16 components defined but never imported · they sit in the system as wrong answers waiting to be picked
<Alert><AlertDescription><AlertTitle><CardContext><CardListCard><CompositeLogo><DubWireframeGraphic><FilterList>+8 more

Routers, dynamic imports and barrel files can hide real usage, so treat this as a shortlist to check, not a demolition order.