I'm building a documentation site generator using Nuxt.js and MDX. I've created a custom CodeBlock component to render code snippets within my MDX files. The component seems to be rendering correctly, and the styling is generally fine, except for one peculiar issue:
Problem:
The first line of code within block is consistently indented or pushed slightly to the right compared to subsequent lines. This happens only on the first line of the code block. The code tag itself does not have any padding set, and I've verified that there's no extra whitespace in my source MDX files that would cause this.
Here's a visual of the issue:
Setup:
Nuxt.js: Using a recent version (please specify version, if possible)
MDX: Using MDX to parse markdown
Custom CodeBlock Component: The MDX is rendered using a custom CodeBlock Vue component that generates the code block.
Syntax Highlighting: (mention if you're using something like Prism.js or highlight.js)
the source code of my vue component
<!-- filepath: /c:/Users/admin/Desktop/Full Typescript Projects/cognito 1.0/Vdocs/components/DocsBlocks/codeblock.vue -->
<script setup>
import { Button } from '@/components/ui/button'
import { IconCopy } from '@tabler/icons-vue'
import { Toaster, toast } from 'vue-sonner'
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css'
import vue from "highlight.js/lib/languages/vbscript-html"
import bash from "highlight.js/lib/languages/bash"
// Register the Vue and Bash languages
hljs.registerLanguage('vue', vue)
hljs.registerLanguage('bash', bash)
const props = defineProps({
content: { type: String, default: '' },
lang: { type: String, default: 'plaintext' },
})
import { ref, watch } from 'vue'
const highlightedCode = ref('')
watch(
() => props.content,
(newCode) => {
try {
highlightedCode.value =
props.lang && props.lang !== 'auto'
? hljs.highlight(props.lang, newCode.trim()).value
: hljs.highlightAuto(newCode.trim()).value
} catch {
highlightedCode.value = newCode.trim()
}
},
{ immediate: true }
)
const copyQuick = () => {
navigator.clipboard.writeText(props.content.trim())
toast.success('Copied to clipboard')
}
</script>
<template>
<div class="flex flex-col gap-0 my-1">
<div class="codeblocknav relative w-full py-2 px-3 flex-place-center border-x border-t rounded-t-md border-border">
<div class="text-sm px-3 rounded-md py-1 border border-border">
{{ props.lang }}
</div>
<Button @click="copyQuick()" variant="outline" size="icon" class="ml-auto right-2 top-1">
<IconCopy class="size-5" />
</Button>
</div>
<Toaster />
<div class="bg-background border border-border rounded-md mt-0 rounded-t-none w-full p-4 codeblock">
<pre class="code-content">
<code v-html="highlightedCode" :class="['language-' + props.lang]"></code>
</pre>
</div>
</div>
</template>
<style scoped>
/* Firefox (uncomment to work in Firefox, although other properties will not work!) */
/** {
scrollbar-width: thin;
scrollbar-color: #397524 #DFE9EB;
}*/
/* Chrome, Edge and Safari */
*::-webkit-scrollbar {
height: 10px;
width: 10px;
}
*::-webkit-scrollbar-track {
border-radius: 5px;
@apply bg-background;
}
*::-webkit-scrollbar-thumb {
@apply bg-border pb-2 rounded-md;
}
.code-content {
@apply overflow-x-auto mb-1;
}
.code-content pre{
@apply m-0 p-0;
}
.code-content code{
@apply m-0 p-0;
white-space: pre;
}
</style>
here is a workable example in vue playground:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre
The HTML element represents preformatted text which is to be presented exactly as written in the HTML file
so despite you are wrapping your text in <code>
elements, the white spaces preceeding those elements still count as text nodes that will be rendered as is.
The solution could be to just use a regular <div>
instead of the parent <pre>