Skip to content

feat: add Kbd component - #886

Open
Shreyag02 wants to merge 4 commits into
mainfrom
feat/kbd-component
Open

feat: add Kbd component#886
Shreyag02 wants to merge 4 commits into
mainfrom
feat/kbd-component

Conversation

@Shreyag02

@Shreyag02 Shreyag02 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Description

Adds Kbd, a component for displaying keyboard keys and shortcut sequences.

Apsara has no shared way to render a keyboard key today, so it gets hand-rolled with inline styles wherever it appears. Command.Shortcut renders unstyled <kbd> elements, Menu has no shortcut support, and tooltips have no way to show an action's shortcut.

<Kbd>Esc</Kbd>

<Kbd.Group>
  <Kbd></Kbd>
  <Kbd>K</Kbd>
</Kbd.Group>

The API follows shadcn's Kbd, converted to Apsara conventions: dot-notation sub-components and --rs-* tokens. The visual treatment reuses the existing key styling from Command.Shortcut and the docs-site Kbd, so no new tokens were added.

One note for review: Kbd.Group renders a <kbd> rather than a <div>, since the HTML spec defines a nested kbd as an individual key within a larger input.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactor (no functional changes, no bug fixes just code improvements)
  • Chore (changes to the build process or auxiliary tools and libraries such as documentation generation)
  • Style (changes that do not affect the meaning of the code (white-space, formatting, etc))
  • Test (adding missing tests or correcting existing tests)
  • Improvement (Improvements to existing code)
  • Other (please specify)

How Has This Been Tested?

  • 18 new tests covering rendering, className merging, ref forwarding, composition, and the data-slot contract. Full suite: 2589 passed, 1 skipped.
  • pnpm build:apsara and pnpm --filter www build both pass; the docs page generates and appears in the sidebar.
  • tsc --noEmit and biome check clean on all new files.
  • Checked in light and dark themes on a tooltip surface, in a menu row, and inline in text.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (.mdx files)
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works

Screenshots (if appropriate):

N/A

Related Issues

N/A

@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
apsara Ready Ready Preview Aug 17, 2026 8:16am

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7b3d1c2e-93a0-4c05-9914-0e80c01fadd1

📥 Commits

Reviewing files that changed from the base of the PR and between b5a3b97 and 5a6df31.

📒 Files selected for processing (4)
  • apps/www/src/content/docs/components/command/index.mdx
  • apps/www/src/content/docs/components/command/props.ts
  • apps/www/src/content/docs/components/kbd/index.mdx
  • apps/www/src/content/docs/components/kbd/props.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/www/src/content/docs/components/command/index.mdx
  • apps/www/src/content/docs/components/kbd/props.ts
  • apps/www/src/content/docs/components/command/props.ts
  • apps/www/src/content/docs/components/kbd/index.mdx

Included review availability: Your plan includes up to 1 review per rolling hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Adds the semantic Kbd component and compound Kbd.Group API. Both support native props, refs, custom classes, variants, and slot attributes. Integrates Command.Shortcut with Kbd.Group. Adds styling, package exports, tests, documentation, accessibility guidance, and demos for keys, shortcuts, separators, text, inputs, and tooltips.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant Button
  participant Tooltip
  participant Kbd
  User->>Button: Activate outline button
  Button->>Tooltip: Trigger tooltip
  Tooltip->>Kbd: Render Open search shortcut
Loading

Suggested reviewers: rohanchkrabrty

Merge Risk: ⚪ Minimal · up to 5a6df

This PR adds the Kbd component and related documentation and tests without any actionable merge-blocking risk remaining; it is merge-ready after normal checks and review.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main change: adding the Kbd component.
Description check ✅ Passed The description explains the Kbd component, its API, related Command.Shortcut changes, tests, documentation, and validation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Aug 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/raystack/apsara/@raystack/apsara@886

commit: 5a6df31

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/www/src/content/docs/components/kbd/demo.ts`:
- Around line 5-8: In apps/www/src/content/docs/components/kbd/demo.ts, add
descriptive aria-label values to every symbol-only Kbd example: label Command at
lines 5-8, 13-19, 24-34, 42-46, 61-68, and 73-86; additionally label Shift and
Enter in the 13-19 example and Shift in the 24-34 example. Use labels such as
“Command,” “Shift,” and “Enter” while preserving the displayed symbols.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b976dc67-dfd8-49f0-97d5-98284057b0fe

📥 Commits

Reviewing files that changed from the base of the PR and between 8d138d4 and d679eef.

📒 Files selected for processing (9)
  • apps/www/src/content/docs/components/kbd/demo.ts
  • apps/www/src/content/docs/components/kbd/index.mdx
  • apps/www/src/content/docs/components/kbd/props.ts
  • packages/raystack/components/kbd/__tests__/data-slots.test.tsx
  • packages/raystack/components/kbd/__tests__/kbd.test.tsx
  • packages/raystack/components/kbd/index.tsx
  • packages/raystack/components/kbd/kbd.module.css
  • packages/raystack/components/kbd/kbd.tsx
  • packages/raystack/index.tsx

Comment thread apps/www/src/content/docs/components/kbd/demo.ts Outdated
Comment on lines +15 to +19
/**
* Renders a `<kbd>` rather than a `<div>`: per the HTML spec a `kbd` nested
* inside a `kbd` represents an individual key within a larger input, which is
* exactly a shortcut sequence.
*/

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.

Please remove comments

Comment on lines +8 to +12
color: var(--rs-color-foreground-base-tertiary);
font-family: var(--rs-font-body);
font-size: var(--rs-font-size-mini);
line-height: var(--rs-line-height-mini);
letter-spacing: var(--rs-letter-spacing-mini);

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.

font color and styles should be applied only on .kbd and not the group so that there is a difference of text between two sets of .kbd

@rohanchkrabrty rohanchkrabrty Aug 12, 2026

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.

Create 2 variants

  • solid (default)
background: var(--rs-color-background-neutral-primary);
color: color: var(--rs-color-foreground-base-secondary);
  • ghost
background: transparent;
color: color: var(--rs-color-foreground-base-tertiary);

letter-spacing: var(--rs-letter-spacing-mini);
}

.kbd {

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 have pointer-events: none and user-select: none

justify-content: center;
box-sizing: border-box;
height: var(--rs-space-6);
/* Square minimum so a narrow "K" reads the same width as a wide "⌘". */

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.

Remove comments

box-sizing: border-box;
height: var(--rs-space-6);
/* Square minimum so a narrow "K" reads the same width as a wide "⌘". */
min-width: var(--rs-space-6);

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.

Also add width: fit-content or else it will stretch in certain cases


## Accessibility

- `Kbd` is presentational and renders the semantic `<kbd>` element, which screen readers announce as keyboard input.

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.

which screen readers announce as keyboard input. is misleading here, remove it

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.

Let's add a playground too since we are introducing variants now

Comment on lines +79 to +83
### Inline with text

Keys sit on the text baseline, so they can be dropped into a sentence.

<Demo data={withTextDemo} />

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.

Even tho the example says Inline with text, the actual example is rendering in side a Flex, so it's misleading

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.

There is a sub-component in Command named Command.Shortcut
That should now be an alias of Kbd component with the default as ghost. All props should be forwarded

Shreyag02 and others added 2 commits August 12, 2026 16:08
Addresses the review on #886.

Component:
- Add `solid` (default) and `ghost` variants. `variant` on `Kbd.Group`
  propagates to its keys via context, with a per-key override.
- Add `width: fit-content` so a key no longer stretches in a column-flex
  or grid parent, and `pointer-events: none` alongside `user-select: none`.
- Move typography onto `.kbd` only; `.kbd-group` takes `font: inherit` so
  separator text picks up the surrounding type instead of the key styling
  (and still escapes normalize's monospace default for `kbd`).
- Drop the explanatory comments.

Command:
- `Command.Shortcut` is now an alias of `Kbd.Group` + `Kbd` defaulting to
  `ghost`, forwarding all props. Whitespace splitting and the
  `command-shortcut` / `command-shortcut-key` slots are preserved, and
  element children pass through without a second key wrapper.
- Remove the now-unused shortcut typography.

Accessibility:
- `ghost` uses `foreground-base-secondary`. `tertiary` measured 3.33-4.46:1
  across the surfaces Kbd is documented on, below AA's 4.5 for 11px text
  and worst on a hovered row; `secondary` clears it everywhere at >=5.19:1.
- Correct the docs claim about screen readers: `kbd` maps to no ARIA role
  and no accessible object per HTML-AAM.
- Label symbol-only keys in the examples.

Docs:
- Add a playground and a Variants section, reword the description and the
  Group summary, and make the "Inline with text" example actually inline.
- Add an Input example, using a single key since the trailing slot is
  sized for an icon and clips a multi-key group.
- Document `variant` on `Command.Shortcut`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/www/src/content/docs/components/kbd/props.ts`:
- Around line 21-25: Align the variant documentation across all three sites: in
apps/www/src/content/docs/components/kbd/props.ts lines 21-25, describe the
group variant as the inherited default for child keys, with explicit child Kbd
variants taking precedence; in
apps/www/src/content/docs/components/command/props.ts lines 141-145, document
that child variants override the shortcut variant; and in
apps/www/src/content/docs/components/command/index.mdx line 100, state that
ghost is the default variant rather than the only supported variant.
- Around line 7-11: The documentation prop interfaces in
apps/www/src/content/docs/components/kbd/props.ts (lines 7-11 and 21-25) and
apps/www/src/content/docs/components/command/props.ts (lines 135-146) omit
native element attributes and refs. Extend or reuse the published Kbd and
Command component types so the generated tables include supported properties
such as id, title, and aria-* attributes while preserving the existing
component-specific props.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5bc9bed4-0236-4b95-82d6-68f442080194

📥 Commits

Reviewing files that changed from the base of the PR and between d679eef and b5a3b97.

📒 Files selected for processing (12)
  • apps/www/src/content/docs/components/command/index.mdx
  • apps/www/src/content/docs/components/command/props.ts
  • apps/www/src/content/docs/components/kbd/demo.ts
  • apps/www/src/content/docs/components/kbd/index.mdx
  • apps/www/src/content/docs/components/kbd/props.ts
  • packages/raystack/components/command/__tests__/command.test.tsx
  • packages/raystack/components/command/command-misc.tsx
  • packages/raystack/components/command/command.module.css
  • packages/raystack/components/kbd/__tests__/kbd.test.tsx
  • packages/raystack/components/kbd/index.tsx
  • packages/raystack/components/kbd/kbd.module.css
  • packages/raystack/components/kbd/kbd.tsx
💤 Files with no reviewable changes (1)
  • packages/raystack/components/command/command.module.css
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/raystack/components/kbd/index.tsx
  • packages/raystack/components/kbd/kbd.tsx
  • apps/www/src/content/docs/components/kbd/index.mdx

Comment thread apps/www/src/content/docs/components/kbd/props.ts
Comment thread apps/www/src/content/docs/components/kbd/props.ts
The group and shortcut variants are inherited defaults, not applied
unconditionally: an explicit `variant` on a child `Kbd` wins, and a `Kbd`
passed to `Command.Shortcut` as an element child keeps its own variant.
Reword the four places that described the variant as applying to every
key, and state that `ghost` is `Command.Shortcut`'s default rather than
its only option.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
export type KbdGroupProps = ComponentProps<'kbd'> & VariantProps<typeof kbd>;

const KbdGroup = ({ className, variant, ...props }: KbdGroupProps) => (
<KbdGroupContext.Provider value={variant ?? undefined}>

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.

Why do we need context here? Let's not use context in a simple component like KBD.

The variant at group level can be solved using css only. If it can't be done let's remove the support and keep the variant at kbd level only.

CommandSeparator.displayName = 'Command.Separator';

export type CommandShortcutProps = ComponentProps<'span'>;
export type CommandShortcutProps = KbdGroupProps;

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.

Let's remove the Command.Shortcut component. I missed that it used to map over a string.

Keeping that complexity of loop an valid element check doesn't make sense especially when it's just used as a trailingIcon.

<Command.Item trailingIcon={<Command.Shortcut>⌘ B</Command.Shortcut>}

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