mirror of
https://github.com/waynesutton/markdown-site.git
synced 2026-01-12 04:09:14 +00:00
274 lines
6.2 KiB
Markdown
274 lines
6.2 KiB
Markdown
# Markdown Site
|
|
|
|
A minimalist markdown site built with React, Convex, and Vite. Optimized for SEO, AI agents, and LLM discovery.
|
|
|
|
## Features
|
|
|
|
- Markdown-based blog posts with frontmatter
|
|
- Syntax highlighting for code blocks
|
|
- Four theme options: Dark, Light, Tan (default), Cloud
|
|
- Real-time data with Convex
|
|
- Fully responsive design
|
|
|
|
### SEO and Discovery
|
|
|
|
- RSS feeds at `/rss.xml` and `/rss-full.xml` (with full content)
|
|
- Dynamic sitemap at `/sitemap.xml`
|
|
- JSON-LD structured data for Google rich results
|
|
- Open Graph and Twitter Card meta tags
|
|
- `robots.txt` with AI crawler rules
|
|
- `llms.txt` for AI agent discovery
|
|
|
|
### AI and LLM Access
|
|
|
|
- `/api/posts` - JSON list of all posts for agents
|
|
- `/api/post?slug=xxx` - Single post JSON or markdown
|
|
- `/rss-full.xml` - Full content RSS for LLM ingestion
|
|
- Copy Page dropdown for sharing to ChatGPT, Claude, Cursor, VS Code
|
|
|
|
## Getting Started
|
|
|
|
### Prerequisites
|
|
|
|
- Node.js 18 or higher
|
|
- A Convex account
|
|
|
|
### Setup
|
|
|
|
1. Install dependencies:
|
|
|
|
```bash
|
|
npm install
|
|
```
|
|
|
|
2. Initialize Convex:
|
|
|
|
```bash
|
|
npx convex dev
|
|
```
|
|
|
|
This will create your Convex project and generate the `.env.local` file.
|
|
|
|
3. Start the development server:
|
|
|
|
```bash
|
|
npm run dev
|
|
```
|
|
|
|
4. Open http://localhost:5173
|
|
|
|
## Writing Blog Posts
|
|
|
|
Create markdown files in `content/blog/` with frontmatter:
|
|
|
|
## Static Pages (Optional)
|
|
|
|
Create optional pages like About, Projects, or Contact in `content/pages/`:
|
|
|
|
```markdown
|
|
---
|
|
title: "About"
|
|
slug: "about"
|
|
published: true
|
|
order: 1
|
|
---
|
|
|
|
Your page content here...
|
|
```
|
|
|
|
Pages appear as navigation links in the top right, next to the theme toggle. The `order` field controls display order (lower numbers first).
|
|
|
|
```markdown
|
|
---
|
|
title: "Your Post Title"
|
|
description: "A brief description"
|
|
date: "2025-01-15"
|
|
slug: "your-post-slug"
|
|
published: true
|
|
tags: ["tag1", "tag2"]
|
|
readTime: "5 min read"
|
|
image: "/images/my-header.png"
|
|
---
|
|
|
|
Your markdown content here...
|
|
```
|
|
|
|
## Images
|
|
|
|
### Open Graph Images
|
|
|
|
Add an `image` field to frontmatter for social media previews:
|
|
|
|
```yaml
|
|
image: "/images/my-header.png"
|
|
```
|
|
|
|
Recommended dimensions: 1200x630 pixels. Images can be local (`/images/...`) or external URLs.
|
|
|
|
### Inline Images
|
|
|
|
Add images in markdown content:
|
|
|
|
```markdown
|
|

|
|
```
|
|
|
|
Place image files in `public/images/`. The alt text displays as a caption.
|
|
|
|
### Site Logo
|
|
|
|
Edit `src/pages/Home.tsx` to set your site logo:
|
|
|
|
```typescript
|
|
const siteConfig = {
|
|
logo: "/images/logo.svg", // Set to null to hide
|
|
// ...
|
|
};
|
|
```
|
|
|
|
Replace `public/images/logo.svg` with your own logo file.
|
|
|
|
### Favicon
|
|
|
|
Replace `public/favicon.svg` with your own icon. The default is a rounded square with the letter "m". Edit the SVG to change the letter or style.
|
|
|
|
### Default Open Graph Image
|
|
|
|
The default OG image is used when posts do not have an `image` field. Replace `public/images/og-default.svg` with your own image (1200x630 recommended).
|
|
|
|
Update the reference in `src/pages/Post.tsx`:
|
|
|
|
```typescript
|
|
const DEFAULT_OG_IMAGE = "/images/og-default.svg";
|
|
```
|
|
|
|
## Syncing Posts
|
|
|
|
Posts are synced to Convex at build time. To manually sync:
|
|
|
|
```bash
|
|
npm run sync
|
|
```
|
|
|
|
## Deployment
|
|
|
|
### Netlify
|
|
|
|
1. Connect your repository to Netlify
|
|
2. Set environment variables:
|
|
- `VITE_CONVEX_URL` - Your Convex deployment URL
|
|
3. Update `netlify.toml` with your Convex HTTP URL (replace `YOUR_CONVEX_DEPLOYMENT`)
|
|
4. Deploy with:
|
|
|
|
```bash
|
|
npm run deploy
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
personal-blog/
|
|
├── content/blog/ # Markdown blog posts
|
|
├── convex/ # Convex backend
|
|
│ ├── http.ts # HTTP endpoints (sitemap, API, RSS)
|
|
│ ├── posts.ts # Post queries and mutations
|
|
│ ├── rss.ts # RSS feed generation
|
|
│ └── schema.ts # Database schema
|
|
├── netlify/ # Netlify edge functions
|
|
├── public/ # Static assets
|
|
│ ├── images/ # Blog images and OG images
|
|
│ ├── robots.txt # Crawler rules
|
|
│ └── llms.txt # AI agent discovery
|
|
├── scripts/ # Build scripts
|
|
└── src/
|
|
├── components/ # React components
|
|
├── context/ # Theme context
|
|
├── pages/ # Page components
|
|
└── styles/ # Global CSS
|
|
```
|
|
|
|
## Tech Stack
|
|
|
|
- React 18
|
|
- TypeScript
|
|
- Vite
|
|
- Convex
|
|
- react-markdown
|
|
- react-syntax-highlighter
|
|
- date-fns
|
|
- lucide-react
|
|
- Netlify
|
|
|
|
## API Endpoints
|
|
|
|
| Endpoint | Description |
|
|
| ------------------------------ | ------------------------------- |
|
|
| `/rss.xml` | RSS feed with post descriptions |
|
|
| `/rss-full.xml` | RSS feed with full post content |
|
|
| `/sitemap.xml` | Dynamic XML sitemap |
|
|
| `/api/posts` | JSON list of all posts |
|
|
| `/api/post?slug=xxx` | Single post as JSON |
|
|
| `/api/post?slug=xxx&format=md` | Single post as markdown |
|
|
| `/meta/post?slug=xxx` | Open Graph HTML for crawlers |
|
|
|
|
## How Blog Post Slugs Work
|
|
|
|
Slugs are defined in the frontmatter of each markdown file:
|
|
|
|
```markdown
|
|
---
|
|
slug: "my-post-slug"
|
|
---
|
|
```
|
|
|
|
The slug becomes the URL path: `yourdomain.com/my-post-slug`
|
|
|
|
Rules:
|
|
|
|
- Slugs must be unique across all posts
|
|
- Use lowercase letters, numbers, and hyphens
|
|
- The sync script reads the `slug` field from frontmatter
|
|
- Posts are queried by slug using a Convex index
|
|
|
|
## Theme Configuration
|
|
|
|
The default theme is Tan. Users can cycle through themes using the toggle:
|
|
|
|
- Dark (Moon icon)
|
|
- Light (Sun icon)
|
|
- Tan (Half icon) - default
|
|
- Cloud (Cloud icon)
|
|
|
|
To change the default theme, edit `src/context/ThemeContext.tsx`:
|
|
|
|
```typescript
|
|
const DEFAULT_THEME: Theme = "tan"; // Change to "dark", "light", or "cloud"
|
|
```
|
|
|
|
## Font Configuration
|
|
|
|
The blog uses a serif font (New York) by default. To switch fonts, edit `src/styles/global.css`:
|
|
|
|
```css
|
|
body {
|
|
/* Sans-serif option */
|
|
font-family:
|
|
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
|
|
Cantarell, sans-serif;
|
|
|
|
/* Serif option (default) */
|
|
font-family:
|
|
"New York",
|
|
-apple-system-ui-serif,
|
|
ui-serif,
|
|
Georgia,
|
|
Cambria,
|
|
"Times New Roman",
|
|
Times,
|
|
serif;
|
|
}
|
|
```
|
|
|
|
Replace the `font-family` property with your preferred font stack.
|
|
# markdown-site
|