Skip to content

feat(Kbd): add color prop & soft variant #4549

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/content/3.components/kbd.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,27 @@ items:
---
::

### Color :badge{label="Soon" class="align-text-top"}

Use the `color` prop to change the color of the Kbd.

::component-code
---
props:
color: neutral
slots:
default: K
---
::

### Variant

Use the `variant` prop to change the variant of the Kbd.

::component-code
---
props:
color: neutral
variant: solid
slots:
default: K
Expand Down
12 changes: 4 additions & 8 deletions playground/app/pages/components/kbd.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,16 @@ import theme from '#build/ui/kbd'
import { kbdKeysMap } from '@nuxt/ui/composables/useKbd.js'

const sizes = Object.keys(theme.variants.size) as Array<keyof typeof theme.variants.size>
const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme.variants.variant>
const colors = Object.keys(theme.variants.color) as Array<keyof typeof theme.variants.color>

const kbdKeys = Object.keys(kbdKeysMap)
</script>

<template>
<div class="flex flex-col gap-2">
<div class="flex items-center gap-1">
<UKbd value="meta" />
</div>
<div class="flex items-center gap-1">
<UKbd value="meta" variant="subtle" />
</div>
<div class="flex items-center gap-1">
<UKbd value="meta" variant="solid" />
<div v-for="color in colors" :key="color" class="flex items-center gap-1 ms-[-22px]">
<UKbd v-for="variant in variants" :key="`${color}-${variant}`" value="meta" :variant="variant" :color="color" />
</div>
<div class="flex items-center gap-1 ms-[-220px]">
<UKbd v-for="(kdbKey, index) in kbdKeys" :key="index" :value="kdbKey" />
Expand Down
6 changes: 5 additions & 1 deletion src/runtime/components/Kbd.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export interface KbdProps {
*/
as?: any
value?: KbdKey | string
/**
* @defaultValue 'neutral'
*/
color?: Kbd['variants']['color']
/**
* @defaultValue 'outline'
*/
Expand Down Expand Up @@ -48,7 +52,7 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.kbd || {}) }
</script>

<template>
<Primitive :as="as" :class="ui({ variant, size, class: props.class })">
<Primitive :as="as" :class="ui({ class: props.class, color: props.color, variant: props.variant, size: props.size })">
<slot>
{{ getKbdKey(value) }}
</slot>
Expand Down
51 changes: 46 additions & 5 deletions src/theme/kbd.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,60 @@
export default {
import type { ModuleOptions } from '../module'

export default (options: Required<ModuleOptions>) => ({
base: 'inline-flex items-center justify-center px-1 rounded-sm font-medium font-sans',
variants: {
color: {
...Object.fromEntries((options.theme.colors || []).map((color: string) => [color, ''])),
neutral: ''
},
variant: {
solid: 'bg-inverted text-inverted',
outline: 'bg-default text-highlighted ring ring-inset ring-accented',
subtle: 'bg-elevated text-default ring ring-inset ring-accented'
solid: '',
outline: '',
soft: '',
subtle: ''
},
size: {
sm: 'h-4 min-w-[16px] text-[10px]',
md: 'h-5 min-w-[20px] text-[11px]',
lg: 'h-6 min-w-[24px] text-[12px]'
}
},
compoundVariants: [...(options.theme.colors || []).map((color: string) => ({
color,
variant: 'solid',
class: `text-inverted bg-${color}`
})), ...(options.theme.colors || []).map((color: string) => ({
color,
variant: 'outline',
class: `ring ring-inset ring-${color}/50 text-${color}`
})), ...(options.theme.colors || []).map((color: string) => ({
color,
variant: 'soft',
class: `text-${color} bg-${color}/10`
})), ...(options.theme.colors || []).map((color: string) => ({
color,
variant: 'subtle',
class: `text-${color} ring ring-inset ring-${color}/25 bg-${color}/10`
})), {
color: 'neutral',
variant: 'solid',
class: 'text-inverted bg-inverted'
}, {
color: 'neutral',
variant: 'outline',
class: 'ring ring-inset ring-accented text-default bg-default'
}, {
color: 'neutral',
variant: 'soft',
class: 'text-default bg-elevated'
}, {
color: 'neutral',
variant: 'subtle',
class: 'ring ring-inset ring-accented text-default bg-elevated'
}],
defaultVariants: {
variant: 'outline',
color: 'neutral',
size: 'md'
}
}
})
3 changes: 2 additions & 1 deletion test/components/Kbd.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ describe('Kbd', () => {
// Props
['with value', { props: { value: 'K' } }],
...sizes.map((size: string) => [`with size ${size}`, { props: { value: 'K', size } }]),
...variants.map((variant: string) => [`with variant ${variant}`, { props: { value: 'K', variant } }]),
...variants.map((variant: string) => [`with primary variant ${variant}`, { props: { value: 'K', variant } }]),
...variants.map((variant: string) => [`with neutral variant ${variant}`, { props: { value: 'K', variant, color: 'neutral' } }]),
['with as', { props: { value: 'K', as: 'span' } }],
['with class', { props: { value: 'K', class: 'font-bold' } }],
// Slots
Expand Down
170 changes: 85 additions & 85 deletions test/components/__snapshots__/CommandPalette-vue.spec.ts.snap

Large diffs are not rendered by default.

Loading