- Author
- app-hacks
- Published
Tailwind CSS / Astro / Markdown
Styling Markdown Beautifully with Tailwind CSS Typography
Use Tailwind CSS 4 and the Typography plugin to make headings, code, links, and long-form Markdown content readable in an Astro site.
HTML generated from Markdown differs from ordinary components: you cannot attach utility classes to every h2, p, ul, and pre. Tailwind CSS Typography applies a consistent reading style to those descendants through a single prose class on their parent.
This project uses Tailwind CSS 4 with @tailwindcss/vite, not the older @astrojs/tailwind integration intended for Tailwind CSS 3 projects.
Packages and configuration
npx astro add tailwind
npm install @tailwindcss/typography
Register Tailwind as a Vite plugin in Astro:
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({ vite: { plugins: [tailwindcss()] } });
Load Tailwind and Typography from global CSS. Tailwind CSS 4 is CSS-first, so a tailwind.config.js file is not needed merely to enable the plugin.
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@theme {
--color-ink: #17212b;
--color-muted: #667582;
--color-accent: #0f83d8;
}
Importing this stylesheet from the shared layout makes utilities and prose available to every article without repeated imports.
Apply prose to rendered Markdown
---
import { render } from 'astro:content';
const { post } = Astro.props;
const { Content } = await render(post);
---
<article class="mx-auto max-w-3xl rounded-2xl bg-white px-6 py-10">
<div class="prose prose-slate max-w-none prose-headings:font-bold prose-a:text-sky-700 prose-a:no-underline hover:prose-a:underline">
<Content />
</div>
</article>
Typography supplies its own maximum width. When the outer article already controls line length, max-w-none prevents a second width limit. Keep an outer limit such as max-w-3xl; readable typography depends on line length as well as font size.
Tune article colors with CSS variables
@layer components {
.article-prose {
--tw-prose-body: #33414d;
--tw-prose-headings: #17212b;
--tw-prose-links: #0968ad;
--tw-prose-code: #17212b;
--tw-prose-pre-code: #e6edf3;
--tw-prose-pre-bg: #15222e;
--tw-prose-quote-borders: #69b8ed;
font-size: 1.0625rem;
line-height: 1.95;
}
.article-prose :where(h2, h3) {
scroll-margin-top: 6rem;
letter-spacing: -0.025em;
}
}
Use dark gray for long body text and a stronger tone for headings. Links must remain identifiable without relying on hover alone; if underlines are removed, preserve sufficient color contrast and a clear focus indicator.
Separate code blocks from inline code
.article-prose :where(pre) {
overflow-x: auto;
border: 1px solid #263848;
border-radius: 0.75rem;
box-shadow: 0 10px 30px rgb(23 33 43 / 8%);
}
.article-prose :where(code):not(:where(pre code)) {
border: 1px solid #d9e2e9;
border-radius: 0.35rem;
background: #eef3f6;
padding: 0.12em 0.36em;
font-size: 0.88em;
font-weight: 600;
}
.article-prose :where(code):not(:where(pre code))::before,
.article-prose :where(code):not(:where(pre code))::after { content: none; }
Allow long code lines to scroll horizontally so indentation and structure remain intact. Inline code uses its own background and border. Removing Typography’s decorative pseudo-element quotes avoids confusing them with real code characters.
Typography is a foundation, not a finished theme. Coordinate content width, line height, link recognition, focus states, and code overflow so the whole article behaves as one accessible reading surface.