How a Tool Directory Stays Useful When It Has Too Many Tools
The hardest part of a small utility site is not adding one more calculator. It is helping a visitor find the right one without making them learn the site's architecture first. I built the Be Good Tool home page as a real
The hardest part of a small utility site is not adding one more calculator. It is helping a visitor find the right one without making them learn the site's architecture first. I built the Be Good Tool home page as a real Vue view, and the interesting engineering problem became keeping navigation, search, metadata, and responsive layout connected without duplicating the catalogue.
The home page is data, not a pile of links
The view imports a list composable and turns it into translated links:
const List = useList(store.state.isSSR);
const linkList = computed(() => getExchangeList(t));
useList supplies the main catalogue while getExchangeList(t) supplies the exchange-related links. Keeping those sources separate matters: the component can render a common navigation shell while each list owns its own data shape and translation keys. The computed wrapper also means a locale change can produce new labels without rebuilding the page manually.
I use the store's isSSR flag when creating the list because this view is rendered both on the server and in the browser. A composable that reads browser-only state during server rendering can produce a different first HTML tree, which is exactly the kind of mismatch that looks random in production. Passing the rendering mode explicitly makes that boundary visible.
Search and navigation have different jobs
The template contains a SearchBox component for discovery, but it still renders ordinary router-link elements for navigation. That distinction is easy to miss. Search is an input interaction; a catalogue link is a stable URL that the browser, keyboard, crawler, and back button can understand. I do not turn the whole directory into click handlers just because the page feels like an app.
The page also has announcement links and a few disabled βcoming soonβ entries. They are intentionally not fake routes. A disabled anchor with href="#" communicates that an item is visible but unavailable, instead of sending a visitor to a broken route. The trade-off is that those placeholders need careful accessibility treatment later; a real button or non-link element would be better if the item is never actionable.
Structured data must agree with the visible page
The view emits two JSON-LD blocks. The first describes the store:
const jsonld = JSON.stringify({
"@context": "https://schema.org",
"@type": "Store",
name: t("message.site_name"),
url: t("message.canonical"),
description: t("message.description"),
brand: "Be Good Tool",
priceRange: "$",
});
The important detail is that url, name, and description come from the active locale. Metadata that always points at one language while the visible page changes language is internally contradictory. The component also includes an @type: "Review" object, so changing this view requires checking both the rendered content and the generated head/schema output.
Responsive visuals are part of the information design
The banner uses a desktop image and a mobile source:
<picture>
<source media="(max-width: 768px)" :srcset="newBg" />
<img class="banner__bg" :src="bg2" :srcset="`${bg2} 1440w`" sizes="100vw" />
</picture>
This is a small but practical performance choice. A phone does not need to download the exact same composition as a wide screen, and <picture> lets the browser make that decision before JavaScript runs. It also leaves an actual img fallback for browsers that do not use the media source.
The limitation is that a directory can still become overwhelming as the catalogue grows. Computed lists do not solve information architecture, and the current disabled placeholders are not a substitute for a proper category or filter model. The home page also embeds a hard-coded address and telephone number in JSON-LD, so those values must be reviewed whenever the business information changes.
What I would test before adding more entries
I would test this page at three different boundaries: server rendering, a language switch, and a narrow viewport. In the first case I want the list and metadata to exist without browser globals. In the second I want every translated URL to stay stable while labels change. In the third I want the <picture> source and the grid styles to avoid horizontal scrolling. These are not glamorous tests, but directory regressions are often navigation regressions rather than algorithm bugs.
I would also keep the source of each card explicit. A home page that copies every tool title into one giant component eventually develops spelling drift: the route says one thing, the card says another, and the search result says a third. The current view already moves in the better direction by delegating list data to useList and exchange links to getExchangeList. If categories or ranking are added later, I would extend those data providers instead of creating another parallel array in the template.
That approach keeps the home page a composition layer. It can decide where a section appears and how it responds, while the data and translation layers decide what a link means. For a growing utility site, that separation is more valuable than another decorative banner.
I turned this catalogue into the live home page: Be Good Tool Home.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.