Why I Rebuilt Plant Smart
Back in June I wrote a little blurb that said, in its entirety, “I should update Plant Smart.” Bolder and more visionary words have never been written. So bold, in fact, I went right back to demolishing this blog instead and let the poor thing sit for another two months.
Well, I finally did the thing.
Three and a half years ago I wrote Why I Built Plant Smart. A free and searchable database of plants that are toxic to pets. I built it over a holiday break to answer a simple question, “Can I bring this plant home without endangering the fluffy little gremlin that runs our household?” It was also my excuse to learn SvelteKit, TypeScript, TailwindCSS and Vite, because my frontend peers wouldn’t shut the hell up about them.
The site then chugged along quietly and untouched for years, which sounds like a compliment to its architecture and very much is not one to the maintainer.
The toolchain aged like fine milk.
The thing no one really thinks about when they build something like this with shiny new frontend tools is that it doesn’t rust, it curdles. When I finally came back to give Plant Smart a fresh coat of paint, the build no longer ran. Not “a few deprecation warnings” didn’t run. It didn’t run at all. Two years of an unattended package.json had fermented into something of a cheese-like consistency.
My original requirements list from 2022 said the site must be fast, static, ( almost ) free to run and open with its data. Nowhere did it say “must still compile in two years”.
So, I decided to spend a few weeks off and on to throw out the toolchain and give it a much-needed refresh. Just like this site, Hugo now builds the entire thing. There’s a single hand-written stylesheet and a few small scripts of delicious vanilla JavaScript. No npm install, because there is no longer anything to install. If I disappear for another two years ( likely ), the site will build exactly the same the day I come back.
First things first, absolute parity.
The rebuild had one rule that saved me from myself: the first pass had to be a 1:1 replica of the old site. It needed the same pages, filters, URLs and quirks. The pass was meant to be boring on purpose. I had to resist the temptation of doing both the replatforming and redesigning all in one go.
My primary constraint was the plant data. Plant Smart’s whole gimmick is what I called a “dumb API”. The entire primary database lives in one JSON file with other, smaller, supplemental files you could use as lookup tables. Since this was all hosted on Cloudflare’s CDN it was effectively free to use for others without keys or annoying rate limits. The old derivations were originally done by a pile of Go magefiles. The new site had to reproduce them exactly, down to the deliberately unsorted first-appearance ordering, or every downstream consumer would notice.
Hugo, it turns out, can do all of it. A content adapter reads the database and births every plant page directly with no intermediate files. A handful of templates re-implement the old Go derivation logic and regenerate the API on every build. I diffed the generated files against the originals and they came back value-identical on the first honest attempt.
Then came my favourite commit of the whole project:
retire the sveltekit stack: remove src, magefiles and the node toolchain
566 files changed, 112 insertions(+), 9905 deletions(-)
I deleted nearly ten thousand lines and the site did more than before. This is every developer’s favourite kind of commit.
I made the site cute on purpose.
With the replica shipped, the redesign could actually be fun. The old site was fine but it was very much “2022 Tailwind starter” vibe. A lot of cool greys, hard shadows and gradients on anything that stood still. Meanwhile the logo, a randomised squishy little pastel potted plant with a face, deserved better surroundings.
So, I set out to make the logo the design brief. Cream paper instead of white with warm espresso ink instead of a dull grey. Sage and moss greens with honey and clay accents were all lifted straight from the icon set. Every shadow is tinted warm because plain black shadows on cream look like stickers. The corners got a bit rounder and the controls became pills. The whole thing is set in Fredoka, a rounded display face that weighs about 30kB which I thought fit perfectly with the cute little mascot.
Here are a few before and after shots of the main content pages, with a bonus shot of the new pill navigation that smaller screens finally got:
The data enrichments will continue until “Morella” improves.
Get it? No? Ok, I’ll see myself out.
The database had severity ratings for every plant, computed from symptom groupings and the old site displayed them precisely nowhere. Now, every card has a severity pill rated from Mild to Critical. The pills are also clickable filters on the main page’s plant cards. Search happens as you type and if you fumble the Latin a bit, a little Levenshtein pass offers “Did you mean Lilium?”. I thought this was a little bit more helpful than what the old site did after literally joking about spelling mistakes in its empty state.
A few other mentionable updates:
- A safe plants list, because the site only ever told you what not to buy. Every entry has to clear the ASPCA non-toxic list and be absent from my own toxic database before it’s allowed in.
- A symptom finder that allows you to perform a plant search backwards. You can click on multiple symptoms and it’ll filter accordingly.
- An emergency strip on every plant page with the poison-control hotlines. Unfortunately, for now, these are US numbers. Localising them by region is on the roadmap.
- Wikipedia descriptions for nearly every plant credited under CC BY-SA.
- Around 3,000 common-name redirects, so
/plant/peace-lily/finally lands somewhere useful. - Because it made sense, the whole thing is now a PWA and works completely offline. So, now you can use the site in a greenhouse with no signal, which is precisely where this site is most likely to get used.
My friends can no longer accuse me of trying to teach people how to poison their neighbour's pets.
Once I started auditing the data properly, I started to see a few additional gaps. There were several plants with multiple entries under different, but similar, names. I deduped and merged the symptom data and also updated the severity groupings to make more sense.
I also quickly realised that merging seizure into seizures would have silently promoted over a hundred plants to “Critical”. The severity groups treated those two spellings as different levels. The little cleaner script I threw together caught it during a dry run. The script grew a rule about never merging across severity levels as a result. The plants that genuinely deserved promotion got one. For instance white snakeroot, the plant that killed Lincoln’s mother, was somehow rated “Severe”. The script properly re-rated it to “Critical”.
The enrichment didn’t stop there. I sourced proper taxonomy for every record via GBIF, per-plant toxicity principles with a glossary and little world maps showing their native origins.
There was also quite a bit of fat to trim.
Somewhere in all this I noticed every page weighed over 200kB. This included pages that were basically just copy. The culprits were painfully self-inflicted. There was an 85kB inline script shipping all ten animated logo SVGs to every single page. The same animal icon inlined seventy times on the front page. Oh, and let’s not forget my personal favourite, the attributions page carrying 149kB of pure indentation that a template loop had quietly emitted. I was shipping whitespace at scale.
The fixes were old-school and very satisfying. A single fingerprinted and cached JS bundle, SVG sprites so each drawing only ships once and one well-placed whitespace trim yielded the following results:
| page | before | after |
|---|---|---|
| home | 247 KB | 56 KB |
| attributions | 238 KB | 24 KB |
| safe plants | 270 KB | 33 KB |
| plant pages | 114 KB | 36 KB |
No framework was harmed, or indeed present, in the making of this facelift.
We ship from the homelab now.
The last box to tick from the original post was the bittersweet one. In 2022 I wanted the project “open-source and community-driven” on GitHub. In practice, the community contribution was me, at midnight, fixing my own typos. So, the repository followed this blog into my homelab using the exact same moving parts as outlined in My Blog Now Ships From My Homelab!. It just made sense after this site’s migration. The data remains as open as ever through the API, which was always the part worth opening. That will never change.
While migrating I had a small panic about the 315MB of plant photos sitting in my working tree, imagining every CI build hauling them across the network. Turns out my 2023 self had already gitignored them and the entire repository clones at 7.5MB. Every once in a blue moon past me does something right. I’ve decided to let sleeping dogs lie and not investigate what else he buried in there.
Was the juice worth the squeeze?
Very much so. The site is faster, cuter and finally says the most important thing it knows on every card: how dangerous is this plant, not just whether. It builds from nothing with a single binary and deploys itself when I push. The data is cleaner and more robust than it has ever been and enriched well past what 2022 me had the patience for.
But the honest win is the same one as the blog demolition. The whole thing is now boring to operate and boring is what lets a hobby project survive its maintainer’s attention span.
If you have a plant-curious pet, Plant Smart is free, the API is free and the safe list might even let you buy something nice for yourself. And if four years from now there’s a third post in this series, may it be shorter than this one.
The data enrichments will continue until "Morella" improves.
Anyways, adios…
1 revision since publication
- 2291638 +25 −15 add some before and after screenshots along with a bit of mild editing
- 6f904ff +108 −0 published
Comments