Nakodo did not get a redesign. It got a research document first, and the CSS came out of it. The document is still in the repo at docs/design-direction.md: 794 lines, written before a single token changed, opening with the line "Research report. No project files were modified."
I have been publishing engineering posts about this app for most of a month, which means I now have something more useful than the document: a live site and a repo to check it against. So this post is the report, and then the scorecard.
The diagnosis
The report starts by explaining why the screens looked generic, and it is not kind about it:
Why it looks like default shadcn. It is literally the default: zero-chroma OKLCH greys, Geist, the radix-nova ring-bordered
rounded-xlcards, androunded-4xlpill badges. The only colour comes from stock Tailwindemerald/amber/blue/red-50/700classes insrc/components/status-badge.tsxandresults-view.tsx. Those are the "random highlight colours" the owner objects to.
That is the whole problem stated in four facts, each of which is a grep. Chroma 0 on every neutral, one font that ships with the framework, and four unrelated accent colours arriving from Tailwind's default palette because a status badge needed to be green.
The method was reading other people's CSS
The part I would repeat on any future product is section 1b. Rather than collecting opinions about the category, it fetched the competitors' homepages and extracted what they actually serve:
I fetched each homepage and its CSS on 2026-10-01 and extracted the fonts and most-used colours.
Eight influencer tools, each reduced to a font stack and two or three dominant colours in OKLCH hue terms. The conclusion is a map rather than a mood board:
- Hue clusters: 270-285 at very high chroma (four tools) and 18-55 orange/red (three tools).
- Open lanes: ink blue at moderate chroma (h 245-260), petrol teal (h 195-215), and warm paper with a modern newspaper serif.
One competitor had already taken the third lane with Playfair and deep green, which is noted, with the font named, so nobody rediscovers it later.
Then three directions, each costed and named: A "Ledger" (editorial research desk, ink on warm paper), B "Instrument" (precision tool, petrol teal on slate), C "On Air" (expressive creator culture, tomato red). The recommendation is A, with six numbered reasons, of which the one I still think is the strongest is the dullest:
It suits the product. The outputs really are a brief, a dossier and a letter.
What the report asked for, and what the code says now
Here is the part that only a repo can answer. Every number below is from the current tree or the live site.
One hue, one ramp, roles per step: held. The colour file is OKLCH top to bottom with no hex codes in it, and the step roles from the report (50-100 tints, 200-300 borders, 600-700 solids, 800-950 text and primary) are the roles the semantic aliases map to.
"Ban raw palette utilities": held in fact, never enforced. The report wanted a CI grep for (bg|text|border|ring|fill|stroke)-(red|green|emerald|...)-\d.
$ grep -rnE "(bg|text|border|ring|fill|stroke)-(red|green|emerald|amber|yellow|blue|sky|indigo|violet|purple|pink|rose|orange|lime|teal|cyan|fuchsia)-[0-9]" src --include="*.tsx" | wc -l
0
$ grep -rl "emerald" src | wc -l
0
Zero hits, and the word that the diagnosis complained about appears nowhere in the source. But no test checks it, so what is holding is a habit, not a rule. The first careless text-red-600 will ship.
"Avoid gradients, glass, glow, sparkles": mostly held, and it bent exactly where I expected.
$ grep -rn "bg-gradient\|linear-gradient" src --include="*.tsx" --include="*.css" | wc -l
2
$ grep -rn "Sparkles" src | wc -l
0
$ grep -rn "backdrop-blur" src --include="*.tsx" | wc -l
10
The two gradients are both functional: the 45-degree hatching pattern that means "locked" and a mask on the hero animation. No sparkles icon anywhere, which was on the report's avoid list as the "AI" cliché.
The ten backdrop-blur uses are the interesting ones, because "glassmorphism and backdrop-blur headers" is on that same avoid list. All ten are behind the sign-in wall: one sticky app header at bg-background/95, six sticky save bars at the bottom of editors, and three modal overlays where the blur is wrapped in supports-backdrop-filter: so a browser without it still gets a flat scrim. Zero on the public pages. A rule written for a marketing site met a sticky action bar over scrolling data and lost, in one place, with a fallback. I am fine with that, and I would rather the document and the code disagree visibly than quietly.
Fonts: replaced, and the replacement is documented. The report specified Newsreader and Schibsted Grotesk with IBM Plex Mono. The site ships Besley and Archivo with IBM Plex Mono. That is not drift, because the follow-up document records the swap in a table:
| From Ledger | Status |
|---|---|
| Newsreader + Schibsted Grotesk | Replaced by Besley + Archivo: a heavier, more recognisable display face and a UI face with a width axis for dense labels |
| IBM Plex Mono | Kept |
| Ink blue ramp (h 258) | Evolved into "Cyano" at h 252, used as whole fields of colour, not just for text and buttons |
| Austerity | Dropped: a mascot, 12 spots, a voice, cyanotype panels and dense plates |
The width axis is the reason the swap was worth doing, and it is the one decision from this whole exercise that paid for itself in bytes as well as in looks: one variable file covers font-stretch: 62% to 125%, so the condensed small caps on every column header cost no extra font.
One avoid-list item is also still true, and it is a one-line check anyone can run:
$ curl -s https://nakodo.app/ | grep -c 'gstatic\|fonts.googleapis'
0
Dark mode: built as specified, and unreachable. The report was explicit that dark mode should be "a separate mapping, not an inversion", and that is exactly what the stylesheet does: a .dark block of 80 declarations that redeclares every value rather than inverting anything. Nothing in the application ever sets that class. The toast component says so in a comment:
// Always light: nothing turns on the .dark theme, so toasts don't follow the
// system setting either.
And next-themes is in package.json, imported by nothing, left behind by the component CLI. So the most carefully specified part of the colour work is the part no user has ever seen.
The QA section: nothing happened. This is the honest bottom of the scorecard. The report asked for three things:
- Keep the contrast script and the palette validator in CI.
- Test
prefers-reduced-motion,forced-colors, and a 360px viewport.- Take Playwright screenshot diffs of the six key screens.
$ ls -d .github
ls: .github: No such file or directory
$ grep -c playwright package.json
0
$ find src -name "*.test.ts" | wc -l
62
$ grep -rn "forced-colors" src | wc -l
1
There is no CI in this repository at all. 62 test files run from one tsx --test command, and not one of them looks at a colour. prefers-reduced-motion is handled in 15 places across the components and the stylesheet, and the one forced-colors block exists, so the middle item half landed by being expressible in CSS rather than in a test. The two items that needed a pipeline needed a pipeline that was never built.
What I take from reading my own report back
The rules that survived a month of commits are the ones that became tokens, a type, or a grep: a ramp with roles, semantic slots, a banned-class pattern, a font variable per family. The rules that survived as prose are the ones that bent first, and the rules that needed infrastructure did not survive at all.
If I wrote the document again, the avoid list would ship as a lint rule in the same commit, and the QA list would either be a GitHub Actions file in that commit or it would not be in the document. A design decision you cannot grep for is a decision you will relitigate in six weeks with no record of why.
There is one more thing in the report's summary that did get acted on, and it has nothing to do with CSS:
Side flag: "Outreach" is also the name of a large sales-engagement company. If this will be sold, check the name before investing in a wordmark.
The app was unnamed when that was written. It is called Nakodo now.
The result of all of the above is public, so you can judge the outcome rather than the process: the landing page, pricing, a guide page for the editorial side of it, and about.
Top comments (0)