Simple Admonitions in Astro

Add clean, professional tip, warning, danger, note, info, and caution blocks to your Astro blog with a soft stone theme and optional custom titles.

Add clean, professional, and accessible admonition blocks to your Astro site — perfect for documentation, tutorials, or blog posts.

This guide gives you:

  • 6 types: tip, warning, danger, note, info, caution
  • Custom titles via [Your Title] syntax
  • Soft stone-themed colors (pale, desaturated, readable)
  • Zero JavaScript, fully static
  • No icons, minimal & elegant

Step 1: Install Required Package

1npm install remark-directive

Step 2: Create the Remark Plugin

Create remark-admonition.js in your project root:

 1import { visit } from 'unist-util-visit';
 2
 3/** Admonition Plugin – compatible with Astro + ESM + remark-directive */
 4function remarkAdmonitionCompatible() {
 5  return (tree) => {
 6    visit(tree, (node) => {
 7      if (
 8        node.type === 'containerDirective' &&
 9        ['tip', 'warning', 'danger', 'note', 'info', 'caution'].includes(node.name)
10      ) {
11        const data = node.data || (node.data = {});
12        const type = node.name;
13        const titleText = type.charAt(0).toUpperCase() + type.slice(1);
14
15        // Main element: <div class="admonition admonition-tip">
16        data.hName = 'div';
17        data.hProperties = {
18          className: [`admonition`, `admonition-${type}`],
19        };
20
21        // Check if the first child is a paragraph (often contains the title)
22        const firstChild = node.children[0];
23        let titleContent = [];
24
25        if (firstChild?.type === 'paragraph') {
26          const textContent = firstChild.children
27            .filter((child) => child.type === 'text')
28            .map((child) => child.value)
29            .join('')
30            .trim();
31
32          if (textContent && !textContent.includes('\n')) {
33            titleContent = firstChild.children;
34            // Remove the first paragraph, as it is now the title
35            node.children.shift();
36          }
37        }
38
39        // If no title from content → default title
40        if (titleContent.length === 0) {
41          titleContent = [{ type: 'text', value: titleText }];
42        }
43
44        // Create title element: <p class="admonition-title">TIP</p>
45        const titleNode = {
46          type: 'paragraph',
47          data: {
48            hName: 'p',
49            hProperties: {
50              className: 'admonition-title',
51            },
52          },
53          children: titleContent,
54        };
55
56        // Insert title at the front
57        node.children.unshift(titleNode);
58      }
59    });
60  };
61}

Step 3: Update astro.config.mjs

 1// astro.config.mjs
 2import { defineConfig } from 'astro/config';
 3import remarkDirective from 'remark-directive';
 4import remarkAdmonitionCompatible from './remark-admonition.js';
 5
 6export default defineConfig({
 7  markdown: {
 8    remarkPlugins: [
 9      remarkDirective,
10      remarkAdmonitionCompatible,
11    ],
12  },
13});

Step 4: Add Global Styles

Either add the styles to your global.css file or create src/styles/admonitions.css and import it into your layout.

 1/* src/styles/admonitions.css */
 2.admonition {
 3  margin: 1.5rem 0;
 4  padding: 0.75rem 1rem;
 5  border-left: 4px solid;
 6  border-radius: 0.375rem;
 7  background-color: var(--color-stone-50);
 8  font-size: 0.9375rem;
 9  line-height: 1.6;
10}
11
12.admonition > :first-child {
13  margin-top: 0;
14}
15
16.admonition > :last-child {
17  margin-bottom: 0;
18}
19
20.admonition p {
21  margin: 0.5rem 0;
22  color: var(--color-stone-700);
23}
24
25.admonition p:first-of-type {
26  margin-top: 0;
27}
28
29.admonition p:last-of-type {
30  margin-bottom: 0;
31}
32
33/* Title */
34.admonition-title {
35  margin: 0 0 0.5rem 0;
36  font-weight: 600;
37  font-size: 0.8125rem;
38  text-transform: uppercase;
39  letter-spacing: 0.05em;
40  color: var(--color-stone-800);
41}
42
43/* Tip – Green */
44.admonition-tip {
45  background-color: var(--color-green-100);
46  border-color: var(--color-green-300);
47  color: var(--color-green-800);
48}
49
50/* Warning – Yellow */
51.admonition-warning {
52  background-color: var(--color-yellow-100);
53  border-color: var(--color-yellow-300);
54  color: var(--color-yellow-800);
55}
56
57/* Danger – Red */
58.admonition-danger {
59  background-color: var(--color-red-100);
60  border-color: var(--color-red-300);
61  color: var(--color-red-800);
62}
63
64/* Note – Blue */
65.admonition-note {
66  background-color: var(--color-blue-100);
67  border-color: var(--color-blue-300);
68  color: var(--color-blue-800);
69}
70
71/* Info – Cyan */
72.admonition-info {
73  background-color: var(--color-cyan-100);
74  border-color: var(--color-cyan-300);
75  color: var(--color-cyan-800);
76}
77
78/* Caution – Orange */
79.admonition-caution {
80  background-color: var(--color-orange-100);
81  border-color: var(--color-orange-300);
82  color: var(--color-orange-800);
83}

Import in your layout:

---
// src/layouts/BlogPost.astro
import '../styles/admonitions.css';
---

If you are not using Tailwind.css as I am, add these color variables to your global CSS or :root:

 1:root {
 2  /* Stone */
 3  --color-stone-50: #fafaf9;
 4  --color-stone-700: #44403c;
 5  --color-stone-800: #292524;
 6
 7  /* Green */
 8  --color-green-100: #f0fdf4;
 9  --color-green-300: #86efac;
10  --color-green-800: #166534;
11
12  /* Yellow */
13  --color-yellow-100: #fefce8;
14  --color-yellow-300: #fde047;
15  --color-yellow-800: #9a3412;
16
17  /* Red */
18  --color-red-100: #fef2f2;
19  --color-red-300: #fca5a5;
20  --color-red-800: #991b1b;
21
22  /* Blue */
23  --color-blue-100: #eff6ff;
24  --color-blue-300: #93c5fd;
25  --color-blue-800: #1e40af;
26
27  /* Cyan */
28  --color-cyan-100: #ecfeff;
29  --color-cyan-300: #67e8f9;
30  --color-cyan-800: #0e7490;
31
32  /* Orange */
33  --color-orange-100: #fff7ed;
34  --color-orange-300: #fdba74;
35  --color-orange-800: #9c4221;
36}

Example Usage in Markdown

 1:::tip[Pro Tip]
 2Use `Cmd + K` to open the command palette.
 3:::
 4
 5:::warning
 6Never commit your `.env` file.
 7:::
 8
 9:::danger
10This action **cannot be undone**.
11:::
12
13:::note
14Astro compiles to static HTML by default.
15:::
16
17:::info[Did You Know?]
18You can use `.astro` files inside Markdown with MDX.
19:::
20
21:::caution
22This feature is experimental — test thoroughly.
23:::

Result

:::tip[Pro Tip] Use Cmd + K to open the command palette. :::

:::warning Never commit your .env file. :::

:::danger This action cannot be undone. :::

:::note Astro compiles to static HTML by default. :::

:::info[Did You Know?] You can use .astro files inside Markdown with MDX. :::

:::caution This feature is experimental — test thoroughly. :::


Done

You now have:

  • 6 flexible admonition types
  • Custom titles with [Title] syntax
  • Clean, stone-themed design
  • Full Astro + ESM compatibility
  • Zero runtime overhead

Pro tip: Want success or example later? Just add the name to the array in the plugin and a CSS rule — done.

Clean. Simple. Powerful.

Reply to this post by email

Nach oben