Skip to content

Add 'Jump to' functionality to Braid Docs#2029

Open
SEEKilian wants to merge 9 commits intomasterfrom
docs-jumpto
Open

Add 'Jump to' functionality to Braid Docs#2029
SEEKilian wants to merge 9 commits intomasterfrom
docs-jumpto

Conversation

@SEEKilian
Copy link
Copy Markdown
Contributor

Adds a Modal that allows for quickly jumping to certain Navigation items. Opens with CTRL/CMD+K, as well as an icon below the theme switcher.

JumpToModal component — A new site-level modal (Leverages Braid's Dialog) that lets users search across Foundations, Components, CSS, and Logic by name. Supports arrow-key navigation through results, Enter to navigate, Shift+Enter to jump directly to a component's props page (if present), and auto-focuses the search input on open.

SearchResults & getSearchItems — Supporting modules for the modal: getSearchItems builds a flat list of all searchable items from the existing navigation helpers/routes, and SearchResults renders them grouped by category.

KeyboardShortcut & KeyboardIcon components — New docs-ui components that render styled key cap badges (e.g. , K) alongside a label, used to surface shortcut hints in the UI.

HeaderNavigation updated — Added an onSearchClick prop and a new search trigger button in the header, showing the platform-appropriate shortcut hint (⌘K / Ctrl+K) using KeyboardShortcut.

Navigation updated — Wires up isSearchOpen state, renders JumpToModal, and passes onSearchClick down to both the fixed and sticky Header instances.

useSearchHotkey hook — Listens for ⌘K (macOS) or Ctrl+K (other) globally and calls onOpen to trigger the modal, with platform detection via a small utility (isMac).

CategoryHeading component — For rendering uppercased category labels within the search results list - will be leveraged within docDetails in a follow-up branch

@SEEKilian SEEKilian requested a review from a team as a code owner April 7, 2026 01:41
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 7, 2026

⚠️ No Changeset found

Latest commit: 29b1a2a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Comment on lines +58 to +83
<Stack space="none">
<Box>{themeToggle}</Box>
<Bleed horizontal="xxsmall" bottom="xxsmall">
<Box
component="button"
padding="xxsmall"
paddingRight="xsmall"
borderRadius="standard"
className={searchButton}
onClick={onSearchClick}
>
<KeyboardShortcut
keys={[
navigator.platform.startsWith('Mac') ||
navigator.platform === 'iPhone' ||
navigator.platform === 'iPad' ||
navigator.platform === 'iPod'
? '⌘'
: 'Ctrl',
'K',
]}
shortcutLabel={<IconSearch />}
/>
</Box>
</Bleed>
</Stack>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Based one where this landed we can remove the wrapping Stack and the Box around the themeToggle.

className={searchButton}
onClick={onSearchClick}
>
<KeyboardShortcut
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A subtle one, but can you ensure all elements rendered in here use span elements. Given we wrap it in a button ideally we dont use div elements inside.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yep good pickup


export const searchButton = style({
':hover': {
background: vars.backgroundColor.neutralSoft,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When we change the background colour outside of React Context we need to manage the colour mode change manually. So here need to use the colorModeStyle utility and specify the right token for dark mode.

You can test it on the docs site by focusing the window and typing braiddark 🥸

"./playroom.config.mts",
"./src",
"./scripts",
"../packages/docs-ui/src/components/KeyboardShortcut"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This should not be necessary. Let me know if this is a deliberate work around for something we need to look at.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Need to add a changeset for this new component

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should add a changeset for this, as its new API for the docs-ui component

Comment on lines +8 to +12
<Box style={{ textTransform: 'uppercase' }} component="li">
<Text size="xsmall" weight="medium" component="h2">
{children}
</Text>
</Box>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I dont think these elements are correct for all usages. I would suggest flipping the Text and Box around and exposing the component prop on the outer Text component, so the element can be set from the call site.

Suggested change
<Box style={{ textTransform: 'uppercase' }} component="li">
<Text size="xsmall" weight="medium" component="h2">
{children}
</Text>
</Box>
<Text component={component} size="xsmall" weight="medium">
<Box component="span" style={{ textTransform: 'uppercase' }}>
{children}
</Box>
</Text>

@@ -0,0 +1,13 @@
import { Box, Text } from 'braid-src/index';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This import path is not right, we might have some setup issues to work through.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I believe getSearchItems can be a static array rather behind a function call. That will mean it also wont need to be memoised as its value never changes.

<TextField
icon={<IconSearch />}
ref={inputRef}
label=""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should use aria-label here instead of a blank label

if (flatResults.length === 0 && searchQuery.trim()) {
return (
<Box
background="neutralSoft"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Favour using neutralLight over neutralSoft, where soft primarily serves the button use case. This will get you dark mode support out the box too.

Maybe a subtle radius on this one too?

Comment on lines +42 to +57
if (!searchQuery.trim()) {
return (
<Box
background="neutralSoft"
padding="xxxlarge"
display="flex"
justifyContent="center"
alignItems="center"
height="full"
>
<Text tone="secondary" size="xsmall" align="center">
Matching pages will appear here.
</Text>
</Box>
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe combine with the previous if block or use a component for this placeholder state to keep them consistent.

Comment on lines +69 to +72
<Stack key={category} space="xxsmall" component="ul">
<Box marginBottom="xsmall">
<CategoryHeading>{category}</CategoryHeading>
</Box>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The use of margin means the stack model is probably not right. This is also highlighted by the separation of the ul and li elements. I suggest something like this:

<Stack space="xsmall">
  <CategoryHeading>...</CategoryHeading>

  <Stack space="xxsmall" component"ul">
    {items.map(() => (
      <Bleed component="li">
        <Spread space="small">
          <ButtonLink>...</ButtonLink>
          <KeyboardShortcut />
        </Spread>
      </Bleed>
    ))}
  </Stack>
</Stack>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants