Merge branch 'astro-v2'

This commit is contained in:
satnaing
2023-01-31 20:22:09 +06:30
48 changed files with 1656 additions and 3077 deletions
+3
View File
@@ -18,3 +18,6 @@ pnpm-debug.log*
# macOS-specific files
.DS_Store
# ignore .astro directory
.astro
+2 -1
View File
@@ -9,4 +9,5 @@
!astro.config.mjs
!package.json
!.prettierrc
!.eslintrc.json
!.eslintrc.json
!README.md
+23 -18
View File
@@ -14,6 +14,7 @@ This theme is self-documented \_ which means articles/posts in this theme can al
## 🔥 Features
- [x] type-safe markdown
- [x] super fast performance
- [x] accessible (Keyboard/VoiceOver)
- [x] responsive (mobile ~ desktops)
@@ -54,8 +55,11 @@ Inside of AstroPaper, you'll see the following folders and files:
│ ├── assets/
│ │ └── socialIcons.ts
│ ├── components/
│ ├── contents/
│ │ └── some-blog-posts.md
│ ├── content/
│ │ | blog/
│ │ | └── some-blog-posts.md
│ │ └── _schemas.ts
│ │ └── config.ts
│ ├── layouts/
│ └── pages/
│ └── styles/
@@ -69,16 +73,18 @@ Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page
Any static assets, like images, can be placed in the `public/` directory.
All blog posts are stored in `src/contents/` directory.
All blog posts are stored in `src/content/blog` directory.
## 📖 Documentation
Documentation can be read in two formats\_ _markdown_ & _blog post_.
- Configuration - [markdown](src/contents/how-to-configure-astropaper-theme.md) | [blog post](https://astro-paper.pages.dev/posts/how-to-configure-astropaper-theme/)
- Add Posts - [markdown](src/contents/adding-new-post.md) | [blog post](https://astro-paper.pages.dev/posts/adding-new-posts-in-astropaper-theme/)
- Customize Color Schemes - [markdown](src/contents/customizing-astropaper-theme-color-schemes.md) | [blog post](https://astro-paper.pages.dev/posts/customizing-astropaper-theme-color-schemes/)
- Predefined Color Schemes - [markdown](src/contents/predefined-color-schemes.md) | [blog post](https://astro-paper.pages.dev/posts/predefined-color-schemes/)
- Configuration - [markdown](src/content/blog/how-to-configure-astropaper-theme.md) | [blog post](https://astro-paper.pages.dev/posts/how-to-configure-astropaper-theme/)
- Add Posts - [markdown](src/content/blog/adding-new-post.md) | [blog post](https://astro-paper.pages.dev/posts/adding-new-posts-in-astropaper-theme/)
- Customize Color Schemes - [markdown](src/content/blog/customizing-astropaper-theme-color-schemes.md) | [blog post](https://astro-paper.pages.dev/posts/customizing-astropaper-theme-color-schemes/)
- Predefined Color Schemes - [markdown](src/content/blog/predefined-color-schemes.md) | [blog post](https://astro-paper.pages.dev/posts/predefined-color-schemes/)
> For AstroPaper v1, check out [this branch](https://github.com/satnaing/astro-paper/tree/astro-paper-v1) and this [live URL](https://astro-paper-v1.astro-paper.pages.dev/)
## 💻 Tech Stack
@@ -121,17 +127,16 @@ PUBLIC_GOOGLE_SITE_VERIFICATION=your-google-site-verification-value
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :--------------------- | :------------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run format:check` | Check code format with Prettier |
| `npm run format` | Format codes with Prettier |
| `npm run cz` | Commit code changes with commitizen |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro preview` |
| `npm run astro --help` | Get help using the Astro CLI |
| Command | Action |
| :--------------------- | :------------------------------------------------------------------------------------------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run format:check` | Check code format with Prettier |
| `npm run format` | Format codes with Prettier |
| `npm run sync` | Generates TypeScript types for all Astro modules. [Learn more](https://docs.astro.build/en/reference/cli-reference/#astro-sync). |
| `npm run cz` | Commit code changes with commitizen |
## ✨ Feedback & Suggestions
+1171 -2721
View File
File diff suppressed because it is too large Load Diff
+8 -6
View File
@@ -7,6 +7,7 @@
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"sync": "astro sync",
"astro": "astro",
"format:check": "prettier --check .",
"format": "prettier --write .",
@@ -14,19 +15,19 @@
"prepare": "husky install"
},
"dependencies": {
"@astrojs/rss": "^1.0.3",
"astro": "^1.9.2",
"@astrojs/rss": "^2.1.0",
"astro": "^2.0.4",
"fuse.js": "^6.6.2",
"github-slugger": "^2.0.0",
"remark-collapse": "^0.1.2",
"remark-toc": "^8.0.1",
"satori": "^0.1.1",
"satori": "^0.1.2",
"tailwindcss": "^3.2.4"
},
"devDependencies": {
"@astrojs/react": "^1.2.2",
"@astrojs/sitemap": "^1.0.0",
"@astrojs/tailwind": "^2.1.3",
"@astrojs/react": "^2.0.2",
"@astrojs/sitemap": "^1.0.1",
"@astrojs/tailwind": "^3.0.1",
"@tailwindcss/typography": "^0.5.9",
"@types/github-slugger": "^1.3.0",
"@types/react": "^18.0.27",
@@ -35,6 +36,7 @@
"husky": "^8.0.3",
"lint-staged": "^13.1.0",
"prettier": "^2.8.3",
"prettier-plugin-tailwindcss": "^0.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
+1 -1
View File
@@ -1,4 +1,4 @@
import type { SocialIcons } from "src/types";
import type { SocialIcons } from "@types";
const socialIcons: SocialIcons = {
Github: `<svg
+10 -4
View File
@@ -22,7 +22,11 @@ breadcrumbList[0] === "posts" &&
breadcrumbList.map((breadcrumb, index) =>
index + 1 === breadcrumbList.length ? (
<li>
<span class={`${index > 0 ? "lowercase" : ""}`} aria-current="page">
<span
class={`${index > 0 ? "lowercase" : "capitalize"}`}
aria-current="page"
>
{/* make the last part lowercase in Home > Tags > some-tag */}
{breadcrumb}
</span>
</li>
@@ -39,14 +43,16 @@ breadcrumbList[0] === "posts" &&
<style>
.breadcrumb {
@apply max-w-3xl mx-auto px-4 w-full mt-8 mb-1;
@apply mx-auto mt-8 mb-1 w-full max-w-3xl px-4;
}
.breadcrumb ul li {
@apply inline;
}
.breadcrumb ul li a,
.breadcrumb ul li a {
@apply capitalize opacity-70;
}
.breadcrumb ul li span {
@apply opacity-70 capitalize;
@apply opacity-70;
}
.breadcrumb ul li:not(:last-child) a {
@apply hover:opacity-100;
+17 -16
View File
@@ -1,31 +1,32 @@
import Datetime from "./Datetime";
import type { Frontmatter } from "src/types";
import type { BlogFrontmatter } from "@content/_schemas";
export interface Props {
href?: string;
post: Frontmatter;
frontmatter: BlogFrontmatter;
secHeading?: boolean;
}
const styles = {
cardContainer: "my-6",
titleLink:
"text-skin-accent font-medium text-lg underline-offset-4 decoration-dashed focus-visible:no-underline focus-visible:underline-offset-0 inline-block",
titleHeading: "font-medium text-lg decoration-dashed hover:underline",
};
export default function Card({ href, post, secHeading = true }: Props) {
export default function Card({ href, frontmatter, secHeading = true }: Props) {
const { title, pubDatetime, description } = frontmatter;
return (
<li className={styles.cardContainer}>
<a href={href} className={styles.titleLink}>
<li className="my-6">
<a
href={href}
className="inline-block text-lg font-medium text-skin-accent decoration-dashed underline-offset-4 focus-visible:no-underline focus-visible:underline-offset-0"
>
{secHeading ? (
<h2 className={styles.titleHeading}>{post.title}</h2>
<h2 className="text-lg font-medium decoration-dashed hover:underline">
{title}
</h2>
) : (
<h3 className={styles.titleHeading}>{post.title}</h3>
<h3 className="text-lg font-medium decoration-dashed hover:underline">
{title}
</h3>
)}
</a>
<Datetime datetime={post.datetime} />
<p>{post.description}</p>
<Datetime datetime={pubDatetime} />
<p>{description}</p>
</li>
);
}
+4 -4
View File
@@ -1,17 +1,17 @@
export interface Props {
datetime: string;
datetime: string | Date;
size?: "sm" | "lg";
className?: string;
}
export default function Datetime({ datetime, size = "sm", className }: Props) {
return (
<div className={`opacity-80 flex items-center space-x-2 ${className}`}>
<div className={`flex items-center space-x-2 opacity-80 ${className}`}>
<svg
xmlns="http://www.w3.org/2000/svg"
className={`${
size === "sm" ? "scale-90" : "scale-100"
} w-6 h-6 inline-block fill-skin-base`}
} inline-block h-6 w-6 fill-skin-base`}
aria-hidden="true"
>
<path d="M7 11h2v2H7zm0 4h2v2H7zm4-4h2v2h-2zm0 4h2v2h-2zm4-4h2v2h-2zm0 4h2v2h-2z"></path>
@@ -25,7 +25,7 @@ export default function Datetime({ datetime, size = "sm", className }: Props) {
);
}
const FormattedDatetime = ({ datetime }: { datetime: string }) => {
const FormattedDatetime = ({ datetime }: { datetime: string | Date }) => {
const myDatetime = new Date(datetime);
const date = myDatetime.toLocaleDateString([], {
+3 -3
View File
@@ -28,16 +28,16 @@ const { noMarginTop = false } = Astro.props;
@apply w-full;
}
.footer-wrapper {
@apply py-6 sm:py-4 flex flex-col sm:flex-row-reverse justify-between items-center;
@apply flex flex-col items-center justify-between py-6 sm:flex-row-reverse sm:py-4;
}
.link-button {
@apply p-2 my-1 hover:rotate-6;
@apply my-1 p-2 hover:rotate-6;
}
.link-button svg {
@apply scale-125;
}
.copyright-wrapper {
@apply my-2 flex flex-col sm:flex-row items-center whitespace-nowrap;
@apply my-2 flex flex-col items-center whitespace-nowrap sm:flex-row;
}
.separator {
@apply hidden sm:inline;
+15 -15
View File
@@ -1,5 +1,5 @@
---
import { LOGO_IMAGE, SITE } from "src/config";
import { LOGO_IMAGE, SITE } from "@config";
import Hr from "./Hr.astro";
import LinkButton from "./LinkButton.astro";
@@ -107,35 +107,35 @@ const { activeNav } = Astro.props;
<style>
#skip-to-content {
@apply bg-skin-accent text-skin-inverted py-2 px-3 absolute -top-full focus:top-4 left-16 z-50 transition-all;
@apply absolute -top-full left-16 z-50 bg-skin-accent py-2 px-3 text-skin-inverted transition-all focus:top-4;
}
.nav-container {
@apply max-w-3xl mx-auto flex flex-col sm:flex-row justify-between items-center;
@apply mx-auto flex max-w-3xl flex-col items-center justify-between sm:flex-row;
}
.top-nav-wrap {
@apply relative w-full p-4 sm:py-8 flex justify-between items-start sm:items-center;
@apply relative flex w-full items-start justify-between p-4 sm:items-center sm:py-8;
}
.logo {
@apply absolute sm:static font-semibold text-xl sm:text-2xl py-1;
@apply absolute py-1 text-xl font-semibold sm:static sm:text-2xl;
}
.hamburger-menu {
@apply self-end p-2 sm:hidden;
}
.hamburger-menu svg {
@apply fill-skin-base w-6 h-6 scale-125;
@apply h-6 w-6 scale-125 fill-skin-base;
}
nav {
@apply w-full sm:py-0 sm:ml-2 flex flex-col items-center sm:justify-end sm:flex-row sm:space-x-4 bg-skin-fill;
@apply flex w-full flex-col items-center bg-skin-fill sm:ml-2 sm:flex-row sm:justify-end sm:space-x-4 sm:py-0;
}
nav ul {
@apply mt-4 w-44 grid grid-rows-4 grid-cols-2 gap-y-2 gap-x-2 sm:gap-y-0 sm:mt-0 sm:ml-0 sm:w-auto sm:gap-x-5;
@apply mt-4 grid w-44 grid-cols-2 grid-rows-4 gap-y-2 gap-x-2 sm:mt-0 sm:ml-0 sm:w-auto sm:gap-y-0 sm:gap-x-5;
}
nav ul li {
@apply col-span-2 flex justify-center items-center;
@apply col-span-2 flex items-center justify-center;
}
nav ul li a {
@apply px-4 py-3 w-full text-center sm:px-2 sm:py-1 sm:my-0 font-medium hover:text-skin-accent;
@apply w-full px-4 py-3 text-center font-medium hover:text-skin-accent sm:my-0 sm:px-2 sm:py-1;
}
nav ul li:nth-child(4) a {
@apply w-auto;
@@ -145,7 +145,7 @@ const { activeNav } = Astro.props;
@apply col-span-1;
}
nav a.active {
@apply underline underline-offset-4 decoration-wavy decoration-2;
@apply underline decoration-wavy decoration-2 underline-offset-4;
}
nav a.active svg {
@apply fill-skin-accent;
@@ -155,20 +155,20 @@ const { activeNav } = Astro.props;
@apply p-1;
}
nav button svg {
@apply w-6 h-6 fill-skin-base hover:fill-skin-accent;
@apply h-6 w-6 fill-skin-base hover:fill-skin-accent;
}
#theme-btn {
@apply p-3 sm:p-1;
}
#theme-btn svg {
@apply hover:rotate-12 scale-125 sm:scale-100;
@apply scale-125 hover:rotate-12 sm:scale-100;
}
.icon-container {
@apply w-6 h-5 flex-col justify-between items-end;
@apply h-5 w-6 flex-col items-end justify-between;
}
.icon-container div {
@apply bg-skin-inverted h-0.5 transition-all;
@apply h-0.5 bg-skin-inverted transition-all;
}
#first-line {
@apply w-full;
+12 -13
View File
@@ -2,13 +2,12 @@ import Fuse from "fuse.js";
import { useEffect, useRef, useState } from "react";
import Card from "@components/Card";
import slugify from "@utils/slugify";
import type { Frontmatter } from "src/types";
import type { BlogFrontmatter } from "@content/_schemas";
type SearchItem = {
export type SearchItem = {
title: string;
description: string;
headings: string[];
frontmatter: Frontmatter;
data: BlogFrontmatter;
};
interface Props {
@@ -32,7 +31,7 @@ export default function SearchBar({ searchList }: Props) {
};
const fuse = new Fuse(searchList, {
keys: ["title", "description", "headings"],
keys: ["title", "description"],
includeMatches: true,
minMatchCharLength: 2,
threshold: 0.5,
@@ -79,14 +78,14 @@ export default function SearchBar({ searchList }: Props) {
</svg>
</span>
<input
className="placeholder:italic placeholder:text-opacity-75 py-3 pl-10 pr-3
block bg-skin-fill w-full rounded
border border-skin-fill border-opacity-40
focus:outline-none focus:border-skin-accent"
className="block w-full rounded border border-skin-fill
border-opacity-40 bg-skin-fill py-3 pl-10
pr-3 placeholder:italic placeholder:text-opacity-75
focus:border-skin-accent focus:outline-none"
placeholder="Search for anything..."
type="text"
name="search"
defaultValue={inputVal}
value={inputVal}
onChange={handleChange}
autoComplete="off"
autoFocus
@@ -108,9 +107,9 @@ export default function SearchBar({ searchList }: Props) {
{searchResults &&
searchResults.map(({ item, refIndex }) => (
<Card
post={item.frontmatter}
href={`/posts/${slugify(item.frontmatter)}`}
key={`${refIndex}-${slugify(item.frontmatter)}`}
href={`/posts/${slugify(item.data)}`}
frontmatter={item.data}
key={`${refIndex}-${slugify(item.data)}`}
/>
))}
</ul>
+2 -2
View File
@@ -1,5 +1,5 @@
---
import { SOCIALS } from "src/config";
import { SOCIALS } from "@config";
import LinkButton from "./LinkButton.astro";
import socialIcons from "@assets/socialIcons";
@@ -29,6 +29,6 @@ const { centered = false } = Astro.props;
@apply flex-wrap justify-center gap-1;
}
.link-button {
@apply p-2 sm:p-1 hover:rotate-6;
@apply p-2 hover:rotate-6 sm:p-1;
}
</style>
+5 -5
View File
@@ -13,8 +13,8 @@ const { name, size = "sm" } = Astro.props;
}`}
>
<a
href={`/tags/${name}`}
class={`${size === "sm" ? "pr-1 text-sm" : "pr-2 text-lg"} group`}
href={`/tags/${name.toLowerCase()}`}
class={`${size === "sm" ? "text-sm" : "text-lg"} pr-2 group`}
>
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -23,15 +23,15 @@ const { name, size = "sm" } = Astro.props;
d="M16.018 3.815 15.232 8h-4.966l.716-3.815-1.964-.37L8.232 8H4v2h3.857l-.751 4H3v2h3.731l-.714 3.805 1.965.369L8.766 16h4.966l-.714 3.805 1.965.369.783-4.174H20v-2h-3.859l.751-4H21V8h-3.733l.716-3.815-1.965-.37zM14.106 14H9.141l.751-4h4.966l-.752 4z"
></path>
</svg>
&nbsp;<span>{name}</span>
&nbsp;<span>{name.toLowerCase()}</span>
</a>
</li>
<style>
a {
@apply underline decoration-dashed hover:text-skin-accent relative hover:-top-0.5 focus-visible:p-1;
@apply relative underline decoration-dashed hover:-top-0.5 hover:text-skin-accent focus-visible:p-1;
}
a svg {
@apply w-6 h-6 text-skin-base scale-95 opacity-80 -mr-5 group-hover:fill-skin-accent;
@apply -mr-5 h-6 w-6 scale-95 text-skin-base opacity-80 group-hover:fill-skin-accent;
}
</style>
+15
View File
@@ -0,0 +1,15 @@
import { z } from "astro:content";
export const blogSchema = z.object({
author: z.string().optional(),
pubDatetime: z.date(),
title: z.string(),
postSlug: z.string().optional(),
featured: z.boolean().optional(),
draft: z.boolean().optional(),
tags: z.array(z.string()).default(["others"]),
ogImage: z.string().optional(),
description: z.string(),
});
export type BlogFrontmatter = z.infer<typeof blogSchema>;
@@ -1,8 +1,8 @@
---
author: Sat Naing
datetime: 2022-09-23T15:22:00Z
pubDatetime: 2022-09-23T15:22:00Z
title: Adding new posts in AstroPaper theme
slug: adding-new-posts-in-astropaper-theme
postSlug: adding-new-posts-in-astropaper-theme
featured: true
draft: false
tags:
@@ -23,33 +23,48 @@ Frontmatter is the main place to store some important information about the post
Here is the list of frontmatter property for each post.
| 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. | default = SITE.desc |
| **_author_** | Author of the post. | default = SITE.author |
| **_datetime_** | Published datetime in ISO 8601 format. | |
| **_slug_** | Slug for the post. Usually the all lowercase title seperated in `-` instead of whtiespace | default = slugified title |
| **_featured_** | Whether or not display this post in featured section of home page | default = false |
| **_draft_** | Mark this post 'unpublished'. | default = false |
| **_tags_** | Related keywords for this post. Written in array yaml format. | |
| **_ogImage_** | OG image of the post. Useful for social media sharing and SEO. | default = SITE.ogImage |
| 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> |
| **_author_** | Author of the post. | default = SITE.author |
| **_postSlug_** | Slug for the post. Will automatically be slugified. | default = slugified title |
| **_featured_** | Whether or not display this post in featured section of home page | default = false |
| **_draft_** | Mark this post '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. | default = SITE.ogImage or generated SVG image |
`title` and `slug` fields in frontmatter must be specified.
Only `title`, `description` and `pubDatetime` fields in frontmatter must be specified.
Title is the title of the post and it is very important for search engine optimization (SEO).
Title and description (excerpt) are important for search engine optimization (SEO) and thus AstroPaper encourages to include these in blog posts.
`slug` is the unique identifier of the url. Thus, `slug` must be unique and different from other posts. The whitespace of `slug` needs to be separated with `-` or `_` but `-` is recommended. If slug is not specified, the slugified title of the post will be used as slug.
`slug` is the unique identifier of the url. Thus, `slug` must be unique and different from other posts. The whitespace of `slug` needs to be separated with `-` or `_` but `-` is recommended. However, even if you don't write the correct slug, AstroPaper will automatically slugify your incorrect slug. If slug is not specified, the slugified title of the post will be used as slug.
Here is the sample frontmatter for the post.
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 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(),
});
```
### Sample Frontmatter
Here is the sample frontmatter for a post.
```yaml
# src/contents/sample-post.md
---
title: The title of the post
author: your name
datetime: 2022-09-21T05:17:19Z
slug: the-title-of-the-post
pubDatetime: 2022-09-21T05:17:19Z
postSlug: the-title-of-the-post
featured: true
draft: false
tags:
@@ -101,3 +116,5 @@ My recommendation for image compression sites.
### OG Image
The default OG image will be placed if a post does not specify the OG image. Though not required, OG image related to the post should be specify in the frontmatter. The recommended size for OG image is **_1200 X 640_** px.
> Since AstroPaper v1.4.0, OG images will be generated automatically if not specified. Check out [the announcement](https://astro-paper.pages.dev/posts/dynamic-og-image-generation-in-astropaper-blog-posts/).
+91
View File
@@ -0,0 +1,91 @@
---
author: Sat Naing
pubDatetime: 2023-01-30T15:57:52.737Z
title: AstroPaper 2.0
postSlug: astro-paper-2
featured: true
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 cirtea 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
@@ -1,8 +1,8 @@
---
author: Sat Naing
datetime: 2022-09-25T15:20:35Z
pubDatetime: 2022-09-25T15:20:35Z
title: Customizing AstroPaper theme color schemes
slug: ""
postSlug: ""
featured: false
draft: false
tags:
@@ -130,3 +130,5 @@ Here is an example of changing the light color scheme.
}
}
```
> Check out some [predefined color schemes](https://astro-paper.pages.dev/posts/predefined-color-schemes/) AstroPaper has already crafted for you.
@@ -1,8 +1,8 @@
---
author: Sat Naing
datetime: 2022-12-28T04:59:04.866Z
pubDatetime: 2022-12-28T04:59:04.866Z
title: Dynamic OG image generation in AstroPaper blog posts
slug: dynamic-og-image-generation-in-astropaper-blog-posts
postSlug: dynamic-og-image-generation-in-astropaper-blog-posts
featured: false
draft: false
tags:
@@ -1,8 +1,8 @@
---
author: Sat Naing
datetime: 2022-09-23T04:58:53Z
pubDatetime: 2022-09-23T04:58:53Z
title: How to configure AstroPaper theme
slug: how-to-configure-astropaper-theme
postSlug: how-to-configure-astropaper-theme
featured: true
draft: false
tags:
@@ -37,15 +37,15 @@ export const SITE = {
Here are SITE configuration options
| Options | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `website` | Your deployed website url |
| `author` | Your name |
| `desc` | Your site description. Useful for SEO and social media sharing. |
| `title` | Your site name |
| `ogImage` | Your default OG image for the site. Useful for social media sharing. OG image can be updated under `/public` directory. |
| `lightAndDarkMode` | Enable or disable `light & dark mode` for the website. If disabled, primary color scheme will be used. This option is enabled by default. |
| `postPerPage` | You can specify how many posts will be displayed in each posts page. (eg: if you set SITE.postPerPage to 3, each page will only show 3 posts per page) |
| Options | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `website` | Your deployed website url |
| `author` | Your name |
| `desc` | Your site description. Useful for SEO and social media sharing. |
| `title` | Your site name |
| `ogImage` | Your default OG image for the site. Useful for social media sharing. OG images can be an external image url or they can be placed under `/public` directory. |
| `lightAndDarkMode` | Enable or disable `light & dark mode` for the website. If disabled, primary color scheme will be used. This option is enabled by default. |
| `postPerPage` | You can specify how many posts will be displayed in each posts page. (eg: if you set SITE.postPerPage to 3, each page will only show 3 posts per page) |
## Configuring logo or title
@@ -1,9 +1,9 @@
---
title: How to connect AstroPaper blog with Forestry CMS
author: Sat Naing
datetime: 2022-09-21T05:17:19Z
slug: how-to-connect-astro-paper-blog-with-forestry-cms
featured: true
pubDatetime: 2022-09-21T05:17:19Z
postSlug: how-to-connect-astro-paper-blog-with-forestry-cms
featured: false
draft: false
tags:
- docs
@@ -15,6 +15,8 @@ description:
Headless CMS.
---
> Important!!! Forestry is going to be discontinued on April 22nd, 2023. You can [read their announcement](https://forestry.io/blog/forestry.io-end-of-life/) for more info.
In this article, I will explain step by step process of connecting AstroPaper theme with the Forestry headless CMS. So, let's get started 🎉
## Table of contents
@@ -1,8 +1,8 @@
---
title: How Do I Develop My Portfolio Website & Blog
author: Sat Naing
datetime: 2022-03-25T16:55:12.000+00:00
slug: how-do-i-develop-my-portfolio-and-blog
pubDatetime: 2022-03-25T16:55:12.000+00:00
postSlug: how-do-i-develop-my-portfolio-and-blog
featured: false
draft: false
tags:
@@ -10,6 +10,7 @@ tags:
- TailwindCSS
- HeadlessCMS
- Blog
ogImage: ""
description:
"EXAMPLE POST: My experience about developing my first portfolio website and a blog
using NextJS and a headless CMS."
@@ -1,8 +1,8 @@
---
author: Sat Naing
datetime: 2022-09-26T12:13:24Z
pubDatetime: 2022-09-26T12:13:24Z
title: Predefined color schemes
slug: predefined-color-schemes
postSlug: predefined-color-schemes
featured: false
draft: false
tags:
@@ -75,6 +75,21 @@ html[data-theme="light"] {
Light color scheme has to be defined as `html[data-theme="dark"]`.
### AstroPaper 1 original Dark Theme
![AstroPaper 1 default dark theme](https://user-images.githubusercontent.com/53733092/215769153-13b0ad8d-5ba2-44b1-af06-e5ae61293f62.png)
```css
html[data-theme="dark"] {
--color-fill: 47, 55, 65;
--color-text-base: 230, 230, 230;
--color-accent: 26, 217, 217;
--color-card: 63, 75, 90;
--color-card-muted: 89, 107, 129;
--color-border: 59, 70, 85;
}
```
### Deep Oyster
![deep-oyster-color-scheme](https://user-images.githubusercontent.com/53733092/192314524-45ec5904-3d8f-450a-9edf-1e32c5e11d6c.png)
@@ -104,3 +119,34 @@ html[data-theme="dark"] {
--color-border: 134, 67, 107;
}
```
### Astro dark (High Contrast)
![astro-dark-color-scheme](https://user-images.githubusercontent.com/53733092/215680520-59427bb0-f4cb-48c0-bccc-f182a428d72d.svg)
```css
html[data-theme="dark"] {
--color-fill: 16, 23, 42; /* higher contrast bgColor */
--color-fill: 33, 39, 55;
--color-text-base: 234, 237, 243;
--color-accent: 255, 107, 1;
--color-card: 27, 39, 70;
--color-card-muted: 138, 51, 2;
--color-border: 171, 75, 8;
}
```
### Astro dark (New default dark theme in AstroPaper 2)
![new dark color scheme - low contrast](https://user-images.githubusercontent.com/53733092/215772856-d5b7ae35-ddaa-4ed6-b0bf-3fa5dbcf834c.png)
```css
html[data-theme="dark"] {
--color-fill: 33, 39, 55; /* lower contrast bgColor */
--color-text-base: 234, 237, 243;
--color-accent: 255, 107, 1;
--color-card: 52, 63, 96;
--color-card-muted: 138, 51, 2;
--color-border: 171, 75, 8;
}
```
@@ -1,8 +1,8 @@
---
title: My recent article 2
author: Sat Naing
datetime: 2022-06-06T04:06:31Z
slug: my-recent-2
pubDatetime: 2022-06-06T04:06:31Z
postSlug: my-recent-2
featured: false
draft: true
tags:
@@ -1,12 +1,13 @@
---
title: Tailwind Typography Plugin
author: Sat Naing
datetime: 2022-07-05T02:05:51Z
pubDatetime: 2022-07-05T02:05:51Z
featured: false
draft: false
tags:
- TypeScript
- Astro
ogImage: ""
description: "EXAMPLE POST: About Tailwind Typography Plugin and how you can use it effectively."
---
@@ -1,8 +1,8 @@
---
title: How Do I Develop My Terminal Portfolio Website with React
author: Sat Naing
datetime: 2022-06-09T03:42:51Z
slug: how-do-i-develop-my-terminal-portfolio-website-with-react
pubDatetime: 2022-06-09T03:42:51Z
postSlug: how-do-i-develop-my-terminal-portfolio-website-with-react
featured: false
draft: false
tags:
+8
View File
@@ -0,0 +1,8 @@
import { defineCollection } from "astro:content";
import { blogSchema } from "./_schemas";
const blog = defineCollection({
schema: blogSchema,
});
export const collections = { blog };
+3 -3
View File
@@ -1,5 +1,5 @@
---
import { SITE } from "src/config";
import { SITE } from "@config";
import Breadcrumbs from "@components/Breadcrumbs.astro";
import Footer from "@components/Footer.astro";
import Header from "@components/Header.astro";
@@ -19,8 +19,8 @@ const { frontmatter } = Astro.props;
<Header activeNav="about" />
<Breadcrumbs />
<main id="main-content">
<section id="about" class="mb-28 prose max-w-3xl prose-img:border-0">
<h1 class="text-2xl sm:text-3xl tracking-wider">{frontmatter.title}</h1>
<section id="about" class="prose mb-28 max-w-3xl prose-img:border-0">
<h1 class="text-2xl tracking-wider sm:text-3xl">{frontmatter.title}</h1>
<slot />
</section>
</main>
+1 -1
View File
@@ -1,5 +1,5 @@
---
import { SITE } from "src/config";
import { SITE } from "@config";
import "../styles/base.css";
const googleSiteVerification = import.meta.env.PUBLIC_GOOGLE_SITE_VERIFICATION;
+2 -2
View File
@@ -18,10 +18,10 @@ const { pageTitle, pageDesc } = Astro.props;
<style>
#main-content {
@apply max-w-3xl mx-auto px-4 pb-12 w-full;
@apply mx-auto w-full max-w-3xl px-4 pb-12;
}
#main-content h1 {
@apply font-semibold text-2xl sm:text-3xl;
@apply text-2xl font-semibold sm:text-3xl;
}
#main-content p {
@apply mt-2 mb-6 italic;
+12 -11
View File
@@ -4,16 +4,17 @@ import Header from "@components/Header.astro";
import Footer from "@components/Footer.astro";
import Tag from "@components/Tag.astro";
import Datetime from "@components/Datetime";
import type { MarkdownInstance } from "astro";
import type { Frontmatter } from "src/types";
import type { CollectionEntry } from "astro:content";
export interface Props {
post: MarkdownInstance<Frontmatter>;
post: CollectionEntry<"blog">;
}
const { frontmatter, Content } = Astro.props.post;
const { post } = Astro.props;
const { title, author, description, ogImage, datetime, tags } = frontmatter;
const { title, author, description, ogImage, pubDatetime, tags } = post.data;
const { Content } = await post.render();
const ogUrl = new URL(ogImage ? ogImage : `${title}.svg`, Astro.url.origin)
.href;
@@ -21,9 +22,9 @@ const ogUrl = new URL(ogImage ? ogImage : `${title}.svg`, Astro.url.origin)
<Layout title={title} author={author} description={description} ogImage={ogUrl}>
<Header />
<div class="max-w-3xl mx-auto w-full px-2 flex justify-start">
<div class="mx-auto flex w-full max-w-3xl justify-start px-2">
<button
class="mt-8 mb-2 hover:opacity-75 flex focus-outline"
class="focus-outline mt-8 mb-2 flex hover:opacity-75"
onclick="history.back()"
>
<svg xmlns="http://www.w3.org/2000/svg"
@@ -35,8 +36,8 @@ const ogUrl = new URL(ogImage ? ogImage : `${title}.svg`, Astro.url.origin)
</div>
<main id="main-content">
<h1 class="post-title">{title}</h1>
<Datetime datetime={datetime} size="lg" className="my-2" />
<article id="article" role="article" class="mx-auto max-w-3xl mt-8 prose">
<Datetime datetime={pubDatetime} size="lg" className="my-2" />
<article id="article" role="article" class="prose mx-auto mt-8 max-w-3xl">
<Content />
</article>
@@ -49,10 +50,10 @@ const ogUrl = new URL(ogImage ? ogImage : `${title}.svg`, Astro.url.origin)
<style>
main {
@apply max-w-3xl mx-auto px-4 pb-12 w-full;
@apply mx-auto w-full max-w-3xl px-4 pb-12;
}
.post-title {
@apply font-semibold text-2xl text-skin-accent;
@apply text-2xl font-semibold text-skin-accent;
}
.tags-container {
@apply my-8;
+8 -11
View File
@@ -1,5 +1,5 @@
---
import { SITE } from "src/config";
import { SITE } from "@config";
import Layout from "@layouts/Layout.astro";
import Main from "@layouts/Main.astro";
import Header from "@components/Header.astro";
@@ -7,13 +7,12 @@ import Footer from "@components/Footer.astro";
import Card from "@components/Card";
import LinkButton from "@components/LinkButton.astro";
import slugify from "@utils/slugify";
import type { MarkdownInstance } from "astro";
import type { Frontmatter } from "src/types";
import type { CollectionEntry } from "astro:content";
export interface Props {
pageNum: number;
totalPages: number;
posts: MarkdownInstance<Frontmatter>[];
posts: CollectionEntry<"blog">[];
}
const { pageNum, totalPages, posts } = Astro.props;
@@ -27,11 +26,9 @@ const next = pageNum < totalPages ? "" : "disabled";
<Main pageTitle="Posts" pageDesc="All the articles I've posted.">
<ul>
{
posts.map(({ frontmatter }) => {
return (
<Card href={`/posts/${slugify(frontmatter)}`} post={frontmatter} />
);
})
posts.map(({ data }) => (
<Card href={`/posts/${slugify(data)}`} frontmatter={data} />
))
}
</ul>
</Main>
@@ -69,10 +66,10 @@ const next = pageNum < totalPages ? "" : "disabled";
<style>
.pagination-wrapper {
@apply flex justify-center mt-auto mb-8;
@apply mt-auto mb-8 flex justify-center;
}
.disabled {
@apply opacity-50 hover:text-skin-base group-hover:fill-skin-base pointer-events-none select-none;
@apply pointer-events-none select-none opacity-50 hover:text-skin-base group-hover:fill-skin-base;
}
.disabled-svg {
@apply group-hover:!fill-skin-base;
+5 -5
View File
@@ -1,5 +1,5 @@
---
import { SITE } from "src/config";
import { SITE } from "@config";
import Layout from "@layouts/Layout.astro";
import Header from "@components/Header.astro";
import Footer from "@components/Footer.astro";
@@ -28,15 +28,15 @@ import LinkButton from "@components/LinkButton.astro";
<style>
#main-content {
@apply max-w-3xl mx-auto flex-1 justify-center items-center flex;
@apply mx-auto flex max-w-3xl flex-1 items-center justify-center;
}
.not-found-wrapper {
@apply flex flex-col items-center justify-center mb-14;
@apply mb-14 flex flex-col items-center justify-center;
}
.not-found-wrapper h1 {
@apply font-bold text-9xl text-skin-accent;
@apply text-9xl font-bold text-skin-accent;
}
.not-found-wrapper p {
@apply text-2xl sm:text-3xl mt-4;
@apply mt-4 text-2xl sm:text-3xl;
}
</style>
+6 -12
View File
@@ -1,24 +1,18 @@
import type { APIRoute, MarkdownInstance } from "astro";
import { getCollection } from "astro:content";
import generateOgImage from "@utils/generateOgImage";
import type { Frontmatter } from "@types";
import type { APIRoute } from "astro";
export const get: APIRoute = async ({ params }) => ({
body: await generateOgImage(params.ogTitle),
});
const postImportResult = import.meta.glob<MarkdownInstance<Frontmatter>>(
"../contents/**/**/*.md",
{
eager: true,
}
);
const postImportResult = await getCollection("blog", ({ data }) => !data.draft);
const posts = Object.values(postImportResult);
export function getStaticPaths() {
return posts
.filter(({ frontmatter }) => !frontmatter.draft)
.filter(({ frontmatter }) => !frontmatter.ogImage)
.map(({ frontmatter }) => ({
params: { ogTitle: frontmatter.title },
.filter(({ data }) => !data.ogImage)
.map(({ data }) => ({
params: { ogTitle: data.title },
}));
}
+16 -18
View File
@@ -1,22 +1,20 @@
---
import { getCollection } from "astro:content";
import Layout from "@layouts/Layout.astro";
import Header from "@components/Header.astro";
import Footer from "@components/Footer.astro";
import LinkButton from "@components/LinkButton.astro";
import Hr from "@components/Hr.astro";
import Card from "@components/Card";
import Socials from "@components/Socials.astro";
import getSortedPosts from "@utils/getSortedPosts";
import slugify from "@utils/slugify";
import type { Frontmatter } from "src/types";
import Socials from "@components/Socials.astro";
import { SOCIALS } from "@config";
const posts = await Astro.glob<Frontmatter>("../contents/**/*.md");
const posts = await getCollection("blog");
const sortedPosts = getSortedPosts(posts);
const featuredPosts = sortedPosts.filter(
({ frontmatter }) => frontmatter.featured
);
const featuredPosts = sortedPosts.filter(({ data }) => data.featured);
const socialCount = SOCIALS.filter(social => social.active).length;
---
@@ -76,10 +74,10 @@ const socialCount = SOCIALS.filter(social => social.active).length;
<section id="featured">
<h2>Featured</h2>
<ul>
{featuredPosts.map(({ frontmatter }) => (
{featuredPosts.map(({ data }) => (
<Card
href={`/posts/${slugify(frontmatter)}`}
post={frontmatter}
href={`/posts/${slugify(data)}`}
frontmatter={data}
secHeading={false}
/>
))}
@@ -95,11 +93,11 @@ const socialCount = SOCIALS.filter(social => social.active).length;
<ul>
{
sortedPosts.map(
({ frontmatter }, index) =>
({ data }, index) =>
index < 4 && (
<Card
href={`/posts/${slugify(frontmatter)}`}
post={frontmatter}
href={`/posts/${slugify(data)}`}
frontmatter={data}
secHeading={false}
/>
)
@@ -128,22 +126,22 @@ const socialCount = SOCIALS.filter(social => social.active).length;
@apply pt-8 pb-6;
}
#hero h1 {
@apply inline-block font-bold my-4 sm:my-8 text-3xl sm:text-5xl;
@apply my-4 inline-block text-3xl font-bold sm:my-8 sm:text-5xl;
}
#hero .rss-link {
@apply mb-6;
}
#hero .rss-icon {
@apply h-6 w-6 scale-110 sm:scale-125 mb-2 sm:mb-3 fill-skin-accent;
@apply mb-2 h-6 w-6 scale-110 fill-skin-accent sm:mb-3 sm:scale-125;
}
#hero p {
@apply my-2;
}
.social-wrapper {
@apply flex flex-col sm:flex-row sm:items-center mt-4;
@apply mt-4 flex flex-col sm:flex-row sm:items-center;
}
.social-links {
@apply whitespace-nowrap mr-2 sm:mb-0 mb-1;
@apply mr-2 mb-1 whitespace-nowrap sm:mb-0;
}
/* ===== Featured & Recent Posts Sections ===== */
@@ -153,9 +151,9 @@ const socialCount = SOCIALS.filter(social => social.active).length;
}
#featured h2,
#recent-posts h2 {
@apply font-semibold text-2xl tracking-wide;
@apply text-2xl font-semibold tracking-wide;
}
.all-posts-btn-wrapper {
@apply text-center my-8;
@apply my-8 text-center;
}
</style>
+12 -38
View File
@@ -1,53 +1,27 @@
---
import { SITE } from "src/config";
import { CollectionEntry, getCollection } from "astro:content";
import Posts from "@layouts/Posts.astro";
import PostDetails from "@layouts/PostDetails.astro";
import getSortedPosts from "@utils/getSortedPosts";
import getPageNumbers from "@utils/getPageNumbers";
import type { MarkdownInstance } from "astro";
import type { Frontmatter } from "src/types";
import slugify from "@utils/slugify";
import { SITE } from "@config";
export interface Props {
post: MarkdownInstance<Frontmatter>;
post: CollectionEntry<"blog">;
}
type PostResult = {
params: {
slug: string | number;
};
props?: {
post: MarkdownInstance<Record<string, any>>;
};
}[];
type PagePaths = {
params: {
slug: string;
};
}[];
export async function getStaticPaths() {
const posts = await Astro.glob<Frontmatter>("../../contents/**/*.md");
const posts = await getCollection("blog", ({ data }) => !data.draft);
const filteredPosts = posts.filter(({ frontmatter }) => !frontmatter.draft);
const postResult = posts.map(post => ({
params: { slug: slugify(post.data) },
props: { post },
}));
let postResult: PostResult = filteredPosts.map(post => {
return {
params: {
slug: slugify(post.frontmatter),
},
props: {
post,
},
};
});
const pagePaths: PagePaths = getPageNumbers(filteredPosts.length).map(
pageNum => ({
params: { slug: String(pageNum) },
})
);
const pagePaths = getPageNumbers(posts.length).map(pageNum => ({
params: { slug: String(pageNum) },
}));
return [...postResult, ...pagePaths];
}
@@ -55,7 +29,7 @@ export async function getStaticPaths() {
const { slug } = Astro.params;
const { post } = Astro.props;
const posts = await Astro.glob<Frontmatter>("../../contents/**/*.md");
const posts = await getCollection("blog");
const sortedPosts = getSortedPosts(posts);
+4 -3
View File
@@ -1,11 +1,12 @@
---
import { SITE } from "src/config";
import { SITE } from "@config";
import Posts from "@layouts/Posts.astro";
import getSortedPosts from "@utils/getSortedPosts";
import getPageNumbers from "@utils/getPageNumbers";
import type { Frontmatter } from "src/types";
const posts = await Astro.glob<Frontmatter>("../../contents/**/*.md");
import { getCollection } from "astro:content";
const posts = await getCollection("blog");
const sortedPosts = getSortedPosts(posts);
+12 -21
View File
@@ -1,28 +1,19 @@
import { SITE } from "src/config";
import rss from "@astrojs/rss";
import type { Frontmatter } from "src/types";
import type { MarkdownInstance } from "astro";
import { getCollection } from "astro:content";
import { SITE } from "@config";
import slugify from "@utils/slugify";
const postImportResult = import.meta.glob<MarkdownInstance<Frontmatter>>(
"../contents/**/**/*.md",
{
eager: true,
}
);
const posts = Object.values(postImportResult);
export const get = () =>
rss({
export async function get() {
const posts = await getCollection("blog", ({ data }) => !data.draft);
return rss({
title: SITE.title,
description: SITE.desc,
site: SITE.website,
items: posts
.filter(({ frontmatter }) => !frontmatter.draft)
.map(({ frontmatter }) => ({
link: `posts/${slugify(frontmatter)}`,
title: frontmatter.title,
description: frontmatter.description,
pubDate: new Date(frontmatter.datetime),
})),
items: posts.map(({ data }) => ({
link: `posts/${slugify(data)}`,
title: data.title,
description: data.description,
pubDate: new Date(data.pubDatetime),
})),
});
}
+8 -11
View File
@@ -1,24 +1,21 @@
---
import { SITE } from "src/config";
import { getCollection } from "astro:content";
import { SITE } from "@config";
import Layout from "@layouts/Layout.astro";
import Main from "@layouts/Main.astro";
import Header from "@components/Header.astro";
import Footer from "@components/Footer.astro";
import Search from "@components/Search";
import type { Frontmatter } from "src/types";
// Retrieve all articles
const posts = await Astro.glob<Frontmatter>("../contents/**/*.md");
const posts = await getCollection("blog", ({ data }) => !data.draft);
// List of items to search in
const searchList = posts
.filter(({ frontmatter }) => !frontmatter.draft)
.map(post => ({
title: post.frontmatter.title,
description: post.frontmatter.description,
headings: post.getHeadings().map(h => h.text),
frontmatter: post.frontmatter,
}));
const searchList = posts.map(({ data }) => ({
title: data.title,
description: data.description,
data,
}));
---
<Layout title={`Search | ${SITE.title}`}>
+9 -18
View File
@@ -1,5 +1,5 @@
---
import { SITE } from "src/config";
import { CollectionEntry, getCollection } from "astro:content";
import Layout from "@layouts/Layout.astro";
import Main from "@layouts/Main.astro";
import Header from "@components/Header.astro";
@@ -7,39 +7,30 @@ import Footer from "@components/Footer.astro";
import Card from "@components/Card";
import getUniqueTags from "@utils/getUniqueTags";
import getPostsByTag from "@utils/getPostsByTag";
import type { MarkdownInstance } from "astro";
import type { Frontmatter } from "src/types";
import slugify from "@utils/slugify";
import { SITE } from "@config";
export interface Props {
post: MarkdownInstance<Frontmatter>;
post: CollectionEntry<"blog">;
tag: string;
}
export async function getStaticPaths() {
const posts: MarkdownInstance<Frontmatter>[] = await Astro.glob(
"../../contents/**/*.md"
);
const posts = await getCollection("blog");
const tags = getUniqueTags(posts);
return tags.map(tag => {
return {
params: {
tag,
},
props: {
tag,
},
params: { tag },
props: { tag },
};
});
}
const { tag } = Astro.props;
const posts: MarkdownInstance<Frontmatter>[] = await Astro.glob(
"../../contents/**/*.md"
);
const posts = await getCollection("blog", ({ data }) => !data.draft);
const tagPosts = getPostsByTag(posts, tag);
---
@@ -52,8 +43,8 @@ const tagPosts = getPostsByTag(posts, tag);
>
<ul>
{
tagPosts.map(({ frontmatter }) => (
<Card href={`/posts/${slugify(frontmatter)}`} post={frontmatter} />
tagPosts.map(({ data }) => (
<Card href={`/posts/${slugify(data)}`} frontmatter={data} />
))
}
</ul>
+3 -3
View File
@@ -1,14 +1,14 @@
---
import { SITE } from "src/config";
import { getCollection } from "astro:content";
import Header from "@components/Header.astro";
import Footer from "@components/Footer.astro";
import Layout from "@layouts/Layout.astro";
import Main from "@layouts/Main.astro";
import Tag from "@components/Tag.astro";
import getUniqueTags from "@utils/getUniqueTags";
import type { Frontmatter } from "src/types";
import { SITE } from "@config";
const posts = await Astro.glob<Frontmatter>("../../contents/**/*.md");
const posts = await getCollection("blog");
let tags = getUniqueTags(posts);
---
+34 -34
View File
@@ -13,12 +13,12 @@
--color-border: 236, 233, 233;
}
html[data-theme="dark"] {
--color-fill: 47, 55, 65;
--color-text-base: 230, 230, 230;
--color-accent: 26, 217, 217;
--color-card: 63, 75, 90;
--color-card-muted: 89, 107, 129;
--color-border: 59, 70, 85;
--color-fill: 33, 39, 55;
--color-text-base: 234, 237, 243;
--color-accent: 255, 107, 1;
--color-card: 52, 63, 96;
--color-card-muted: 138, 51, 2;
--color-border: 171, 75, 8;
}
#sun-svg,
html[data-theme="dark"] #moon-svg {
@@ -29,45 +29,45 @@
display: block;
}
body {
@apply font-mono bg-skin-fill text-skin-base min-h-screen flex flex-col
selection:bg-skin-accent selection:text-skin-inverted selection:bg-opacity-70;
@apply flex min-h-screen flex-col bg-skin-fill font-mono text-skin-base
selection:bg-skin-accent selection:bg-opacity-70 selection:text-skin-inverted;
}
section,
footer {
@apply max-w-3xl mx-auto px-4;
@apply mx-auto max-w-3xl px-4;
}
a {
@apply outline-offset-1 outline-skin-fill outline-2
focus-visible:outline-dashed focus-visible:no-underline;
@apply outline-2 outline-offset-1 outline-skin-fill
focus-visible:no-underline focus-visible:outline-dashed;
}
svg {
@apply w-6 h-6 inline-block fill-skin-base group-hover:fill-skin-accent;
@apply inline-block h-6 w-6 fill-skin-base group-hover:fill-skin-accent;
}
svg.icon-tabler {
@apply w-6 h-6 scale-125 sm:scale-110 opacity-90
inline-block fill-transparent stroke-2 stroke-current
group-hover:fill-transparent;
@apply inline-block h-6 w-6 scale-125 fill-transparent
stroke-current stroke-2 opacity-90 group-hover:fill-transparent
sm:scale-110;
}
.prose {
@apply prose-a:underline-offset-8 prose-ul:overflow-x-clip
prose-a:!decoration-dashed hover:prose-a:text-skin-accent
prose-img:mx-auto prose-img:border-2 prose-img:border-skin-line
prose-img:!mt-2 prose-headings:!mb-3
prose-th:border prose-th:border-skin-line
prose-td:border prose-td:border-skin-line
prose-h3:italic
@apply prose-headings:!mb-3 prose-headings:!text-skin-base
prose-h3:italic prose-p:!text-skin-base
prose-a:!text-skin-base prose-a:!decoration-dashed prose-a:underline-offset-8
hover:prose-a:text-skin-accent prose-blockquote:!border-l-skin-accent
prose-blockquote:border-opacity-50 prose-blockquote:opacity-80
prose-figcaption:!text-skin-base prose-figcaption:opacity-70
prose-strong:!text-skin-base
prose-p:!text-skin-base prose-headings:!text-skin-base
prose-ol:!text-skin-base prose-ul:!text-skin-base prose-blockquote:opacity-80
prose-figcaption:!text-skin-base prose-figcaption:opacity-70
prose-code:bg-skin-card prose-code:bg-opacity-75
prose-code:!text-skin-base prose-code:p-1 prose-code:rounded
prose-code:after:!content-[''] prose-code:before:!content-['']
prose-strong:!text-skin-base prose-table:text-skin-base
prose-pre:!text-skin-base prose-a:!text-skin-base
prose-hr:!border-skin-line prose-li:marker:!text-skin-accent
prose-blockquote:!border-l-skin-accent prose-blockquote:border-opacity-50;
prose-code:rounded prose-code:bg-skin-card
prose-code:bg-opacity-75 prose-code:p-1 prose-code:!text-skin-base
prose-code:before:!content-[''] prose-code:after:!content-['']
prose-pre:!text-skin-base prose-ol:!text-skin-base
prose-ul:overflow-x-clip prose-ul:!text-skin-base prose-li:marker:!text-skin-accent
prose-table:text-skin-base prose-th:border
prose-th:border-skin-line prose-td:border
prose-td:border-skin-line prose-img:mx-auto
prose-img:!mt-2 prose-img:border-2
prose-img:border-skin-line prose-hr:!border-skin-line;
}
.prose a {
@apply hover:!text-skin-accent;
@@ -81,7 +81,7 @@
@apply mb-2;
}
.prose details {
@apply text-skin-base cursor-pointer inline-block select-none;
@apply inline-block cursor-pointer select-none text-skin-base;
}
.prose summary {
@apply focus-outline;
@@ -121,6 +121,6 @@
@apply hidden;
}
.focus-outline {
@apply outline-offset-1 outline-skin-fill outline-2 focus-visible:outline-dashed focus-visible:no-underline;
@apply outline-2 outline-offset-1 outline-skin-fill focus-visible:no-underline focus-visible:outline-dashed;
}
}
-12
View File
@@ -1,15 +1,3 @@
export interface Frontmatter {
title: string;
ogImage?: string;
description: string;
author: string;
datetime: string;
slug: string;
featured: boolean;
draft: boolean;
tags: string[];
}
export type SocialObjects = {
name: SocialMedia;
href: string;
+1 -1
View File
@@ -1,4 +1,4 @@
import { SITE } from "src/config";
import { SITE } from "@config";
const getPageNumbers = (numberOfPosts: number) => {
const numberOfPages = numberOfPosts / Number(SITE.postPerPage);
+3 -4
View File
@@ -1,8 +1,7 @@
import { slufigyAll } from "./slugify";
import type { MarkdownInstance } from "astro";
import type { Frontmatter } from "../types";
import type { CollectionEntry } from "astro:content";
const getPostsByTag = (posts: MarkdownInstance<Frontmatter>[], tag: string) =>
posts.filter(post => slufigyAll(post.frontmatter.tags).includes(tag));
const getPostsByTag = (posts: CollectionEntry<"blog">[], tag: string) =>
posts.filter(post => slufigyAll(post.data.tags).includes(tag));
export default getPostsByTag;
+5 -6
View File
@@ -1,13 +1,12 @@
import type { MarkdownInstance } from "astro";
import type { Frontmatter } from "../types";
import type { CollectionEntry } from "astro:content";
const getSortedPosts = (posts: MarkdownInstance<Frontmatter>[]) =>
const getSortedPosts = (posts: CollectionEntry<"blog">[]) =>
posts
.filter(({ frontmatter }) => !frontmatter.draft)
.filter(({ data }) => !data.draft)
.sort(
(a, b) =>
Math.floor(new Date(b.frontmatter.datetime).getTime() / 1000) -
Math.floor(new Date(a.frontmatter.datetime).getTime() / 1000)
Math.floor(new Date(b.data.pubDatetime).getTime() / 1000) -
Math.floor(new Date(a.data.pubDatetime).getTime() / 1000)
);
export default getSortedPosts;
+4 -5
View File
@@ -1,12 +1,11 @@
import { slugifyStr } from "./slugify";
import type { MarkdownInstance } from "astro";
import type { Frontmatter } from "../types";
import type { CollectionEntry } from "astro:content";
const getUniqueTags = (posts: MarkdownInstance<Frontmatter>[]) => {
const getUniqueTags = (posts: CollectionEntry<"blog">[]) => {
let tags: string[] = [];
const filteredPosts = posts.filter(({ frontmatter }) => !frontmatter.draft);
const filteredPosts = posts.filter(({ data }) => !data.draft);
filteredPosts.forEach(post => {
tags = [...tags, ...post.frontmatter.tags]
tags = [...tags, ...post.data.tags]
.map(tag => slugifyStr(tag))
.filter(
(value: string, index: number, self: string[]) =>
+3 -3
View File
@@ -1,10 +1,10 @@
import { slug as slugger } from "github-slugger";
import type { Frontmatter } from "src/types";
import type { BlogFrontmatter } from "@content/_schemas";
export const slugifyStr = (str: string) => slugger(str);
const slugify = (frontmatter: Frontmatter) =>
frontmatter.slug ? slugger(frontmatter.slug) : slugger(frontmatter.title);
const slugify = (post: BlogFrontmatter) =>
post.postSlug ? slugger(post.postSlug) : slugger(post.title);
export const slufigyAll = (arr: string[]) => arr.map(str => slugifyStr(str));