From e0506cb41e438162a2ac81a2e66e929cb03f12a6 Mon Sep 17 00:00:00 2001 From: Sat Naing Date: Sat, 6 Jun 2026 09:09:08 +0700 Subject: [PATCH] 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 --- astro.config.ts | 2 + package.json | 1 + pnpm-lock.yaml | 15 ++++ src/content/posts/adding-new-post.mdx | 73 ++++++++++++++++++- src/content/posts/dynamic-og-images.md | 24 ++---- .../how-to-configure-astropaper-theme.mdx | 5 +- src/layouts/Layout.astro | 4 +- src/scripts/theme.ts | 4 +- src/styles/global.css | 1 + src/styles/typography.css | 2 +- 10 files changed, 106 insertions(+), 25 deletions(-) diff --git a/astro.config.ts b/astro.config.ts index acc8b72..db76e95 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -10,6 +10,7 @@ import sitemap from "@astrojs/sitemap"; import { unified } from "@astrojs/markdown-remark"; import remarkToc from "remark-toc"; import remarkCollapse from "remark-collapse"; +import rehypeCallouts from "rehype-callouts"; import { transformerNotationDiff, transformerNotationHighlight, @@ -40,6 +41,7 @@ export default defineConfig({ remarkToc, [remarkCollapse, { test: "Table of contents" }], ], + rehypePlugins: [rehypeCallouts], }), shikiConfig: { themes: { light: "min-light", dark: "night-owl" }, diff --git a/package.json b/package.json index 3607a79..646efc4 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "astro": "^6.4.2", "dayjs": "^1.11.21", "lodash.kebabcase": "^4.1.1", + "rehype-callouts": "^2.1.2", "remark-collapse": "^0.1.2", "remark-toc": "^9.0.0", "satori": "^0.26.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 64df576..3fe63d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: lodash.kebabcase: specifier: ^4.1.1 version: 4.1.1 + rehype-callouts: + specifier: ^2.1.2 + version: 2.1.2 remark-collapse: specifier: ^0.1.2 version: 0.1.2 @@ -2263,6 +2266,10 @@ packages: regex@6.1.0: 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: resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} @@ -5341,6 +5348,14 @@ snapshots: dependencies: 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: dependencies: '@types/hast': 3.0.4 diff --git a/src/content/posts/adding-new-post.mdx b/src/content/posts/adding-new-post.mdx index 7244ecb..ae30204 100644 --- a/src/content/posts/adding-new-post.mdx +++ b/src/content/posts/adding-new-post.mdx @@ -1,7 +1,7 @@ --- author: Sat Naing 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 slug: adding-new-posts-in-astropaper-theme 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 ``` +> [!TIP] > Files and directories prefixed with `_` are excluded from routing. Use them for drafts, shared assets, or internal-only content. ## Frontmatter @@ -71,7 +72,8 @@ Here is the list of frontmatter properties for each post: -> 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. @@ -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. +## 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 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. -> 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) @@ -203,6 +265,7 @@ Example: suppose you want to display `example.jpg` whose path is `src/assets/ima ``` +> [!TIP] > Technically, you can store images inside any directory under `src`. `src/assets` is just a recommendation. ### Inside `public/` directory @@ -225,7 +288,8 @@ Example: assume `example.jpg` is located at `public/assets/images/example.jpg`. ### 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: @@ -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. +> [!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/). diff --git a/src/content/posts/dynamic-og-images.md b/src/content/posts/dynamic-og-images.md index 612bcd1..0234971 100644 --- a/src/content/posts/dynamic-og-images.md +++ b/src/content/posts/dynamic-og-images.md @@ -1,7 +1,7 @@ --- author: Sat Naing 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 slug: dynamic-og-image-generation-in-astropaper-blog-posts featured: false @@ -49,9 +49,8 @@ Dynamic OG images include _the blog post title_, _author name_, and _site title_ ### 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. - -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). +> [!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. ```ts file="astro.config.ts" 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. -## Trade-off - -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. - -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). - -## 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. +> [!WARNING] Caveats +> +> - **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. +> - **Emoji** in titles can be tricky — some may not render correctly. diff --git a/src/content/posts/how-to-configure-astropaper-theme.mdx b/src/content/posts/how-to-configure-astropaper-theme.mdx index 6a53bb2..585b0a5 100644 --- a/src/content/posts/how-to-configure-astropaper-theme.mdx +++ b/src/content/posts/how-to-configure-astropaper-theme.mdx @@ -1,7 +1,7 @@ --- author: Sat Naing 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 slug: how-to-configure-astropaper-theme 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. -> **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 diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 6e1918b..f136d5e 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -106,7 +106,9 @@ const rssHref = getRelativeLocaleUrl( "(prefers-color-scheme: dark)" ).matches; 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. window.__theme = { value: theme }; })(); diff --git a/src/scripts/theme.ts b/src/scripts/theme.ts index 1b18b5e..9ee31c7 100644 --- a/src/scripts/theme.ts +++ b/src/scripts/theme.ts @@ -21,7 +21,9 @@ function persist(): 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); // Fill with the computed background colour so diff --git a/src/styles/global.css b/src/styles/global.css index 00735e6..262e0a1 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -1,6 +1,7 @@ @import "tailwindcss"; @import "./theme.css"; @import "./typography.css"; +@import "rehype-callouts/theme/obsidian"; @custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *)); diff --git a/src/styles/typography.css b/src/styles/typography.css index e55b5c2..5fb89db 100644 --- a/src/styles/typography.css +++ b/src/styles/typography.css @@ -82,7 +82,7 @@ @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!; }