feat: add callout support (#655)

* feat: toggle .dark class on theme change

* feat: add callouts with rehype-callouts plugin

* docs: update blog posts with callouts
This commit is contained in:
Sat Naing
2026-06-06 09:09:08 +07:00
committed by GitHub
parent 98f15ee3bc
commit e0506cb41e
10 changed files with 106 additions and 25 deletions
+2
View File
@@ -10,6 +10,7 @@ import sitemap from "@astrojs/sitemap";
import { unified } from "@astrojs/markdown-remark"; import { unified } from "@astrojs/markdown-remark";
import remarkToc from "remark-toc"; import remarkToc from "remark-toc";
import remarkCollapse from "remark-collapse"; import remarkCollapse from "remark-collapse";
import rehypeCallouts from "rehype-callouts";
import { import {
transformerNotationDiff, transformerNotationDiff,
transformerNotationHighlight, transformerNotationHighlight,
@@ -40,6 +41,7 @@ export default defineConfig({
remarkToc, remarkToc,
[remarkCollapse, { test: "Table of contents" }], [remarkCollapse, { test: "Table of contents" }],
], ],
rehypePlugins: [rehypeCallouts],
}), }),
shikiConfig: { shikiConfig: {
themes: { light: "min-light", dark: "night-owl" }, themes: { light: "min-light", dark: "night-owl" },
+1
View File
@@ -23,6 +23,7 @@
"astro": "^6.4.2", "astro": "^6.4.2",
"dayjs": "^1.11.21", "dayjs": "^1.11.21",
"lodash.kebabcase": "^4.1.1", "lodash.kebabcase": "^4.1.1",
"rehype-callouts": "^2.1.2",
"remark-collapse": "^0.1.2", "remark-collapse": "^0.1.2",
"remark-toc": "^9.0.0", "remark-toc": "^9.0.0",
"satori": "^0.26.0", "satori": "^0.26.0",
+15
View File
@@ -29,6 +29,9 @@ importers:
lodash.kebabcase: lodash.kebabcase:
specifier: ^4.1.1 specifier: ^4.1.1
version: 4.1.1 version: 4.1.1
rehype-callouts:
specifier: ^2.1.2
version: 2.1.2
remark-collapse: remark-collapse:
specifier: ^0.1.2 specifier: ^0.1.2
version: 0.1.2 version: 0.1.2
@@ -2263,6 +2266,10 @@ packages:
regex@6.1.0: regex@6.1.0:
resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==}
rehype-callouts@2.1.2:
resolution: {integrity: sha512-ZZWZ6EknUHiSzr4pQ88C7db3su4DElfJRmphZJbXpDdwW3urTwlYZpHckoC9pjEvBmUEEiJAM0uuc2uxyLdTfg==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
rehype-parse@9.0.1: rehype-parse@9.0.1:
resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==}
@@ -5341,6 +5348,14 @@ snapshots:
dependencies: dependencies:
regex-utilities: 2.3.0 regex-utilities: 2.3.0
rehype-callouts@2.1.2:
dependencies:
'@types/hast': 3.0.4
hast-util-from-html: 2.0.3
hast-util-is-element: 3.0.0
hastscript: 9.0.1
unist-util-visit: 5.1.0
rehype-parse@9.0.1: rehype-parse@9.0.1:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
+69 -4
View File
@@ -1,7 +1,7 @@
--- ---
author: Sat Naing author: Sat Naing
pubDatetime: 2022-09-23T15:22:00Z pubDatetime: 2022-09-23T15:22:00Z
modDatetime: 2026-05-17T04:47:08.212Z modDatetime: 2026-06-03T00:00:00.000Z
title: Adding new posts in AstroPaper theme title: Adding new posts in AstroPaper theme
slug: adding-new-posts-in-astropaper-theme slug: adding-new-posts-in-astropaper-theme
featured: true featured: true
@@ -44,6 +44,7 @@ src/content/posts/docs/_legacy/how-to.md -> mysite.com/posts/docs/how-to
src/content/posts/Example Dir/Dummy Post.md -> mysite.com/posts/example-dir/dummy-post src/content/posts/Example Dir/Dummy Post.md -> mysite.com/posts/example-dir/dummy-post
``` ```
> [!TIP]
> Files and directories prefixed with `_` are excluded from routing. Use them for drafts, shared assets, or internal-only content. > Files and directories prefixed with `_` are excluded from routing. Use them for drafts, shared assets, or internal-only content.
## Frontmatter ## Frontmatter
@@ -71,7 +72,8 @@ Here is the list of frontmatter properties for each post:
</ResponsiveTable> </ResponsiveTable>
> Tip! You can get an ISO 8601 datetime by running `new Date().toISOString()` in the console. > [!TIP]
> You can get an ISO 8601 datetime by running `new Date().toISOString()` in the console.
Only `title`, `description`, and `pubDatetime` fields in frontmatter must be specified. Only `title`, `description`, and `pubDatetime` fields in frontmatter must be specified.
@@ -116,6 +118,65 @@ AstroPaper includes workspace snippets to speed up creating new posts:
These snippets live in `.vscode/astro-paper.code-snippets`. If you use VS Code (or Cursor), they should be available automatically when you open the workspace. These snippets live in `.vscode/astro-paper.code-snippets`. If you use VS Code (or Cursor), they should be available automatically when you open the workspace.
## Callouts
AstroPaper started supporting callouts in AstroPaper v6.1. They use a simple blockquote syntax powered by `rehype-callouts` (Obsidian theme).
Here are the most commonly used types:
> [!NOTE]
> Supplementary information the reader should be aware of.
> [!TIP]
> Helpful advice, shortcuts, or best practices.
> [!WARNING]
> Something that could go wrong or have unintended consequences.
> [!DANGER]
> Serious risk of failure, data loss, or incorrect behavior.
> [!INFO]
> Neutral informational context — less urgent than a note.
> [!SUCCESS]
> Confirmation that something worked or is correct.
The full list of supported types includes: `NOTE`, `ABSTRACT`, `INFO`, `TODO`, `TIP`, `SUCCESS`, `QUESTION`, `WARNING`, `FAILURE`, `DANGER`, `BUG`, `EXAMPLE`, `QUOTE` — each with its own icon and color. Many types also accept aliases (e.g. `HINT` and `IMPORTANT` for `TIP`, `CAUTION` for `WARNING`). See the [rehype-callouts docs](https://github.com/lin-stephanie/rehype-callouts) for the complete reference.
### Collapsible callouts
Add `-` after the type to make the callout collapsed by default, or `+` to make it expanded but collapsible:
> [!WARNING]- Read before proceeding
> This content is hidden until the reader expands it. Useful for long caveats that would otherwise interrupt the flow.
> [!TIP]+ Pro tip (expanded by default)
> This starts open but can be collapsed. Great for optional detail you still want visible on first load.
### Custom titles
Replace the default type label with any title you like by adding text after the type:
> [!NOTE] Did you know?
> The text after the type becomes the callout's heading. Leave it out and the type name is used automatically.
### Syntax summary
```md
> [!NOTE]
> Supplementary information.
> [!WARNING]- Collapsed by default
> Hidden until expanded.
> [!TIP]+ Expanded, but collapsible
> Starts open.
> [!DANGER] Custom title
> Replaces the default heading.
```
## Adding table of contents ## Adding table of contents
By default, a post does not include any table of contents (TOC). To include one, write `Table of contents` as an h2 heading (`##` in Markdown) and place it where you want it to appear: By default, a post does not include any table of contents (TOC). To include one, write `Table of contents` as an h2 heading (`##` in Markdown) and place it where you want it to appear:
@@ -181,7 +242,8 @@ export default defineConfig({
Here are two methods for storing images and using them inside a markdown file. Here are two methods for storing images and using them inside a markdown file.
> Note: If you need to style optimized images in markdown, you should [use MDX](https://docs.astro.build/en/guides/images/#images-in-mdx-files). > [!IMPORTANT]
> If you need to style optimized images in markdown, you should [use MDX](https://docs.astro.build/en/guides/images/#images-in-mdx-files).
### Inside `src/assets/` directory (recommended) ### Inside `src/assets/` directory (recommended)
@@ -203,6 +265,7 @@ Example: suppose you want to display `example.jpg` whose path is `src/assets/ima
<!-- ^^ This is wrong --> <!-- ^^ This is wrong -->
``` ```
> [!TIP]
> Technically, you can store images inside any directory under `src`. `src/assets` is just a recommendation. > Technically, you can store images inside any directory under `src`. `src/assets` is just a recommendation.
### Inside `public/` directory ### Inside `public/` directory
@@ -225,7 +288,8 @@ Example: assume `example.jpg` is located at `public/assets/images/example.jpg`.
### Image compression ### Image compression
When putting images in a blog post (especially those in the `public/` directory), it is recommended to compress them. This will affect the overall performance of the website. > [!WARNING]
> When putting images in a blog post (especially those in the `public/` directory), compress them first. Unoptimized images significantly hurt page performance.
Recommended image compression sites: Recommended image compression sites:
@@ -236,4 +300,5 @@ Recommended image compression sites:
The default OG image will be used if a post does not specify one. Though not required, an OG image relevant to the post should be specified in the frontmatter. The recommended size for OG images is **_1200 X 640_** px. The default OG image will be used if a post does not specify one. Though not required, an OG image relevant to the post should be specified in the frontmatter. The recommended size for OG images is **_1200 X 640_** px.
> [!TIP]
> Since AstroPaper v1.4.0, OG images are generated automatically if not specified. Check out [the announcement](https://astro-paper.pages.dev/posts/dynamic-og-image-generation-in-astropaper-blog-posts/). > Since AstroPaper v1.4.0, OG images are generated automatically if not specified. Check out [the announcement](https://astro-paper.pages.dev/posts/dynamic-og-image-generation-in-astropaper-blog-posts/).
+8 -16
View File
@@ -1,7 +1,7 @@
--- ---
author: Sat Naing author: Sat Naing
pubDatetime: 2022-12-28T04:59:04.866Z pubDatetime: 2022-12-28T04:59:04.866Z
modDatetime: 2026-05-04T00:00:00Z modDatetime: 2026-06-03T00:00:00.000Z
title: Dynamic OG image generation in AstroPaper blog posts title: Dynamic OG image generation in AstroPaper blog posts
slug: dynamic-og-image-generation-in-astropaper-blog-posts slug: dynamic-og-image-generation-in-astropaper-blog-posts
featured: false featured: false
@@ -49,9 +49,8 @@ Dynamic OG images include _the blog post title_, _author name_, and _site title_
### Issue with Non-Latin Characters ### Issue with Non-Latin Characters
Titles with non-latin characters won't display properly out of the box. In AstroPaper v6, dynamic OG images load font files from Astro's **Fonts** configuration (`astro.config.ts`) and register them with Satori. > [!CAUTION]
> Titles with non-latin characters won't display properly out of the box. Switch the Google font family to one that covers your writing system, and include **both** `400` and `700` weights — Satori uses separate buffers for regular and bold, so missing either causes mismatched rendering.
To fix missing glyphs, switch the Google font family to one that covers your writing system, and make sure you include **both** `400` and `700` weights (Satori uses separate buffers for regular + bold).
```ts file="astro.config.ts" ```ts file="astro.config.ts"
import { defineConfig, fontProviders } from "astro/config"; import { defineConfig, fontProviders } from "astro/config";
@@ -79,15 +78,8 @@ If you change `cssVariable`, also update the matching key in:
> Check out [this PR](https://github.com/satnaing/astro-paper/pull/318) for more info. > Check out [this PR](https://github.com/satnaing/astro-paper/pull/318) for more info.
## Trade-off > [!WARNING] Caveats
>
While this is a nice feature to have, there's still a trade-off: AstroPaper generates one PNG per eligible post at build time (when og image is not specified in the frontmatter), so total build time grows with content volume. > - **Build time** grows with content volume — one PNG per eligible post is generated at build time. Generation is faster in v6 (PR [#632](https://github.com/satnaing/astro-paper/pull/632)), but on very large sites you can disable it with `features.dynamicOgImage: false` in `astro-paper.config.ts`.
> - **RTL languages** are not supported yet.
In AstroPaper v6, OG image generation is significantly faster (PR [#632](https://github.com/satnaing/astro-paper/pull/632)) than earlier implementations, so the per-image overhead is much lower in practice. If you still want to minimize build time on very large sites, you can disable it by setting `features.dynamicOgImage: false` in `astro-paper.config.ts` (and provide per-post `ogImage` files instead). > - **Emoji** in titles can be tricky — some may not render correctly.
## Limitations
At the time of writing this, [Satori](https://github.com/vercel/satori) is fairly new and has not reached major release yet. So, there are still some limitations to this dynamic OG image feature.
- RTL languages are not supported yet.
- [Using emoji](https://github.com/vercel/satori#emojis) in the title might be a little bit tricky.
@@ -1,7 +1,7 @@
--- ---
author: Sat Naing author: Sat Naing
pubDatetime: 2022-09-23T04:58:53Z pubDatetime: 2022-09-23T04:58:53Z
modDatetime: 2026-05-17T05:50:08.212Z modDatetime: 2026-06-03T00:00:00.000Z
title: How to configure AstroPaper theme title: How to configure AstroPaper theme
slug: how-to-configure-astropaper-theme slug: how-to-configure-astropaper-theme
featured: true featured: true
@@ -296,7 +296,8 @@ import { Font } from "astro:assets";
The `--font-app` variable is used throughout the theme via the `font-app` Tailwind utility class, so updating this single variable applies your custom font everywhere. The `--font-app` variable is used throughout the theme via the `font-app` Tailwind utility class, so updating this single variable applies your custom font everywhere.
> **Note**: Make sure the font name matches exactly as it appears on [Google Fonts](https://fonts.google.com). For other font providers or local fonts, refer to the [Astro Fonts documentation](https://docs.astro.build/en/guides/fonts/). > [!WARNING]
> Make sure the font name matches exactly as it appears on [Google Fonts](https://fonts.google.com). For other font providers or local fonts, refer to the [Astro Fonts documentation](https://docs.astro.build/en/guides/fonts/).
## See also ## See also
+3 -1
View File
@@ -106,7 +106,9 @@ const rssHref = getRelativeLocaleUrl(
"(prefers-color-scheme: dark)" "(prefers-color-scheme: dark)"
).matches; ).matches;
const theme = stored ?? (prefersDark ? "dark" : "light"); const theme = stored ?? (prefersDark ? "dark" : "light");
document.firstElementChild?.setAttribute("data-theme", theme); const root = document.firstElementChild;
root?.setAttribute("data-theme", theme);
root?.classList.toggle("dark", theme === "dark");
// Expose value so theme.ts can skip re-detection. // Expose value so theme.ts can skip re-detection.
window.__theme = { value: theme }; window.__theme = { value: theme };
})(); })();
+3 -1
View File
@@ -21,7 +21,9 @@ function persist(): void {
} }
function reflect(): void { function reflect(): void {
document.firstElementChild?.setAttribute("data-theme", themeValue); const root = document.firstElementChild;
root?.setAttribute("data-theme", themeValue);
root?.classList.toggle("dark", themeValue === DARK);
document.querySelector("#theme-btn")?.setAttribute("aria-label", themeValue); document.querySelector("#theme-btn")?.setAttribute("aria-label", themeValue);
// Fill <meta name="theme-color"> with the computed background colour so // Fill <meta name="theme-color"> with the computed background colour so
+1
View File
@@ -1,6 +1,7 @@
@import "tailwindcss"; @import "tailwindcss";
@import "./theme.css"; @import "./theme.css";
@import "./typography.css"; @import "./typography.css";
@import "rehype-callouts/theme/obsidian";
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *)); @custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
+1 -1
View File
@@ -82,7 +82,7 @@
@apply border-s-accent/80 wrap-break-word opacity-80; @apply border-s-accent/80 wrap-break-word opacity-80;
} }
details { details:not(.callout) {
@apply text-foreground inline-block cursor-pointer select-none [&_p]:hidden [&_ul]:my-0!; @apply text-foreground inline-block cursor-pointer select-none [&_p]:hidden [&_ul]:my-0!;
} }