Skip to content

Feat/web 308 improve search on documentation website related tasks#376

Open
niallobrien wants to merge 25 commits intomainfrom
feat/web-308-improve-search-on-documentation-website-related-tasks
Open

Feat/web 308 improve search on documentation website related tasks#376
niallobrien wants to merge 25 commits intomainfrom
feat/web-308-improve-search-on-documentation-website-related-tasks

Conversation

@niallobrien
Copy link
Copy Markdown
Collaborator

@niallobrien niallobrien commented Mar 31, 2026

Story

https://linear.app/cloudsmith/issue/WEB-308/improve-search-on-documentation-website-and-related-tasks

Description

This feature replaces the existing search feature with an Algolia-backed search implementation. This required changes to the existing Algolia docs index (Crawler config), the docs site and the website. See website PR.

I used the dev_DOCS index while building this feature, and used Algolia's duplicate feature to move the index records into Docs only after I had first tested my local instance of the website against the dev_DOCS index.
The Algolia index has already been updated and I've confirmed that these changes do not break the search results in production.

The frontend implementation was a little fiddly in places due to the design requirements and the tokens being used on the site (vs the DS). There is future work planned to align the docs site tokens with the product.
I brought the SearchDialog component over from the website codebase and adjusted accordingly. The main changes being the inline and compact variants, along with theming options, keyboard activation, http method badges (as on the current implementation) and alternative grouping/ranking compared to the website's implementation.

As much as I'd have loved for the two sites to share a common search package implementation here, the search result ordering & grouping is quite different between the two, and is already quite verbose in places, but this could potentially be tackled in a future story, especially if maintenance due to deviation becomes an issue. Future work will also include a light and dark mode, so I tried to somewhat account for this in advance.

Screenshots

Release plan

  • Update the Algolia Crawler config for production at go-live. Used the dev_DOCS Crawler config.
  • Merge website and docs changes.

Checklist

  • I have completed a design review if necessary.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cloudsmith-docs Ready Ready Preview, Comment Apr 3, 2026 3:48pm

Request Review

@niallobrien niallobrien marked this pull request as ready for review April 3, 2026 15:12
Copilot AI review requested due to automatic review settings April 3, 2026 15:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Replaces the existing documentation search with an Algolia-backed implementation, while also updating related UI/layout components (navbar, homepage hero, quick nav) to support the new search experience and refreshed design.

Changes:

  • Implemented Algolia-powered search dialog (InstantSearch) with merged docs + marketing results, grouping, and keyboard navigation.
  • Updated navigation/layout (Navbar, QuickNav, Sidenav, AppShell) and introduced new shared UI pieces (Footer, BackgroundGrid, icons).
  • Adjusted homepage hero/content and supporting utilities/constants to align with the new UI.

Reviewed changes

Copilot reviewed 64 out of 66 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/util/url.js Adds URL/path utilities used by navigation and search.
src/util/strings.js Adds string helpers used for search grouping/labels.
src/lib/types.ts Updates homepage content typing (Hero/buttons).
src/lib/search/types.ts Removes snippet field from legacy search types.
src/lib/search/server.ts Removes snippet generation from legacy server-side search.
src/lib/constants/quickNav.ts Replaces content id with selector + ignored fragments set.
src/icons/svgs/Heart.tsx Adds heart icon used in the new footer.
src/icons/svgs/External.tsx Fixes SVG attribute casing for React.
src/icons/icon-registry.tsx Registers the new heart icon.
src/content/homepage.json Updates homepage hero content to match new hero layout.
src/components/WithQuickNav/WithQuicknav.tsx Switches QuickNav hook-up to a data attribute selector.
src/components/WithQuickNav/WithQuicknav.module.css Improves anchor scroll offset behavior.
src/components/Tag/Tag.tsx Wraps tag content for improved visual centering.
src/components/Tag/Tag.module.css Adds centering/layout styles for tag content.
src/components/Sidenav/Sidenav.tsx Updates sidenav behavior and body class for layout styling.
src/components/Sidenav/Sidenav.module.css Adjusts toggler sizing and transition behavior.
src/components/SearchDialog/utils/searchHitUtils.js Adds hit normalization, dedupe, href normalization, and breadcrumb segment building.
src/components/SearchDialog/utils/searchGroupUtils.js Adds grouping/sorting logic for mixed-source search results.
src/components/SearchDialog/utils/data.js Adds filter/group definition data for search UI.
src/components/SearchDialog/types.ts Adds typed models for Algolia hits/groups/filters.
src/components/SearchDialog/SearchTrigger/SearchTrigger.tsx Introduces themed/variant search trigger component.
src/components/SearchDialog/SearchTrigger/SearchTrigger.module.css Styles new search trigger (inline/compact + themes).
src/components/SearchDialog/SearchTrigger/index.ts Barrel export for the new SearchTrigger.
src/components/SearchDialog/SearchTrigger.tsx Removes previous trigger implementation.
src/components/SearchDialog/SearchTrigger.module.css Removes previous trigger styles.
src/components/SearchDialog/SearchForm.tsx Removes previous search input/hotkeys implementation.
src/components/SearchDialog/SearchForm.module.css Removes previous search form styles.
src/components/SearchDialog/SearchDialogContent/SearchResults/SearchResults.tsx Adds results rendering, grouping, and keyboard navigation behavior.
src/components/SearchDialog/SearchDialogContent/SearchResults/SearchResults.module.css Styles results list, grouping headers, and empty/error states.
src/components/SearchDialog/SearchDialogContent/SearchResults/index.ts Barrel export for SearchResults.
src/components/SearchDialog/SearchDialogContent/SearchFilters/SearchFilters.tsx Adds group “filters” nav that scrolls to sections.
src/components/SearchDialog/SearchDialogContent/SearchFilters/SearchFilters.module.css Styles search group navigation.
src/components/SearchDialog/SearchDialogContent/SearchFilters/index.ts Barrel export for SearchFilters.
src/components/SearchDialog/SearchDialogContent/SearchDialogContent.tsx Adds SearchBox + debounce queryHook + layout for filters/results.
src/components/SearchDialog/SearchDialogContent/SearchDialogContent.module.css Styles dialog header/search input and results layout grid.
src/components/SearchDialog/SearchDialogContent/index.ts Barrel export for SearchDialogContent.
src/components/SearchDialog/SearchDialog.tsx Replaces legacy fuzzy/server search with Algolia InstantSearch + multi-index merge.
src/components/SearchDialog/SearchDialog.module.css Updates dialog shell styling and adds light/dark theme variables.
src/components/SearchDialog/index.ts Simplifies exports to just SearchDialog.
src/components/SearchDialog/FilterButtons.tsx Removes legacy section filter buttons.
src/components/SearchDialog/FilterButtons.module.css Removes legacy filter button styles.
src/components/QuickNav/useHeadingsObserver.ts Updates observer scope to use selector instead of element id.
src/components/QuickNav/QuickNav.tsx Updates QuickNav to use selector scope + adds hash scrolling behavior.
src/components/Navbar/Navbar.tsx Updates navbar layout, adds SearchDialog variants, and revises mobile nav ordering.
src/components/Navbar/Navbar.module.css Major navbar styling updates (new layout, mobile nav, sticky offsets).
src/components/index.ts Exports new Footer component.
src/components/Illustrations/Grid.tsx Adds reusable SVG grid illustration.
src/components/HomepageHero/HomepageHero.tsx Reworks hero layout and embeds SearchDialog.
src/components/HomepageHero/HomepageHero.module.css Updates hero styling and background grid tokenization.
src/components/Footer/index.ts Adds footer barrel export.
src/components/Footer/Footer.tsx Introduces new footer with nav + legal links.
src/components/Footer/Footer.module.css Styles the new footer.
src/components/BackgroundGrid/index.ts Adds BackgroundGrid barrel export.
src/components/BackgroundGrid/BackgroundGrid.tsx Adds BackgroundGrid wrapper for vignette + illustration composition.
src/components/BackgroundGrid/BackgroundGrid.module.css Styles BackgroundGrid layout and vignettes.
src/components/AppShell/AppShell.module.css Adjusts secondary nav stickiness behavior at breakpoints.
src/components/ApiRequest/ApiRequest.tsx Adds data-api-method attribute for request wrapper.
src/app/navigation.tsx Minor import ordering cleanup.
src/app/layout.tsx Adds Footer to the root layout.
src/app/(documentation)/design-system/page.module.css Media query + hover style adjustments for icon wrapper.
src/app/_styles/variables.css Adds grid-line token and updates navbar sizing variables.
src/app/_styles/global.css Adds body var override when sidenav toggle class is present.
package.json Adds Algolia + InstantSearch dependencies.
package-lock.json Locks newly added Algolia/InstantSearch dependency tree.
.gitignore Adds .history and normalizes docs/graph ignore formatting.
.env Adds Algolia-related env vars (currently includes a committed key).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +7 to +13
NEXT_PUBLIC_ALGOLIA_APP_ID=
NEXT_PUBLIC_ALGOLIA_API_KEY=77bed0e1d7c9a7b0572d1ffdb55b2be9 # this is only for development, a restricted key is used in production
NEXT_PUBLIC_ALGOLIA_DOCS_INDEX=Docs
NEXT_PUBLIC_ALGOLIA_INDEX=prod_WEBSITE
# NEXT_PUBLIC_ALGOLIA_INDEX=dev_WEBSITE

ALGOLIA_WRITE_KEY=
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A public Algolia API key is committed in this tracked .env file (and it’s a NEXT_PUBLIC_* value, so it will be exposed to the browser). Even if restricted, committing keys in-repo increases the risk of accidental reuse/privilege expansion and makes rotation harder. Remove the key from version control (e.g., move to developer-local env files and provide a .env.example with placeholders) and rotate the exposed key.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a dev-only key. Will remove to be safe I guess.

Comment on lines +61 to +68
export interface Hero {
title: string;
description: string;
buttons: Button[];
buttons: {
label: string;
href: string;
icon: IconName;
}[];
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hero.buttons is still required in the Hero/HomepageContent type, but src/content/homepage.json no longer contains hero.buttons. This forces consumers to lie with type assertions and can lead to runtime undefined values. Either make buttons optional (or default to an empty array in the content shape), or update the JSON to include "buttons": [] to match the type.

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +23
const normalizeComparableSearchValue = (value) => {
const normalizedValue = normalizeSearchValue(value);
if (!normalizedValue) return '';

return slugify(decodeURIComponent(normalizedValue).replace(/\+/g, ' '));
};
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normalizeComparableSearchValue calls decodeURIComponent without a try/catch. If Algolia returns a value containing malformed percent-encoding, this will throw and break the search UI. Consider reusing decodeSearchValue (which already guards) or wrapping this decodeURIComponent call in a try/catch with a safe fallback.

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +23
export const scrollToHashTarget = (hash = window.location.hash) => {
const normalizedHash = hash.replace(/^#/, '');
if (!normalizedHash) return false;

const targetId = decodeURIComponent(normalizedHash);
const target = document.getElementById(targetId);
if (!target) return false;

target.scrollIntoView({ block: 'start' });
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scrollToHashTarget uses decodeURIComponent on the hash fragment without guarding against malformed percent-encoding. A malformed URL hash can throw here and break QuickNav rendering/behavior. Wrap the decode in try/catch (fall back to the raw fragment) before calling getElementById.

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +45
const queryHook = useCallback((nextQuery: string, search: (value: string) => void) => {
const trimmed = nextQuery.trim();

if (trimmed.length === 0) {
if (debounceRef.current) window.clearTimeout(debounceRef.current);
search('');
return;
}

if (trimmed.length < 2) return;
if (debounceRef.current) window.clearTimeout(debounceRef.current);

debounceRef.current = window.setTimeout(() => {
search(nextQuery);
}, 150);
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In queryHook, queries with 1 character return early without calling search('') or otherwise clearing results. This can leave stale results visible while the input shows a new (too-short) query. Consider explicitly clearing/refining to an empty query when trimmed.length < 2, or hiding results until the minimum length is reached.

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +33
// Guard against missing Algolia credentials to prevent breaking the navbar
const hasAlgoliaCredentials = Boolean(algoliaAppId && algoliaApiKey);
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasAlgoliaCredentials only checks app ID + API key, but InstantSearchNext is still passed indexName={algoliaIndexName} which may be undefined/empty (and createSearchClient falls back to an empty index name). This can lead to runtime errors if NEXT_PUBLIC_ALGOLIA_INDEX isn’t configured. Consider including algoliaIndexName in the guard (or providing a safe default / clearer failure mode).

Suggested change
// Guard against missing Algolia credentials to prevent breaking the navbar
const hasAlgoliaCredentials = Boolean(algoliaAppId && algoliaApiKey);
// Guard against missing Algolia configuration to prevent breaking the navbar.
const hasAlgoliaCredentials = Boolean(
algoliaAppId && algoliaApiKey && algoliaIndexName,
);

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +3
import { slugify, titleCase } from '../../../util/strings';
import { isExternalHref } from '../../../util/url';
import { filtersData } from './data';
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file imports util modules via a deep relative path (../../../util/...) while the rest of the SearchDialog code uses the @/util/... alias. Keeping imports consistent reduces churn if folders move and matches existing conventions in this area (e.g., searchHitUtils.js). Switch these imports to the @/util/... form for consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +36
<span className={styles.kbdHint}>
<kbd>
<span className={styles.kbdText}>Ctrl</span>
</kbd>
<kbd>
<span className={styles.kbdText}>K</span>
</kbd>
</span>
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The keyboard shortcut hint is hard-coded to “Ctrl K”. On macOS this should typically display “⌘K”, and the previous implementation already derived the modifier key dynamically. Consider restoring OS-aware rendering for the modifier key (or hiding the hint on platforms where it’s not applicable).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants