Thoughts in orbit. Powered by Astro.
A polished, feature-rich personal blog and knowledge base template. Built with Astro 6 and Tailwind CSS 4 — static by default, interactive where it counts.
Live reference: soren-s-blog-base.pages.dev
README Languages: English / 中文 / 日本語 / 繁體中文
From blog posts and knowledge base articles to interactive music listening, this template brings together everything you need for a personal web presence. Whether you're writing LaTeX-heavy notes, organizing learning materials, or enjoying high-fidelity audio, it's all here in one polished, zero-cost static site.
- Full-screen Hero cover with two-stage loading: pure wallpaper → typewriter title + particles + navbar fade-in
- Scroll-bound wave transition — canvas color extraction from wallpaper, backdrop-filter blur, flowing SVG layers
- Independent hero wallpaper carousel (8-10s crossfade) from
public/assets/hero-wallpaper/ - Classic typewriter effect with blinking cursor on site title
- Time-aware subtitle carousel (morning / afternoon / evening phrases)
- Multi-layer particle engine (starlight + glow dots + light rays, 3 canvas layers)
- Parallax scroll collapse: hero fades out, navbar transitions transparent → glass
- Hero now-playing bar (glassmorphism music status)
- Mobile: 50dvh hero, reduced particles, disabled parallax
- Built with Astro 6 + Tailwind CSS 4 with OKLCH color space
- Smooth page transitions via Astro View Transitions (SPA)
- Light/dark theme switching with system preference detection
- Dynamic hue slider — change the entire site's accent color with one CSS variable
- Featured articles magazine grid (staggered layout, tilt effect)
- Fullscreen wallpaper carousel with glassmorphism card effects
- Sakura particle animation (Canvas, toggleable)
- Fully responsive design for all devices
- Polaroid-style photo grid with randomized tilt
- Round glass lightbox — click cards to view full images, not navigate away
- Keyboard navigation (← → arrows, Esc to close)
- Scroll-safe: locks body overflow, restores scroll position on close
- Web Audio API DSP signal chain:
ReplayGain → Compressor → Crossfeed → Analyser - 20+ audio formats: FLAC, ALAC, WAV, AIFF, WavPack, APE, TAK, TTA, MP3, AAC, Opus, OGG, etc.
- Auto quality classification: Studio Master / Hi-Res / CD Quality / Standard Lossless
- Roon-inspired immersive full-screen playback with blurred cover background
- Apple Music-style library browser: browse by album, artist, genre, year
- NetEase Cloud Music integration — import playlists, liked songs; merged with local library
- Album cover auto-extraction from audio file metadata
- ReplayGain (track/album gain normalization)
- EQ presets: Off, Classical, Rock, Jazz, Headphones, Voice
- Recent play history with stale-entry auto-cleanup
- Dual-view: Card grid + Time River (SVG wavy river with animated particles)
- Category tabs with color-coded cards
- Drag-to-scroll on Time River view
- Sparkle dots, ripple nodes, flowing particles along the river
- Markdown/MDX writing with Zod-typed content collections
- Enhanced code blocks via Expressive Code: line numbers, copy button, collapsible sections
- LaTeX math: KaTeX (server-side) + MathJax 3 (client fallback)
- Mermaid diagrams with auto light/dark theme switching
- GitHub-flavored admonitions:
:::note,:::tip,:::important,:::caution,:::warning - GitHub repository cards via
:github[user/repo]syntax - Image width control and centering:
 - Static full-text search via Pagefind with CJK support
- Reading time estimation (Chinese 400 cpm, English 200 wpm)
- Floating table of contents with scroll tracking
- RSS 2.0 feed
- Zero-JS-by-default output (Islands Architecture)
- PWA support with offline access and installable app
- JSON-LD structured data for SEO
- Content collections with draft system (visible in dev, hidden in production)
- Auto-generated OG images via Satori
- Back-to-top button with scroll progress ring
- Custom remark/rehype plugin pipeline (12 plugins)
- NetEase Cloud Music API proxy (NCM login, playlist sync, song URL resolution)
- Wallpaper auto-resize integration (responsive images per viewport)
- Chinese and English i18n
- Vercel Analytics ready
| Dependency | Version |
|---|---|
| Node.js | >= 18 (LTS recommended) |
| npm | >= 9 |
# 1. Clone the repository
git clone https://github.com/your-username/sorbellar.git
cd sorbellar
# 2. Install dependencies
npm install
> [!IMPORTANT]
> **`.npmrc` — why `legacy-peer-deps=true` is required**
>
> `@vite-pwa/astro@1.2.0` (latest) only supports Astro `^1.0 – ^5.0` as a peer dependency, but this project uses **Astro 6**. Until upstream releases a compatible version, the project ships a `.npmrc` file that enables `legacy-peer-deps` to suppress the peer conflict.
>
> This has no impact on PWA functionality — the plugin works correctly with Astro 6 despite the unresolved peer range. If you remove `.npmrc`, `npm ci` will fail on CI and fresh installs.
# 3. (Optional) Add hero wallpapers
# Place images in public/assets/hero-wallpaper/desktop/ and mobile/
# Supported: .webp, .jpg, .png
# 4. (Optional) Add your music files
# Place audio files in public/assets/music/url/
# Supported: .mp3 .flac .m4a .ogg .wav .wma .aiff .ape .wv .opus .aac .mpc .mp4 .alac .tak .tta .ac3 .dts
# Note: DSD (.dsf/.dff) is NOT supported — they are automatically skipped by the scanner
# 5. Scan music and generate playlist
npm run scan-music
# 6. Start the dev server
npm run devOpen http://localhost:3000 in your browser.
---
title: "Article Title" # Required
published: 2026-06-10 # Required
updated: 2026-06-10 # Optional
description: "SEO description" # Recommended
image: "/assets/posts_picture/1.webp" # Optional cover image
imagePosition: "center" # CSS object-position (e.g. "top", "bottom 20%")
tags: ["tag1", "tag2"] # Default: []
category: "Tech" # Optional
draft: false # true = hidden in production
pinned: false # true = pinned to top
---Draft posts are visible during development (npm run dev) but automatically hidden in production builds. Use this to work on posts before publishing.
Place cover images in public/assets/posts_picture/ with sequential numeric names (e.g. 1.webp, 2.webp). Use the imagePosition field to control which part of the image is visible as the cover crop.
Add .md or .mdx files to src/content/docs/ for long-form articles and documentation. They appear under the /docs section with a sidebar navigation tree.
| Command | Description |
|---|---|
npm run dev |
Scan music + start dev server at localhost:3000 |
npm run build |
Scan music + production build + Pagefind search index |
npm run preview |
Preview production build locally |
npm run scan-music |
Scan music files and generate playlist |
npm run lint |
Lint source files with ESLint |
npm run format |
Format code with Prettier |
npm test |
Run tests with Vitest |
MediaElement → Gain(ReplayGain) → DynamicsCompressor → Crossfeed → Analyser → Destination
| Tier | Criteria | Badge |
|---|---|---|
| Studio Master | > 96kHz / 24bit | SM |
| Hi-Res Lossless | > 48kHz / 24bit | HR |
| CD Quality | 44.1kHz / 16bit | CD |
| Standard Lossless | 44.1kHz / 16bit | — |
| High-bitrate Lossy | > 256kbps | — |
| Standard Lossy | > 128kbps | — |
| Low-bitrate Lossy | < 128kbps | — |
- Place audio files in
public/assets/music/url/ - Run
npm run scan-music - Covers are auto-extracted to
public/assets/music/cover/ - Playlist is generated at
public/api/music-playlist.json
The scanner reads embedded metadata (title, artist, album, year, genre, composer, track number, ReplayGain) using the music-metadata library. Format detection uses magic bytes — file extensions don't matter.
Log in via the music library sidebar to import your NetEase Cloud Music playlists. Imported playlists can be merged into the local library, making NCM tracks appear alongside local files in Browse, Artists, Albums, and Years views. Song URLs are resolved on-demand when playback starts.
Personalize your site by editing these files:
| File | Purpose |
|---|---|
src/config/siteConfig.ts |
Title, subtitle, URL, theme hue, layout options |
src/config/profileConfig.ts |
Your name, bio, avatar, social links |
src/config/navBarConfig.ts |
Navigation bar visibility toggles |
src/config/musicPlayerConfig.ts |
Music player on/off |
astro.config.ts |
Production site URL, PWA manifest |
Assets to replace:
| Path | What it is |
|---|---|
public/assets/avatar.png |
Your avatar (or remove — a letter placeholder will appear) |
public/assets/wallpaper/ |
Homepage banner wallpapers (.webp, 1920×1080) |
public/assets/hero-wallpaper/ |
Hero section background carousel |
public/assets/music/url/ |
Local audio files for the music player |
public/assets/posts_picture/ |
Post cover images |
All colors derive from a single --hue CSS variable using OKLCH color space. Change one number to recolor the entire site:
:root {
--hue: 250; /* Purple-blue by default */
--primary: oklch(65% 0.25 var(--hue));
}sorbellar/
├── public/
│ ├── assets/
│ │ ├── font/ # Self-hosted fonts (woff2)
│ │ ├── hero-wallpaper/ # Hero wallpapers (desktop/ + mobile/)
│ │ ├── music/
│ │ │ ├── url/ # Audio files (place your music here)
│ │ │ └── cover/ # Cover art (auto-extracted)
│ │ ├── posts_picture/ # Blog post cover images
│ │ └── wallpaper/ # Page wallpaper carousel
│ ├── api/
│ │ └── music-playlist.json # Playlist API (auto-generated)
│ └── js/ # Client-side scripts (public, not bundled)
│ ├── hero.js # Hero timing, typewriter, parallax
│ ├── hero-wave-colors.js # Canvas color extraction → wave tint
│ ├── particles-multi.js # Multi-layer particle engine (3 canvases)
│ ├── music-player.js # Web Audio API engine
│ ├── music-player-library.js # Library browser UI
│ ├── gallery-lightbox.js # Image lightbox
│ ├── sakura.js # Cherry blossom particles
│ └── ...
├── scripts/ # Build helpers
│ ├── scan-music.mjs # Music metadata scanner
│ ├── generate-responsive-wallpapers.mjs
│ └── ...
├── src/
│ ├── __tests__/ # Test files (Vitest)
│ ├── components/ # Astro components
│ │ ├── archive/ # Archive page components
│ │ ├── music/ # Music player components
│ │ ├── HeroSection.astro # Hero full-screen cover
│ │ ├── Navbar.astro # Navigation bar
│ │ ├── Footer.astro # Page footer
│ │ ├── MusicPlayer.astro # Music player shell
│ │ └── ...
│ ├── config/ # Site configuration files
│ ├── content/ # Blog posts and docs (Markdown / MDX)
│ ├── i18n/ # UI translation strings (zh-CN, en)
│ ├── integrations/ # Astro integrations (wallpaper gen, NCM server)
│ ├── layouts/ # Page layouts
│ ├── pages/ # Route pages
│ ├── plugins/ # Remark / Rehype plugins (12 files)
│ ├── scripts/ # Client-side TypeScript modules (Vite-bundled)
│ │ ├── archive/ # Archive logic (river-math, drag, tabs)
│ │ ├── music-player/ # Music player UI, progress bar
│ │ ├── modules/ # NCM integration
│ │ └── client/ # Client app entry point
│ ├── styles/ # CSS design system
│ ├── types/ # TypeScript type definitions
│ └── utils/ # Utility functions (dates, slugs, links)
├── astro.config.ts
├── package.json
└── package-lock.json
Beyond standard GitHub Flavored Markdown, this template supports:
| Extension | Syntax | Description |
|---|---|---|
| Admonitions | :::note / :::warning etc. |
5 types of callout boxes |
| Math | $E=mc^2$ / $$\int$$ |
LaTeX via KaTeX + MathJax |
| Diagrams | ```mermaid |
Flowcharts, sequence, Gantt |
| GitHub Cards | :github[user/repo] |
Repository info cards |
| Image Width |  |
Custom width + centering |
| Code Blocks | ```lang |
Line numbers, copy, collapse |
Push to GitHub and import the repo into Vercel. No configuration required.
npm run build
# Deploy dist/ to the gh-pages branchUpdate site in astro.config.ts and siteURL in src/config/siteConfig.ts to match your GitHub Pages URL.
npm run build
# Deploy the dist/ directory via Cloudflare dashboard or wrangler CLINo environment variables required. The project works out of the box with zero configuration for deployment.
- Built with Astro — the best static site framework
- Fonts: LXGW WenKai (Chinese), JetBrains Mono Nerd Font (code)
- Math: KaTeX + MathJax
- Search: Pagefind
- Inspiration: Roon (music UI), Apple Music (library browser), foobar2000 (DSP), Kirameku (archive river)
Code: MIT — use it, modify it, learn from it.
If you find this project helpful, please consider giving it a star!