diff --git a/.github/workflows/bootstrap-pages-branch.yml b/.github/workflows/bootstrap-pages-branch.yml new file mode 100644 index 0000000..bd2150a --- /dev/null +++ b/.github/workflows/bootstrap-pages-branch.yml @@ -0,0 +1,50 @@ +name: Bootstrap gh-pages branch + +on: + workflow_dispatch: + +permissions: + contents: write + +jobs: + bootstrap: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Create gh-pages branch if missing + shell: bash + run: | + set -euo pipefail + git fetch --all + + if git ls-remote --exit-code --heads origin gh-pages >/dev/null 2>&1; then + echo "gh-pages branch already exists; nothing to do." + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + git checkout --orphan gh-pages + git rm -rf . >/dev/null 2>&1 || true + + cat > index.html <<'HTML' + + + + + + Marcus Astro Pages Bootstrap + + +

GitHub Pages bootstrap branch created. Deploy workflows will replace this content.

+ + + HTML + + touch .nojekyll + git add index.html .nojekyll + git commit -m "Bootstrap gh-pages branch" + git push origin gh-pages diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..264a3f5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI + +on: + pull_request: + push: + branches: [main, master, dev, work] + +permissions: + contents: read + +jobs: + quality: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Astro + uses: withastro/action@v3 + with: + path: . + node-version: 20 + package-manager: npm + + - name: Type/content check + run: npm run check + + - name: Build site + run: npm run build diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml new file mode 100644 index 0000000..053dcc2 --- /dev/null +++ b/.github/workflows/deploy-dev.yml @@ -0,0 +1,54 @@ +name: Deploy development site + +on: + push: + branches: [dev] + workflow_dispatch: + inputs: + ref: + description: 'Branch/tag/SHA to deploy as preview' + required: false + default: 'dev' + +permissions: + contents: write + +env: + PAGES_BRANCH: gh-pages + +concurrency: + group: deploy-dev + cancel-in-progress: true + +jobs: + deploy: + runs-on: ubuntu-latest + env: + DEPLOY_TARGET: dev + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.ref || 'dev' }} + + - name: Setup Astro + uses: withastro/action@v3 + with: + path: . + node-version: 20 + package-manager: npm + + - name: Build development preview site + run: npm run build + + - name: Add .nojekyll for GitHub Pages + run: touch dist/.nojekyll + + - name: Deploy to pages branch /preview + uses: JamesIves/github-pages-deploy-action@v4 + with: + branch: ${{ env.PAGES_BRANCH }} + folder: dist + target-folder: preview + clean: true diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml new file mode 100644 index 0000000..280cfe6 --- /dev/null +++ b/.github/workflows/deploy-prod.yml @@ -0,0 +1,47 @@ +name: Deploy production site + +on: + push: + branches: [main, master] + workflow_dispatch: + +permissions: + contents: write + +env: + PAGES_BRANCH: gh-pages + +concurrency: + group: deploy-prod + cancel-in-progress: true + +jobs: + deploy: + runs-on: ubuntu-latest + env: + DEPLOY_TARGET: prod + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Astro + uses: withastro/action@v3 + with: + path: . + node-version: 20 + package-manager: npm + + - name: Build production site + run: npm run build + + - name: Add .nojekyll for GitHub Pages + run: touch dist/.nojekyll + + - name: Deploy to pages branch root + uses: JamesIves/github-pages-deploy-action@v4 + with: + branch: ${{ env.PAGES_BRANCH }} + folder: dist + clean: true + clean-exclude: preview diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 0000000..f1da402 --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,129 @@ +name: Preview on Pull Request + +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + contents: write + pull-requests: write + +concurrency: + group: preview-${{ github.event.pull_request.number }} + cancel-in-progress: true + +env: + PAGES_BRANCH: gh-pages + +jobs: + preview-deploy: + runs-on: ubuntu-latest + env: + DEPLOY_TARGET: pr + DEPLOY_PREVIEW_PATH: pr-${{ github.event.pull_request.number }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Ensure gh-pages branch exists + shell: bash + run: | + set -euo pipefail + git fetch --all + if git ls-remote --exit-code --heads origin "$PAGES_BRANCH" >/dev/null 2>&1; then + echo "Branch $PAGES_BRANCH exists" + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout --orphan "$PAGES_BRANCH" + git rm -rf . >/dev/null 2>&1 || true + printf 'Bootstrap

Preview bootstrap.

' > index.html + touch .nojekyll + git add index.html .nojekyll + git commit -m "Bootstrap gh-pages for previews" + git push origin "$PAGES_BRANCH" + + - name: Setup Astro + uses: withastro/action@v3 + with: + path: . + node-version: 20 + package-manager: npm + + - name: Build site + run: npm run build + + - name: Add .nojekyll for GitHub Pages + run: touch dist/.nojekyll + + - name: Deploy PR preview to gh-pages + uses: JamesIves/github-pages-deploy-action@v4 + with: + branch: ${{ env.PAGES_BRANCH }} + folder: dist + target-folder: preview/pr-${{ github.event.pull_request.number }} + clean: true + + - name: Verify preview URL is reachable + shell: bash + env: + PREVIEW_URL: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/preview/pr-${{ github.event.pull_request.number }}/ + run: | + set -euo pipefail + echo "Checking $PREVIEW_URL" + for i in $(seq 1 20); do + code=$(curl -s -o /dev/null -w "%{http_code}" "$PREVIEW_URL" || true) + if [ "$code" = "200" ]; then + echo "Preview is live" + exit 0 + fi + echo "Attempt $i/20: HTTP $code, retrying in 15s..." + sleep 15 + done + echo "Preview did not become reachable in time" + exit 1 + + - name: Comment preview URL on PR + if: success() + uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request.number; + const url = `https://${context.repo.owner}.github.io/${context.repo.repo}/preview/pr-${pr}/`; + const body = [ + '🔍 PR preview is ready.', + '', + `**Open preview:** ${url}` + ].join('\n'); + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr, + body + }); + + - name: Comment failure help on PR + if: failure() + uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request.number; + const body = [ + '❌ PR preview deployment finished but URL was not reachable in time.', + '', + 'Please check **Settings → Pages**:', + '- Source: `Deploy from a branch`', + '- Branch: `gh-pages`', + '- Folder: `/(root)`', + '', + 'Then re-run the `Preview on Pull Request` workflow.' + ].join('\n'); + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr, + body + }); diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..953a5b8 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ +# presence of this file disables Jekyll processing on GitHub Pages diff --git a/README.md b/README.md new file mode 100644 index 0000000..b1e3b83 --- /dev/null +++ b/README.md @@ -0,0 +1,168 @@ +# Marcus Astro Theme + +A minimal, editorial Astro theme inspired by calm personal blogs and designed for technical writing. + +## Features + +- Structured front page with sectioned lists (Posts + Micro) +- Content collections with typed frontmatter (`posts`, `micro`) +- Responsive reading width (optimized around `880px` content column) +- Optional sticky left TOC on long-form post pages +- Light/dark mode with persistent preference (`localStorage`) +- Clearly-marked sample content included so the theme works out of the box + +## Quick start + +```bash +npm install +npm run dev +``` + +## Theme structure + +- `src/layouts/BaseLayout.astro` — metadata, shell, and global header +- `src/components/Header.astro` — minimal top navigation +- `src/components/ThemeToggle.astro` — client-side theme switching +- `src/pages/` — homepage, list pages, and dynamic content routes +- `src/content/` — sample posts and micro notes + +## Build checks + +```bash +npm run check +npm run build +``` + + +## Sample content note + +All files under `src/content/` are intentionally demo placeholders so you can safely replace them. + + +## Where to see the sample website + +This repo now supports **two Astro deployments from one repository**: + +- **Production**: `main`/`master` branch → `gh-pages` root +- **Development preview**: `dev` branch → `gh-pages/preview/` + +For a project site, URLs are typically: + +- Prod: `https://sudheer.github.io/marcus-astro/` +- Dev: `https://sudheer.github.io/marcus-astro/preview/` + +## GitHub Pages setup (one-time) + +Yes, this is mostly a one-time setup. + +1. Open **Actions** and run **Bootstrap gh-pages branch** once. + - This creates `gh-pages` so it appears in Pages branch selection. +2. Open **Settings → Pages** in your repository. +3. Under **Build and deployment**, choose: + - **Source**: `Deploy from a branch` + - **Branch**: `gh-pages` + - **Folder**: `/(root)` +4. Save. + +After that: + +- pushing to `main`/`master` runs `.github/workflows/deploy-prod.yml` +- pushing to `dev` runs `.github/workflows/deploy-dev.yml` + +The workflows publish to the same `gh-pages` branch, with prod at root and dev under `/preview/`. + +## Review website before merging (PR previews) + +A pull request preview workflow is included at `.github/workflows/preview.yml`. + +When you open or update a PR: + +1. GitHub Actions builds the site for that PR. +2. It deploys to `gh-pages/preview/pr-/`. +3. It comments a direct preview URL on the PR. + +Preview URL format: + +- `https://sudheer.github.io/marcus-astro/preview/pr-/` + +This provides a clickable live URL for each PR without using the alpha GitHub Pages preview feature. + + +## Troubleshooting `/preview/` 404 + +If `https://sudheer.github.io/marcus-astro/preview/` shows 404, it usually means Pages is reading the wrong branch. + +Verify this exact setup: + +1. **Settings → Pages** +2. **Source** = `Deploy from a branch` +3. **Branch** = `gh-pages` +4. **Folder** = `/(root)` +5. Save + +Then run: + +- **Actions → Deploy development site** (manual run is okay) to publish `/preview/` +- **Actions → Deploy production site** to refresh root site + +Important: if Pages is set to any branch other than `gh-pages` (for example your feature branch), `/preview/` will not exist there and returns 404. + + +### Why `gh-pages` was missing + +GitHub only shows existing branches in Pages settings. If `gh-pages` was never created, it will not appear in the dropdown. Running the bootstrap workflow once fixes that. + + +## If you see Jekyll error on `.astro` files + +If GitHub logs show `Invalid YAML front matter ... .astro`, your repo is being built by the default **pages build and deployment** (Jekyll) job on source branch files. + +Fix: + +1. Set **Settings → Pages → Source** to `Deploy from a branch` +2. Select branch `gh-pages`, folder `/(root)` +3. Run **Bootstrap gh-pages branch** once (if missing) +4. Re-run deploy workflows + +This repository also includes `_config.yml` + `.nojekyll` fallback to avoid Jekyll parsing Astro source if branch configuration is temporarily incorrect. + + +## If GitHub Pages build logs show `Source: ./docs` + +That means repository Pages is currently configured to build from the `docs/` folder. + +This repo now includes a minimal `docs/index.md` so that configuration no longer fails with `No such file or directory ... /docs`. + +Still recommended for Astro deployments: + +1. **Settings → Pages** +2. **Source**: `Deploy from a branch` +3. **Branch**: `gh-pages` +4. **Folder**: `/(root)` + + +### Why preview URL can exist but still look broken + +Astro static assets are emitted under `/_astro/`. On GitHub Pages, if `.nojekyll` is missing in the published output, Jekyll can ignore underscore-prefixed folders and assets may 404. + +Deploy workflows now write `dist/.nojekyll` before publish (prod/dev/PR preview) to keep preview links working correctly. + + +### PR preview reliability checks + +PR preview workflow now: + +1. Ensures `gh-pages` exists (bootstraps if missing) +2. Deploys PR build to `/preview/pr-/` +3. Verifies URL returns HTTP 200 before posting the preview link +4. Posts a failure comment with exact Pages setting fixes if the URL is unreachable + + +## Workflow stack + +This repo now uses the **official Astro GitHub Action** (`withastro/action`) for setup/build steps, while keeping **branch-based publish** to `gh-pages` for prod/dev/PR preview paths. + + +### CI note: no lockfile + +`withastro/action` requires explicit package manager when a lockfile is not present. Workflows are configured with `package-manager: npm` to avoid setup failures. diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..e96e947 --- /dev/null +++ b/_config.yml @@ -0,0 +1,15 @@ +# Fallback config only for accidental GitHub Pages Jekyll builds. +# The real site is Astro and is deployed by GitHub Actions workflows. +exclude: + - src + - node_modules + - package.json + - package-lock.json + - tsconfig.json + - astro.config.mjs + - .github + - README.md + +# Prevent Jekyll from trying to transform Astro source when branch-based +# Pages build runs unexpectedly. +markdown: kramdown diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000..ce9c762 --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,32 @@ +import { defineConfig } from 'astro/config'; + +const repository = process.env.GITHUB_REPOSITORY?.split('/')[1]; +const isUserSiteRepo = repository === 'sudheer.github.io'; +const isGithubActions = process.env.GITHUB_ACTIONS === 'true'; +const deployTarget = process.env.DEPLOY_TARGET; +const previewPath = process.env.DEPLOY_PREVIEW_PATH; + +const repoBase = repository && !isUserSiteRepo ? `/${repository}` : ''; + +const normalizeBase = (value) => { + const withLeadingSlash = value.startsWith('/') ? value : `/${value}`; + return withLeadingSlash.endsWith('/') ? withLeadingSlash : `${withLeadingSlash}/`; +}; + +const base = !isGithubActions + ? '/' + : deployTarget === 'dev' + ? normalizeBase(`${repoBase}/preview`) + : deployTarget === 'pr' && previewPath + ? normalizeBase(`${repoBase}/preview/${previewPath}`) + : normalizeBase(repoBase || '/'); + +export default defineConfig({ + site: 'https://sudheer.github.io', + base, + markdown: { + shikiConfig: { + theme: 'github-dark' + } + } +}); diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..bd8456b --- /dev/null +++ b/docs/.nojekyll @@ -0,0 +1 @@ +# disable jekyll processing in docs output mode diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..ee523d1 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,22 @@ +--- +layout: default +title: Marcus Astro +--- + +# Marcus Astro Theme + +This repository is built and deployed as an Astro site via GitHub Actions. + +- Production URL: +- Preview URL: + +If you are seeing this page, GitHub Pages is currently configured to build from `docs/`. + +## Recommended setting + +Use the workflow-based branch deployment: + +- **Settings → Pages** +- **Source**: Deploy from a branch +- **Branch**: `gh-pages` +- **Folder**: `/(root)` diff --git a/package.json b/package.json new file mode 100644 index 0000000..33a5090 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "marcus-astro", + "version": "0.1.0", + "type": "module", + "private": true, + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro check && astro build", + "preview": "astro preview", + "check": "astro check" + }, + "dependencies": { + "astro": "^5.2.0" + } +} diff --git a/src/components/Header.astro b/src/components/Header.astro new file mode 100644 index 0000000..80839ec --- /dev/null +++ b/src/components/Header.astro @@ -0,0 +1,92 @@ +--- +import ThemeToggle from './ThemeToggle.astro'; + +const base = import.meta.env.BASE_URL; + +const links = [ + { href: '', label: 'Home' }, + { href: 'posts/', label: 'Posts' }, + { href: 'micro/', label: 'Micro' } +]; + +const normalizePath = (pathname: string) => { + if (!pathname.startsWith(base)) return pathname; + const rest = pathname.slice(base.length); + return `/${rest}`.replace(/\/+/g, '/'); +}; + +const isActive = (href: string, pathname: string) => { + const target = `/${href}`.replace(/\/+/g, '/').replace(/\/$/, '') || '/'; + const current = normalizePath(pathname).replace(/\/$/, '') || '/'; + return target === '/' ? current === '/' : current.startsWith(target); +}; + +const pathname = Astro.url.pathname; +--- + +
+
+ SuDo + +
+
+ + diff --git a/src/components/PostSection.astro b/src/components/PostSection.astro new file mode 100644 index 0000000..9d92791 --- /dev/null +++ b/src/components/PostSection.astro @@ -0,0 +1,75 @@ +--- +interface Item { + slug: string; + data: { + title: string; + pubDate: Date; + }; +} + +interface Props { + title: string; + href: string; + items: Item[]; +} + +const { title, href, items } = Astro.props; +const base = import.meta.env.BASE_URL; +const formatter = new Intl.DateTimeFormat('en-CA'); +--- + +
+

{title} →

+
    + {items.map((item) => ( +
  • + {item.data.title} + +
  • + ))} +
+
+ + diff --git a/src/components/ThemeToggle.astro b/src/components/ThemeToggle.astro new file mode 100644 index 0000000..6c7571c --- /dev/null +++ b/src/components/ThemeToggle.astro @@ -0,0 +1,38 @@ + + + + + diff --git a/src/content/config.ts b/src/content/config.ts new file mode 100644 index 0000000..0a2a0e0 --- /dev/null +++ b/src/content/config.ts @@ -0,0 +1,24 @@ +import { defineCollection, z } from 'astro:content'; + +const baseSchema = z.object({ + title: z.string(), + description: z.string().optional(), + pubDate: z.coerce.date(), + updatedDate: z.coerce.date().optional(), + tags: z.array(z.string()).default([]), + draft: z.boolean().default(false) +}); + +const posts = defineCollection({ + schema: baseSchema.extend({ + category: z.enum(['Writing', 'Notes', 'TIL', 'Reading', 'Projects']).default('Writing') + }) +}); + +const micro = defineCollection({ + schema: baseSchema.extend({ + category: z.literal('Micro').default('Micro') + }) +}); + +export const collections = { posts, micro }; diff --git a/src/content/micro/first-micro-note-sample.md b/src/content/micro/first-micro-note-sample.md new file mode 100644 index 0000000..0b8c4b7 --- /dev/null +++ b/src/content/micro/first-micro-note-sample.md @@ -0,0 +1,9 @@ +--- +title: "First micro note (sample)" +description: "A tiny placeholder note to preview the micro card style." +pubDate: 2026-01-20 +category: "Micro" +tags: ["sample"] +--- + +This is sample micro content. Swap it with your own short notes. diff --git a/src/content/micro/second-micro-note-sample.md b/src/content/micro/second-micro-note-sample.md new file mode 100644 index 0000000..503b0d6 --- /dev/null +++ b/src/content/micro/second-micro-note-sample.md @@ -0,0 +1,9 @@ +--- +title: "Second micro note (sample)" +description: "Another placeholder entry to demonstrate spacing and date alignment." +pubDate: 2026-01-19 +category: "Micro" +tags: ["sample"] +--- + +Use this format for quick thoughts, links, changelog-style updates, or TIL snippets. diff --git a/src/content/micro/third-micro-note-sample.md b/src/content/micro/third-micro-note-sample.md new file mode 100644 index 0000000..da602e5 --- /dev/null +++ b/src/content/micro/third-micro-note-sample.md @@ -0,0 +1,9 @@ +--- +title: "Third micro note (sample)" +description: "Short sample used for previewing list density in the micro page." +pubDate: 2026-01-16 +category: "Micro" +tags: ["sample"] +--- + +The theme intentionally keeps visual noise low so your writing is the focus. diff --git a/src/content/posts/building-with-astro-content-collections.md b/src/content/posts/building-with-astro-content-collections.md new file mode 100644 index 0000000..8b0d025 --- /dev/null +++ b/src/content/posts/building-with-astro-content-collections.md @@ -0,0 +1,26 @@ +--- +title: "Building with Astro Content Collections" +description: "A sample post that demonstrates tables, lists, and content structure." +pubDate: 2026-01-08 +category: "Notes" +tags: ["sample", "astro"] +--- + +This is another **sample article** included to preview list pages and post pages. + +## Content checklist + +1. Add frontmatter. +2. Write clear headings. +3. Keep paragraphs short. +4. Add code only when useful. + +## Table example + +| Section | Purpose | +| --- | --- | +| Home | Shows latest grouped content | +| Posts | Long-form articles | +| Micro | Short notes and quick updates | + +The point is to make this unmistakably demo content that users can replace quickly. diff --git a/src/content/posts/welcome-to-marcus-theme.md b/src/content/posts/welcome-to-marcus-theme.md new file mode 100644 index 0000000..e905346 --- /dev/null +++ b/src/content/posts/welcome-to-marcus-theme.md @@ -0,0 +1,27 @@ +--- +title: "Welcome to Marcus Theme" +description: "A starter post that shows heading rhythm, spacing, and code styling." +pubDate: 2026-01-15 +category: "Writing" +tags: ["sample", "welcome"] +--- + +> This is sample content bundled with the theme. + +Marcus Theme is a minimal Astro starter designed for long-form technical writing. + +## Why this sample exists + +- Demonstrates title + metadata layout. +- Shows paragraph spacing for readable essays. +- Gives you a quick page to replace with your own content. + +## Code block example + +```ts +export function greet(name: string) { + return `Hello, ${name}!`; +} +``` + +Replace this file with your first real article when you start your blog. diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro new file mode 100644 index 0000000..a51ae92 --- /dev/null +++ b/src/layouts/BaseLayout.astro @@ -0,0 +1,30 @@ +--- +import Header from '../components/Header.astro'; +import '../styles/global.css'; + +interface Props { + title: string; + description?: string; +} + +const { + title, + description = 'Minimal Astro writing theme focused on editorial readability and technical content.' +} = Astro.props; +--- + + + + + + + + {title} + + +
+
+ +
+ + diff --git a/src/pages/index.astro b/src/pages/index.astro new file mode 100644 index 0000000..1d3bebf --- /dev/null +++ b/src/pages/index.astro @@ -0,0 +1,32 @@ +--- +import { getCollection } from 'astro:content'; +import BaseLayout from '../layouts/BaseLayout.astro'; +import PostSection from '../components/PostSection.astro'; + +const posts = (await getCollection('posts', ({ data }) => !data.draft)).sort( + (a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime() +); + +const micro = (await getCollection('micro', ({ data }) => !data.draft)).sort( + (a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime() +); +--- + + +
+

+ Minimal Astro theme for technical writing. Structured lists, calm typography, optional TOC, and + dark mode included. +

+
+ + + +
+ + diff --git a/src/pages/micro/[slug].astro b/src/pages/micro/[slug].astro new file mode 100644 index 0000000..a5be1ac --- /dev/null +++ b/src/pages/micro/[slug].astro @@ -0,0 +1,47 @@ +--- +import { getCollection } from 'astro:content'; +import BaseLayout from '../../layouts/BaseLayout.astro'; + +export async function getStaticPaths() { + const notes = await getCollection('micro', ({ data }) => !data.draft); + return notes.map((post) => ({ + params: { slug: post.slug }, + props: { post } + })); +} + +const { post } = Astro.props; +const { Content } = await post.render(); +const formatter = new Intl.DateTimeFormat('en-CA'); +--- + + +
+

{post.data.title}

+

+ +

+ +
+
+ + diff --git a/src/pages/micro/index.astro b/src/pages/micro/index.astro new file mode 100644 index 0000000..88bcabb --- /dev/null +++ b/src/pages/micro/index.astro @@ -0,0 +1,82 @@ +--- +import { getCollection } from 'astro:content'; +import BaseLayout from '../../layouts/BaseLayout.astro'; + +const notes = (await getCollection('micro', ({ data }) => !data.draft)).sort( + (a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime() +); +const formatter = new Intl.DateTimeFormat('en-CA'); +const base = import.meta.env.BASE_URL; +--- + + +
+

Micro

+
+ {notes.map((note) => ( +
+
+

{note.data.title}

+ +
+

{note.data.description}

+
+ ))} +
+
+
+ + diff --git a/src/pages/posts/[slug].astro b/src/pages/posts/[slug].astro new file mode 100644 index 0000000..1b1de1a --- /dev/null +++ b/src/pages/posts/[slug].astro @@ -0,0 +1,98 @@ +--- +import { getCollection } from 'astro:content'; +import BaseLayout from '../../layouts/BaseLayout.astro'; + +export async function getStaticPaths() { + const posts = await getCollection('posts', ({ data }) => !data.draft); + return posts.map((post) => ({ + params: { slug: post.slug }, + props: { post } + })); +} + +const { post } = Astro.props; +const { Content, headings } = await post.render(); +const formatter = new Intl.DateTimeFormat('en-CA'); +--- + + +
+ + +
+
+

{post.data.title}

+

+ +

+
+ +
+
+
+ + diff --git a/src/pages/posts/index.astro b/src/pages/posts/index.astro new file mode 100644 index 0000000..59dd1c9 --- /dev/null +++ b/src/pages/posts/index.astro @@ -0,0 +1,60 @@ +--- +import { getCollection } from 'astro:content'; +import BaseLayout from '../../layouts/BaseLayout.astro'; + +const posts = (await getCollection('posts', ({ data }) => !data.draft)).sort( + (a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime() +); +const formatter = new Intl.DateTimeFormat('en-CA'); +const base = import.meta.env.BASE_URL; +--- + + +
+

Posts

+
    + {posts.map((post) => ( +
  • + {post.data.title} + +
  • + ))} +
+
+
+ + diff --git a/src/styles/global.css b/src/styles/global.css new file mode 100644 index 0000000..e09d7ce --- /dev/null +++ b/src/styles/global.css @@ -0,0 +1,93 @@ +:root { + color-scheme: light dark; + font-size: 17px; + --bg: #f5f7f9; + --panel: #ffffff; + --text: #1f2933; + --muted: #627282; + --line: #d8e0e7; + --code-bg: #eff3f6; + --link: #1f2933; + --link-hover: #000; + --max-width: 920px; + --toc-width: 240px; + --radius: 12px; +} + +:root[data-theme='dark'] { + --bg: #0f1419; + --panel: #131c24; + --text: #e8edf2; + --muted: #9eb0c0; + --line: #243343; + --code-bg: #1b2834; + --link: #eef3f8; + --link-hover: #fff; +} + +* { box-sizing: border-box; } + +html, body { + margin: 0; + padding: 0; + font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; + background: var(--bg); + color: var(--text); +} + +body { + line-height: 1.65; + min-height: 100vh; +} + +a { color: var(--link); text-decoration: none; } +a:hover { color: var(--link-hover); } + +.shell { + width: min(calc(100% - 2.5rem), 1180px); + margin: 0 auto; +} + +.content { + width: min(100%, var(--max-width)); +} + +h1, h2, h3 { line-height: 1.2; letter-spacing: -0.02em; } + +.prose p, +.prose li, +.prose table { font-size: 1.08rem; } + +.prose pre { + background: var(--code-bg); + border-radius: 10px; + padding: 1rem; + overflow-x: auto; +} + +.prose code { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, monospace; + font-size: 0.92em; +} + +.prose table { + border-collapse: collapse; + display: block; + overflow-x: auto; + width: 100%; +} + +.prose th, +.prose td { + border: 1px solid var(--line); + padding: 0.55rem 0.75rem; + text-align: left; +} + +.muted { color: var(--muted); } + +@media (max-width: 960px) { + .shell { + width: min(calc(100% - 2rem), 100%); + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..801d1ab --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "astro/tsconfigs/strict", + "compilerOptions": { + "baseUrl": "." + } +}