Theming
Learn how to customize the appearance of vue-stream-markdown components.
vue-stream-markdown is designed to be flexible and customizable, allowing you to adapt its appearance to match your application's design system. This guide covers the various ways you can modify vue-stream-markdown's styles to suit your needs.
No Atomic CSS Required
Important: vue-stream-markdown does not require Tailwind CSS, UnoCSS, or any other atomic CSS framework. All styles are self-contained and scoped strictly within the .stream-markdown class, ensuring they won't interfere with your existing styles or require additional dependencies.
Scoped Styling
All vue-stream-markdown styles are scoped under the .stream-markdown class. This means:
- Styles are isolated and won't affect other parts of your application
- You can safely use vue-stream-markdown alongside any CSS framework or methodology
<template>
<Markdown :content="content" :is-dark="false" />
</template>This renders as:
<div class="stream-markdown light">
<!-- markdown content -->
</div>Targeting Specific Elements
vue-stream-markdown uses semantic data-stream-markdown attributes on all rendered elements, allowing you to target specific element types for styling. This provides a clean, maintainable way to customize individual components without relying on complex CSS selectors.
Available Data Attributes
Each rendered element has a data-stream-markdown attribute with a semantic value:
heading-1throughheading-6- Headingsparagraph- Paragraphsblockquote- Blockquotescode- Code blocksinline-code- Inline codelink- Linksimage- Imagestable- Tableslist-item- List itemsmath- Math blocksinline-math- Inline math- And many more...
Customizing with Data Attributes
You can target any element using the data-stream-markdown attribute:
.stream-markdown [data-stream-markdown='heading-1'] {
color: #3b82f6;
font-weight: 700;
}
.stream-markdown [data-stream-markdown='code'] {
background-color: #1e293b;
border-radius: 8px;
}
.stream-markdown [data-stream-markdown='link'] {
color: #8b5cf6;
}CSS Variables (Recommended)
vue-stream-markdown components use CSS variables for theming, following the shadcn/ui design system. This is the simplest way to customize colors, borders, and other design tokens across all components.
Default Theme
The library includes a default theme based on the shadcn/ui blue theme using oklch color space, with light and dark variants. Import it in your application:
<script setup lang="ts">
import { Markdown } from 'vue-stream-markdown'
import 'vue-stream-markdown/index.css'
import 'vue-stream-markdown/theme.css'
</script>Variables Used by vue-stream-markdown
The following CSS variables are actually used in the source code:
/* Typography */
--font-sans
--font-mono
/* Colors */
--background
--foreground
--primary
--accent
--border
--muted
--muted-foreground
--popover
--popover-foreground
/* Transitions */
--default-transition-duration
--typewriter-transition-durationCustomizing CSS Variables
You can override these variables scoped to .stream-markdown. The library automatically applies .light or .dark classes based on the isDark prop, allowing you to customize both themes:
/* Override for all themes */
.stream-markdown {
--primary: #8b5cf6;
--border: #e5e7eb;
}
/* Light theme specific */
.stream-markdown.light {
--background: #ffffff;
--foreground: #0f172a;
}
/* Dark theme specific */
.stream-markdown.dark {
--background: #0f172a;
--foreground: #f8fafc;
}Using with Tailwind CSS v3
If you're using Tailwind CSS v3 with HSL color values (e.g., 0 0% 100% instead of hsl(0 0% 100%)), you can use the useTailwindV3Theme composable to automatically convert these values to the proper HSL format:
<script setup lang="ts">
import { nextTick, ref, watch } from 'vue'
import { Markdown, useTailwindV3Theme } from 'vue-stream-markdown'
const isDark = ref(false)
// Configure Tailwind v3 theme adapter
const { generateCSS } = useTailwindV3Theme({
styleScope: '.stream-markdown', // Optional, defaults to '.stream-markdown'
element: document.body, // Optional, defaults to document.body
})
// Regenerate CSS when theme changes
watch(() => isDark.value, () => {
nextTick(generateCSS)
})
</script>In your CSS, define variables with HSL values without the hsl() wrapper:
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--border: 214.3 31.8% 91.4%;
}The useTailwindV3Theme composable will automatically detect these values and convert them to hsl() format for vue-stream-markdown to use.
Best Practices
- Use CSS Variables for Colors: Prefer overriding CSS variables over direct element styling for consistency
- Scope Your Styles: Always prefix custom styles with
.stream-markdownto avoid conflicts - Use Data Attributes: Target elements using
data-stream-markdownattributes for maintainable, semantic styling - Respect Theme Variants: Consider both light and dark themes when customizing
- Maintain Accessibility: Ensure your custom styles maintain proper color contrast, focus states, and semantic HTML structure
- Test During Streaming: Verify that your custom styles work well with incomplete content during streaming
- Preserve Animations: vue-stream-markdown includes built-in animations for smooth streaming. Be careful not to override animation-related classes unless intentional
Styling Priority
The three styling approaches have the following priority (highest to lowest):
- Custom Components - Complete control over rendering
- CSS via data-stream-markdown selectors - Element-specific styling
- CSS Variables - Global theme tokens