feat!: AstroPaper v6 (#631)

* feat: add initial configuration and social icons for AstroPaper theme

* feat: add theme styles and design tokens

* chore: configure Astro with sitemap, i18n, fonts, and tooling

- Set site URL, i18n (single locale, no prefix), sitemap integration
- Add Google Sans Code font via stable fonts API
- Configure Shiki dual-theme markdown (min-light / night-owl)
- Fix astro-paper.config.ts to use relative import (alias not
  available when Vite loads root-level config files)
- Add @astrojs/sitemap and @typescript-eslint/parser dependencies
- Wire @typescript-eslint/parser into ESLint so TypeScript syntax
  in Astro frontmatter (type Props, etc.) parses correctly

* feat: add base styles, dark variant, and typography

- global.css: dark variant via [data-theme], base layer for * / a /
  button selectors, scrollbar tokens, layout utilities (max-w-app,
  app-layout), active-nav as @utility for Tailwind variant support
- html and body classes moved directly onto elements in BaseLayout
  (Tailwind-native approach) so only truly global selectors remain here
- typography.css: prose overrides, Shiki light/dark code block
  switching, diff/highlight/word-highlight transformer styles

* feat: add i18n types, English strings, and useTranslations helper

Plain typed object approach — no third-party i18n library needed.
Paraglide.js was considered but requires SSR output (hard blocker
for a static Astro site).

- UIStrings interface with nav, post, pagination, and a11y groups
- en.ts with `satisfies UIStrings` for literal-type preservation
- useTranslations(locale) helper falls back to English automatically

Adding a new language is a one-file addition; full routing stays
as a registry add-on.

* feat: add client-side theme toggle script

- Reads window.__theme.value set by the inline FOUC-prevention script
  to avoid re-detecting the theme on first load
- Toggles data-theme on <html> and persists to localStorage
- Fills <meta name="theme-color"> with the computed background colour
  so Android's browser chrome matches the page
- Re-runs setup on astro:after-swap for View Transitions navigation
- Carries theme-color across astro:before-swap to prevent navbar
  flicker on Android during page transitions
- Syncs with OS prefers-color-scheme changes at runtime

* feat: add BaseLayout and PostLayout

BaseLayout (replaces scaffold Layout.astro):
- Font, canonical URL via Astro.site, RSS autodiscovery
- FOUC-prevention inline script sets data-theme before first paint
  and exposes window.__theme.value for theme.ts to reuse
- html/body classes on elements directly (Tailwind-native)
- <slot name="head"> for child layout injection
- ClientRouter for View Transitions (respects lightAndDarkMode flag)
- googleVerification rendered from config instead of .env — it is a
  public string, not a secret, and not environment-specific

PostLayout (new):
- Wraps BaseLayout; injects article-specific head content via slot
- Overrides og:type to "article"
- Adds article:published_time / article:modified_time conditionally
- JSON-LD BlogPosting with conditional datePublished/dateModified
  (fixes old bug where absent dates produced "undefined" string)
- Structured data only on actual post pages (not home/archives/tags)

SiteConfig: add optional googleVerification field

* chore: replace scaffold index page with BaseLayout placeholder

* feat: add Header component

* feat: add Footer and Socials component

* chore: install dayjs, slugify, zod, @astrojs/mdx and add MDX integration

* feat: add resolved config that applies defaults in one place

Defaults for posts, features, socials, and shareLinks are merged in
src/config.ts so all consuming code gets non-optional types with no
scattered ?? fallbacks.

* feat: add content schema, blog path, and post utilities

Renames collection blog→posts, moves content to src/content/posts/,
adds .mdx support, and adds a typed pages collection. Includes
getSortedPosts, postFilter, getPath, and slugify utilities.

* feat: add Datetime and Card components

* refactor: switch layouts and components to resolved config

Replaces @/astro-paper.config imports with @/config and removes
optional chaining on feature flags now that the resolved config
guarantees non-optional values.

* feat: add home and footer i18n strings

* feat: implement home page with hero, featured, and recent posts

Replaces the BaseLayout placeholder with the full index page:
hero section with RSS link and social links, featured posts section,
recent posts capped at posts.perIndex, and an All Posts link. All
section labels come from i18n.

* feat: add pagination component

* feat: add Breadcrumb component with locale support

* feat: add Main component

* refactor: update code formatting in Card.astro

* feat: add posts page

* fix: add missing @astrojs/mdx dependency

* refactor: rename meta to pages in i18n

* refactor: improve code formatting in Breadcrumb

* refactor: destructure props in Main component for clarity

* feat: add tag pages

* feat: add About page

* chore: update i18n configuration to use site language from config

* feat: add Archives page

* feat: integrate Pagefind search functionality

* feat: add dynamic Open Graph image generation for posts

* feat: add post detail page

* feat: add Shiki transformers

* chore: remove unused edit link text

* chore: update .gitignore to include generated Pagefind directory

* chore: update site URL in configuration to production link

* chore: replace favicon and remove unused SVG assets, add new Open Graph image

* feat: update site config with default values

* feat: add default OG image path resolution

* fix: ensure edit post feature checks for enabled status correctly

* feat: implement dynamic Open Graph image generation

* feat: add RSS feed generation functionality

* feat: add 404 Not Found page

* feat: add robots.txt generation for SEO optimization

* refactor: change getSortedPosts to a named export

* refactor: change postFilter to a named export

* refactor: rename BaseLayout to Layout

* refactor: standardize locale handling across components

* refactor: update getPostsByGroupCondition location

* chore: remove unused Welcome component

* refactor(i18n): update links using getRelativeLocaleUrl

* refactor(i18n): add dynamic locale loading

* fix: update language attribute to use current locale

* fix: correct wording for updatedAt in post translations

* fix: update layout titles to use correct translation keys for tags pages

* chore: use new colors for better DX and a11y

* feat: add underline icon for active icon nav

* feat: add toc for markdown contents

* refactor: remove unused feature flags in config

* refactor(i18n): enhance locale handling and path management

* fix: ts unused variable hint

* refactor: extract locale stripping logic into a util func

* refactor(i18n): improve locale handling in tag pages

* refactor: update post URL helper functions

* refactor(i18n): replace pathWithBase with getRelativeLocaleUrl for locale handling

* refactor: rename fileWithBase to getAssetPath

* refactor: use runtime agnostic APIs for file paths

* refactor: use transition directive for heading transition

* refactor: remove unused title element from tag page

* fix: decouple ClientRouter from lightAndDarkMode flag

* refactor: rename `postPaths.ts` to `getPostPaths.ts`

* refactor: remove unused `minutesRead` string from post translations

* refactor: simplify AdjacentPostNav component by passing prevPost and nextPost directly

* refactor: remove unused code declaration

* docs: add existing blog posts

* chore: update astro and @astrojs/check dependencies to latest versions

* build: use experimental svg optimizer

* chore(repo): restore project meta files and Docker configs

* refactor: update code formatting with Prettier

* docs: update README and docs

* docs: add JSDoc comments to utility functions

* docs: add JSDoc comments to resolved config file

* fix(og): avoid double slashes when appending index.png

* chore(ci): update Node.js version to 22.12.0 in CI workflow

* feat: improve og image generation with Astro's getFontFileURL API (#632)

* feat: improve og image generation with Astro's getFontFileURL API

- remove @resvg/resvg-js and use sharp to convert SVG to PNG
- update og image generation to use Astro's getFontFileURL API for better font handling

* docs: update README and dynamic OG images documentation

- remove outdated content and clarify dynamic OG image generation process in README.
- add new image example and update instructions for handling non-Latin characters in dynamic OG images.
- adjust copyright year in README to 2026.

* fix(header): adjust icon button alignment in mobile

* feat(docs): add ResponsiveTable to use it in MDX posts (#634)

* feat(docs): add ResponsiveTable to use it in MDX posts

Introduce ResponsiveTable with variant-based styling. Rename docs posts to
.mdx, wrap tables in the component, and remove redundant table `code` rules
from prose typography.

* chore(prettier): ignore .mdx files when checking format

* chore(ci): update Node.js version to 24 in CI workflow

* bump: upgrade Astro and dependencies

* fix: remove redundant backUrl script bug

* fix(header): adjust button and icon alignment

* fix(a11y): improve muted-foreground color contrast

* docs: update predefined color schemes

* chore: support site verification env as fallback for googleVerification

* docs: add astro paper v6 updates in docs

* docs: add AstroPaper-v6.png in astro-paper-6 blog post

* fix: update back-to-top button DOM placement (#641)

Closes #626, #131, #493
This commit is contained in:
Sat Naing
2026-05-17 16:17:58 +07:00
committed by GitHub
parent f3005328e5
commit f0b644de3a
122 changed files with 5387 additions and 4561 deletions
+37
View File
@@ -0,0 +1,37 @@
---
title: "About"
description: "A bit about me and this blog."
---
AstroPaper is a minimal, accessible and SEO-friendly blog theme built with [Astro](https://astro.build/) and [Tailwind CSS](https://tailwindcss.com/).
![Astro Paper](@/assets/images/astropaper-og.jpg)
AstroPaper provides a solid foundation for blogs, or even portfolios\_ with full markdown support, built-in dark mode, and a clean layout that works out-of-the-box.
The blog posts in this theme also serve as guides, docs or example articles\_ making AstroPaper a flexible starting point for your next content-driven site.
## Features
AstroPaper comes with a set of useful features that make content publishing easy and effective:
- SEO-friendly
- Fast performance
- Light & dark mode
- Highly customizable
- Organizable blog posts
- Responsive & accessible
- Static search with [PageFind](https://pagefind.app/)
- Automatic social image generation
and so much more.
## Show your support
If you like [AstroPaper](https://github.com/satnaing/astro-paper), consider giving it a star ⭐️.
Found a bug 🐛 or have an improvement ✨ in mind? Feel free to open an [issue](https://github.com/satnaing/astro-paper/issues), submit a [pull request](https://github.com/satnaing/astro-paper/pulls) or start a [discussion](https://github.com/satnaing/astro-paper/discussions).
If you find this theme helpful, you can also [sponsor me on GitHub](https://github.com/sponsors/satnaing) or [buy me a coffee](https://buymeacoffee.com/satnaing) to show your support — every penny counts.
Kyay zuu! 🙏🏼
Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 KiB

@@ -0,0 +1,235 @@
---
author: Sat Naing
pubDatetime: 2022-09-26T12:13:24Z
modDatetime: 2026-05-16T11:29:26.658Z
title: Predefined color schemes
slug: predefined-color-schemes
featured: false
draft: false
tags:
- color-schemes
description:
Some of the well-crafted, updated predefined color schemes for AstroPaper.
---
import ResponsiveTable from '@/components/ResponsiveTable.astro';
AstroPaper includes a collection of predefined color schemes that can be applied to customize the theme appearance. Each scheme defines a complete set of CSS custom properties (variables) for light and dark modes.
## Table of contents
## Quick Start
To apply a predefined color scheme, copy the CSS variable definitions into your theme configuration. For detailed setup instructions, see the [color scheme configuration guide](https://astro-paper.pages.dev/posts/customizing-astropaper-theme-color-schemes/).
## CSS Variables Reference
All color schemes use the following CSS custom properties:
<ResponsiveTable variant="striped-minimal">
| Variable | Purpose |
| --------------------- | -------------------------------------------------------- |
| `--background` | Primary background color |
| `--foreground` | Primary text color |
| `--accent` | Accent/interactive elements (links, buttons, highlights) |
| `--accent-foreground` | Text color on accent backgrounds |
| `--muted` | Secondary background color for subtle sections |
| `--muted-foreground` | Text color for secondary content |
| `--border` | Border and divider color |
</ResponsiveTable>
## Light Schemes
Light color schemes are defined using the CSS selectors `:root` and `[data-theme="light"]`.
### Paper Light
Default AstroPaper light theme.
![Paper Light](assets/paper-light.png)
```css
:root,
[data-theme="light"] {
--background: #fdfdfd;
--foreground: #282728;
--accent: #006cac;
--accent-foreground: #ffffff;
--muted: #e6e6e6;
--muted-foreground: #6b7280;
--border: #ece9e9;
}
```
### Kha-Yan
Purple-focused light scheme with warm background.
![Kha-Yan](assets/kha-yan.png)
```css
:root,
[data-theme="light"] {
--background: #fefaec;
--foreground: #120e01;
--accent: #6e10cf;
--accent-foreground: #fefaec;
--muted: #dcdcdc;
--muted-foreground: #6b7280;
--border: #cdc4d6;
}
```
### Nila
Light purple scheme with cool blue undertones.
![Nila](assets/nila.png)
```css
:root,
[data-theme="light"] {
--background: #f6f6fb;
--foreground: #0c0c19;
--accent: #6760b4;
--accent-foreground: #f3f3f3;
--muted: #dddcea;
--muted-foreground: #54515b;
--border: #d8d6ec;
}
```
### Jadeite
Teal-accented light scheme with neutral background.
![Jadeite](assets/jadeite.png)
```css
:root,
[data-theme="light"] {
--background: #f6fcf7;
--foreground: #060b07;
--accent: #027c6d;
--accent-foreground: #ffffff;
--muted: #c9e4e2;
--muted-foreground: #6b7280;
--border: #d4e1df;
}
```
### Pyit Tine Htaung
Red and gold accent scheme with warm tones.
![Pyit Tine Htaung](assets/pyit-tine-htaung.png)
```css
:root,
[data-theme="light"] {
--background: #fffaf6;
--foreground: #060503;
--accent: #aa0215;
--accent-foreground: #ffcf75;
--muted: #ffdc98;
--muted-foreground: #54515b;
--border: #ffdc98;
}
```
## Dark Schemes
Dark color schemes are defined using the CSS selector `[data-theme="dark"]`.
### Paper Dark
Original AstroPaper dark theme with cyan accents.
![AstroPaper 1 default dark theme](https://user-images.githubusercontent.com/53733092/215769153-13b0ad8d-5ba2-44b1-af06-e5ae61293f62.png)
```css
[data-theme="dark"] {
--background: #2f3741;
--foreground: #e6e6e6;
--accent: #1ad9d9;
--accent-foreground: #0d2b2b;
--muted: #596b81;
--muted-foreground: #8faabb;
--border: #3b4655;
}
```
### Paper Dark II
Current default dark theme with orange accents.
![new dark color scheme - low contrast](https://user-images.githubusercontent.com/53733092/215772856-d5b7ae35-ddaa-4ed6-b0bf-3fa5dbcf834c.png)
```css
[data-theme="dark"] {
--background: #212737;
--foreground: #eaedf3;
--accent: #ff6b01;
--accent-foreground: #ffffff;
--muted: #343f60;
--muted-foreground: #afb9ca;
--border: #ab4b08;
}
```
### Deep Purple
Vibrant magenta accents dark scheme.
![AstroPaper v3 new theme](https://github.com/satnaing/astro-paper/assets/53733092/c8b5d7e1-a3bc-4852-a5ad-4abf7b3cec79)
```css
[data-theme="dark"] {
--background: #212737;
--foreground: #eaedf3;
--accent: #eb3fd3;
--accent-foreground: #1a0d1a;
--muted: #513f51;
--muted-foreground: #c09abc;
--border: #642451;
}
```
### Ember
Warm, muted dark scheme with red accents.
![Ember](assets/ember.png)
```css
[data-theme="dark"] {
--background: #1a1a1a;
--foreground: #f5efe4;
--accent: #ff3737;
--accent-foreground: #1a1a1a;
--muted: #38342f;
--muted-foreground: #a59a8c;
--border: #6f5648;
}
```
### Espresso
Brown-focused warm dark scheme.
![Coffee](assets/espresso.png)
```css
[data-theme="dark"] {
--background: #2f2f2f;
--foreground: #ebe5e1;
--accent: #ee781e;
--accent-foreground: #1a1a1a;
--muted: #4f4b44;
--muted-foreground: #ddbfa7;
--border: #6f5648;
}
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 821 KiB

@@ -0,0 +1,91 @@
---
author: Sat Naing
pubDatetime: 2023-01-30T15:57:52.737Z
title: AstroPaper 2.0
slug: astro-paper-2
featured: false
ogImage: https://user-images.githubusercontent.com/53733092/215771435-25408246-2309-4f8b-a781-1f3d93bdf0ec.png
tags:
- release
description: AstroPaper with the enhancements of Astro v2. Type-safe markdown contents, bug fixes and better dev experience etc.
---
Astro 2.0 has been released with some cool features, breaking changes, DX improvements, better error overlay and so on. AstroPaper takes advantage of those cool features, especially Content Collections API.
<!-- ![Introducing AstroPaper 2.0](https://user-images.githubusercontent.com/53733092/215683840-dc2502f5-8c5a-44f0-a26c-4e7180455056.png) -->
![Introducing AstroPaper 2.0](https://user-images.githubusercontent.com/53733092/215771435-25408246-2309-4f8b-a781-1f3d93bdf0ec.png)
## Table of contents
## Features & Changes
### Type-safe Frontmatters and Redefined Blog Schema
Frontmatter of AstroPaper 2.0 markdown contents are now type-safe thanks to Astros Content Collections. Blog schema is defined inside the `src/content/_schemas.ts` file.
### New Home for Blog contents
All the blog posts were moved from `src/contents` to `src/content/blog` directory.
### New Fetch API
Contents are now fetched with `getCollection` function. No relative path to the content needs to be specified anymore.
```ts
// old content fetching method
- const postImportResult = import.meta.glob<MarkdownInstance<Frontmatter>>(
"../contents/**/**/*.md",);
// new content fetching method
+ const postImportResult = await getCollection("blog");
```
### Modified Search Logic for better Search Result
In the older version of AstroPaper, when someone search some article, the search criteria keys that will be searched are `title`, `description` and `headings` (heading means all the headings h1 ~ h6 of the blog post). In AstroPaper v2, only `title` and `description` will be searched as the user types.
### Renamed Frontmatter Properties
The following frontmatter properties are renamed.
| Old Names | New Names |
| --------- | ----------- |
| datetime | pubDatetime |
| slug | postSlug |
### Default Tag for blog post
If a blog post doesn't have any tag (in other words, frontmatter property `tags` is not specified), the default tag `others` will be used for that blog post. But you can set the default tag in the `/src/content/_schemas.ts` file.
```ts
// src/contents/_schemas.ts
export const blogSchema = z.object({
// ---
// replace "others" with whatever you want
tags: z.array(z.string()).default(["others"]),
ogImage: z.string().optional(),
description: z.string(),
});
```
### New Predefined Dark Color Scheme
AstroPaper v2 has a new dark color scheme (high contrast & low contrast) which is based on Astro's dark logo. Check out [this link](https://astro-paper.pages.dev/posts/predefined-color-schemes#astro-dark) for more info.
![New Predefined Dark Color Scheme](https://user-images.githubusercontent.com/53733092/215680520-59427bb0-f4cb-48c0-bccc-f182a428d72d.svg)
### Automatic Class Sorting
AstroPaper 2.0 includes automatic class sorting with [TailwindCSS Prettier plugin](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier)
### Updated Docs & README
All the [#docs](https://astro-paper.pages.dev/tags/docs/) blog posts and [README](https://github.com/satnaing/astro-paper#readme) are updated for this AstroPaper v2.
## Bug Fixes
- fix broken tags in the Blog Post page
- in a tag page, the last part of the breadcrumb is now updated to lower-case for consistency
- exclude draft posts in a tag page
- fix 'onChange value not updating issue' after a page reload
@@ -0,0 +1,173 @@
---
author: Sat Naing
pubDatetime: 2023-09-25T10:25:54.547Z
title: AstroPaper 3.0
slug: astro-paper-v3
featured: false
ogImage: https://github.com/satnaing/astro-paper/assets/53733092/1ef0cf03-8137-4d67-ac81-84a032119e3a
tags:
- release
description: "AstroPaper Version 3: Elevating Your Web Experience with Astro v3 and Seamless View Transitions"
---
We're excited to announce the release of AstroPaper v3, packed with new features, enhancements, and bug fixes to elevate your web development experience. Let's dive into the highlights of this release:
![AstroPaper v3](@/assets/images/AstroPaper-v3.png)
## Table of contents
## Features & Changes
### Astro v3 Integration
<video autoplay loop="loop" muted="muted" plays-inline="true">
<source src="https://github.com/satnaing/astro-paper/assets/53733092/18fdb604-1ca3-41a0-8372-1367759091ff" type="video/mp4">
<!-- <source src="/assets/docs/astro-paper-v3-view-transitions-demo.mp4" type="video/mp4"> -->
</video>
AstroPaper now fully supports [Astro v3](https://astro.build/blog/astro-3/), offering improved performance and rendering speed.
Besides, we've added support for Astro's [ViewTransitions API](https://docs.astro.build/en/guides/view-transitions/), allowing you to create captivating and dynamic transitions between views.
In the "Recent Section", only non-featured posts will be displayed to avoid duplications and better support for ViewTransitions API.
### Update OG Image Generation Logic
![Example OG Image](https://user-images.githubusercontent.com/40914272/269252964-a0dc6735-80f7-41ed-8e74-4d4d70f96891.png)
We've updated the logic for automatic OG image generation, making it even more reliable and efficient. Besides, it now supports special characters in post titles, ensuring accurate, flexible and eye-catching social media previews.
`SITE.ogImage` is now optional. If it is not specified, AstroPaper will automatically generate an OG image using `SITE.title`, `SITE.desc` and `SITE.website`
### Theme meta tag
The theme-color meta tag has been added to dynamically adapt to theme switches, ensuring a seamless user experience.
> Notice the difference at the top
**_AstroPaper v2 theme switch_**
<video autoplay loop="loop" muted="muted" plays-inline="true">
<source src="https://github.com/satnaing/astro-paper/assets/53733092/3ab5a1e8-1891-4264-a5bb-0ded69143c1a" type="video/mp4">
</video>
**_AstroPaper v3 theme switch_**
<video autoplay loop="loop" muted="muted" plays-inline="true">
<source src="https://github.com/satnaing/astro-paper/assets/53733092/8ac9deb8-d1f8-4029-86bd-6aa0def380b4" type="video/mp4">
</video>
## Other Changes
### Astro Prettier Plugin
Astro Prettier Plugin is installed out-of-the-box in order to keep the project tidy and organized.
### Minor Style Changes
The single-line code block wrapping issue has been solved, making your code snippets look pristine.
Update nav style CSS to allow adding more nav links to the navigation.
## Upgrade to AstroPaper v3
> This section is only for those who want to upgrade AstroPaper v3 from the older versions.
This section will help you migrate from AstroPaper v2 to AstroPaper v3.
Before reading the rest of the section, you might also want to check [this article](https://astro-paper.pages.dev/posts/how-to-update-dependencies/) for upgrading dependencies and AstroPaper.
## Option 1: Fresh Restart (recommended)
In this release, a lot of changes have been made\_ replacing old Astro APIs with newer APIs, bug fixes, new features etc. Thus, if you are someone who didn't make customization very much, you should follow this approach.
**_Step 1: Keep all your updated files_**
It's important to keep all the files which have been already updated. These files include
- `/src/config.ts` (didn't touch in v3)
- `/src/styles/base.css` (minor changes in v3; mentioned below)
- `/src/assets/` (didn't touch in v3)
- `/public/assets/` (didn't touch in v3)
- `/content/blog/` (it's your blog content directory 🤷🏻‍♂️)
- Any other customizations you've made.
```css
/* file: /src/styles/base.css */
@layer base {
/* Other Codes */
::-webkit-scrollbar-thumb:hover {
@apply bg-skin-card-muted;
}
/* Old code
code {
white-space: pre;
overflow: scroll;
}
*/
/* New code */
code,
blockquote {
word-wrap: break-word;
}
pre > code {
white-space: pre;
}
}
@layer components {
/* other codes */
}
```
**_Step 1: Replace everything else with AstroPaper v3_**
In this step, replace everything\_ except above files/directories (plus your customized files/directories)\_ with AstroPaper v3.
**_Step 3: Schema Updates_**
Keep in mind that `/src/content/_schemas.ts` has been replaced with `/src/content/config.ts`.
Besides, there is no longer `BlogFrontmatter` type exported from `/src/content/config.ts`.
Therefore, all the `BlogFrontmatter` type inside files need to be updated with `CollectionEntry<"blog">["data"]`.
For example: `src/components/Card.tsx`
```ts
// AstroPaper v2
import type { BlogFrontmatter } from "@content/_schemas";
export interface Props {
href?: string;
frontmatter: BlogFrontmatter;
secHeading?: boolean;
}
```
```ts
// AstroPaper v3
import type { CollectionEntry } from "astro:content";
export interface Props {
href?: string;
frontmatter: CollectionEntry<"blog">["data"];
secHeading?: boolean;
}
```
## Option 2: Upgrade using Git
This approach is not recommended for most users. You should do the "Option 1" if you can. Only do this if you know how to resolve merge conflicts and you know what you're doing.
Actually, I've already written a blog post for this case and you can check out [here](https://astro-paper.pages.dev/posts/how-to-update-dependencies/#updating-astropaper-using-git).
## Outro
Ready to explore the exciting new features and improvements in AstroPaper v3? Start [using AstroPaper](https://github.com/satnaing/astro-paper) now.
For other bug fixes and integration updates, check out the [release notes](https://github.com/satnaing/astro-paper/releases/tag/v3.0.0) to learn more.
If you encounter any bugs or face difficulties during the upgrade process, please feel free to open an issue or start a discussion on [GitHub](https://github.com/satnaing/astro-paper).
@@ -0,0 +1,124 @@
---
author: Sat Naing
pubDatetime: 2024-01-04T09:30:41.816Z
title: AstroPaper 4.0
slug: "astro-paper-v4"
featured: false
ogImage: ../../../assets/images/AstroPaper-v4.png
tags:
- release
description: "AstroPaper v4: ensuring a smoother and more feature-rich blogging experience."
---
Hello everyone! Wishing you a happy New Year 🎉 and all the best for 2024! We're excited to announce the release of AstroPaper v4, a significant update that introduces a range of new features, improvements, and bug fixes to elevate your blogging experience. A big thank you to all the contributors for their valuable input and efforts in making version 4 possible!
![AstroPaper v4](@/assets/images/AstroPaper-v4.png)
## Table of contents
## Major Changes
### Upgrade to Astro v4 [#202](https://github.com/satnaing/astro-paper/pull/202)
AstroPaper now leverages the power and capabilities of Astro v4. However, its a subtle upgrade and wont break most Astro users.
![Astro v4](https://astro.build/_astro/header-astro-4.YunweN9V_OmV0l.webp)
### Replace `postSlug` with Astro Content `slug` [#197](https://github.com/satnaing/astro-paper/pull/197)
The `postSlug` in the blog content schema is no longer available in AstroPaper v4. Initially Astro doesn't have a `slug` mechanism and thus we have to figure it out on our own. Since Astro v3, it supports content collection and slug features. Now, we believe it's time to adopt Astro's out-of-the-box `slug` feature.
**_file: src/content/blog/astro-paper-4.md_**
```bash
---
author: Sat Naing
pubDatetime: 2024-01-01T04:35:33.428Z
title: AstroPaper 4.0
slug: "astro-paper-v4" # if slug is not specified, it will be 'astro-paper-4' (file name).
# slug: "" ❌ cannot be an empty string
---
```
The behavior of the `slug` is slightly different now. In the previous versions of AstroPaper, if the `postSlug` is not specified in a blog post (markdown file), the title of that blog post would be slugified and used as the `slug`. However, in AstroPaper v4, if the `slug` field is not specified, the markdown file name will be used as the `slug`. One thing to keep in mind is that the `slug` field can be omitted, but it cannot be an empty string (slug: "" ❌).
If you're upgrading AstroPaper from v3 to v4, make sure to replace `postSlug` in your `src/content/blog/*.md` files with `slug`.
## New Features
### Add code-snippets for content creation [#206](https://github.com/satnaing/astro-paper/pull/206)
AstroPaper now includes VSCode snippets for new blog posts, eliminating the need for manual copy/pasting of the frontmatter and content structure (table of contents, heading, excerpt, etc.).
Read more about VSCode Snippets [here](https://code.visualstudio.com/docs/editor/userdefinedsnippets#:~:text=In%20Visual%20Studio%20Code%2C%20snippets,Snippet%20in%20the%20Command%20Palette).
<video autoplay muted="muted" controls plays-inline="true" class="border border-skin-line">
<source src="https://github.com/satnaing/astro-paper/assets/53733092/136f1903-bade-40a2-b6bb-285a3c726350" type="video/mp4">
</video>
### Add Modified Datetime in Blog Posts [#195](https://github.com/satnaing/astro-paper/pull/195)
Keep readers informed about the latest updates by displaying the modified datetime in blog posts. This not only instills user trust in the freshness of the articles but also contributes to improved SEO for the blog.
![Last Modified Date feature in AstroPaper](https://github.com/satnaing/astro-paper/assets/53733092/cc89585e-148e-444d-9da1-0d496e867175)
You can add a `modDatetime` to your blog post if you've made modifications. Now, the sorting behavior of the posts is slightly different. All posts are sorted by both `pubDatetime` and `modDatetime`. If a post has both a `pubDatetime` and `modDatetime`, its sorting position will be determined by the `modDatetime`. If not, only `pubDatetime` will be considered to determine the post's sorting order.
### Implement Back-to-Top Button [#188](https://github.com/satnaing/astro-paper/pull/188)
Enhance user navigation on your blog detail post with the newly implemented back-to-top button.
![Back to top button in AstroPaper](https://github.com/satnaing/astro-paper/assets/53733092/79854957-7877-4f19-936e-ad994b772074)
### Add Pagination in Tag Posts [#201](https://github.com/satnaing/astro-paper/pull/201)
Improve content organization and navigation with the addition of pagination in tag posts, making it easier for users to explore related content. This ensures that if a tag has many posts, readers won't be overwhelmed by all the tag-related posts.
<video autoplay loop="loop" muted="muted" plays-inline="true" class="border border-skin-line">
<source src="https://github.com/satnaing/astro-paper/assets/53733092/9bad87f5-dcf5-4b79-b67a-d6c7244cd616" type="video/mp4">
</video>
### Dynamically Generate robots.txt [#130](https://github.com/satnaing/astro-paper/pull/130)
AstroPaper v4 now dynamically generates the robots.txt file, giving you more control over search engine indexing and web crawling. Besides, sitemap URL will also be added inside `robot.txt` file.
### Add Docker-Compose File [#174](https://github.com/satnaing/astro-paper/pull/174)
Managing your AstroPaper environment is now easier than ever with the addition of a Docker-Compose file, simplifying deployment and configuration.
## Refactoring & Bug Fixes
### Replace Slugified Title with Unslugified Tag Name [#198](https://github.com/satnaing/astro-paper/pull/198)
To improve clarity, user experience and SEO, titles (`Tag: some-tag`) in tag page are no longer slugified (`Tag: Some Tag`).
![Unslugified Tag Names](https://github.com/satnaing/astro-paper/assets/53733092/2fe90d6e-ec52-467b-9c44-95009b3ae0b7)
### Implement 100svh for Min-Height ([79d569d](https://github.com/satnaing/astro-paper/commit/79d569d053036f2113519f41b0d257523d035b76))
We've updated the min-height on the body to use 100svh, offering a better UX for mobile users.
### Update Site URL as Single Source of Truth [#143](https://github.com/satnaing/astro-paper/pull/143)
The site URL is now a single source of truth, streamlining configuration and avoiding inconsistencies. Read more at this [PR](https://github.com/satnaing/astro-paper/pull/143) and its related issue(s).
### Solve Invisible Text Code Block Issue in Light Mode [#163](https://github.com/satnaing/astro-paper/pull/163)
We've fixed the invisible text code block issue in light mode.
### Decode Unicode Tag Characters in Breadcrumb [#175](https://github.com/satnaing/astro-paper/pull/175)
The last part of Tag in the breadcrumb is now decoded, making non-English Unicode characters display better.
### Update LOCALE Config to Cover Overall Locales ([cd02b04](https://github.com/satnaing/astro-paper/commit/cd02b047d2b5e3b4a2940c0ff30568cdebcec0b8))
The LOCALE configuration has been updated to cover a broader range of locales, catering to a more diverse audience.
## Outtro
We believe these updates will significantly elevate your AstroPaper experience. Thank you to everyone who contributed, solved issues, and gave stars to AstroPaper. We look forward to seeing the amazing content you create with AstroPaper v4!
Happy Blogging!
[Sat Naing](https://satnaing.dev) <br/>
Creator of AstroPaper
@@ -0,0 +1,101 @@
---
pubDatetime: 2025-03-08T08:18:19.693Z
title: AstroPaper 5.0
slug: astro-paper-v5
featured: false
ogImage: ../../../assets/images/AstroPaper-v5.png
tags:
- release
description: "AstroPaper v5: keep the clean look, updates under the hood."
---
At last, the long-awaited AstroPaper v5 is finally here. AstroPaper v5 keeps the same minimal & clean look, but comes with significant updates under the hood.
![AstroPaper v5](@/assets/images/AstroPaper-v5.png)
## Table of contents
## Major Changes
### Upgrade to Astro v5 [#455](https://github.com/satnaing/astro-paper/pull/455)
AstroPaper now comes with Astro v5, bringing all the new features and improvements that come with it.
### Tailwind v4
AstroPaper has been upgraded to Tailwind v4, which includes many style changes under the hood. The `tailwind.config.js` file has been removed, and now all the configuration is located within the `src/styles/global.css` file. Typography-related styles have been extracted and moved to `src/styles/typography.css`.
Due to the new behavior in TailwindCSS v4, styles inside `<style>` blocks within components have been removed and replaced with inline Tailwind classes.
Additionally, the color palette across the UI has been updated. The new palette now consists of only five colors:
```css
:root,
html[data-theme="light"] {
--background: #fdfdfd;
--foreground: #282728;
--accent: #006cac;
--muted: #e6e6e6;
--border: #ece9e9;
}
html[data-theme="dark"] {
--background: #212737;
--foreground: #eaedf3;
--accent: #ff6b01;
--muted: #343f60bf;
--border: #ab4b08;
}
```
### Remove React + Fuse.js in favor of Pagefind search
In previous versions, React.js and Fuse.js were used for search functionality and OG image generation. In AstroPaper v5, React.js has been removed and replaced with [Pagefind](https://pagefind.app/), a static site search tool.
The search experience is almost identical to previous versions, but now all contents, not just titles and descriptions, are indexed and searchable, thanks to Pagefind.
The idea of using Pagefind in dev mode was inspired by [this blog post](https://chrispennington.blog/blog/pagefind-static-search-for-astro-sites/).
### Updated import alias
The import alias has been updated from `@directory` to `@/directory`, which means you now have to import like this:
```astro
---
import { slugifyStr } from "@/utils/slugify";
import IconHash from "@/assets/icons/IconHash.svg";
---
```
### Move to `pnpm`
AstroPaper has switched from `npm` to `pnpm`, which offers faster and more efficient package management.
### Replace icons/svg with Astro's Svg Component
AstroPaper v5 replaces inline SVGs with Astros experimental [SVG Component](https://docs.astro.build/en/reference/experimental-flags/svg/). This update reduces the need for predefined SVG code in the `socialIcons` object, making the codebase cleaner and more maintainable.
### Separate Constants and Config
The project structure has been reorganized. The `src/config.ts` file now only contains the `SITE` object, which holds the main configuration for the project. All constants, such as `LOCALE`, `SOCIALS`, and `SHARE_LINKS`, have been moved to the `src/constants.ts` file.
## Other notable changes
- The blog posts directory has been updated from `src/content/blog/` to `src/data/blog/`.
- Collection definitions file (`src/content/config.ts`) is now replaced with `src/content.config.ts`.
- Various dependencies have been upgraded for improved performance and security.
- Removed `IBM Plex Mono` font and switched to the default system mono font.
- The `Go back` button logic has been updated. Now, instead of triggering the browser's history API, AstroPaper v5 uses the browser session to temporarily store the back URL. If no back URL exists in the session, it will redirect to the homepage.
- There are some minor styles and layout changes as well.
## Outtro
AstroPaper v5 brings many changes, but the core experience remains the same. Enjoy a smoother, more efficient blogging platform while keeping the clean and minimal design that AstroPaper is known for!
Feel free to explore the changes and share your thoughts. As always, thank you for your support!
If you enjoy this theme, please consider starring the repo. You can also support me via GitHub Sponsors or you can buy me a coffee if you'd like. However, of course, these actions are entirely optional and not required.
Enjoy!
[Sat Naing](https://satnaing.dev/)
@@ -0,0 +1,199 @@
---
pubDatetime: 2026-05-17T07:15:45.792Z
title: AstroPaper 6.0
slug: astro-paper-v6
featured: true
ogImage: assets/AstroPaper-v6.png
tags:
- release
description: "AstroPaper v6: a from-scratch rewrite on Astro v6, Tailwind v4, and a new config system."
---
AstroPaper v6 is a complete rewrite built on Astro v6, Tailwind CSS v4, and TypeScript v6. This release replaces the legacy `SITE` / `constants.ts` configuration with a single unified config file and introduces several structural improvements across the codebase.
![AstroPaper v6](assets/AstroPaper-v6.png)
## Table of contents
## Major Changes
### Upgrade to Astro v6
AstroPaper now ships with Astro v6.3, which includes:
- **Stable Content Layer API** — `glob()` loader replaces the old `type: "content"` collection pattern.
- **Stable Fonts API** — `experimental.fonts` has graduated to a top-level `fonts` key in `astro.config.ts`.
- **TypeScript v6** — full support for the latest TypeScript compiler.
### New unified config system
The flat `SITE` object in `src/config.ts` and the separate `constants.ts` file have been replaced by a single `astro-paper.config.ts` at the project root. Use `defineAstroPaperConfig()` for full IntelliSense:
```ts file="astro-paper.config.ts"
import { defineAstroPaperConfig } from "./src/types/config";
export default defineAstroPaperConfig({
site: {
url: "https://your-site.com/",
title: "AstroPaper",
description: "…",
author: "Your Name",
lang: "en",
timezone: "UTC",
googleVerification: "your-verification-value",
},
posts: {
perPage: 4,
perIndex: 4,
scheduledPostMargin: 15 * 60 * 1000, // ms
},
features: {
lightAndDarkMode: true,
dynamicOgImage: true,
showArchives: true,
showBackButton: true,
editPost: { enabled: true, url: "https://github.com/…/edit/main/" },
search: "pagefind",
},
socials: [{ name: "github", url: "https://github.com/…" }],
shareLinks: [{ name: "x", url: "https://x.com/intent/post?url=" }],
});
```
All options — site metadata, pagination, feature flags, social links, and share links — now live in one file.
### Stable Fonts API
Font configuration has graduated from `experimental.fonts` to a top-level `fonts` key in `astro.config.ts`, matching Astro v6's stable API:
```ts file="astro.config.ts"
export default defineConfig({
fonts: [
{
name: "Google Sans Code",
cssVariable: "--font-google-sans-code",
provider: fontProviders.google(),
weights: [300, 400, 500, 600, 700],
styles: ["normal", "italic"],
},
],
});
```
### MDX support
`@astrojs/mdx` is now included. Posts can use the `.mdx` extension to embed components, use JSX expressions, and import from other files. The content loader pattern `**/[^_]*.{md,mdx}` picks up both formats automatically.
### Content collection restructure
Blog posts have moved from `src/data/blog/` to `src/content/posts/`, aligning with Astro conventions. A new `pages` collection at `src/content/pages/` covers standalone pages (About, etc.). The `posts` collection uses Astro's `glob()` loader — `defineCollection` with `type: "content"` is no longer used:
```ts file="src/content.config.ts"
const posts = defineCollection({
loader: glob({ pattern: "**/[^_]*.{md,mdx}", base: "./src/content/posts" }),
schema: ({ image }) =>
z.object({
author: z.string(),
pubDatetime: z.date(),
title: z.string(),
tags: z.array(z.string()).default(["others"]),
description: z.string(),
// …
}),
});
```
### Design token system
The 5-token color palette from v5 has grown to 7 tokens in `src/styles/theme.css`. Tokens are defined as CSS custom properties and registered to Tailwind v4 via `@theme inline`:
```css file="src/styles/theme.css"
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-border: var(--border);
}
:root,
[data-theme="light"] {
--background: #fdfdfd;
--foreground: #282728;
--accent: #006cac;
--accent-foreground: #ffffff;
--muted: #e6e6e6;
--muted-foreground: #6b7280;
--border: #ece9e9;
}
[data-theme="dark"] {
--background: #212737;
--foreground: #eaedf3;
--accent: #ff6b01;
--accent-foreground: #ffffff;
--muted: #343f60;
--muted-foreground: #afb9ca;
--border: #ab4b08;
}
```
`theme.css` is a separate file imported by `global.css`. The two new tokens are `--accent-foreground` and `--muted-foreground`.
### i18n string extraction
All UI strings are extracted to `src/i18n/lang/en.ts` with the `UIStrings` interface. Adding a new language requires only a new file in `src/i18n/lang/`:
```ts file="src/i18n/lang/en.ts"
export default {
nav: { home: "Home", posts: "Posts" /* … */ },
post: { publishedAt: "Published at" /* … */ },
/* … */
} satisfies UIStrings;
```
The `tplStr()` helper handles parameterized strings so translators can reorder tokens freely.
### Base path and subdirectory deploy support
All internal links go through `getRelativeLocaleUrl()` and the `withBase.ts` helpers (`stripLocale`, `stripBase`, `getAssetPath`). Deploying to a subdirectory (e.g. `/astro-paper`) works without manual link updates.
### Google Site Verification via config
The preferred way to set Google Site Verification is `site.googleVerification` in `astro-paper.config.ts`:
```ts file="astro-paper.config.ts"
export default defineAstroPaperConfig({
site: {
// …
googleVerification: "your-google-site-verification-value",
},
});
```
The `PUBLIC_GOOGLE_SITE_VERIFICATION` environment variable is still supported as a fallback for cases where you prefer not to commit the value to the config file.
```bash file=".env"
PUBLIC_GOOGLE_SITE_VERIFICATION=your-google-site-verification-value
```
When both are set, `site.googleVerification` takes precedence.
## Other Notable Changes
- Updated and renamed helper/util functions.
- Adjacent post navigation (prev/next) is now computed once in `getStaticPaths` and passed as props — the component no longer fetches all posts per page.
- `_components/` scoping: post-specific components live under `pages/posts/[...slug]/_components/` and do not pollute the global `src/components/` directory.
- `PostLayout.astro` handles structured data and SEO only — post page logic lives in the page file itself.
## Summary
AstroPaper v6 retains its minimal, clean look while rebuilding the internals around Astro v6's new primitives. The config system is simpler, the codebase is easier to navigate, and the theme ships ready for i18n and subdirectory deployments out of the box.
## See also
- [Predefined Color Schemes](/posts/predefined-color-schemes/)
- [How to configure AstroPaper theme](/posts/how-to-configure-astropaper-theme/)
- [Adding new posts in AstroPaper](/posts/adding-new-posts-in-astropaper-theme)
+239
View File
@@ -0,0 +1,239 @@
---
author: Sat Naing
pubDatetime: 2022-09-23T15:22:00Z
modDatetime: 2026-05-17T04:47:08.212Z
title: Adding new posts in AstroPaper theme
slug: adding-new-posts-in-astropaper-theme
featured: true
draft: false
tags:
- docs
description: "Some rules & recommendations for creating or adding new posts using AstroPaper theme."
---
import ResponsiveTable from '@/components/ResponsiveTable.astro';
This guide covers the rules and conventions for creating new posts in AstroPaper — file placement, frontmatter fields, images, and syntax highlighting.
<figure>
<img
src="https://images.pexels.com/photos/159618/still-life-school-retro-ink-159618.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
alt="Free Classic wooden desk with writing materials, vintage clock, and a leather bag. Stock Photo"
/>
<figcaption class="text-center">
Photo by{" "}
<a href="https://www.pexels.com/photo/brown-wooden-desk-159618/">Pixabay</a>
</figcaption>
</figure>
## Table of contents
## Creating a Blog Post
To write a new blog post, create a markdown (or MDX) file inside the `src/content/posts/` directory.
You can organize posts into subdirectories to make content easier to manage. The subdirectory name becomes part of the post URL. For example, `src/content/posts/2025/example-post.md` will be available at `/posts/2025/example-post`.
If you want a subdirectory for organization only, without it affecting the URL, prefix the folder name with an underscore (`_`).
```bash
# Example: post file paths and their URLs
src/content/posts/very-first-post.md -> mysite.com/posts/very-first-post
src/content/posts/2025/example-post.md -> mysite.com/posts/2025/example-post
src/content/posts/_2026/another-post.md -> mysite.com/posts/another-post
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
```
> Files and directories prefixed with `_` are excluded from routing. Use them for drafts, shared assets, or internal-only content.
## Frontmatter
Frontmatter is the main place to store metadata about a blog post. It lives at the top of the file in YAML format. Read more about frontmatter and its usage in [Astro documentation](https://docs.astro.build/en/guides/markdown-content/).
Here is the list of frontmatter properties for each post:
<ResponsiveTable variant="striped-minimal">
| Property | Description | Remark |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| **_title_** | Title of the post. (h1) | required<sup>\*</sup> |
| **_description_** | Description of the post. Used in post excerpt and site description of the post. | required<sup>\*</sup> |
| **_pubDatetime_** | Published datetime in ISO 8601 format. | required<sup>\*</sup> |
| **_modDatetime_** | Modified datetime in ISO 8601 format. (only add this property when a blog post is modified) | optional |
| **_author_** | Author of the post. | default = `site.author` |
| **_featured_** | Whether or not to display this post in the featured section of the home page. | default = false |
| **_draft_** | Mark this post as 'unpublished'. | default = false |
| **_tags_** | Related keywords for this post. Written in array YAML format. | default = others |
| **_ogImage_** | OG image of the post. Useful for social media sharing and SEO. Can be a remote URL or an image path relative to the current folder. | default = `site.ogImage` or generated OG image |
| **_canonicalURL_** | Canonical URL (absolute), in case the article already exists on another source. | default = `Astro.site` + `Astro.url.pathname` |
| **_hideEditPost_** | Hide the edit-post button under the post title. Applies only to the current post. | default = false |
| **_timezone_** | Specify a timezone in IANA format for the current post. Overrides the global `site.timezone` config for this post only. | default = `site.timezone` |
</ResponsiveTable>
> 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.
Title and description (excerpt) are important for search engine optimization (SEO) and thus AstroPaper encourages you to include these in all blog posts.
If you omit `tags` in a blog post (in other words, if no tag is specified), the default tag `others` will be used as a tag for that post. You can set the default tag in `src/content.config.ts`:
```ts file="src/content.config.ts"
// ...
tags: z.array(z.string()).default(["others"]), // replace "others" with whatever you want
// ...
```
### Sample Frontmatter
Here is sample frontmatter for a post.
```yaml file="src/content/posts/sample-post.md"
---
title: The title of the post
author: your name
pubDatetime: 2022-09-21T05:17:19Z
featured: true
draft: false
tags:
- some
- example
- tags
ogImage: ../../assets/images/example.png # src/assets/images/example.png
# ogImage: "https://example.org/remote-image.png" # remote URL
description: This is the example description of the example post.
canonicalURL: https://example.org/my-article-was-already-posted-here
---
```
### VS Code snippets (optional)
AstroPaper includes workspace snippets to speed up creating new posts:
- **frontmatter**: inserts the recommended frontmatter block
- **template**: inserts a basic post template (including `## Table of contents`)
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.
## 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:
```md
---
# frontmatter
---
Here are some recommendations, tips & tricks for creating new posts in AstroPaper blog theme.
<!-- [!code ++] -->
## Table of contents
<!-- the rest of the post -->
```
## Headings
There's one thing to note about headings. AstroPaper blog posts use `title` (from frontmatter) as the main heading of the post. Therefore, the rest of the headings in the post should use `h2` \~ `h6`.
This rule is not mandatory, but highly recommended for visual, accessibility, and SEO purposes.
## Syntax Highlighting
AstroPaper uses [Shiki](https://shiki.style/) as the default syntax highlighter, with [@shikijs/transformers](https://shiki.style/packages/transformers) for enhanced fenced code blocks. If you don't want to use the transformers, you can remove them:
```bash
pnpm remove @shikijs/transformers
```
```ts file="astro.config.ts"
// ...
// [!code --:5]
import {
transformerNotationDiff,
transformerNotationHighlight,
transformerNotationWordHighlight,
} from "@shikijs/transformers";
export default defineConfig({
// ...
markdown: {
remarkPlugins: [remarkToc, [remarkCollapse, { test: "Table of contents" }]],
shikiConfig: {
themes: { light: "min-light", dark: "night-owl" },
defaultColor: false,
wrap: false,
transformers: [
transformerFileName(),
// [!code --:3]
transformerNotationHighlight(),
transformerNotationWordHighlight(),
transformerNotationDiff({ matchAlgorithm: "v3" }),
],
},
},
// ...
});
```
## Storing Images for Blog Content
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).
### Inside `src/assets/` directory (recommended)
You can store images inside the `src/assets/` directory. These images will be automatically optimized by Astro through the [Image Service API](https://docs.astro.build/en/reference/image-service-reference/).
You can use a relative path or alias path (`@/assets/`) to reference these images.
Example: suppose you want to display `example.jpg` whose path is `src/assets/images/example.jpg`.
```md
![something](@/assets/images/example.jpg)
<!-- OR -->
![something](../../assets/images/example.jpg)
<!-- Using img tag or Image component won't work in markdown ❌ -->
<img src="@/assets/images/example.jpg" alt="something">
<!-- ^^ This is wrong -->
```
> Technically, you can store images inside any directory under `src`. `src/assets` is just a recommendation.
### Inside `public/` directory
You can store images inside the `public/` directory. Keep in mind that images stored in `public/` remain untouched by Astro, meaning they will be unoptimized and you need to handle image optimization yourself.
For these images, use an absolute path. They can be displayed using [markdown image syntax](https://www.markdownguide.org/basic-syntax/#images-1) or an HTML `img` tag.
Example: assume `example.jpg` is located at `public/assets/images/example.jpg`.
```md
![something](/assets/images/example.jpg)
<!-- OR -->
<img src="/assets/images/example.jpg" alt="something">
```
## Bonus
### 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.
Recommended image compression sites:
- [TinyPng](https://tinypng.com/)
- [TinyJPG](https://tinyjpg.com/)
### OG Image
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.
> 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/).
@@ -0,0 +1,103 @@
---
author: Sat Naing
pubDatetime: 2022-09-25T15:20:35Z
modDatetime: 2026-05-17T04:57:06.476Z
title: Customizing AstroPaper theme color schemes
featured: false
draft: false
tags:
- color-schemes
- docs
description:
How you can enable/disable light & dark mode; and customize color schemes
of AstroPaper theme.
---
import ResponsiveTable from '@/components/ResponsiveTable.astro';
This guide covers how to enable or disable light and dark mode, and how to customize the color scheme for the entire site.
## Table of contents
## Enable/disable light & dark mode
AstroPaper theme includes light and dark mode by default. This default behavior can be disabled in `astro-paper.config.ts`:
```ts file="astro-paper.config.ts"
export default defineAstroPaperConfig({
// ...
features: {
lightAndDarkMode: true, // [!code highlight]
// ...
},
});
```
To disable `light & dark mode`, set `features.lightAndDarkMode` to `false`. When disabled, the site will use only the light color scheme defined in `src/styles/theme.css`.
## Customize color schemes
Both light and dark color schemes of AstroPaper theme are defined in `src/styles/theme.css`.
```css file="src/styles/theme.css"
/* Light theme values */
:root,
[data-theme="light"] {
--background: #fdfdfd;
--foreground: #282728;
--accent: #006cac;
--accent-foreground: #ffffff;
--muted: #e6e6e6;
--muted-foreground: #6b7280;
--border: #ece9e9;
}
/* Dark theme values */
[data-theme="dark"] {
--background: #212737;
--foreground: #eaedf3;
--accent: #ff6b01;
--accent-foreground: #ffffff;
--muted: #343f60;
--muted-foreground: #afb9ca;
--border: #ab4b08;
}
```
The `:root` and `[data-theme="light"]` selectors define the light color scheme, while `[data-theme="dark"]` defines the dark color scheme.
To customize your own color scheme, specify your light colors inside `:root, [data-theme="light"]`, and your dark colors inside `[data-theme="dark"]`.
Here is a detailed explanation of each color property:
<ResponsiveTable variant="minimal" class="max-sm:-mx-4 [&_td]:first-of-type:text-nowrap">
| Color Property | Definition & Usage |
| --------------------- | --------------------------------------------------------------------- |
| `--background` | Primary color of the website. Usually the main background. |
| `--foreground` | Secondary color of the website. Usually the text color. |
| `--accent` | Accent color. Used for links, hover states, and interactive elements. |
| `--accent-foreground` | Foreground color displayed on top of `--accent` backgrounds. |
| `--muted` | Muted background color. Used for cards, tags, and hover states. |
| `--muted-foreground` | Text color displayed on top of `--muted` backgrounds. |
| `--border` | Border color. Used for dividers and visual separation. |
</ResponsiveTable>
Here is an example of changing the light color scheme:
```css file="src/styles/theme.css"
/* ... */
:root,
[data-theme="light"] {
--background: #f6eee1;
--foreground: #012c56;
--accent: #e14a39;
--accent-foreground: #ffffff;
--muted: #efd8b0;
--muted-foreground: #6b7280;
--border: #dc9891;
}
/* ... */
```
> Check out some [predefined color schemes](https://astro-paper.pages.dev/posts/predefined-color-schemes/) AstroPaper has already crafted for you.
+93
View File
@@ -0,0 +1,93 @@
---
author: Sat Naing
pubDatetime: 2022-12-28T04:59:04.866Z
modDatetime: 2026-05-04T00:00:00Z
title: Dynamic OG image generation in AstroPaper blog posts
slug: dynamic-og-image-generation-in-astropaper-blog-posts
featured: false
draft: false
tags:
- docs
- release
description: New feature in AstroPaper v1.4.0, introducing dynamic OG image generation for blog posts.
---
New feature in AstroPaper v1.4.0, introducing dynamic OG image generation for blog posts.
![Dynamic OG image generation in AstroPaper blog posts](/posts/dynamic-og-image-generation-in-astropaper-blog-posts/index.png)
## Table of contents
## Intro
OG images (aka Social Images) play an important role in social media engagements. In case you don't know what OG image means, it is an image displayed whenever we share our website URL on social media such as Facebook, Discord etc.
> The Social Image used for Twitter is technically not called OG image. However, in this post, I'll be using the term OG image for all types of Social Images.
## Default/Static OG image (the old way)
AstroPaper already provided a way to add an OG image to a blog post. The author can specify the OG image in the frontmatter `ogImage`. Even when the author doesn't define the OG image in the frontmatter, the default OG image will be used as a fallback (in this case `public/default-og.jpg`). But the problem is that the default OG image is static, which means every blog post that does not include an OG image in the frontmatter will always use the same default OG image despite each post title/content being different from others.
## Dynamic OG Image
Generating a dynamic OG image for each post allows the author to avoid specifying an OG image for every single blog post. Besides, this will prevent the fallback OG image from being identical to all blog posts.
In AstroPaper v1.4.0, Vercel's [Satori](https://github.com/vercel/satori) package is used for dynamic OG image generation.
In AstroPaper v6+, the same idea remains (Satori renders SVG, then PNG is produced via [Sharp](https://sharp.pixelplumbing.com/)), but fonts are sourced from Astro's **Fonts** configuration and loaded via [`experimental_getFontFileURL()`](https://astro.build/blog/astro-620/) so OG generation can reuse the same font pipeline as the site.
Dynamic OG images will be generated at build time for blog posts that:
- don't include OG image in the frontmatter
- are not marked as draft.
## Anatomy of AstroPaper dynamic OG image
Dynamic OG images include _the blog post title_, _author name_, and _site title_. Author name and site title are retrieved from `site.author` and `site.title` in `astro-paper.config.ts`. The title is generated from the blog post frontmatter `title`.
![Example Dynamic OG Image link](https://user-images.githubusercontent.com/53733092/209704501-e9c2236a-3f4d-4c67-bab3-025aebd63382.png)
### 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).
```ts file="astro.config.ts"
import { defineConfig, fontProviders } from "astro/config";
export default defineConfig({
fonts: [
{
// Example: Japanese coverage (pick what you need for your audience)
name: "Noto Sans JP",
cssVariable: "--font-google-sans-code",
provider: fontProviders.google(),
fallbacks: ["monospace"],
weights: [400, 700],
styles: ["normal", "italic"],
formats: ["woff", "ttf"],
},
],
});
```
If you change `cssVariable`, also update the matching key in:
- `src/pages/og.png.ts`
- `src/pages/posts/[...slug]/index.png.ts`
> 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.
@@ -0,0 +1,21 @@
---
title: Example Draft Post
author: Sat Naing
pubDatetime: 2022-06-06T04:06:31Z
slug: example-draft-post
featured: false
draft: true
tags:
- TypeScript
- Astro
description:
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel
fringilla est
---
Users cannot see this post because it is in draft.
## Motivation
rec 1
@@ -0,0 +1,105 @@
---
title: How Do I Develop My Portfolio Website & Blog
author: Sat Naing
pubDatetime: 2022-03-25T16:55:12.000+00:00
slug: how-do-i-develop-my-portfolio-and-blog
featured: false
draft: false
tags:
- NextJS
- TailwindCSS
- HeadlessCMS
- Blog
description:
"EXAMPLE POST: My experience about developing my first portfolio website and a blog
using NextJS and a headless CMS."
timezone: "Asia/Yangon"
---
> This article is originally from my [blog post](https://satnaing.dev/blog/posts/how-do-i-develop-my-portfolio-and-blog). I put this article to demonstrate how you can write blog posts/articles using AstroPaper theme.
My experience about developing my first portfolio website and a blog using NextJS and a headless CMS.
![Building portfolio](https://satnaing.dev/_ipx/w_2048,q_75/https%3A%2F%2Fres.cloudinary.com%2Fnoezectz%2Fimage%2Fupload%2Fv1653050141%2FSatNaing%2Fblog_at_cafe_ei1wf4.jpg?url=https%3A%2F%2Fres.cloudinary.com%2Fnoezectz%2Fimage%2Fupload%2Fv1653050141%2FSatNaing%2Fblog_at_cafe_ei1wf4.jpg&w=2048&q=75)
## Motivation
I've been always thinking about launching my own website with my custom domain name (**satnaing.dev**) since my college student life. But that never happened until this project. I've done several projects and works about web application development but I didn't make an effort to do this.
So, "what about blog?" you may ask. Yeah, blog also has been in my project list for some time. I always wanted to make a blog project using some of the latest technologies. However, I've been busy with my works and other projects so that blog project has never been started.
In these days, I tend to develop my own projects with the focus in good quality rather than quantity. After the project is done, I usually put a proper readme file in the GitHub repo. But GitHub repo readme is only suitable for technical aspects (this is just my thought). I want to write down my experiences and challenges. Thus, I decided to make my own blog. Plus, at this point, I have decent experiences and confidence to develop this project.
## Tech Stack
For the front-end, I wanted to use [React](https://reactjs.org/ "React Official Website"). But React alone is not good enough for SEO; and I did have to consider many factors like routing, image optimization etc. So, I chose [NextJS](https://nextjs.org/ "NextJS Official Website") as my main front-end stack. And of course TypeScript for type checking. (It's said that you'll love TypeScript when you're used to it 😉)
For styling, I use [TailwindCSS](https://tailwindcss.com/ "Tailwind CSS Official Website"). This is because I love developer experience that Tailwind gives and it has a lot of flexibilities compared to other component UI libraries like MUI or React Bootstrap.
All contents of this project reside within the GitHub repository. All my blog posts (including this one) are written in Markdown file format since I'm very used to with this. But to write Markdown along with its frontmatter effortlessly, I use [Forestry](https://forestry.io/ "Forestry Official Website") headless CMS. It is a git-based CMS that can serve Markdown and other contents. Because of this, I can write my contents either using Markdown or wysiwyg editor. Besides, writing frontmatters with this is a breeze.
Images and assets are uploaded and stored in [Cloudinary](https://cloudinary.com/ "Cloudinary Official Website"). I connect Cloudinary via Forestry and manage them directly in the dashboard.
In conclusion, these are the tech stack I've used for this project.
- Front-end: NextJS (TypeScript)
- Styling: TailwindCSS
- Animations: GSAP
- CMS: Forestry Headless CMS
- Deployment: Vercel
## Features
The following are certain features of my portfolio and blog
### SEO Friendly
The entire project is developed with SEO focus in mind. I've used proper meta tags, descriptions and heading alignments. This website is now indexed by Google.
> You can search this website on google by using keywords like 'sat naing dev'
![searching satnaing.dev on google](https://res.cloudinary.com/noezectz/image/upload/v1648231400/SatNaing/satnaing-on-google_asflq6.png "satnaing.dev is indexed")
Moreover, this website will be displayed well when shared to social media due to properly used meta tags.
![satnaing.dev card layout when shared to Facebook](https://res.cloudinary.com/noezectz/image/upload/v1653106955/SatNaing/satnaing-dev-share-on-facebook_1_zjoehx.png "Card layout when shared to Facebook")
### Dynamic Sitemap
Sitemap plays an important part in SEO. Because of this, every single page of this site should be included in sitemap.xml. I made an auto generated sitemap in my website whenever I create a new content or tags or categories.
### Light & Dark Themes
Due to dark theme trend in recent years, many websites include dark theme out of the box nowadays. Certainly, my website also supports light & dark themes.
### Fully Accessible
This website is fully accessible. You can navigate around by only using keyboard. I put all a11y enhancement best practices like including alt text in all images, no skipping headings, using semantic HTML tags, using aria-attributes properly.
### Search box, Categories & Tags
All blog contents can be searched by search box. Moreover, contents can be filtered by categories and tags. In this way, blog readers can search and read what they really want.
### Performance and Lighthouse Score
This website got very good performance and lighthouse score thanks to proper development and best practices. Here's the lighthouse score for this website.
![satnaing.dev Lighthouse score](https://user-images.githubusercontent.com/53733092/159957822-7082e459-11e9-4616-8f1e-49d0881f7cbb.png "satnaing.dev Lighthouse score")
### Animations
Initially I used [Framer Motion](https://www.framer.com/motion/ "Framer Motion") to add animations and micro interactions for this website. However, when I tried to use some complex animations and parallax effects, I found it inconvenient to integrate with Framer Motion (Maybe I'm not very good at and used to working with it). Hence, I decided to use [GSAP](https://greensock.com/ "GSAP Animation Library") for all of my animations. It is one of the most popular animation library and it is capable of doing complex and advanced animations. You can see animations and micro interactions on pretty much every page of this website.
![animations at satnaing.dev](https://res.cloudinary.com/noezectz/image/upload/v1653108324/SatNaing/ezgif.com-gif-maker_2_hehtlm.gif "satnaing.dev website")
## Outro
In conclusion, this project gives me a lot of experience and confidence about developing blog site (SSG). Now, I have gained knowledge of git-based CMS and how it interacts with NextJS. I've also learned about SEO, dynamic sitemap generation and indexing Google procedures. I will make better projects in the future. So, stay tuned! ✌🏻
And... last but not least, I would like to say 'thanks' to my friend [Swann Fevian Kyaw](https://www.facebook.com/bon.zai.3910 "Swann Fevian Kyaw's Facebook Account") (@[ToonHa](https://www.facebook.com/ToonHa-102639465752883 "ToonHa Facebook Page")) who has drawn a beautiful illustration for my hero section of the website.
## Project Links
- Website: [https://satnaing.dev/](https://satnaing.dev/ "https://satnaing.dev/")
- Blog: [https://satnaing.dev/blog](https://satnaing.dev/blog "https://satnaing.dev/blog")
- Repo: [https://github.com/satnaing/my-portfolio](https://github.com/satnaing/my-portfolio "https://github.com/satnaing/my-portfolio")
@@ -0,0 +1,206 @@
---
title: Tailwind Typography Plugin
author: Sat Naing
pubDatetime: 2022-07-05T02:05:51Z
featured: false
draft: false
tags:
- TypeScript
- Astro
description: "EXAMPLE POST: About Tailwind Typography Plugin and how you can use it effectively."
---
> This article is from [TailwindLabs](https://tailwindcss-typography.vercel.app/). I put this article to demonstrate how you can write blog posts/articles using AstroPaper theme.
By default, Tailwind removes all of the default browser styling from paragraphs, headings, lists and more. This ends up being really useful for building application UIs because you spend less time undoing user-agent styles, but when you _really are_ just trying to style some content that came from a rich-text editor in a CMS or a markdown file, it can be surprising and unintuitive.
We get lots of complaints about it actually, with people regularly asking us things like:
> Why is Tailwind removing the default styles on my `h1` elements? How do I disable this? What do you mean I lose all the other base styles too?
> We hear you, but we're not convinced that simply disabling our base styles is what you really want. You don't want to have to remove annoying margins every time you use a `p` element in a piece of your dashboard UI. And I doubt you really want your blog posts to use the user-agent styles either — you want them to look _awesome_, not awful.
The `@tailwindcss/typography` plugin is our attempt to give you what you _actually_ want, without any of the downsides of doing something stupid like disabling our base styles.
It adds a new `prose` class that you can slap on any block of vanilla HTML content and turn it into a beautiful, well-formatted document:
```html
<article class="prose">
<h1>Garlic bread with cheese: What the science tells us</h1>
<p>
For years parents have espoused the health benefits of eating garlic bread
with cheese to their children, with the food earning such an iconic status
in our culture that kids will often dress up as warm, cheesy loaf for
Halloween.
</p>
<p>
But a recent study shows that the celebrated appetizer may be linked to a
series of rabies cases springing up around the country.
</p>
<!-- ... -->
</article>
```
For more information about how to use the plugin and the features it includes, [read the documentation](https://github.com/tailwindcss/typography/blob/master/README.md).
---
## What to expect from here on out
What follows from here is just a bunch of absolute nonsense I've written to dogfood the plugin itself. It includes every sensible typographic element I could think of, like **bold text**, unordered lists, ordered lists, code blocks, block quotes, _and even italics_.
It's important to cover all of these use cases for a few reasons:
1. We want everything to look good out of the box.
2. Really just the first reason, that's the whole point of the plugin.
3. Here's a third pretend reason though a list with three items looks more realistic than a list with two items.
Now we're going to try out another header style.
### Typography should be easy
So that's a header for you — with any luck if we've done our job correctly that will look pretty reasonable.
Something a wise person once told me about typography is:
> Typography is pretty important if you don't want your stuff to look like trash. Make it good then it won't be bad.
> It's probably important that images look okay here by default as well:
<figure>
<img
src="https://images.unsplash.com/photo-1556740758-90de374c12ad?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1000&q=80"
alt=""
/>
<figcaption>
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of
classical Latin literature from 45 BC, making it over 2000 years old.
</figcaption>
</figure>
Now I'm going to show you an example of an unordered list to make sure that looks good, too:
- So here is the first item in this list.
- In this example we're keeping the items short.
- Later, we'll use longer, more complex list items.
And that's the end of this section.
## What if we stack headings?
### We should make sure that looks good, too.
Sometimes you have headings directly underneath each other. In those cases you often have to undo the top margin on the second heading because it usually looks better for the headings to be closer together than a paragraph followed by a heading should be.
### When a heading comes after a paragraph …
When a heading comes after a paragraph, we need a bit more space, like I already mentioned above. Now let's see what a more complex list would look like.
- **I often do this thing where list items have headings.**
For some reason I think this looks cool which is unfortunate because it's pretty annoying to get the styles right.
I often have two or three paragraphs in these list items, too, so the hard part is getting the spacing between the paragraphs, list item heading, and separate list items to all make sense. Pretty tough honestly, you could make a strong argument that you just shouldn't write this way.
- **Since this is a list, I need at least two items.**
I explained what I'm doing already in the previous list item, but a list wouldn't be a list if it only had one item, and we really want this to look realistic. That's why I've added this second list item so I actually have something to look at when writing the styles.
- **It's not a bad idea to add a third item either.**
I think it probably would've been fine to just use two items but three is definitely not worse, and since I seem to be having no trouble making up arbitrary things to type, I might as well include it.
After this sort of list I usually have a closing statement or paragraph, because it kinda looks weird jumping right to a heading.
## Code should look okay by default.
I think most people are going to use [highlight.js](https://highlightjs.org/) or [Prism](https://prismjs.com/) or something if they want to style their code blocks but it wouldn't hurt to make them look _okay_ out of the box, even with no syntax highlighting.
Here's what a default `tailwind.config.js` file looks like at the time of writing:
```js
module.exports = {
purge: [],
theme: {
extend: {},
},
variants: {},
plugins: [],
};
```
Hopefully that looks good enough to you.
### What about nested lists?
Nested lists basically always look bad which is why editors like Medium don't even let you do it, but I guess since some of you goofballs are going to do it we have to carry the burden of at least making it work.
1. **Nested lists are rarely a good idea.**
- You might feel like you are being really "organized" or something but you are just creating a gross shape on the screen that is hard to read.
- Nested navigation in UIs is a bad idea too, keep things as flat as possible.
- Nesting tons of folders in your source code is also not helpful.
2. **Since we need to have more items, here's another one.**
- I'm not sure if we'll bother styling more than two levels deep.
- Two is already too much, three is guaranteed to be a bad idea.
- If you nest four levels deep you belong in prison.
3. **Two items isn't really a list, three is good though.**
- Again please don't nest lists if you want people to actually read your content.
- Nobody wants to look at this.
- I'm upset that we even have to bother styling this.
The most annoying thing about lists in Markdown is that `<li>` elements aren't given a child `<p>` tag unless there are multiple paragraphs in the list item. That means I have to worry about styling that annoying situation too.
- **For example, here's another nested list.**
But this time with a second paragraph.
- These list items won't have `<p>` tags
- Because they are only one line each
- **But in this second top-level list item, they will.**
This is especially annoying because of the spacing on this paragraph.
- As you can see here, because I've added a second line, this list item now has a `<p>` tag.
This is the second line I'm talking about by the way.
- Finally here's another list item so it's more like a list.
- A closing list item, but with no nested list, because why not?
And finally a sentence to close off this section.
## There are other elements we need to style
I almost forgot to mention links, like [this link to the Tailwind CSS website](https://tailwindcss.com). We almost made them blue but that's so yesterday, so we went with dark gray, feels edgier.
We even included table styles, check it out:
| Wrestler | Origin | Finisher |
| ----------------------- | ------------ | ------------------ |
| Bret "The Hitman" Hart | Calgary, AB | Sharpshooter |
| Stone Cold Steve Austin | Austin, TX | Stone Cold Stunner |
| Randy Savage | Sarasota, FL | Elbow Drop |
| Vader | Boulder, CO | Vader Bomb |
| Razor Ramon | Chuluota, FL | Razor's Edge |
We also need to make sure inline code looks good, like if I wanted to talk about `<span>` elements or tell you the good news about `@tailwindcss/typography`.
### Sometimes I even use `code` in headings
Even though it's probably a bad idea, and historically I've had a hard time making it look good. This _"wrap the code blocks in backticks"_ trick works pretty well though really.
Another thing I've done in the past is put a `code` tag inside of a link, like if I wanted to tell you about the [`tailwindcss/docs`](https://github.com/tailwindcss/docs) repository. I don't love that there is an underline below the backticks but it is absolutely not worth the madness it would require to avoid it.
#### We haven't used an `h4` yet
But now we have. Please don't use `h5` or `h6` in your content, Medium only supports two heading levels for a reason, you animals. I honestly considered using a `before` pseudo-element to scream at you if you use an `h5` or `h6`.
We don't style them at all out of the box because `h4` elements are already so small that they are the same size as the body copy. What are we supposed to do with an `h5`, make it _smaller_ than the body copy? No thanks.
### We still need to think about stacked headings though.
#### Let's make sure we don't screw that up with `h4` elements, either.
Phew, with any luck we have styled the headings above this text and they look pretty good.
Let's add a closing paragraph here so things end with a decently sized block of text. I can't explain why I want things to end that way but I have to assume it's because I think things will look weird or unbalanced if there is a heading too close to the end of the document.
What I've written here is probably long enough, but adding this final sentence can't hurt.
@@ -0,0 +1,89 @@
---
title: How Do I Develop My Terminal Portfolio Website with React
author: Sat Naing
pubDatetime: 2022-06-09T03:42:51Z
slug: how-do-i-develop-my-terminal-portfolio-website-with-react
featured: false
draft: false
tags:
- JavaScript
- ReactJS
- ContextAPI
- Styled-Components
- TypeScript
description:
"EXAMPLE POST: Developing a terminal-like website using ReactJS, TypeScript and Styled-Components.
Includes features like autocomplete, multiple themes, command hints etc."
timezone: "Asia/Yangon"
---
> This article is originally from my [blog post](https://satnaing.dev/blog/posts/how-do-i-develop-my-terminal-portfolio-website-with-react). I put this article to demonstrate how you can write blog posts/articles using AstroPaper theme.
Developing a terminal-like website using ReactJS, TypeScript and Styled-Components. Includes features like autocomplete, multiple themes, command hints etc.
![Sat Naing's Terminal Portfolio](https://satnaing.dev/_ipx/w_2048,q_75/https%3A%2F%2Fres.cloudinary.com%2Fnoezectz%2Fimage%2Fupload%2Fv1654754125%2FSatNaing%2Fterminal-screenshot_gu3kkc.png?url=https%3A%2F%2Fres.cloudinary.com%2Fnoezectz%2Fimage%2Fupload%2Fv1654754125%2FSatNaing%2Fterminal-screenshot_gu3kkc.png&w=2048&q=75)
## Table of contents
## Intro
Recently, I've developed and published my portfolio + a blog. Im glad I got some good feedback for it. Today, I want to introduce my new terminal-like portfolio website. It is developed using ReactJS, TypeScript. I got this idea from CodePen and YouTube.
## Tech Stack
This project is a frontend project without any backend codes. The UI/UX part is designed in Figma. For the frontend user-interface, I chose React over pain JavaScript and NextJS. Why?
- Firstly, I want to write declarative code. Managing HTML DOM using JavaScript imperatively is really tedious.
- Secondly, because it is React!!! It is fast, and reliable.
- Lastly, I dont need much of the SEO features, routing and image optimization provided by NextJS.
And of course there's TypeScript for type checking.
For styling, I took a different approach than what I usually do. Instead of choosing Pure CSS, Sass, or Utility CSS Framework like TailwindCSS, I chose the CSS-in-JS way (Styled-Components). Although Ive known about Styled-Components for some time, Ive never tried it out. So, the writing style and structures of Styled-Components in this project may not be very organized or very good.
This project doesnt need very complex state management. I just use ContextAPI in this project for multiple theming and to avoid prop drilling.
Heres a quick recap for the tech stack.
- Frontend: [ReactJS](https://reactjs.org/ "React Website"), [TypeScript](https://www.typescriptlang.org/ "TypeScript Website")
- Styling: [Styled-Components](https://styled-components.com/ "Styled-Components Website")
- UI/UX: [Figma](https://figma.com/ "Figma Website")
- State Management: [ContextAPI](https://reactjs.org/docs/context.html "React ContextAPI")
- Deployment: [Netlify](https://www.netlify.com/ "Netlify Website")
## Features
Here are some features of the project.
### Multiple Themes
Users can change multiple themes. At the time of writing this post, there are 5 themes; and more themes will probably be added in the future. The selected theme is saved in local storage so that the theme wont change on page refresh.
![Setting different theme](https://i.ibb.co/fSTCnWB/terminal-portfolio-multiple-themes.gif)
### Command-line Completion
To look and feel as close to the actual terminal as possible, I put a command-line completion feature which auto fills in partially typed commands by simply pressing Tab or Ctrl + i.
![Demonstrating command-line completion](https://i.ibb.co/CQTGGLF/terminal-autocomplete.gif)
### Previous Commands
Users can go back to the previous commands or navigate the previously typed commands by pressing Up & Down Arrows.
![Going back to previous commands with UP Arrow](https://i.ibb.co/vD1pSRv/terminal-up-down.gif)
### View/Clear Command History
previously typed commands can be viewed by typing history in the command line. All the command history and terminal screen can be wiped out by typing clear or pressing Ctrl + l.
![Clearing the terminal with 'clear' or 'Ctrl + L' command](https://i.ibb.co/SJBy8Rr/terminal-clear.gif)
## Outro
This is a really fun project, and one special part of this project is I had to focus on logic rather than user-interface (even though this is kind of a frontend project).
## Project Links
- Website: [https://terminal.satnaing.dev/](https://terminal.satnaing.dev/ "https://terminal.satnaing.dev/")
- Repo: [https://github.com/satnaing/terminal-portfolio](https://github.com/satnaing/terminal-portfolio "https://github.com/satnaing/terminal-portfolio")
@@ -0,0 +1,156 @@
---
author: Alberto Perdomo
pubDatetime: 2024-09-08T20:58:52.737Z
modDatetime: 2025-03-22T09:25:46.734Z
title: How to add LaTeX Equations in Astro blog posts
tags:
- docs
description: Learn how to add LaTeX equations in Astro blog posts using Markdown, KaTeX, and remark/rehype plugins.
---
This document demonstrates how to use LaTeX equations in your Markdown files for AstroPaper. LaTeX is a powerful typesetting system often used for mathematical and scientific documents.
<figure>
<img
src="https://images.pexels.com/photos/22690748/pexels-photo-22690748/free-photo-of-close-up-of-complicated-equations-written-on-a-blackboard.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
alt="Free Close-up of complex equations on a chalkboard, showcasing chemistry and math symbols. Stock Photo"
/>
<figcaption class="text-center">
Photo by <a href="https://www.pexels.com/photo/close-up-of-complicated-equations-written-on-a-blackboard-22690748/">Vitaly Gariev</a>
</figcaption>
</figure>
## Table of contents
## Instructions
In this section, you will find instructions on how to add support for LaTeX in your Markdown files for AstroPaper.
1. Install the necessary remark and rehype plugins by running:
```bash
pnpm install rehype-katex remark-math katex
```
2. Update the Astro configuration to use the these plugins:
```ts file=astro.config.ts
// ...
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
export default defineConfig({
// ...
markdown: {
remarkPlugins: [
remarkMath, // [!code ++]
remarkToc,
[remarkCollapse, { test: "Table of contents" }],
],
rehypePlugins: [rehypeKatex], // [!code ++]
shikiConfig: {
// For more themes, visit https://shiki.style/themes
themes: { light: "min-light", dark: "night-owl" },
wrap: false,
},
},
// ...
});
```
3. Import KaTeX CSS in the main layout file
```astro file=src/layouts/Layout.astro
---
import { SITE } from "@config";
// astro code
---
<!doctype html>
<!-- Other elements -->
<meta property="og:image" content={socialImageURL} />
<!-- [!code highlight:4] -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.css"
/>
<body>
<slot />
</body>
```
4. As the last step, add a text-color for `katex` in `typography.css`.
```css file=src/styles/typography.css
@plugin "@tailwindcss/typography";
@layer base {
/* other classes */
/* Katex text color */
/* [!code highlight:3] */
.prose .katex-display {
@apply text-foreground;
}
/* ===== Code Blocks & Syntax Highlighting ===== */
/* other classes */
}
```
And _voilà_, this setup allows you to write LaTeX equations in your Markdown files, which will be rendered properly when the site is built. Once you do it, the rest of the document will appear rendered correctly.
---
## Inline Equations
Inline equations are written between single dollar signs `$...$`. Here are some examples:
1. The famous mass-energy equivalence formula: `$E = mc^2$`
2. The quadratic formula: `$x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$`
3. Euler's identity: `$e^{i\pi} + 1 = 0$`
---
## Block Equations
For more complex equations or when you want the equation to be displayed on its own line, use double dollar signs `$$...$$`:
The Gaussian integral:
```bash
$$ \int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi} $$
```
The definition of the Riemann zeta function:
```bash
$$ \zeta(s) = \sum_{n=1}^{\infty} \frac{1}{n^s} $$
```
Maxwell's equations in differential form:
```bash
$$
\begin{aligned}
\nabla \cdot \mathbf{E} &= \frac{\rho}{\varepsilon_0} \\
\nabla \cdot \mathbf{B} &= 0 \\
\nabla \times \mathbf{E} &= -\frac{\partial \mathbf{B}}{\partial t} \\
\nabla \times \mathbf{B} &= \mu_0\left(\mathbf{J} + \varepsilon_0 \frac{\partial \mathbf{E}}{\partial t}\right)
\end{aligned}
$$
```
---
## Using Mathematical Symbols
LaTeX provides a wide range of mathematical symbols:
- Greek letters: `$\alpha$`, `$\beta$`, `$\gamma$`, `$\delta$`, `$\epsilon$`, `$\pi$`
- Operators: `$\sum$`, `$\prod$`, `$\int$`, `$\partial$`, `$\nabla$`
- Relations: `$\leq$`, `$\geq$`, `$\approx$`, `$\sim$`, `$\propto$`
- Logical symbols: `$\forall$`, `$\exists$`, `$\neg$`, `$\wedge$`, `$\vee$`
@@ -0,0 +1,304 @@
---
author: Sat Naing
pubDatetime: 2022-09-23T04:58:53Z
modDatetime: 2026-05-17T05:50:08.212Z
title: How to configure AstroPaper theme
slug: how-to-configure-astropaper-theme
featured: true
draft: false
tags:
- configuration
- docs
description: How you can make AstroPaper theme absolutely yours.
---
import ResponsiveTable from '@/components/ResponsiveTable.astro';
This guide covers the available configuration options in AstroPaper — from site metadata and feature flags to fonts, social links, and layout settings.
## Table of contents
## Configuring astro-paper.config.ts
All site-wide configuration lives in `astro-paper.config.ts` at the root of the project. Use `defineAstroPaperConfig()` to get full IntelliSense support:
```ts file="astro-paper.config.ts"
import { defineAstroPaperConfig } from "./src/types/config";
export default defineAstroPaperConfig({
site: {
url: "https://your-site.com/", // replace with your deployed URL
title: "AstroPaper",
description: "A minimal, responsive and SEO-friendly Astro blog theme.",
author: "Sat Naing",
profile: "https://satnaing.dev",
ogImage: "default-og.jpg",
lang: "en",
timezone: "Asia/Bangkok",
dir: "ltr",
},
posts: {
perPage: 4,
perIndex: 4,
scheduledPostMargin: 15 * 60 * 1000, // 15 minutes
},
features: {
lightAndDarkMode: true,
dynamicOgImage: true,
showArchives: true,
showBackButton: true,
editPost: {
enabled: true,
url: "https://github.com/satnaing/astro-paper/edit/main/",
},
search: "pagefind",
},
socials: [
{ name: "github", url: "https://github.com/satnaing/astro-paper" },
{ name: "x", url: "https://x.com/username" },
{ name: "linkedin", url: "https://www.linkedin.com/in/username/" },
{ name: "mail", url: "mailto:yourmail@gmail.com" },
],
shareLinks: [
{ name: "whatsapp", url: "https://wa.me/?text=" },
{ name: "facebook", url: "https://www.facebook.com/sharer.php?u=" },
{ name: "x", url: "https://x.com/intent/post?url=" },
{ name: "telegram", url: "https://t.me/share/url?url=" },
{ name: "mail", url: "mailto:?subject=See%20this%20post&body=" },
],
});
```
### `site` options
<ResponsiveTable>
| Option | Description |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `url` | Your deployed website URL. Used for canonical URLs, OG image URLs, RSS feed, and sitemap. In production this must be set correctly. |
| `title` | Your site name. |
| `description` | Your site description. Useful for SEO and social media sharing. |
| `author` | Your name. Used as the default post author. |
| `profile` | Your personal/portfolio website URL, used for structured data. Set to `undefined` if you don't have one. |
| `ogImage` | Default OG image filename in `/public` (e.g. `"default-og.jpg"`). Used when no post-specific OG image is set and `dynamicOgImage` is disabled. |
| `lang` | HTML ISO language code for `<html lang="...">`. Defaults to `"en"`. |
| `timezone` | IANA timezone for post dates (e.g. `"Asia/Bangkok"`). Ensures consistent timestamps across localhost and your deployed site. |
| `dir` | Text direction for `<html dir="...">`. Supports `"ltr"` \| `"rtl"` \| `"auto"`. |
| `googleVerification` | Google Search Console verification meta tag value. Optional. Takes precedence over the `PUBLIC_GOOGLE_SITE_VERIFICATION` environment variable. |
</ResponsiveTable>
### `posts` options
<ResponsiveTable>
| Option | Description |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `perPage` | Number of posts shown per page on paginated listing pages. Defaults to `4`. |
| `perIndex` | Number of posts shown in the Recent section on the home page. Defaults to `4`. |
| `scheduledPostMargin` | Posts with a future `pubDatetime` within this window (in ms) are treated as published. Defaults to 15 minutes (`15 * 60 * 1000`). |
</ResponsiveTable>
### `features` options
<ResponsiveTable>
| Option | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `lightAndDarkMode` | Enable or disable the light/dark mode toggle. Defaults to `true`. |
| `dynamicOgImage` | Generate a dynamic OG image per post when no `ogImage` is specified in frontmatter. Defaults to `true`. See the [trade-off](https://astro-paper.pages.dev/posts/dynamic-og-image-generation-in-astropaper-blog-posts/#trade-off) for details. |
| `showArchives` | Show the `/archives` page and its header link. Defaults to `true`. |
| `showBackButton` | Show the "Go back" button on post pages. Defaults to `true`. |
| `editPost` | An "Edit page" link shown under post titles. Set `enabled: true` and provide the base `url` for your repository's edit URL. Per-post override via `hideEditPost` frontmatter. |
| `search` | Search provider. `"pagefind"` is the default. Set to `false` to disable search entirely. |
</ResponsiveTable>
## Update layout width
The default `max-width` for the entire blog is `768px` (`max-w-3xl`). If you'd like to change it, update the `max-w-app` utility in `src/styles/global.css`:
```css file="src/styles/global.css"
@utility max-w-app {
/* [!code --:1] */
@apply max-w-3xl;
/* [!code ++:1] */
@apply max-w-4xl xl:max-w-5xl;
}
```
You can explore more `max-width` values in the [Tailwind CSS docs](https://tailwindcss.com/docs/max-width).
## Configuring logo or title
![An arrow pointing at the website logo](https://res.cloudinary.com/noezectz/v1663911318/astro-paper/AstroPaper-logo-config_goff5l.png)
There are 3 options you can do:
### Option 1: Site title text
This is the easiest option. Update `site.title` in `astro-paper.config.ts`.
### Option 2: Astro's SVG component
You might want to use this option if you want to use an SVG logo.
- First add an SVG inside `src/assets/` directory. (e.g. `src/assets/dummy-logo.svg`)
- Then import that SVG inside `Header.astro`
```astro file="src/components/Header.astro"
---
// ...
import DummyLogo from "@/assets/dummy-logo.svg";
---
```
- Finally, replace `{config.site.title}` with imported logo.
```html
<a
href="/"
class="absolute py-1 text-left text-2xl leading-7 font-semibold whitespace-nowrap sm:static"
>
<DummyLogo class="scale-75 dark:invert" />
<!-- {config.site.title} -->
</a>
```
The best part of this approach is that you can customize your SVG styles as needed. In the example above, you can see how the SVG logo color can be inverted in dark mode.
### Option 3: Astro's Image component
If your logo is an image but not SVG, you can use Astro's Image component.
- Add your logo inside `src/assets/` directory. (e.g. `src/assets/dummy-logo.png`)
- Import `Image` and your logo in `Header.astro`
```astro file="src/components/Header.astro"
---
// ...
import { Image } from "astro:assets";
import dummyLogo from "@/assets/dummy-logo.png";
---
```
- Then, replace `{config.site.title}` with imported logo.
```html
<a
href="/"
class="absolute py-1 text-left text-2xl leading-7 font-semibold whitespace-nowrap sm:static"
>
<image src="{dummyLogo}" alt="My Blog" class="dark:invert" />
<!-- {config.site.title} -->
</a>
```
With this approach, you can still adjust your image's appearance using CSS classes. However, this might not always fit what you want. If you need to display different logo images based on light or dark mode, check how light/dark icons are handled inside the `Header.astro` component.
## Configuring social links
![An arrow pointing at social link icons](https://github.com/user-attachments/assets/8b895400-d088-442f-881b-02d2443e00cf)
Social links are configured in the `socials` array inside `astro-paper.config.ts`. Each entry requires a `name` matching an SVG filename in `src/assets/icons/socials/` and a `url`:
```ts file="astro-paper.config.ts"
export default defineAstroPaperConfig({
// ...
socials: [
{ name: "github", url: "https://github.com/satnaing/astro-paper" },
{ name: "x", url: "https://x.com/username" },
{ name: "linkedin", url: "https://www.linkedin.com/in/username/" },
{ name: "mail", url: "mailto:yourmail@gmail.com" },
],
});
```
To add a social not in the defaults, add its SVG icon to `src/assets/icons/socials/` and add an entry to the array. The `name` must match the SVG filename without the `.svg` extension.
## Configuring share links
![An arrow pointing at share link icons](https://github.com/user-attachments/assets/4f930b68-b625-45df-8c41-e076dd2b838e)
Share links are configured in the `shareLinks` array. Each entry requires a `name` (matching an SVG in `src/assets/icons/socials/`) and a base `url` to which the post URL is appended:
```ts file="astro-paper.config.ts"
export default defineAstroPaperConfig({
// ...
shareLinks: [
{ name: "whatsapp", url: "https://wa.me/?text=" },
{ name: "facebook", url: "https://www.facebook.com/sharer.php?u=" },
{ name: "x", url: "https://x.com/intent/post?url=" },
{ name: "telegram", url: "https://t.me/share/url?url=" },
{ name: "mail", url: "mailto:?subject=See%20this%20post&body=" },
],
});
```
## Configuring fonts
AstroPaper uses Astro's [fonts API](https://docs.astro.build/en/guides/fonts/) with [Google Sans Code](https://fonts.google.com/specimen/Google+Sans+Code) as the default font. This provides consistent typography across all platforms with automatic font optimizations including preloading and caching.
### Using the default font
The font is automatically configured in `astro.config.ts` and loaded in `Layout.astro`. No additional configuration is needed to use the default Google Sans Code font.
### Customizing the font
To use a different font, update three places:
1. **Update the font configuration in `astro.config.ts`:**
```ts file="astro.config.ts"
import { defineConfig, fontProviders } from "astro/config";
export default defineConfig({
// ...
fonts: [
{
name: "Your Font Name", // [!code highlight]
cssVariable: "--font-your-font", // [!code highlight]
provider: fontProviders.google(),
fallbacks: ["monospace"],
weights: [300, 400, 500, 600, 700],
styles: ["normal", "italic"],
},
],
});
```
2. **Update the Font component in `Layout.astro`:**
```astro file="src/layouts/Layout.astro"
---
import { Font } from "astro:assets";
// ...
---
<head>
<!-- ... -->
<Font
cssVariable="--font-your-font"
preload={[{ subset: "latin", weight: 400, style: "normal" }]}
/>
<!-- ... -->
</head>
```
3. **Update the CSS variable mapping in `src/styles/theme.css`:**
```css file="src/styles/theme.css"
@theme inline {
--font-app: var(--font-your-font); /* [!code highlight] */
/* ... */
}
```
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/).
## See also
- [Customizing AstroPaper theme color schemes](https://astro-paper.pages.dev/posts/customizing-astropaper-theme-color-schemes/) — change or add color schemes via `src/styles/theme.css`.
- [Adding new posts](https://astro-paper.pages.dev/posts/adding-new-posts-in-astropaper-theme/) — frontmatter reference and file conventions.
@@ -0,0 +1,206 @@
---
author: FjellOverflow
pubDatetime: 2024-07-25T11:11:53Z
modDatetime: 2025-03-12T12:28:53Z
title: How to integrate Giscus comments into AstroPaper
slug: how-to-integrate-giscus-comments
featured: false
draft: false
tags:
- astro
- blog
- docs
description: Comment function on a static blog hosted on GitHub Pages with Giscus.
---
Hosting a thin static blog on a platform like [GitHub Pages](https://docs.github.com/en/pages/getting-started-with-github-pages/creating-a-github-pages-site) has numerous advantages, but also takes away some interactivity. Fortunately, [Giscus](https://giscus.app/) exists and offers a way to embed user comments on static sites.
## Table of contents
## How _Giscus_ works
[Giscus uses the GitHub API](https://github.com/giscus/giscus?tab=readme-ov-file#how-it-works) to read and store comments made by _GitHub_ users in the `Discussions` associated with a repository.
Embed the _Giscus_ client-side script bundle on your site, configure it with the correct repository URL, and users can view and write comments (when logged into _GitHub_).
The approach is serverless, as the comments are stored on _GitHub_ and dynamically loaded from there on client side, hence perfect for a static blog, like _AstroPaper_.
## Setting up _Giscus_
_Giscus_ can be set up easily on [giscus.app](https://giscus.app/), but I will outline the process shortly still.
### Prerequisites
Prerequisites to get _Giscus_ working are
- the repository is [public](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/managing-repository-settings/setting-repository-visibility#making-a-repository-public)
- the [Giscus app](https://github.com/apps/giscus) is installed
- the [Discussions](https://docs.github.com/en/github/administering-a-repository/managing-repository-settings/enabling-or-disabling-github-discussions-for-a-repository) feature is turned on for your repository
If any of these conditions cannot be fulfilled for any reason, unfortunately, _Giscus_ cannot be integrated.
### Configuring _Giscus_
Next, configuring _Giscus_ is necessary. In most cases, the preselected defaults are suitable, and you should only modify them if you have a specific reason and know what you are doing. Don't worry too much about making the wrong choices; you can always adjust the configuration later on.
However you need to
- select the right language for the UI
- specify the _GitHub_ repository you want to connect, typically the repository containing your statically hosted _AstroPaper_ blog on _GitHub Pages_
- create and set an `Announcement` type discussion on _GitHub_ if you want to ensure nobody can create random comments directly on _GitHub_
- define the color scheme
After configuring the settings, _Giscus_ provides you with a generated `<script>` tag, which you will need in the next steps.
## Simple script tag
You should now have a script tag that looks like this:
```html
<script
src="https://giscus.app/client.js"
data-repo="[ENTER REPO HERE]"
data-repo-id="[ENTER REPO ID HERE]"
data-category="[ENTER CATEGORY NAME HERE]"
data-category-id="[ENTER CATEGORY ID HERE]"
data-mapping="pathname"
data-strict="0"
data-reactions-enabled="1"
data-emit-metadata="0"
data-input-position="bottom"
data-theme="preferred_color_scheme"
data-lang="en"
crossorigin="anonymous"
async
></script>
```
Simply add that to the source code of the site. Most likely, if you're using _AstroPaper_ and want to enable comments on posts, navigate to `PostDetails.astro` and paste it into the desired location where you want the comments to appear, perhaps underneath the `Share this post on:` buttons.
```astro file=src/layouts/PostDetails.astro
<Layout {...layoutProps}>
<main>
<ShareLinks />
<!-- [!code ++:6] -->
<script
src="https://giscus.app/client.js"
data-repo="[ENTER REPO HERE]"
data-repo-id="[ENTER REPO ID HERE]"
data-category="[ENTER CATEGORY NAME HERE]"
data-category-id="[ENTER CATEGORY ID HERE]"></script>
</main>
<Footer />
</Layout>
```
And it's done! You have successfully integrated comments in _AstroPaper_!
## React component with light/dark theme
The embedded script tag in the layout is quite static, with the _Giscus_ configuration, including `theme`, hardcoded into the layout. Given that _AstroPaper_ features a light/dark theme toggle, it would be nice for the comments to seamlessly transition between light and dark themes along with the rest of the site. To achieve this, a more sophisticated approach to embedding _Giscus_ is required.
Firstly, we are going to install the [React component](https://www.npmjs.com/package/@giscus/react) for _Giscus_:
```bash
npm i @giscus/react && npx astro add react
```
Then we create a new `Comments.tsx` React component in `src/components`:
```tsx file=src/components/Comments.tsx
import Giscus, { type Theme } from "@giscus/react";
import { GISCUS } from "@/constants";
import { useEffect, useState } from "react";
interface CommentsProps {
lightTheme?: Theme;
darkTheme?: Theme;
}
export default function Comments({
lightTheme = "light",
darkTheme = "dark",
}: CommentsProps) {
const [theme, setTheme] = useState(() => {
const currentTheme = localStorage.getItem("theme");
const browserTheme = window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
: "light";
return currentTheme || browserTheme;
});
useEffect(() => {
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
const handleChange = ({ matches }: MediaQueryListEvent) => {
setTheme(matches ? "dark" : "light");
};
mediaQuery.addEventListener("change", handleChange);
return () => mediaQuery.removeEventListener("change", handleChange);
}, []);
useEffect(() => {
const themeButton = document.querySelector("#theme-btn");
const handleClick = () => {
setTheme(prevTheme => (prevTheme === "dark" ? "light" : "dark"));
};
themeButton?.addEventListener("click", handleClick);
return () => themeButton?.removeEventListener("click", handleClick);
}, []);
return (
<div className="mt-8">
<Giscus theme={theme === "light" ? lightTheme : darkTheme} {...GISCUS} />
</div>
);
}
```
This _React_ component not only wraps the native _Giscus_ component, but also introduces additional props, namely `lightTheme` and `darkTheme`. Leveraging two event listeners, the _Giscus_ comments will align with the site's theme, dynamically switching between dark and light themes whenever the site or browser theme is changed.
We also need to define the `GISCUS` config, for which the optimal location is in `constants.ts`:
```ts file=src/constants.ts
import type { GiscusProps } from "@giscus/react";
...
export const GISCUS: GiscusProps = {
repo: "[ENTER REPO HERE]",
repoId: "[ENTER REPO ID HERE]",
category: "[ENTER CATEGORY NAME HERE]",
categoryId: "[ENTER CATEGORY ID HERE]",
mapping: "pathname",
reactionsEnabled: "0",
emitMetadata: "0",
inputPosition: "bottom",
lang: "en",
loading: "lazy",
};
```
Note that specifying a `theme` here will override the `lightTheme` and `darkTheme` props, resulting in a static theme setting, similar to the previous approach of embedding _Giscus_ with the `<script>` tag.
To complete the process, add the new Comments component to `PostDetails.astro` (replacing the `script` tag from the previous step).
```jsx file=src/layouts/PostDetails.astro
// [!code ++:1]
import Comments from "@/components/Comments";
<ShareLinks />
// [!code ++:1]
<Comments client:only="react" />
<hr class="my-6 border-dashed" />
<Footer />
```
And that's it!
@@ -0,0 +1,114 @@
---
title: How to update dependencies of AstroPaper
author: Sat Naing
pubDatetime: 2023-07-20T15:33:05.569Z
slug: how-to-update-dependencies
featured: false
draft: false
ogImage: ../../assets/images/forrest-gump-quote.png
tags:
- FAQ
description: How to update project dependencies and AstroPaper template.
---
Updating the dependencies of a project can be tedious. However, neglecting to update project dependencies is not a good idea either 😬. In this post, I will share how I usually update my projects, focusing on AstroPaper as an example. Nonetheless, these steps can be applied to other js/node projects as well.
![Forrest Gump Fake Quote](@/assets/images/forrest-gump-quote.png)
## Table of contents
## Updating Package Dependencies
There are several ways to update dependencies, and I've tried various methods to find the easiest path. One way to do it is by manually updating each package using `npm install package-name@latest`. This method is the most straightforward way of updating. However, it may not be the most efficient option.
My recommended way of updating dependencies is by using the [npm-check-updates package](https://www.npmjs.com/package/npm-check-updates). There's a good [article](https://www.freecodecamp.org/news/how-to-update-npm-dependencies/) from freeCodeCamp about that, so I won't be explaining the details of what it is and how to use that package. Instead, I'll show you my typical approach.
First, install `npm-check-updates` package globally.
```bash
npm install -g npm-check-updates
```
Before making any updates, its a good idea to check all new dependencies that can be updated.
```bash
ncu
```
Most of the time, patch dependencies can be updated without affecting the project at all. So, I usually update patch dependencies by running either `ncu -i --target patch` or `ncu -u --target patch`. The difference is that `ncu -u --target patch` will update all the patches, while `ncu -i --target patch` will give an option to toggle which package to update. Its up to you to decide which approach to take.
The next part involves updating minor dependencies. Minor package updates usually won't break the project, but it is always good to check the release notes of the respective packages. These minor updates often include some cool features that can be applied to our projects.
```bash
ncu -i --target minor
```
Last but not least, there might be some major package updates in the dependencies. So, check the rest of the dependency updates by running
```bash
ncu -i
```
If there are any major updates (or some updates you still have to make), the above command will output those remaining packages. If the package is a major version update, you have to be very careful since this will likely break the whole project. Therefore, please read the respective release note (or) docs very carefully and make changes accordingly.
If you run `ncu -i` and found no more packages to be updated, _**Congrats!!!**_ you have successfully updated all the dependencies in your project.
## Updating AstroPaper template
Like other open-source projects, AstroPaper is evolving with bug fixes, feature updates, and so on. So if youre someone who is using AstroPaper as a template, you might also want to update the template when theres a new release.
The thing is, you might already have updated the template according to your flavor. Therefore, I cant exactly show **"the one-size-fits-all perfect way"** to update the template to the most recent release. However, here are some tips to update the template without breaking your repo. Keep in mind that, most of the time, updating the package dependencies might be sufficient for you.
### Files and Directories to keep in mind
In most cases, the files and directories you might not want to override (as you've likely updated those files) are `src/content/blog/`, `src/config.ts`, `src/pages/about.md`, and other assets & styles like `public/` and `src/styles/base.css`.
If youre someone who only updates the bare minimum of the template, it should be okay to replace everything with the latest AstroPaper except the above files and directories. Its like pure Android OS and other vendor-specific OSes like OneUI. The less you modify the base, the less you have to update.
You can manually replace every file one by one, or you can use the magic of git to update everything. I wont show you the manual replacement process since it is very straightforward. If youre not interested in that straightforward and inefficient method, bear with me 🐻.
### Updating AstroPaper using Git
**IMPORTANT!!!**
> Only do the following if you know how to resolve merge conflicts. Otherwise, youd better replace files manually or update dependencies only.
First, add astro-paper as the remote in your project.
```bash
git remote add astro-paper https://github.com/satnaing/astro-paper.git
```
Checkout to a new branch in order to update the template. If you know what youre doing and youre confident with your git skill, you can omit this step.
```bash
git checkout -b build/update-astro-paper
```
Then, pull the changes from astro-paper by running
```bash
git pull astro-paper main
```
If you face `fatal: refusing to merge unrelated histories` error, you can resolve that by running the following command
```bash
git pull astro-paper main --allow-unrelated-histories
```
After running the above command, youre likely to encounter conflicts in your project. You'll need to resolve these conflicts manually and make the necessary adjustments according to your needs.
After resolving the conflicts, test your blog thoroughly to ensure everything is working as expected. Check your articles, components, and any customizations you made.
Once you're satisfied with the result, it's time to merge the update branch into your main branch (only if you are updating the template in another branch). Congratulations! You've successfully updated your template to the latest version. Your blog is now up-to-date and ready to shine! 🎉
## Conclusion
In this article, I've shared some of my insights and processes for updating dependencies and the AstroPaper template. I genuinely hope this article proves valuable and assists you in managing your projects more efficiently.
If you have any alternative or improved approaches for updating dependencies/AstroPaper, I would love to hear from you. Thus, don't hesitate to start a discussion in the repository, email me, or open an issue. Your input and ideas are highly appreciated!
Please understand that my schedule is quite busy these days, and I may not be able to respond quickly. However, I promise to get back to you as soon as possible. 😬
Thank you for taking the time to read this article, and I wish you all the best with your projects!
@@ -0,0 +1,189 @@
---
author: Simon Smale
pubDatetime: 2024-01-03T20:40:08Z
modDatetime: 2024-01-08T18:59:05Z
title: How to use Git Hooks to set Created and Modified Dates
featured: false
draft: false
tags:
- docs
- FAQ
canonicalURL: https://smale.codes/posts/setting-dates-via-git-hooks/
description: How to use Git Hooks to set your Created and Modified Dates on AstroPaper
---
In this post I will explain how to use the pre-commit Git hook to automate the input of the created (`pubDatetime`) and modified (`modDatetime`) in the AstroPaper blog theme frontmatter
## Table of contents
## Have them Everywhere
[Git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) are great for automating tasks like [adding](https://gist.github.com/SSmale/3b380e5bbed3233159fb7031451726ea) or [checking](https://itnext.io/using-git-hooks-to-enforce-branch-naming-policy-ffd81fa01e5e) the branch name to your commit messages or [stopping you committing plain text secrets](https://gist.github.com/SSmale/367deee757a9b2e119d241e120249000). Their biggest flaw is that client-side hooks are per machine.
You can get around this by having a `hooks` directory and manually copy them to the `.git/hooks` directory or set up a symlink, but this all requires you to remember to set it up, and that is not something I am good at doing.
As this project uses npm, we are able to make use of a package called [Husky](https://typicode.github.io/husky/) (this is already installed in AstroPaper) to automatically install the hooks for us.
> Update! In AstroPaper [v4.3.0](https://github.com/satnaing/astro-paper/releases/tag/v4.3.0), the pre-commit hook has been removed in favor of GitHub Actions. However, you can easily [install Husky](https://typicode.github.io/husky/get-started.html) yourself.
## The Hook
As we want this hook to run as we commit the code to update the dates and then have that as part of our change we are going to use the `pre-commit` hook. This has already been set up by this AstroPaper project, but if it hadn't, you would run `npx husky add .husky/pre-commit 'echo "This is our new pre-commit hook"'`.
Navigating to the `hooks/pre-commit` file, we are going to add one or both of the following snippets.
### Updating the modified date when a file is edited
---
UPDATE:
This section has been updated with a new version of the hook that is smarter. It will now not increment the `modDatetime` until the post is published. On the first publish, set the draft status to `first` and watch the magic happen.
---
```shell
# Modified files, update the modDatetime
git diff --cached --name-status |
grep -i '^M.*\.md$' |
while read _ file; do
filecontent=$(cat "$file")
frontmatter=$(echo "$filecontent" | awk -v RS='---' 'NR==2{print}')
draft=$(echo "$frontmatter" | awk '/^draft: /{print $2}')
if [ "$draft" = "false" ]; then
echo "$file modDateTime updated"
cat $file | sed "/---.*/,/---.*/s/^modDatetime:.*$/modDatetime: $(date -u "+%Y-%m-%dT%H:%M:%SZ")/" > tmp
mv tmp $file
git add $file
fi
if [ "$draft" = "first" ]; then
echo "First release of $file, draft set to false and modDateTime removed"
cat $file | sed "/---.*/,/---.*/s/^modDatetime:.*$/modDatetime:/" | sed "/---.*/,/---.*/s/^draft:.*$/draft: false/" > tmp
mv tmp $file
git add $file
fi
done
```
`git diff --cached --name-status` gets the files from git that have been staged for committing. The output looks like:
```shell
A src/content/blog/setting-dates-via-git-hooks.md
```
The letter at the start denotes what action has been taken, in the above example the file has been added. Modified files have `M`
We pipe that output into the grep command where we are looking at each line to find that have been modified. The line needs to start with `M` (`^(M)`), have any number of characters after that (`.*`) and end with the `.md` file extension (`.(md)$`).This is going to filter out the lines that are not modified markdown files `egrep -i "^(M).*\.(md)$"`.
---
#### Improvement - More Explicit
This could be added to only look for files that we markdown files in the `blog` directory, as these are the only ones that will have the right frontmatter
---
The regex will capture the two parts, the letter and the file path. We are going to pipe this list into a while loop to iterate over the matching lines and assign the letter to `a` and the path to `b`. We are going to ignore `a` for now.
To know the draft status of the file, we need its frontmatter. In the following code we are using `cat` to get the content of the file, then using `awk` to split the file on the frontmatter separator (`---`) and taking the second block (the fonmtmatter, the bit between the `---`). From here we are using `awk` again to find the draft key and print is value.
```shell
filecontent=$(cat "$file")
frontmatter=$(echo "$filecontent" | awk -v RS='---' 'NR==2{print}')
draft=$(echo "$frontmatter" | awk '/^draft: /{print $2}')
```
Now we have the value for `draft` we are going to do 1 of 3 things, set the modDatetime to now (when draft is false `if [ "$draft" = "false" ]; then`), clear the modDatetime and set draft to false (when draft is set to first `if [ "$draft" = "first" ]; then`), or nothing (in any other case).
The next part with the sed command is a bit magical to me as I don't often use it, it was copied from [another blog post on doing something similar](https://mademistakes.com/notes/adding-last-modified-timestamps-with-git/). In essence, it is looking inside the frontmatter tags (`---`) of the file to find the `pubDatetime:` key, getting the full line and replacing it with the `pubDatetime: $(date -u "+%Y-%m-%dT%H:%M:%SZ")/"` same key again and the current datetime formatted correctly.
This replacement is in the context of the whole file so we put that into a temporary file (`> tmp`), then we move (`mv`) the new file into the location of the old file, overwriting it. This is then added to git ready to be committed as if we made the change ourselves.
---
#### NOTE
For the `sed` to work the frontmatter needs to already have the `modDatetime` key in the frontmatter. There are some other changes you will need to make for the app to build with a blank date, see [further down](#empty-moddatetime-changes)
---
### Adding the Date for new files
Adding the date for a new file is the same process as above, but this time we are looking for lines that have been added (`A`) and we are going to replace the `pubDatetime` value.
```shell
# New files, add/update the pubDatetime
git diff --cached --name-status | egrep -i "^(A).*\.(md)$" | while read a b; do
cat $b | sed "/---.*/,/---.*/s/^pubDatetime:.*$/pubDatetime: $(date -u "+%Y-%m-%dT%H:%M:%SZ")/" > tmp
mv tmp $b
git add $b
done
```
---
#### Improvement - Only Loop Once
We could use the `a` variable to switch inside the loop and either update the `modDatetime` or add the `pubDatetime` in one loop.
---
## Populating the frontmatter
If your IDE supports snippets then there is the option to create a custom snippet to populate the frontmatter.[In AstroPaper v4 will come with one for VSCode by default.](https://github.com/satnaing/astro-paper/pull/206)
<video autoplay muted="muted" controls plays-inline="true" class="border border-skin-line">
<source src="https://github.com/satnaing/astro-paper/assets/17761689/e13babbc-2d78-405d-8758-ca31915e41b0" type="video/mp4">
</video>
## Empty `modDatetime` changes
To allow Astro to compile the markdown and do its thing, it needs to know what is expected in the frontmatter. It does this via the config in `src/content/config.ts`
To allow the key to be there with no value we need to edit line 10 to add the `.nullable()` function.
```ts
const blog = defineCollection({
type: "content",
schema: ({ image }) =>
z.object({
author: z.string().default(SITE.author),
pubDatetime: z.date(),
modDatetime: z.date().optional(), // [!code --]
modDatetime: z.date().optional().nullable(), // [!code ++]
title: z.string(),
featured: z.boolean().optional(),
draft: z.boolean().optional(),
tags: z.array(z.string()).default(["others"]),
ogImage: image().or(z.string()).optional(),
description: z.string(),
canonicalURL: z.string().optional(),
readingTime: z.string().optional(),
}),
});
```
To stop the IDE complaining in the blog engine files I have also done the following:
1. added `| null` to line 15 in `src/layouts/Layout.astro` so that it looks like
```typescript
export interface Props {
title?: string;
author?: string;
description?: string;
ogImage?: string;
canonicalURL?: string;
pubDatetime?: Date;
modDatetime?: Date | null;
}
```
2. added `| null` to line 5 in `src/components/Datetime.tsx` so that it looks like
```typescript
interface DatetimesProps {
pubDatetime: string | Date;
modDatetime: string | Date | undefined | null;
}
```