maxItems: 4, // Number of logos when scrolling is false
},
links: {
docs: "/docs",
convex: "https://convex.dev",
},
};
```
**Logo configuration:**
-`logo`: Homepage logo path (set to `null` to hide). Uses `public/images/logo.svg` by default.
-`innerPageLogo`: Logo shown on blog page, posts, and static pages. Desktop: top left. Mobile: top right. Set `enabled: false` to hide on inner pages while keeping homepage logo.
**Navigation structure:**
Navigation combines three sources sorted by `order`:
1. Blog link (if `blogPage.enabled` and `blogPage.showInNav` are true)
2. Hardcoded nav items (React routes from `hardcodedNavItems`)
3. Markdown pages (from `content/pages/` with `showInNav: true`)
All items sort by `order` (lower first), then alphabetically by title.
### Featured items
Posts and pages appear in the featured section when marked with `featured: true` in frontmatter.
**Add to featured section:**
```yaml
# In any post or page frontmatter
featured: true
featuredOrder: 1
excerpt: "Short description for card view."
image: "/images/thumbnail.png"
```
Then run `npm run sync` or `npm run sync:all`. No redeploy needed.
| `featured` | Set `true` to show in featured section |
| `featuredOrder` | Order in featured section (lower = first) |
| `excerpt` | Short text shown on card view |
| `image` | Thumbnail for card view (displays as square) |
**Thumbnail images:** In card view, the `image` field displays as a square thumbnail above the title. Non-square images are automatically cropped to center. Square thumbnails: 400x400px minimum (800x800px for retina).
**Posts without images:** Cards display without the image area. The card shows just the title and excerpt with adjusted padding.
**Ordering:** Items with `featuredOrder` appear first (lower numbers first). Items without `featuredOrder` appear after, sorted by creation time.
**Display options (in siteConfig):**
```typescript
// In src/pages/Home.tsx
const siteConfig = {
featuredViewMode: "list", // 'list' or 'cards'
showViewToggle: true, // Let users switch views
};
```
### GitHub contributions graph
Display your GitHub contribution activity on the homepage. Configure in `siteConfig`:
```typescript
gitHubContributions: {
enabled: true, // Set to false to hide
username: "yourusername", // Your GitHub username
showYearNavigation: true, // Show arrows to navigate between years
linkToProfile: true, // Click graph to open GitHub profile
title: "GitHub Activity", // Optional title above the graph
| `showYearNavigation` | Show prev/next year navigation |
| `linkToProfile` | Click graph to visit GitHub profile |
| `title` | Text above graph (`undefined` to hide) |
Theme-aware colors match each site theme. Uses public API (no GitHub token required).
### Visitor map
Display real-time visitor locations on a world map on the stats page. Uses Netlify's built-in geo detection (no third-party API needed). Privacy friendly: only stores city, country, and coordinates. No IP addresses stored.
```typescript
visitorMap: {
enabled: true, // Set to false to hide
title: "Live Visitors", // Optional title above the map
| `title` | Text above map (`undefined` to hide) |
The map displays with theme-aware colors. Visitor dots pulse to indicate live sessions. Location data comes from Netlify's automatic geo headers at the edge.
### Logo gallery
The homepage includes a logo gallery that can scroll infinitely or display as a static grid. Each logo can link to a URL.
| `position` | `'above-footer'` or `'below-featured'` |
| `speed` | Seconds for one scroll cycle (lower = faster) |
| `title` | Text above gallery (`undefined` to hide) |
| `scrolling` | `true` for infinite scroll, `false` for static grid |
| `maxItems` | Max logos to show when `scrolling` is `false` (default: 4) |
**Display modes:**
-`scrolling: true`: Infinite horizontal scroll with all logos
-`scrolling: false`: Static centered grid showing first `maxItems` logos
**To add logos:**
1. Add SVG/PNG files to `public/images/logos/`
2. Update the `images` array with `src` paths and `href` URLs
3. Push to GitHub (requires rebuild)
**To disable:** Set `enabled: false`
**To remove samples:** Delete files from `public/images/logos/` or clear the images array.
### Blog page
The site supports a dedicated blog page at `/blog` with two view modes: list view (year-grouped posts) and card view (thumbnail grid). Configure in `src/config/siteConfig.ts`:
```typescript
blogPage: {
enabled: true, // Enable /blog route
showInNav: true, // Show in navigation
title: "Blog", // Nav link and page title
order: 0, // Nav order (lower = first)
viewMode: "list", // Default view: "list" or "cards"
showViewToggle: true, // Show toggle button to switch views
},
displayOnHomepage: true, // Show posts on homepage
| `order` | Position in navigation (lower = first) |
| `viewMode` | Default view: `"list"` or `"cards"` |
| `showViewToggle` | Show toggle button to switch views |
| `displayOnHomepage` | Show post list on homepage |
**View modes:**
- **List view:** Year-grouped posts with titles, read time, and dates
- **Card view:** Grid of cards showing thumbnails, titles, excerpts, and metadata
**Card view details:**
Cards display post thumbnails (from `image` frontmatter field), titles, excerpts (or descriptions), read time, and dates. Posts without images show cards without thumbnail areas. Grid is responsive: 3 columns on desktop, 2 on tablet, 1 on mobile.
**Navigation order:** The Blog link merges with page links and sorts by order. Pages use the `order` field in frontmatter. Set `blogPage.order: 5` to position Blog after pages with order 0-4.
**View preference:** User's view mode choice is saved to localStorage and persists across page visits.
### Scroll-to-top button
A scroll-to-top button appears after scrolling down. Configure in `src/components/Layout.tsx`:
| Site logo | `public/images/logo.svg` | 512x512 |
| Default OG image | `public/images/og-default.svg` | 1200x630 |
| Post images | `public/images/` | Any |
**Images require git deploy.** Images are served as static files from your repository, not synced to Convex. After adding images to `public/images/`:
1. Commit the image files to git
2. Push to GitHub
3. Wait for Netlify to rebuild
The `npm run sync` command only syncs markdown text content. Images are deployed when Netlify builds your site. Use `npm run sync:discovery` to update discovery files (AGENTS.md, llms.txt) when site configuration changes.
**Adding images to posts:** You can add images using markdown syntax `` or HTML `<img>` tags. The site uses `rehypeRaw` and `rehypeSanitize` to safely render HTML in markdown content. See [Using Images in Blog Posts](/using-images-in-posts) for complete examples and best practices.
**Logo options:**
- **Homepage logo:** Configured via `logo` in `siteConfig.ts`. Set to `null` to hide.
- **Inner page logo:** Configured via `innerPageLogo` in `siteConfig.ts`. Shows on blog page, posts, and static pages. Desktop: top left corner. Mobile: top right corner (smaller). Set `enabled: false` to hide on inner pages while keeping homepage logo.
Configure AI-powered features in `src/config/siteConfig.ts`:
**Semantic Search:**
```typescript
semanticSearch: {
enabled: true, // Requires OPENAI_API_KEY in Convex
},
```
When enabled, users can toggle between keyword and semantic search in the search modal (Cmd+K). Semantic search finds content by meaning using OpenAI embeddings.