README.md
<p align="center">
<a href="https://kremalicious.com"><img src="https://raw.githubusercontent.com/kremalicious/portfolio/main/public/github-header.png" alt="GitHub Header" /></a>
</p>
<p align="center">
<strong>🍭 My blog built with <a href="https://astro.build">Astro</a> + TypeScript. Neat.</strong>
</p>
<p align="center">
<a href="https://kremalicious.com">kremalicious.com</a>
</p>
<p align="center">
<a href="https://github.com/kremalicious/blog/actions"><img src="https://github.com/kremalicious/blog/workflows/CI/badge.svg" alt="CI workflow badge" /></a>
<a href="https://codeclimate.com/github/kremalicious/blog/maintainability"><img src="https://api.codeclimate.com/v1/badges/4e86c791349cd12368cd/maintainability" alt="CodeClimate maintainability badge" /></a>
<a href="https://codeclimate.com/github/kremalicious/blog/test_coverage"><img src="https://api.codeclimate.com/v1/badges/4e86c791349cd12368cd/test_coverage" alt="CodeClimate coverage badge" /></a>
</p>
---
- [🎉 Features](#-features)
- [🌅 Image handling](#-image-handling)
- [🎆 EXIF extraction](#-exif-extraction)
- [💰 Cryptocurrency donation via Web3 browser wallets](#-cryptocurrency-donation-via-web3-browser-wallets)
- [🔍 Search](#-search)
- [🕸 Related Posts](#-related-posts)
- [📝 GitHub Changelog Rendering](#-github-changelog-rendering)
- [🌗 Theme Switcher](#-theme-switcher)
- [💎 SVG assets as components](#-svg-assets-as-components)
- [astro-redirect-from](#astro-redirect-from)
- [RSS \& JSON feeds](#rss--json-feeds)
- [✨ Development](#-development)
- [🔮 Linting](#-linting)
- [🔮 Type Checking](#-type-checking)
- [👩🔬 Testing](#-testing)
- [🎈 Content creation helpers](#-content-creation-helpers)
- [Add a new post](#add-a-new-post)
- [🚚 Deployment](#-deployment)
- [S3 Deployment](#s3-deployment)
- [🏛 Licenses](#-licenses)
- [Posts](#posts)
- [Photos \& images](#photos--images)
---
## 🎉 Features
The whole [blog](https://kremalicious.com) is a statically exported site built with [Astro](https://astro.build) and TypeScript. Almost all components are Astro or native Web Components, with some React components loaded client-side.
Styling happens through a combination of basic global styles and on components level either through CSS modules or CSS in `<style>` tags within Astro components.
Content lives under `content/` and Astro creates a content collection for each subfolder, which are then queried in components. Every post is a folder with a markdown file and all respective post assets co-located inside.
Retrieving content collections will enrich every post's frontmatter metadata, like extracting date and slug from the post folder name, or exif extraction for photos.
### 🌅 Image handling
Uses Astro's native `astro:assets` feature, all required image sizes are automatically generated from source images, working in combination with my own custom `<picture>` component. Making heavy use of Astro's `getImage()` and custom markup results in full image sizing control and properly `object-fit` images with varying aspect ratios.
Teaser images are all defined in a post's frontmatter `image` key, which is then passed to the `<Picture />` component for display.
If you want to know how this works, have a look at the respective files:
- [`src/components/Picture/index.astro`](src/components/Picture/index.astro)
- [`src/components/Picture/index.module.css`](src/components/Picture/index.module.css)
### 🎆 EXIF extraction
Automatically extracts EXIF & IPTC metadata from my photos and adds it to markdown frontmatter of respective photo posts. For minimal overhead, [fast-exif](https://github.com/titarenko/fast-exif) & [node-iptc](https://github.com/derekbaron/node-iptc) is used to parse every JPG file whenever a content collection is accessed.
In the end looks like this, including location display with [pigeon-maps](https://github.com/mariusandra/pigeon-maps):
<img width="1098" alt="screen shot 2018-10-14 at 20 27 39" src="https://user-images.githubusercontent.com/90316/46920507-9d6b7a00-cfef-11e8-84c8-a1997f471cae.png">
If you want to know how this works, have a look at the respective files:
- EXIF extraction with `readOutExif()` helper in [`src/lib/exif/index.ts`](src/lib/exif/index.ts)
- the `loadAndFormatCollection()` helper in [`src/lib/astro.ts`](src/lib/astro.ts)
- output through [`src/components/Exif/`](src/components/Exif/)
### 💰 Cryptocurrency donation via Web3 browser wallets
Lets visitors say thanks with Ether, any ERC-20, or Bitcoin. The Web3 wallet integration uses [RainbowKit](https://www.rainbowkit.com) for wallet connection, my own custom web3 API to fetch wallet token balances and metadata, and [wagmi](https://wagmi.sh) for sending transactions.
<img width="502" alt="Screenshot 2023-11-05 at 20 18 50" src="https://github.com/kremalicious/blog/assets/90316/7eadf4e9-6e98-4cf6-9639-aebf42ac0d4e">
<img width="487" alt="Screenshot 2023-11-05 at 20 20 04" src="https://github.com/kremalicious/blog/assets/90316/2421e64c-2d98-4e2a-a67a-ab1b5640bfb6">
If you want to know how this works, have a look at the respective feature under
- [`src/features/Web3/`](src/features/Web3/)
### 🔍 Search
A global search is provided with fuse.js. Whenever search is opened, all posts metadata is fetched, which is then queried against when the search field is used. This prevents a huge search index from being bundled in the site build.
<img width="700" alt="screen shot 2018-11-18 at 19 44 30" src="https://user-images.githubusercontent.com/90316/48676679-634f4400-eb6a-11e8-936d-293505d5c5d9.png">
If you want to know how this works, have a look at the respective feature under
- [`src/features/Search/`](src/features/Search/)
### 🕸 Related Posts
Under each post a list of related posts is displayed which are based on the tags and other metadata of the currently viewed post, also done with fuse.js.
<img width="700" alt="screen shot 2018-10-11 at 21 03 03" src="https://user-images.githubusercontent.com/90316/46827531-14f39c00-cd99-11e8-84aa-0e851c32c89c.png" />
If you want to know how this works, have a look at the respective component under
- [`src/components/RelatedPosts/`](src/components/RelatedPosts/)
### 📝 GitHub Changelog Rendering
Adds ability to show contents of a changelog, rendered from a `CHANGELOG.md` on GitHub from the given repository. The use case is to enhance release posts about projects hosted on GitHub. Makes use of the GitHub GraphQL API.
Adding this to a post's YAML frontmatter:
```yaml
changelog: kremalicious/gatsby-plugin-matomo
```
will render this at the end of the post:
<img width="700" alt="screen shot 2018-11-21 at 23 03 38" src="https://user-images.githubusercontent.com/90316/48870593-bc74dd00-ede1-11e8-9051-df55ab7b48d1.png">
See it live e.g. on [Matomo plugin for Gatsby](https://kremalicious.com/gatsby-plugin-matomo#changelog).
If you want to know how this works, have a look at the respective component under
- [`src/components/Changelog/`](src/components/Changelog/)
- the `getRepo()` helper in [`src/lib/github/github.ts`](src/lib/github/github.ts)
### 🌗 Theme Switcher
Includes a theme switcher which allows user to toggle between a light and a dark theme. Switching between them also happens automatically based on user's system preferences.
If you want to know how, have a look at the respective components:
- [`src/components/ThemeSwitch/`](src/components/ThemeSwitch/)
### 💎 SVG assets as components
All SVG assets under `src/images/` and from select iconset dependencies are converted to Astro & React components before building the site. Compiled components are placed under `src/images/components/` and all include the cleaned SVGs as inline HTML.
All SVGs can then be imported from `@/images/components` in all Astro or React components.
If you want to know how this works, have a look at the script:
- [`scripts/create-icons/`](scripts/create-icons/)
### astro-redirect-from
Still a remnant of the old [Jekyll](https://jekyllrb.com) days, which survived in [gatsby-redirect-from](https://kremalicious.com/gatsby-redirect-from/) and now works in Astro with [astro-redirect-from](https://kremalicious.com/astro-redirect-from/).
For all post slugs defined in a `redirect_from` frontmatter key, redirects will be put in place by Astro.
### RSS & JSON feeds
Generates rss & json feeds upon build time.
If you want to know how this works, have a look at the respective files:
- [`src/pages/feed.json.ts`](src/pages/feed.json.ts)
- [`src/pages/feed.xml.ts`](src/pages/feed.xml.ts)
## ✨ Development
```bash
git clone git@github.com:kremalicious/blog.git
cd blog/
# required env vars
cp .env.sample .env
vi .env
npm i
npm start
```
### 🔮 Linting
[Biome](https://biomejs.dev) is setup for all linting and formatting purposes:
```bash
npm run lint
```
### 🔮 Type Checking
Type checking can be invoked to check all TypeScript code, including within .astro files:
```bash
npm run typecheck
```
### 👩🔬 Testing
Test suite is setup with [Vitest](https://vitest.dev), [react-testing-library](https://github.com/kentcdodds/react-testing-library), and [Playwright](https://playwright.dev).
All unit test files live beside the respective component with naming pattern `*.test.ts(x)`. Integration test files live under `./test/e2e/` exclusively, with naming pattern `*.spec.ts`.
Testing setup, fixtures, and mocks shared between unit & integration tests can be found in `./test` folder.
To run all unit tests:
```bash
npm run test:unit
# watch mode
npm run test:unit:watch
```
For End-to-End integration testing, ideally run against the production build:
```bash
npm run build && npm run preview
# mapping `playwright` command
npm run test:e2e
npm run test:e2e -- --ui
npm run test:e2e -- path/to/file.spec.ts.
npm run test:e2e -- --update-snapshots
```
## 🎈 Content creation helpers
### Add a new post
```bash
npm run new "Hello World"
npm run new "Hello World" 2017-12-27
```
Create a new photo post with date, title & description pre-filled from EXIF/IPTC data of a given image file:
```bash
npm run new photo /path/to/photo.jpg
npm run new photo /path/to/photo.jpg "Hello Photo Post"
```
- [`scripts/new/`](scripts/new/)
## 🚚 Deployment
Every branch or Pull Request is automatically deployed by [Vercel](https://vercel.com) with their GitHub integration. A link to a preview deployment will appear under each Pull Request. Vercel is not used for the production deployment.
### S3 Deployment
The latest deployment of the `main` branch is automatically deployed to S3 from the GitHub Action as the production deployment, aliased to `kremalicious.com`. The deploy command simply calls the [`scripts/deploy-s3.sh`](scripts/deploy-s3.sh) script, syncing the contents of the `dist/` folder to S3:
```bash
npm run deploy:s3
```
## 🏛 Licenses
The MIT License (MIT)
EXCEPT FOR:
### Posts
[![Creative Commons License](https://i.creativecommons.org/l/by-nc-sa/4.0/80x15.png)](http://creativecommons.org/licenses/by-nc-sa/4.0/)
All post content under `./content/articles` & `./content/links` is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-nc-sa/4.0/).
### Photos & images
All photos & image assets are plain ol' copyright.
Copyright (c) 2008–2023 Matthias Kretschmann
Don't care if you fork & play with it, but you're not allowed to publish anything from it as a whole without my written permission. Also please be aware, the combination of typography, colors & layout makes up my brand identity. So please don't just clone everything, but rather do a remix!