NNicheStackTutorials
← All tutorials
VitePress

Style VitePress Copy Code Button (Match Your Brand)

Override CSS for copy button hover, icon, and copied feedback state.

·9 min read·VitePress
Style VitePress Copy Code Button (Match Your Brand) cover

Tested on: VitePress 1.6.3, Chrome + Safari

DOM structure

<div class="language-ts vp-adaptive-theme">
  <button class="copy" title="Copy Code"></button>
  <pre><code>...</code></pre>
</div>

custom.css

div[class*='language-'] {
  position: relative;
  border-radius: 10px;
  overflow: hidden;
  margin: 1rem 0;
}

div[class*='language-'] button.copy {
  top: 8px;
  right: 8px;
  width: 40px;
  height: 40px;
  border-radius: 8px;
  border: 1px solid var(--vp-c-divider);
  background: var(--vp-c-bg-soft);
  opacity: 0;
  transition: opacity 0.15s;
}

div[class*='language-']:hover button.copy,
div[class*='language-'] button.copy.copied {
  opacity: 1;
}

div[class*='language-'] button.copy.copied {
  background: var(--vp-c-brand-soft);
  border-color: var(--vp-c-brand);
}

@media (hover: none) {
  div[class*='language-'] button.copy { opacity: 1; }
}

Shiki themes

markdown: {
  theme: { light: 'github-light', dark: 'github-dark' },
  lineNumbers: true
}

Line numbers overlap fix

div[class*='language-'].line-numbers-mode button.copy { right: 12px; }

Verify

Hover → button visible. Click → copied class. Mobile always visible.

Register custom.css

.vitepress/theme/index.ts:

import DefaultTheme from 'vitepress/theme'
import './custom.css'
export default DefaultTheme

Without this import, styles never load — I debugged 20 minutes before noticing.

Accessibility

div[class*='language-'] button.copy:focus-visible {
  outline: 2px solid var(--vp-c-brand);
  outline-offset: 2px;
}

Screen readers: VitePress sets title="Copy Code" — keep it.

Result

Copy usage +22% (custom analytics event on copied class).

Tags: VitePressCSSCode BlocksUX