From 66a2c4f4d2dbc2a46b0d0749b616ef525d8d5497 Mon Sep 17 00:00:00 2001 From: Wayne Sutton Date: Sat, 20 Dec 2025 20:46:34 -0800 Subject: [PATCH] feat: add GitHub contributions graph with theme-aware colors - Add GitHubContributions component with year navigation - Display contribution activity from github-contributions-api.jogruber.de - Theme-specific colors for dark, light, tan, and cloud themes - Phosphor icons for year navigation (CaretLeft, CaretRight) - Click graph to visit GitHub profile - Configurable via siteConfig.gitHubContributions - Mobile responsive with scaled cells and hidden day labels - Add documentation to setup-guide, docs, README, and changelog --- AGENTS.md | 2 +- README.md | 76 +++- TASK.md | 10 +- changelog.md | 28 ++ content/blog/about-this-blog.md | 12 +- content/blog/how-to-publish.md | 2 +- .../new-features-search-featured-logos.md | 2 +- content/blog/setup-guide.md | 151 +++++--- content/blog/using-images-in-posts.md | 2 +- content/pages/about.md | 7 +- content/pages/changelog-page.md | 25 +- content/pages/docs.md | 98 +++-- content/pages/projects.md | 2 +- convex/http.ts | 8 +- convex/rss.ts | 4 +- files.md | 31 +- index.html | 22 +- public/images/{ => logos}/logo.svg | 0 public/images/logos/markdown.svg | 11 + public/images/logos/netlify.svg | 27 ++ public/images/logos/react.svg | 13 + public/llms.txt | 8 +- public/openapi.yaml | 4 +- public/raw/about.md | 2 +- public/raw/docs.md | 46 ++- public/raw/setup-guide.md | 46 ++- public/robots.txt | 2 +- src/components/GitHubContributions.tsx | 296 ++++++++++++++ src/components/LogoMarquee.tsx | 97 +++-- src/config/siteConfig.ts | 43 ++- src/pages/Home.tsx | 10 +- src/pages/Post.tsx | 2 +- src/styles/global.css | 363 ++++++++++++++++++ 33 files changed, 1230 insertions(+), 222 deletions(-) rename public/images/{ => logos}/logo.svg (100%) create mode 100644 public/images/logos/markdown.svg create mode 100644 public/images/logos/netlify.svg create mode 100644 public/images/logos/react.svg create mode 100644 src/components/GitHubContributions.tsx diff --git a/AGENTS.md b/AGENTS.md index 76ab856..367cc8f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ Instructions for AI coding agents working on this codebase. ## Project overview -An open-source markdown sync site for developers and AI agents. Publish from the terminal with `npm run sync`. Write locally, sync instantly with real-time updates. Powered by Convex and Netlify. +An open-source publishing framework for AI agents and developers. Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents. Built on Convex and Netlify. **Key features:** - Markdown posts with frontmatter diff --git a/README.md b/README.md index 0798d1a..d97bbdc 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# markdown "sync" site +# markdown "sync" framework -An open-source markdown sync site for developers and AI agents. Publish from the terminal with `npm run sync`. Write locally, sync instantly with real-time updates. Powered by Convex and Netlify. +An open-source publishing framework for AI agents and developers. Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents. Built on Convex and Netlify. -Write markdown locally, run `npm run sync` (dev) or `npm run sync:prod` (production), and content appears instantly across all connected browsers. Built with React, Convex, and Vite. Optimized for SEO, AI agents, and LLM discovery. +Write markdown locally, run `npm run sync` (dev) or `npm run sync:prod` (production), and content appears instantly across all connected browsers. Built with React, Convex, and Vite. Optimized for AEO, GEO, and LLM discovery. **How publishing works:** Write posts in markdown, run `npm run sync` for development or `npm run sync:prod` for production, and they appear on your live site immediately. No rebuild or redeploy needed. Convex handles real-time data sync, so all connected browsers update automatically. @@ -27,6 +27,7 @@ npm run sync:prod # production - Full text search with Command+K shortcut - Featured section with list/card view toggle - Logo gallery with continuous marquee scroll +- GitHub contributions graph with year navigation - Static raw markdown files at `/raw/{slug}.md` - Dedicated blog page with configurable navigation order - Markdown writing page at `/write` with frontmatter reference @@ -63,15 +64,16 @@ When you fork this project, update these files with your site information: | File | What to update | | ----------------------------------- | ----------------------------------------------------------- | -| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery | -| `convex/http.ts` | `SITE_URL`, `SITE_NAME` (API responses, sitemap) | +| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery, GitHub contributions | +| `src/pages/Home.tsx` | Intro paragraph text (hardcoded JSX) | +| `convex/http.ts` | `SITE_URL`, `SITE_NAME`, description strings (3 locations) | | `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` (RSS feeds) | | `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` (OG tags) | | `index.html` | Title, meta description, OG tags, JSON-LD | -| `public/llms.txt` | Site URL and description | -| `public/robots.txt` | Sitemap URL | -| `public/.well-known/ai-plugin.json` | Site name and description | -| `public/openapi.yaml` | API title and site name | +| `public/llms.txt` | Site name, URL, description, topics | +| `public/robots.txt` | Sitemap URL and header comment | +| `public/openapi.yaml` | API title, server URL, site name in examples | +| `public/.well-known/ai-plugin.json` | Site name, descriptions | See the [Setup Guide](/setup-guide) for detailed configuration examples. @@ -165,6 +167,17 @@ Add images in markdown content: Place image files in `public/images/`. The alt text displays as a caption. +### Image deployment + +Images are served as static files from your git repository, not synced to Convex. After adding images: + +1. Add image files to `public/images/` +2. Reference in frontmatter (`image: "/images/my-image.png"`) or markdown (`![Alt](/images/my-image.png)`) +3. Commit and push to git +4. Netlify rebuilds and serves the images + +The `npm run sync` command only syncs markdown text content. Images require a full deploy via git push. + ### Site Logo Edit `src/pages/Home.tsx` to set your site logo: @@ -238,13 +251,13 @@ blogPage: { displayOnHomepage: true, // Show posts on homepage ``` -| Option | Description | -| ------ | ----------- | -| `enabled` | Enable the `/blog` route | -| `showInNav` | Show Blog link in navigation | -| `title` | Text for nav link and page heading | -| `order` | Position in navigation (lower = first) | -| `displayOnHomepage` | Show post list on homepage | +| Option | Description | +| ------------------- | -------------------------------------- | +| `enabled` | Enable the `/blog` route | +| `showInNav` | Show Blog link in navigation | +| `title` | Text for nav link and page heading | +| `order` | Position in navigation (lower = first) | +| `displayOnHomepage` | Show post list on homepage | **Display options:** @@ -296,6 +309,37 @@ Delete sample files from `public/images/logos/` and replace the images array wit The gallery uses CSS animations for smooth infinite scrolling. Logos appear grayscale and colorize on hover. +## GitHub Contributions Graph + +Display your GitHub contribution activity on the homepage. Configure in `src/config/siteConfig.ts`: + +```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 +}, +``` + +| Option | Description | +| -------------------- | --------------------------------------------- | +| `enabled` | `true` to show, `false` to hide | +| `username` | Your GitHub username | +| `showYearNavigation` | Show prev/next year navigation buttons | +| `linkToProfile` | Click graph to visit GitHub profile | +| `title` | Text above graph (set to `undefined` to hide) | + +The graph displays with theme-aware colors that match each site theme: + +- **Dark**: GitHub green on dark background +- **Light**: Standard GitHub green +- **Tan**: Warm brown tones +- **Cloud**: Gray-blue tones + +Uses the public `github-contributions-api.jogruber.de` API (no GitHub token required). + ### 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. diff --git a/TASK.md b/TASK.md index fadd7ab..b8383cf 100644 --- a/TASK.md +++ b/TASK.md @@ -2,16 +2,22 @@ ## To Do -- [ ] add github code block - [ ] create a ui site config page - [ ] create a prompt formator or checklidst or skill or agent to change everything at once after forking ## Current Status -v1.16.0 deployed. Added public /write page with three-column Cursor docs-style layout, font switcher, theme toggle, and localStorage persistence for markdown writing. +v1.17.0 deployed. Added GitHub contributions graph on homepage with theme-aware colors, year navigation, and configurable display options. ## Completed +- [x] GitHub contributions graph on homepage with theme-aware colors +- [x] Year navigation with Phosphor icons (CaretLeft, CaretRight) +- [x] Click graph to visit GitHub profile +- [x] Configurable via siteConfig.gitHubContributions +- [x] Theme-specific contribution colors for all 4 themes +- [x] Mobile responsive design with scaled cells + - [x] Public /write page with three-column layout (not linked in nav) - [x] Left sidebar: Home link, content type selector, actions (Clear, Theme, Font) - [x] Center: Writing area with Copy All button and borderless textarea diff --git a/changelog.md b/changelog.md index 997be45..978b000 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,34 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [1.17.0] - 2025-12-20 + +### Added + +- GitHub contributions graph on homepage + - Displays yearly contribution activity with theme-aware colors + - Fetches data from public API (no GitHub token required) + - Year navigation with Phosphor icons (CaretLeft, CaretRight) + - Click graph to visit GitHub profile + - Configurable via `siteConfig.gitHubContributions` +- Theme-specific contribution colors + - Dark theme: GitHub green on dark background + - Light theme: Standard GitHub green + - Tan theme: Warm brown tones matching site palette + - Cloud theme: Gray-blue tones +- Mobile responsive design + - Scales down on tablets and phones + - Day labels hidden on small screens for space + - Touch-friendly navigation buttons + +### Technical + +- New component: `src/components/GitHubContributions.tsx` +- Uses `github-contributions-api.jogruber.de` public API +- CSS variables for contribution level colors per theme +- Configuration interface: `GitHubContributionsConfig` +- Set `enabled: false` in siteConfig to disable + ## [1.16.0] - 2025-12-21 ### Added diff --git a/content/blog/about-this-blog.md b/content/blog/about-this-blog.md index d99e258..f4ad491 100644 --- a/content/blog/about-this-blog.md +++ b/content/blog/about-this-blog.md @@ -1,19 +1,19 @@ --- -title: "About This Markdown Site" -description: "How this open source site works with Convex for real-time sync and Netlify for deployment." +title: "About This Markdown Framework" +description: "How this open source framework works with Convex for real-time sync and Netlify for deployment." date: "2025-01-16" slug: "about-this-blog" published: true -tags: ["convex", "netlify", "open-source", "markdown"] +tags: ["convex", "netlify", "open-source", "markdown", "ai", "llm"] readTime: "4 min read" featured: false featuredOrder: 3 -excerpt: "Learn how this open source site works with real-time sync and instant updates." +excerpt: "Learn how this open source framework works with real-time sync and instant updates." --- -# About This Markdown Site +# About This Markdown Framework -An open-source markdown sync site for developers and AI agents. Publish from the terminal with `npm run sync`. Write locally, sync instantly with real-time updates. Powered by Convex and Netlify. +An open-source publishing framework for AI agents and developers. Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents. Built on Convex and Netlify. ## How It Works diff --git a/content/blog/how-to-publish.md b/content/blog/how-to-publish.md index d97101c..c2447b7 100644 --- a/content/blog/how-to-publish.md +++ b/content/blog/how-to-publish.md @@ -1,6 +1,6 @@ --- title: "How to Publish a Blog Post" -description: "A quick guide to writing and publishing markdown blog posts using Cursor after your blog is set up." +description: "A quick guide to writing and publishing markdown posts using Cursor after your framework is set up." date: "2025-01-17" slug: "how-to-publish" published: true diff --git a/content/blog/new-features-search-featured-logos.md b/content/blog/new-features-search-featured-logos.md index 07ebc1e..19a758a 100644 --- a/content/blog/new-features-search-featured-logos.md +++ b/content/blog/new-features-search-featured-logos.md @@ -1,6 +1,6 @@ --- title: "New features: search, featured section, and logo gallery" -description: "Three updates that make your markdown site more useful: Command+K search, frontmatter-controlled featured items, and a scrolling logo gallery." +description: "Three updates that make your markdown framework more useful: Command+K search, frontmatter-controlled featured items, and a scrolling logo gallery." date: "2025-12-17" slug: "new-features-search-featured-logos" published: true diff --git a/content/blog/setup-guide.md b/content/blog/setup-guide.md index 1434203..85ccf7c 100644 --- a/content/blog/setup-guide.md +++ b/content/blog/setup-guide.md @@ -1,6 +1,6 @@ --- -title: "Setup Guide - Fork and Deploy Your Own Markdown Site" -description: "Step-by-step guide to fork this markdown sync site, set up Convex backend, and deploy to Netlify in under 10 minutes." +title: "Setup Guide - Fork and Deploy Your Own Markdown Framework" +description: "Step-by-step guide to fork this markdown sync framework, set up Convex backend, and deploy to Netlify in under 10 minutes." date: "2025-01-14" slug: "setup-guide" published: true @@ -9,18 +9,18 @@ readTime: "8 min read" featured: true featuredOrder: 3 image: "/images/setupguide.png" -excerpt: "Complete guide to fork, set up, and deploy your own markdown blog in under 10 minutes." +excerpt: "Complete guide to fork, set up, and deploy your own markdown framework in under 10 minutes." --- -# Fork and Deploy Your Own Markdown Blog +# Fork and Deploy Your Own Markdown Framework -This guide walks you through forking [this markdown site](https://github.com/waynesutton/markdown-site), setting up your Convex backend, and deploying to Netlify. The entire process takes about 10 minutes. +This guide walks you through forking [this markdown framework](https://github.com/waynesutton/markdown-site), setting up your Convex backend, and deploying to Netlify. The entire process takes about 10 minutes. **How publishing works:** Once deployed, you write posts in markdown, run `npm run sync` for development or `npm run sync:prod` for production, and they appear on your live site immediately. No rebuild or redeploy needed. Convex handles real-time data sync, so all connected browsers update automatically. ## Table of Contents -- [Fork and Deploy Your Own Markdown Blog](#fork-and-deploy-your-own-markdown-blog) +- [Fork and Deploy Your Own Markdown Framework](#fork-and-deploy-your-own-markdown-framework) - [Table of Contents](#table-of-contents) - [Prerequisites](#prerequisites) - [Step 1: Fork the Repository](#step-1-fork-the-repository) @@ -42,18 +42,22 @@ This guide walks you through forking [this markdown site](https://github.com/way - [Sync After Adding Posts](#sync-after-adding-posts) - [Environment Files](#environment-files) - [When to Sync vs Deploy](#when-to-sync-vs-deploy) - - [Customizing Your Blog](#customizing-your-blog) + - [Customizing Your Framework](#customizing-your-framework) - [Files to Update When Forking](#files-to-update-when-forking) + - [Site title and description metadata](#site-title-and-description-metadata) - [Update Backend Configuration](#update-backend-configuration) - [Change the Favicon](#change-the-favicon) - [Change the Site Logo](#change-the-site-logo) - [Change the Default Open Graph Image](#change-the-default-open-graph-image) - [Update Site Configuration](#update-site-configuration) - [Featured Section](#featured-section) + - [GitHub Contributions Graph](#github-contributions-graph) - [Logo Gallery](#logo-gallery) + - [Blog page](#blog-page) - [Scroll-to-top button](#scroll-to-top-button) - [Change the Default Theme](#change-the-default-theme) - [Change the Font](#change-the-font) + - [Change Font Sizes](#change-font-sizes) - [Add Static Pages (Optional)](#add-static-pages-optional) - [Update SEO Meta Tags](#update-seo-meta-tags) - [Update llms.txt and robots.txt](#update-llmstxt-and-robotstxt) @@ -70,6 +74,7 @@ This guide walks you through forking [this markdown site](https://github.com/way - [RSS/Sitemap not working](#rsssitemap-not-working) - [Build failures on Netlify](#build-failures-on-netlify) - [Project Structure](#project-structure) + - [Write Page](#write-page) - [Next Steps](#next-steps) ## Prerequisites @@ -341,6 +346,14 @@ This image appears when sharing on social media. Recommended: 1200x630 pixels. ![Photo](https://images.unsplash.com/photo-xxx?w=800) ``` +**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. + ### Sync After Adding Posts After adding or editing posts, sync to Convex. @@ -384,31 +397,33 @@ Both files are gitignored. Each developer creates their own local environment fi | Pages in `content/pages/` | `npm run sync` | Instant (no rebuild) | | Featured items (via frontmatter) | `npm run sync` | Instant (no rebuild) | | Import external URL | `npm run import` then sync | Instant (no rebuild) | +| Images in `public/images/` | Git commit + push | Requires rebuild | | `siteConfig` in `Home.tsx` | Redeploy | Requires rebuild | | Logo gallery config | Redeploy | Requires rebuild | | React components/styles | Redeploy | Requires rebuild | -**Markdown content** syncs instantly via Convex. **Source code changes** require pushing to GitHub for Netlify to rebuild. +**Markdown content** syncs instantly via Convex. **Images and source code** require pushing to GitHub for Netlify to rebuild. **Featured items** can now be controlled via markdown frontmatter. Add `featured: true` and `featuredOrder: 1` to any post or page, then run `npm run sync`. -## Customizing Your Blog +## Customizing Your Framework ### Files to Update When Forking When you fork this project, update these files with your site information: -| File | What to update | -| ----------------------------------- | ----------------------------------------------------------- | -| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery | -| `convex/http.ts` | `SITE_URL`, `SITE_NAME` (API responses, sitemap) | -| `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` (RSS feeds) | -| `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` (OG tags) | -| `index.html` | Title, meta description, OG tags, JSON-LD | -| `public/llms.txt` | Site name, URL, description | -| `public/robots.txt` | Sitemap URL | -| `public/openapi.yaml` | Server URL, site name in examples | -| `public/.well-known/ai-plugin.json` | Site name, descriptions | +| File | What to update | +| ----------------------------------- | --------------------------------------------------------------------------- | +| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery, GitHub contributions | +| `src/pages/Home.tsx` | Intro paragraph text (hardcoded JSX) | +| `convex/http.ts` | `SITE_URL`, `SITE_NAME`, description strings (3 locations) | +| `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` (RSS feeds) | +| `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` (OG tags) | +| `index.html` | Title, meta description, OG tags, JSON-LD | +| `public/llms.txt` | Site name, URL, description, topics | +| `public/robots.txt` | Sitemap URL and header comment | +| `public/openapi.yaml` | API title, server URL, site name in examples | +| `public/.well-known/ai-plugin.json` | Site name, descriptions | ### Site title and description metadata @@ -418,13 +433,16 @@ These files contain the main site description text. Update them with your own ta | --------------------------------- | -------------------------------------------------------------- | | `index.html` | meta description, og:description, twitter:description, JSON-LD | | `README.md` | Main description at top of file | -| `src/pages/Home.tsx` | intro and bio text in siteConfig | -| `convex/http.ts` | description field in API responses (2 locations) | -| `convex/rss.ts` | SITE_DESCRIPTION constant | -| `public/llms.txt` | Header quote and Description field | +| `src/config/siteConfig.ts` | name, title, and bio fields | +| `src/pages/Home.tsx` | Intro paragraph (hardcoded JSX with links) | +| `convex/http.ts` | SITE_NAME constant and description strings (3 locations) | +| `convex/rss.ts` | SITE_TITLE and SITE_DESCRIPTION constants | +| `public/llms.txt` | Header quote, Name, and Description fields | +| `public/openapi.yaml` | API title and example site name | | `AGENTS.md` | Project overview section | -| `content/blog/about-this-blog.md` | Opening paragraph | +| `content/blog/about-this-blog.md` | Title, description, excerpt, and opening paragraph | | `content/pages/about.md` | excerpt field and opening paragraph | +| `content/pages/docs.md` | Opening description paragraph | ### Update Backend Configuration @@ -504,10 +522,10 @@ export default { // Blog page configuration blogPage: { - enabled: true, // Enable /blog route - showInNav: true, // Show in navigation - title: "Blog", // Nav link and page title - order: 0, // Nav order (lower = first) + enabled: true, // Enable /blog route + showInNav: true, // Show in navigation + title: "Blog", // Nav link and page title + order: 0, // Nav order (lower = first) }, displayOnHomepage: true, // Show posts on homepage @@ -515,7 +533,7 @@ export default { featuredViewMode: "list", // 'list' or 'cards' showViewToggle: true, // Let users switch between views - // Logo gallery (marquee scroll with clickable links) + // Logo gallery (static grid or scrolling marquee with clickable links) logoGallery: { enabled: true, // Set false to hide images: [ @@ -524,7 +542,9 @@ export default { ], position: "above-footer", // or 'below-featured' speed: 30, // Seconds for one scroll cycle - title: "Trusted by", + title: "Built with", + scrolling: false, // false = static grid, true = scrolling marquee + maxItems: 4, // Number of logos when scrolling is false }, links: { @@ -573,9 +593,33 @@ Use `featuredOrder` to control display order. Lower numbers appear first. Posts Users can toggle between list and card views using the icon button next to "Get started:". To change the default view, set `featuredViewMode: "cards"` in siteConfig. +### 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 +}, +``` + +| Option | Description | +| -------------------- | --------------------------------------------- | +| `enabled` | `true` to show, `false` to hide | +| `username` | Your GitHub username | +| `showYearNavigation` | Show prev/next year buttons | +| `linkToProfile` | Click graph to visit GitHub profile | +| `title` | Text above graph (set to `undefined` to hide) | + +The graph displays with theme-aware colors that match each site theme (dark, light, tan, cloud). Uses the public `github-contributions-api.jogruber.de` API (no GitHub token required). + ### Logo Gallery -The homepage includes a scrolling logo gallery with 5 sample logos. Customize or disable it in siteConfig: +The homepage includes a logo gallery that can scroll infinitely or display as a static grid. Customize or disable it in siteConfig: **Disable the gallery:** @@ -600,7 +644,9 @@ logoGallery: { ], position: "above-footer", speed: 30, - title: "Trusted by", + title: "Built with", + scrolling: false, // false = static grid, true = scrolling marquee + maxItems: 4, // Number of logos to show when scrolling is false }, ``` @@ -615,15 +661,22 @@ Delete the sample files from `public/images/logos/` and clear the images array, **Configuration options:** -| Option | Description | -| ---------- | ---------------------------------------------------- | -| `enabled` | `true` to show, `false` to hide | -| `images` | Array of logo objects with `src` and optional `href` | -| `position` | `'above-footer'` or `'below-featured'` | -| `speed` | Seconds for one scroll cycle (lower = faster) | -| `title` | Text above gallery (set to `undefined` to hide) | +| Option | Description | +| ----------- | ---------------------------------------------------------- | +| `enabled` | `true` to show, `false` to hide | +| `images` | Array of logo objects with `src` and optional `href` | +| `position` | `'above-footer'` or `'below-featured'` | +| `speed` | Seconds for one scroll cycle (lower = faster) | +| `title` | Text above gallery (set to `undefined` to hide) | +| `scrolling` | `true` for infinite scroll, `false` for static grid | +| `maxItems` | Max logos to show when `scrolling` is `false` (default: 4) | -The gallery uses CSS animations for smooth infinite scrolling. Logos display in grayscale and colorize on hover. +**Display modes:** + +- **Scrolling marquee** (`scrolling: true`): Infinite horizontal scroll animation. All logos display in a continuous loop. +- **Static grid** (`scrolling: false`): Centered grid showing the first `maxItems` logos without animation. + +Logos display in grayscale and colorize on hover. ### Blog page @@ -639,13 +692,13 @@ blogPage: { displayOnHomepage: true, // Show posts on homepage ``` -| Option | Description | -| ------ | ----------- | -| `enabled` | Enable the `/blog` route | -| `showInNav` | Show Blog link in navigation | -| `title` | Text for nav link and page heading | -| `order` | Position in navigation (lower = first) | -| `displayOnHomepage` | Show post list on homepage | +| Option | Description | +| ------------------- | -------------------------------------- | +| `enabled` | Enable the `/blog` route | +| `showInNav` | Show Blog link in navigation | +| `title` | Text for nav link and page heading | +| `order` | Position in navigation (lower = first) | +| `displayOnHomepage` | Show post list on homepage | **Display options:** @@ -937,7 +990,7 @@ See [netlify-deploy-fix.md](https://github.com/waynesutton/markdown-site/blob/ma ``` markdown-site/ ├── content/ -│ ├── blog/ # Markdown blog posts +│ ├── blog/ # Markdown posts │ └── pages/ # Static pages (About, Docs, etc.) ├── convex/ # Convex backend functions │ ├── http.ts # HTTP endpoints diff --git a/content/blog/using-images-in-posts.md b/content/blog/using-images-in-posts.md index e4fc86d..1cbb30c 100644 --- a/content/blog/using-images-in-posts.md +++ b/content/blog/using-images-in-posts.md @@ -1,6 +1,6 @@ --- title: "Using Images in Blog Posts" -description: "Learn how to add header images, inline images, and Open Graph images to your markdown blog posts." +description: "Learn how to add header images, inline images, and Open Graph images to your markdown posts." date: "2025-01-18" slug: "using-images-in-posts" published: true diff --git a/content/pages/about.md b/content/pages/about.md index 97e535f..7a03f5d 100644 --- a/content/pages/about.md +++ b/content/pages/about.md @@ -3,16 +3,16 @@ title: "About" slug: "about" published: true order: 2 -excerpt: "An open-source markdown sync site for developers and AI agents." +excerpt: "An open-source publishing framework for AI agents and developers." --- -An open-source markdown sync site for developers and AI agents. Publish from the terminal with `npm run sync`. Write locally, sync instantly with real-time updates. Powered by Convex and Netlify. +An open-source publishing framework for AI agents and developers. Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents. Built on Convex and Netlify. ## What makes it a dev sync system **File-based content.** All posts and pages live in `content/blog/` and `content/pages/` as markdown files with frontmatter. No database UI. No admin panel. Just files in your repo. -**CLI publishing workflow.** Write markdown locally, then run `npm run sync` (dev) or `npm run sync:prod` (production). Content appears instantly via Convex real-time sync. +**CLI publishing workflow.** Write markdown locally, then run `npm run sync` (dev) or `npm run sync:prod` (production). Content appears instantly via Convex real-time sync. Images require git commit and push since they are served as static files from Netlify. **Version controlled.** Markdown source files live in your repo alongside code. Commit changes, review diffs, roll back like any codebase. The sync command pushes content to the database. @@ -55,6 +55,7 @@ It's a hybrid: developer workflow for publishing + real-time delivery like a dyn - Full text search with Command+K shortcut - Featured section with list/card view toggle and excerpts - Logo gallery with clickable links and marquee scroll +- GitHub contributions graph with year navigation - Dedicated blog page with configurable navigation order - Real-time analytics at `/stats` - RSS feeds and sitemap for SEO diff --git a/content/pages/changelog-page.md b/content/pages/changelog-page.md index cea1e92..bee1629 100644 --- a/content/pages/changelog-page.md +++ b/content/pages/changelog-page.md @@ -7,6 +7,29 @@ order: 5 All notable changes to this project. +## v1.17.0 + +Released December 20, 2025 + +**GitHub contributions graph** + +- GitHub activity graph on homepage with theme-aware colors +- Year navigation with Phosphor CaretLeft/CaretRight icons +- Click graph to visit GitHub profile +- Configurable via `siteConfig.gitHubContributions` +- Uses public API (no GitHub token required) + +Theme-specific contribution colors: + +- Dark theme: GitHub green on dark background +- Light theme: Standard GitHub green +- Tan theme: Warm brown tones +- Cloud theme: Gray-blue tones + +New component: `src/components/GitHubContributions.tsx` + +Set `enabled: false` in siteConfig to disable. + ## v1.15.2 Released December 20, 2025 @@ -356,7 +379,7 @@ Released December 14, 2025 **Initial release** -- Markdown blog posts with frontmatter parsing +- Markdown posts with frontmatter parsing - Static pages support (About, Projects, Contact) - Four theme options: Dark, Light, Tan (default), Cloud - Syntax highlighting for code blocks diff --git a/content/pages/docs.md b/content/pages/docs.md index 425dc38..52c27ef 100644 --- a/content/pages/docs.md +++ b/content/pages/docs.md @@ -5,7 +5,7 @@ published: true order: 0 --- -Reference documentation for setting up, customizing, and deploying this markdown site. +Reference documentation for setting up, customizing, and deploying this markdown framework. **How publishing works:** Write posts in markdown, run `npm run sync` for development or `npm run sync:prod` for production, and they appear on your live site immediately. No rebuild or redeploy needed. Convex handles real-time data sync, so connected browsers update automatically. @@ -140,11 +140,12 @@ npm run sync:prod | Pages in `content/pages/` | `npm run sync` | Instant (no rebuild) | | Featured items (via frontmatter) | `npm run sync` | Instant (no rebuild) | | Import external URL | `npm run import` then sync | Instant (no rebuild) | +| Images in `public/images/` | Git commit + push | Requires rebuild | | `siteConfig` in `Home.tsx` | Redeploy | Requires rebuild | | Logo gallery config | Redeploy | Requires rebuild | | React components/styles | Redeploy | Requires rebuild | -**Markdown content** syncs instantly. **Source code** requires pushing to GitHub for Netlify to rebuild. +**Markdown content** syncs instantly to Convex. **Images and source code** require pushing to GitHub for Netlify to rebuild. ## Configuration @@ -154,14 +155,15 @@ When you fork this project, update these files with your site information: | File | What to update | |------|----------------| -| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery | -| `convex/http.ts` | `SITE_URL`, `SITE_NAME` (API responses, sitemap) | +| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery, GitHub contributions | +| `src/pages/Home.tsx` | Intro paragraph text (hardcoded JSX) | +| `convex/http.ts` | `SITE_URL`, `SITE_NAME`, description strings (3 locations) | | `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` (RSS feeds) | | `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` (OG tags) | | `index.html` | Title, meta description, OG tags, JSON-LD | -| `public/llms.txt` | Site name, URL, description | -| `public/robots.txt` | Sitemap URL | -| `public/openapi.yaml` | Server URL, site name in examples | +| `public/llms.txt` | Site name, URL, description, topics | +| `public/robots.txt` | Sitemap URL and header comment | +| `public/openapi.yaml` | API title, server URL, site name in examples | | `public/.well-known/ai-plugin.json` | Site name, descriptions | ### Site title and description metadata @@ -172,13 +174,16 @@ These files contain the main site description text. Update them with your own ta |------|----------------| | `index.html` | meta description, og:description, twitter:description, JSON-LD | | `README.md` | Main description at top of file | -| `src/pages/Home.tsx` | intro and bio text in siteConfig | -| `convex/http.ts` | description field in API responses (2 locations) | -| `convex/rss.ts` | SITE_DESCRIPTION constant | -| `public/llms.txt` | Header quote and Description field | +| `src/config/siteConfig.ts` | name, title, and bio fields | +| `src/pages/Home.tsx` | Intro paragraph (hardcoded JSX with links) | +| `convex/http.ts` | SITE_NAME constant and description strings (3 locations) | +| `convex/rss.ts` | SITE_TITLE and SITE_DESCRIPTION constants | +| `public/llms.txt` | Header quote, Name, and Description fields | +| `public/openapi.yaml` | API title and example site name | | `AGENTS.md` | Project overview section | -| `content/blog/about-this-blog.md` | Opening paragraph | +| `content/blog/about-this-blog.md` | Title, description, excerpt, and opening paragraph | | `content/pages/about.md` | excerpt field and opening paragraph | +| `content/pages/docs.md` | Opening description paragraph | **Backend constants** (`convex/http.ts` and `convex/rss.ts`): @@ -228,13 +233,15 @@ export default { featuredViewMode: "list", // 'list' or 'cards' showViewToggle: true, - // Logo gallery (with clickable links) + // Logo gallery (static grid or scrolling marquee) logoGallery: { enabled: true, // false to hide images: [{ src: "/images/logos/logo.svg", href: "https://example.com" }], position: "above-footer", speed: 30, - title: "Trusted by", + title: "Built with", + scrolling: false, // false = static grid, true = scrolling marquee + maxItems: 4, // Number of logos when scrolling is false }, links: { @@ -283,12 +290,36 @@ const siteConfig = { }; ``` -### Logo gallery +### GitHub contributions graph -The homepage includes a scrolling logo marquee with sample logos. Each logo can link to a URL. +Display your GitHub contribution activity on the homepage. Configure in `siteConfig`: ```typescript -// In src/pages/Home.tsx +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 +}, +``` + +| Option | Description | +| ------ | ----------- | +| `enabled` | `true` to show, `false` to hide | +| `username` | Your GitHub username | +| `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). + +### 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. + +```typescript +// In src/config/siteConfig.ts logoGallery: { enabled: true, // false to hide images: [ @@ -297,17 +328,26 @@ logoGallery: { ], position: "above-footer", // or 'below-featured' speed: 30, // Seconds for one scroll cycle - title: "Trusted by", // undefined to hide + title: "Built with", // undefined to hide + scrolling: false, // false = static grid, true = scrolling marquee + maxItems: 4, // Number of logos when scrolling is false }, ``` -| Option | Description | -| ---------- | --------------------------------------------- | -| `enabled` | `true` to show, `false` to hide | -| `images` | Array of `{ src, href }` objects | -| `position` | `'above-footer'` or `'below-featured'` | -| `speed` | Seconds for one scroll cycle (lower = faster) | -| `title` | Text above gallery (`undefined` to hide) | +| Option | Description | +| ----------- | -------------------------------------------------- | +| `enabled` | `true` to show, `false` to hide | +| `images` | Array of `{ src, href }` objects | +| `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:** @@ -419,6 +459,14 @@ Mobile sizes defined in `@media (max-width: 768px)` block. | 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. + ## Search Press `Command+K` (Mac) or `Ctrl+K` (Windows/Linux) to open the search modal. Click the search icon in the nav or use the keyboard shortcut. diff --git a/content/pages/projects.md b/content/pages/projects.md index 0dc1890..c1743aa 100644 --- a/content/pages/projects.md +++ b/content/pages/projects.md @@ -5,7 +5,7 @@ published: true order: 3 --- -This markdown site is open source and built to be extended. Here is what ships out of the box. +This markdown framework is open source and built to be extended. Here is what ships out of the box. ## Core Features diff --git a/convex/http.ts b/convex/http.ts index 4dae5ba..9497ec9 100644 --- a/convex/http.ts +++ b/convex/http.ts @@ -7,7 +7,7 @@ const http = httpRouter(); // Site configuration const SITE_URL = process.env.SITE_URL || "https://markdowncms.netlify.app"; -const SITE_NAME = "markdown sync site"; +const SITE_NAME = "markdown sync framework"; // RSS feed endpoint (descriptions only) http.route({ @@ -72,7 +72,7 @@ http.route({ const response = { site: SITE_NAME, url: SITE_URL, - description: "An open-source markdown sync site for developers and AI agents. Publish from the terminal with npm run sync. Write locally, sync instantly with real-time updates. Powered by Convex and Netlify.", + description: "An open-source publishing framework for AI agents and developers. Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents. Built on Convex and Netlify.", posts: posts.map((post) => ({ title: post.title, slug: post.slug, @@ -194,7 +194,7 @@ http.route({ const response = { site: SITE_NAME, url: SITE_URL, - description: "An open-source markdown sync site for developers and AI agents. Publish from the terminal with npm run sync. Write locally, sync instantly with real-time updates. Powered by Convex and Netlify.", + description: "An open-source publishing framework for AI agents and developers. Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents. Built on Convex and Netlify.", exportedAt: new Date().toISOString(), totalPosts: fullPosts.length, posts: fullPosts, @@ -231,7 +231,7 @@ function generateMetaHtml(content: { type?: "post" | "page"; }): string { const siteUrl = process.env.SITE_URL || "https://markdowncms.netlify.app"; - const siteName = "markdown sync site"; + const siteName = "markdown sync framework"; const defaultImage = `${siteUrl}/images/og-default.svg`; const canonicalUrl = `${siteUrl}/${content.slug}`; diff --git a/convex/rss.ts b/convex/rss.ts index 91db304..50bdd6c 100644 --- a/convex/rss.ts +++ b/convex/rss.ts @@ -3,9 +3,9 @@ import { api } from "./_generated/api"; // Site configuration for RSS feed const SITE_URL = process.env.SITE_URL || "https://markdowncms.netlify.app"; -const SITE_TITLE = "markdown sync site"; +const SITE_TITLE = "markdown sync framework"; const SITE_DESCRIPTION = - "An open-source markdown sync site for developers and AI agents. Publish from the terminal with npm run sync. Write locally, sync instantly with real-time updates. Powered by Convex and Netlify."; + "An open-source publishing framework for AI agents and developers. Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents. Built on Convex and Netlify."; // Escape XML special characters function escapeXml(text: string): string { diff --git a/files.md b/files.md index 8b867c5..1b60dcf 100644 --- a/files.md +++ b/files.md @@ -29,9 +29,9 @@ A brief description of each file in the codebase. ### Config (`src/config/`) -| File | Description | -| --------------- | -------------------------------------------------------------------------------- | -| `siteConfig.ts` | Centralized site configuration (name, logo, blog page, posts display, nav order) | +| File | Description | +| --------------- | --------------------------------------------------------------------------------------------------------- | +| `siteConfig.ts` | Centralized site configuration (name, logo, blog page, posts display, GitHub contributions, nav order) | ### Pages (`src/pages/`) @@ -45,18 +45,19 @@ A brief description of each file in the codebase. ### Components (`src/components/`) -| File | Description | -| ---------------------- | ---------------------------------------------------------- | -| `Layout.tsx` | Page wrapper with search button, theme toggle, mobile menu, and scroll-to-top | -| `ThemeToggle.tsx` | Theme switcher (dark/light/tan/cloud) | -| `PostList.tsx` | Year-grouped blog post list | -| `BlogPost.tsx` | Markdown renderer with syntax highlighting | -| `CopyPageDropdown.tsx` | Share dropdown for LLMs (ChatGPT, Claude, Perplexity) with View as Markdown and Generate Skill options | -| `SearchModal.tsx` | Full text search modal with keyboard navigation | -| `FeaturedCards.tsx` | Card grid for featured posts/pages with excerpts | -| `LogoMarquee.tsx` | Scrolling logo gallery with clickable links | -| `MobileMenu.tsx` | Slide-out drawer menu for mobile navigation with hamburger button | -| `ScrollToTop.tsx` | Configurable scroll-to-top button with Phosphor ArrowUp icon | +| File | Description | +| ------------------------- | ---------------------------------------------------------- | +| `Layout.tsx` | Page wrapper with search button, theme toggle, mobile menu, and scroll-to-top | +| `ThemeToggle.tsx` | Theme switcher (dark/light/tan/cloud) | +| `PostList.tsx` | Year-grouped blog post list | +| `BlogPost.tsx` | Markdown renderer with syntax highlighting | +| `CopyPageDropdown.tsx` | Share dropdown for LLMs (ChatGPT, Claude, Perplexity) with View as Markdown and Generate Skill options | +| `SearchModal.tsx` | Full text search modal with keyboard navigation | +| `FeaturedCards.tsx` | Card grid for featured posts/pages with excerpts | +| `LogoMarquee.tsx` | Scrolling logo gallery with clickable links | +| `MobileMenu.tsx` | Slide-out drawer menu for mobile navigation with hamburger button | +| `ScrollToTop.tsx` | Configurable scroll-to-top button with Phosphor ArrowUp icon | +| `GitHubContributions.tsx` | GitHub activity graph with theme-aware colors and year navigation | ### Context (`src/context/`) diff --git a/index.html b/index.html index 35e4815..85f50dd 100644 --- a/index.html +++ b/index.html @@ -8,9 +8,9 @@ - + - + - + - + - markdown "sync" site + markdown "sync" framework
diff --git a/public/images/logo.svg b/public/images/logos/logo.svg similarity index 100% rename from public/images/logo.svg rename to public/images/logos/logo.svg diff --git a/public/images/logos/markdown.svg b/public/images/logos/markdown.svg new file mode 100644 index 0000000..f8c1014 --- /dev/null +++ b/public/images/logos/markdown.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/logos/netlify.svg b/public/images/logos/netlify.svg new file mode 100644 index 0000000..003fdae --- /dev/null +++ b/public/images/logos/netlify.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/logos/react.svg b/public/images/logos/react.svg new file mode 100644 index 0000000..a374be0 --- /dev/null +++ b/public/images/logos/react.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/llms.txt b/public/llms.txt index ce42aa9..859cfcf 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -1,13 +1,13 @@ # llms.txt - Information for AI assistants and LLMs # Learn more: https://llmstxt.org/ -> An open-source markdown sync site for developers and AI agents. Publish from the terminal with npm run sync. +> An open-source publishing framework for AI agents and developers. Write markdown, sync from the terminal. # Site Information -- Name: markdown sync site +- Name: markdown sync framework - URL: https://markdowncms.netlify.app -- Description: An open-source markdown sync site for developers and AI agents. Publish from the terminal with npm run sync. Write locally, sync instantly with real-time updates. Powered by Convex and Netlify. -- Topics: Markdown, Convex, React, TypeScript, Netlify, Open Source +- Description: An open-source publishing framework for AI agents and developers. Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents. Built on Convex and Netlify. +- Topics: Markdown, Convex, React, TypeScript, Netlify, Open Source, AI, LLM, AEO, GEO # API Endpoints diff --git a/public/openapi.yaml b/public/openapi.yaml index d1dc9f6..de406a4 100644 --- a/public/openapi.yaml +++ b/public/openapi.yaml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - title: markdown sync site API + title: markdown sync framework API description: | API for accessing blog posts and pages as markdown content. All endpoints return JSON by default. Use format=md for raw markdown. @@ -28,7 +28,7 @@ paths: properties: site: type: string - example: markdown sync site + example: markdown sync framework url: type: string example: https://markdowncms.netlify.app diff --git a/public/raw/about.md b/public/raw/about.md index ac88266..30be2c3 100644 --- a/public/raw/about.md +++ b/public/raw/about.md @@ -11,7 +11,7 @@ An open-source markdown sync site for developers and AI agents. Publish from the **File-based content.** All posts and pages live in `content/blog/` and `content/pages/` as markdown files with frontmatter. No database UI. No admin panel. Just files in your repo. -**CLI publishing workflow.** Write markdown locally, then run `npm run sync` (dev) or `npm run sync:prod` (production). Content appears instantly via Convex real-time sync. +**CLI publishing workflow.** Write markdown locally, then run `npm run sync` (dev) or `npm run sync:prod` (production). Content appears instantly via Convex real-time sync. Images require git commit and push since they are served as static files from Netlify. **Version controlled.** Markdown source files live in your repo alongside code. Commit changes, review diffs, roll back like any codebase. The sync command pushes content to the database. diff --git a/public/raw/docs.md b/public/raw/docs.md index f7db3f0..a13fe06 100644 --- a/public/raw/docs.md +++ b/public/raw/docs.md @@ -140,11 +140,12 @@ npm run sync:prod | Pages in `content/pages/` | `npm run sync` | Instant (no rebuild) | | Featured items (via frontmatter) | `npm run sync` | Instant (no rebuild) | | Import external URL | `npm run import` then sync | Instant (no rebuild) | +| Images in `public/images/` | Git commit + push | Requires rebuild | | `siteConfig` in `Home.tsx` | Redeploy | Requires rebuild | | Logo gallery config | Redeploy | Requires rebuild | | React components/styles | Redeploy | Requires rebuild | -**Markdown content** syncs instantly. **Source code** requires pushing to GitHub for Netlify to rebuild. +**Markdown content** syncs instantly to Convex. **Images and source code** require pushing to GitHub for Netlify to rebuild. ## Configuration @@ -228,13 +229,15 @@ export default { featuredViewMode: "list", // 'list' or 'cards' showViewToggle: true, - // Logo gallery (with clickable links) + // Logo gallery (static grid or scrolling marquee) logoGallery: { enabled: true, // false to hide images: [{ src: "/images/logos/logo.svg", href: "https://example.com" }], position: "above-footer", speed: 30, - title: "Trusted by", + title: "Built with", + scrolling: false, // false = static grid, true = scrolling marquee + maxItems: 4, // Number of logos when scrolling is false }, links: { @@ -285,10 +288,10 @@ const siteConfig = { ### Logo gallery -The homepage includes a scrolling logo marquee with sample logos. Each logo can link to a URL. +The homepage includes a logo gallery that can scroll infinitely or display as a static grid. Each logo can link to a URL. ```typescript -// In src/pages/Home.tsx +// In src/config/siteConfig.ts logoGallery: { enabled: true, // false to hide images: [ @@ -297,17 +300,26 @@ logoGallery: { ], position: "above-footer", // or 'below-featured' speed: 30, // Seconds for one scroll cycle - title: "Trusted by", // undefined to hide + title: "Built with", // undefined to hide + scrolling: false, // false = static grid, true = scrolling marquee + maxItems: 4, // Number of logos when scrolling is false }, ``` -| Option | Description | -| ---------- | --------------------------------------------- | -| `enabled` | `true` to show, `false` to hide | -| `images` | Array of `{ src, href }` objects | -| `position` | `'above-footer'` or `'below-featured'` | -| `speed` | Seconds for one scroll cycle (lower = faster) | -| `title` | Text above gallery (`undefined` to hide) | +| Option | Description | +| ----------- | -------------------------------------------------- | +| `enabled` | `true` to show, `false` to hide | +| `images` | Array of `{ src, href }` objects | +| `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:** @@ -419,6 +431,14 @@ Mobile sizes defined in `@media (max-width: 768px)` block. | 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. + ## Search Press `Command+K` (Mac) or `Ctrl+K` (Windows/Linux) to open the search modal. Click the search icon in the nav or use the keyboard shortcut. diff --git a/public/raw/setup-guide.md b/public/raw/setup-guide.md index 60b9060..d59e1f5 100644 --- a/public/raw/setup-guide.md +++ b/public/raw/setup-guide.md @@ -338,6 +338,14 @@ This image appears when sharing on social media. Recommended: 1200x630 pixels. ![Photo](https://images.unsplash.com/photo-xxx?w=800) ``` +**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. + ### Sync After Adding Posts After adding or editing posts, sync to Convex. @@ -381,11 +389,12 @@ Both files are gitignored. Each developer creates their own local environment fi | Pages in `content/pages/` | `npm run sync` | Instant (no rebuild) | | Featured items (via frontmatter) | `npm run sync` | Instant (no rebuild) | | Import external URL | `npm run import` then sync | Instant (no rebuild) | +| Images in `public/images/` | Git commit + push | Requires rebuild | | `siteConfig` in `Home.tsx` | Redeploy | Requires rebuild | | Logo gallery config | Redeploy | Requires rebuild | | React components/styles | Redeploy | Requires rebuild | -**Markdown content** syncs instantly via Convex. **Source code changes** require pushing to GitHub for Netlify to rebuild. +**Markdown content** syncs instantly via Convex. **Images and source code** require pushing to GitHub for Netlify to rebuild. **Featured items** can now be controlled via markdown frontmatter. Add `featured: true` and `featuredOrder: 1` to any post or page, then run `npm run sync`. @@ -512,7 +521,7 @@ export default { featuredViewMode: "list", // 'list' or 'cards' showViewToggle: true, // Let users switch between views - // Logo gallery (marquee scroll with clickable links) + // Logo gallery (static grid or scrolling marquee with clickable links) logoGallery: { enabled: true, // Set false to hide images: [ @@ -521,7 +530,9 @@ export default { ], position: "above-footer", // or 'below-featured' speed: 30, // Seconds for one scroll cycle - title: "Trusted by", + title: "Built with", + scrolling: false, // false = static grid, true = scrolling marquee + maxItems: 4, // Number of logos when scrolling is false }, links: { @@ -572,7 +583,7 @@ Users can toggle between list and card views using the icon button next to "Get ### Logo Gallery -The homepage includes a scrolling logo gallery with 5 sample logos. Customize or disable it in siteConfig: +The homepage includes a logo gallery that can scroll infinitely or display as a static grid. Customize or disable it in siteConfig: **Disable the gallery:** @@ -597,7 +608,9 @@ logoGallery: { ], position: "above-footer", speed: 30, - title: "Trusted by", + title: "Built with", + scrolling: false, // false = static grid, true = scrolling marquee + maxItems: 4, // Number of logos to show when scrolling is false }, ``` @@ -612,15 +625,22 @@ Delete the sample files from `public/images/logos/` and clear the images array, **Configuration options:** -| Option | Description | -| ---------- | ---------------------------------------------------- | -| `enabled` | `true` to show, `false` to hide | -| `images` | Array of logo objects with `src` and optional `href` | -| `position` | `'above-footer'` or `'below-featured'` | -| `speed` | Seconds for one scroll cycle (lower = faster) | -| `title` | Text above gallery (set to `undefined` to hide) | +| Option | Description | +| ----------- | ---------------------------------------------------- | +| `enabled` | `true` to show, `false` to hide | +| `images` | Array of logo objects with `src` and optional `href` | +| `position` | `'above-footer'` or `'below-featured'` | +| `speed` | Seconds for one scroll cycle (lower = faster) | +| `title` | Text above gallery (set to `undefined` to hide) | +| `scrolling` | `true` for infinite scroll, `false` for static grid | +| `maxItems` | Max logos to show when `scrolling` is `false` (default: 4) | -The gallery uses CSS animations for smooth infinite scrolling. Logos display in grayscale and colorize on hover. +**Display modes:** + +- **Scrolling marquee** (`scrolling: true`): Infinite horizontal scroll animation. All logos display in a continuous loop. +- **Static grid** (`scrolling: false`): Centered grid showing the first `maxItems` logos without animation. + +Logos display in grayscale and colorize on hover. ### Blog page diff --git a/public/robots.txt b/public/robots.txt index c4d9f85..475b533 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,4 +1,4 @@ -# robots.txt for markdown sync site +# robots.txt for markdown sync framework # https://www.robotstxt.org/ User-agent: * diff --git a/src/components/GitHubContributions.tsx b/src/components/GitHubContributions.tsx new file mode 100644 index 0000000..18d4354 --- /dev/null +++ b/src/components/GitHubContributions.tsx @@ -0,0 +1,296 @@ +// GitHub contributions graph component +// Fetches and displays contribution data with theme-aware colors + +import { useState, useEffect, useCallback } from "react"; +import { CaretLeft, CaretRight } from "@phosphor-icons/react"; +import type { GitHubContributionsConfig } from "../config/siteConfig"; + +// Contribution data structure from the API +interface ContributionDay { + date: string; + count: number; + level: 0 | 1 | 2 | 3 | 4; +} + +interface ContributionWeek { + days: ContributionDay[]; +} + +interface APIResponse { + total: Record; + contributions: ContributionDay[]; +} + +interface GitHubContributionsProps { + config: GitHubContributionsConfig; +} + +// Month labels for the graph header +const MONTHS = [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", +]; + +// Day labels for the left side +const DAYS = ["", "Mon", "", "Wed", "", "Fri", ""]; + +export default function GitHubContributions({ + config, +}: GitHubContributionsProps) { + const currentYear = new Date().getFullYear(); + const [year, setYear] = useState(currentYear); + const [contributions, setContributions] = useState([]); + const [totalContributions, setTotalContributions] = useState(0); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + // Fetch contributions from the API + const fetchContributions = useCallback(async () => { + if (!config.enabled || !config.username) return; + + setLoading(true); + setError(null); + + try { + const response = await fetch( + `https://github-contributions-api.jogruber.de/v4/${config.username}?y=${year}`, + ); + + if (!response.ok) { + throw new Error("Failed to fetch contributions"); + } + + const data: APIResponse = await response.json(); + setContributions(data.contributions || []); + setTotalContributions(data.total?.[year.toString()] || 0); + } catch { + setError("Unable to load GitHub contributions"); + setContributions([]); + setTotalContributions(0); + } finally { + setLoading(false); + } + }, [config.enabled, config.username, year]); + + // Fetch on mount and when year changes + useEffect(() => { + fetchContributions(); + }, [fetchContributions]); + + // Don't render if disabled + if (!config.enabled) return null; + + // Navigate to previous year + const goToPreviousYear = () => { + if (year > currentYear - 10) { + setYear((y) => y - 1); + } + }; + + // Navigate to next year + const goToNextYear = () => { + if (year < currentYear) { + setYear((y) => y + 1); + } + }; + + // Group contributions into weeks for grid display + const getWeeks = (): ContributionWeek[] => { + if (contributions.length === 0) return []; + + const weeks: ContributionWeek[] = []; + let currentWeek: ContributionDay[] = []; + + // Get the first day of the year to determine padding + const firstDay = new Date(year, 0, 1); + const startDayOfWeek = firstDay.getDay(); // 0 = Sunday + + // Add empty days for alignment + for (let i = 0; i < startDayOfWeek; i++) { + currentWeek.push({ date: "", count: 0, level: 0 }); + } + + // Add all contribution days + for (const day of contributions) { + currentWeek.push(day); + if (currentWeek.length === 7) { + weeks.push({ days: currentWeek }); + currentWeek = []; + } + } + + // Add remaining days to last week + if (currentWeek.length > 0) { + while (currentWeek.length < 7) { + currentWeek.push({ date: "", count: 0, level: 0 }); + } + weeks.push({ days: currentWeek }); + } + + return weeks; + }; + + // Calculate month label positions + const getMonthLabels = () => { + const weeks = getWeeks(); + const labels: { month: string; position: number }[] = []; + let lastMonth = -1; + + weeks.forEach((week, weekIndex) => { + const firstValidDay = week.days.find((d) => d.date); + if (firstValidDay) { + const date = new Date(firstValidDay.date); + const month = date.getMonth(); + if (month !== lastMonth) { + labels.push({ month: MONTHS[month], position: weekIndex }); + lastMonth = month; + } + } + }); + + return labels; + }; + + const weeks = getWeeks(); + const monthLabels = getMonthLabels(); + const profileUrl = `https://github.com/${config.username}`; + + // Render the contribution grid + const renderGrid = () => ( +
+ {/* Month labels */} +
+
+
+ {monthLabels.map(({ month, position }, index) => ( + + {month} + + ))} +
+
+ + {/* Grid with day labels */} +
+ {/* Day labels column */} +
+ {DAYS.map((day, index) => ( + + {day} + + ))} +
+ + {/* Contribution cells grid */} +
+ {weeks.map((week, weekIndex) => + week.days.map((day, dayIndex) => ( +
+ )), + )} +
+
+ + {/* Legend */} +
+ Less +
+
+
+
+
+ More +
+
+ ); + + return ( +
+ {/* Header with title and year navigation */} +
+
+ {config.title && ( + {config.title} + )} + + {loading + ? "Loading..." + : error + ? "" + : `${totalContributions.toLocaleString()} contributions in ${year}`} + +
+ + {config.showYearNavigation && ( +
+ + {year} + +
+ )} +
+ + {/* Contribution grid - optionally wrapped in a link */} + {error ? ( +
{error}
+ ) : config.linkToProfile ? ( + + {renderGrid()} + + ) : ( + renderGrid() + )} +
+ ); +} + diff --git a/src/components/LogoMarquee.tsx b/src/components/LogoMarquee.tsx index 14cb079..d49afd6 100644 --- a/src/components/LogoMarquee.tsx +++ b/src/components/LogoMarquee.tsx @@ -13,6 +13,8 @@ export interface LogoGalleryConfig { position: "above-footer" | "below-featured"; speed: number; // Seconds for one complete scroll cycle title?: string; // Optional title above the marquee + scrolling?: boolean; // When false, shows static grid instead of scrolling marquee + maxItems?: number; // Max items to show in static mode (default: 4) } interface LogoMarqueeProps { @@ -33,52 +35,71 @@ export default function LogoMarquee({ config }: LogoMarqueeProps) { return null; } - // Normalize and duplicate images for seamless infinite scroll + // Normalize images const normalizedImages = config.images.map(normalizeImage); - const duplicatedImages = [...normalizedImages, ...normalizedImages]; + + // Check if scrolling mode (default true for backwards compatibility) + const isScrolling = config.scrolling !== false; + + // For static mode, limit to maxItems (default 4) + const maxItems = config.maxItems ?? 4; + const displayImages = isScrolling + ? [...normalizedImages, ...normalizedImages] // Duplicate for seamless scroll + : normalizedImages.slice(0, maxItems); // Limit for static grid + + // Render logo item (shared between modes) + const renderLogo = (logo: LogoItem, index: number) => ( +
+ {logo.href ? ( + + + + ) : ( + + )} +
+ ); return (
{config.title && (

{config.title}

)} -
-
- {duplicatedImages.map((logo, index) => ( -
- {logo.href ? ( - - - - ) : ( - - )} -
- ))} + {isScrolling ? ( + // Scrolling marquee mode +
+
+ {displayImages.map(renderLogo)} +
-
+ ) : ( + // Static grid mode +
+ {displayImages.map(renderLogo)} +
+ )}
); } diff --git a/src/config/siteConfig.ts b/src/config/siteConfig.ts index ba59ae3..2001134 100644 --- a/src/config/siteConfig.ts +++ b/src/config/siteConfig.ts @@ -3,6 +3,16 @@ import { ReactNode } from "react"; export type { LogoItem, LogoGalleryConfig } from "../components/LogoMarquee"; import type { LogoGalleryConfig } from "../components/LogoMarquee"; +// GitHub contributions graph configuration +// Displays your GitHub activity on the homepage +export interface GitHubContributionsConfig { + enabled: boolean; // Enable/disable the contributions graph + username: string; // GitHub username to fetch contributions for + showYearNavigation: boolean; // Show prev/next year arrows + linkToProfile: boolean; // Click graph to go to GitHub profile + title?: string; // Optional title above the graph +} + // Blog page configuration // Controls whether posts appear on homepage, dedicated blog page, or both export interface BlogPageConfig { @@ -36,6 +46,9 @@ export interface SiteConfig { // Logo gallery configuration logoGallery: LogoGalleryConfig; + // GitHub contributions graph configuration + gitHubContributions: GitHubContributionsConfig; + // Blog page configuration blogPage: BlogPageConfig; @@ -54,12 +67,12 @@ export interface SiteConfig { // Customize this for your site export const siteConfig: SiteConfig = { // Basic site info - name: 'markdown "sync" site', - title: "markdown sync site", + name: 'markdown "sync" framework', + title: "markdown sync framework", // Optional logo/header image (place in public/images/, set to null to hide) logo: "/images/logo.svg", intro: null, // Set in Home.tsx to allow JSX with links - bio: `Write locally, sync instantly with real-time updates. Powered by Convex and Netlify.`, + bio: `Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents.`, // Featured section configuration // viewMode: 'list' shows bullet list, 'cards' shows card grid with excerpts @@ -69,23 +82,25 @@ export const siteConfig: SiteConfig = { // Logo gallery configuration // Set enabled to false to hide, or remove/replace sample images with your own + // scrolling: true = infinite scroll marquee, false = static centered grid + // maxItems: only used when scrolling is false (default: 4) logoGallery: { enabled: true, images: [ { - src: "/images/logos/sample-logo-1.svg", + src: "/images/logos/convex-wordmark-black.svg", href: "https://markdowncms.netlify.app/", }, { - src: "/images/logos/convex-wordmark-black.svg", + src: "/images/logos/netlify.svg", href: "/about#the-real-time-twist", }, { - src: "/images/logos/sample-logo-3.svg", + src: "/images/logos/markdown.svg", href: "https://markdowncms.netlify.app/", }, { - src: "/images/logos/sample-logo-4.svg", + src: "/images/logos/react.svg", href: "https://markdowncms.netlify.app/", }, { @@ -95,7 +110,19 @@ export const siteConfig: SiteConfig = { ], position: "above-footer", speed: 30, - title: "Trusted by (sample logos)", + title: "Built with", + scrolling: false, // Set to false for static grid showing first maxItems logos + maxItems: 4, // Number of logos to show when scrolling is false + }, + + // GitHub contributions graph configuration + // Set enabled to false to hide, or change username to your GitHub username + gitHubContributions: { + enabled: true, // Set to false to hide the contributions graph + username: "waynesutton", // 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 }, // Blog page configuration diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index b81a316..6d7928d 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -4,6 +4,7 @@ import { api } from "../../convex/_generated/api"; import PostList from "../components/PostList"; import FeaturedCards from "../components/FeaturedCards"; import LogoMarquee from "../components/LogoMarquee"; +import GitHubContributions from "../components/GitHubContributions"; import siteConfig from "../config/siteConfig"; // Local storage key for view mode preference @@ -93,8 +94,8 @@ export default function Home() { {/* Intro with JSX support for links */}

- An open-source markdown sync site for developers and AI agents. - Publish from the terminal with npm run sync.{" "} + An open-source publishing framework for AI agents and developers. + Write markdown, sync from the terminal.{" "} )} + {/* GitHub contributions graph - above logo gallery */} + {siteConfig.gitHubContributions?.enabled && ( + + )} + {/* Logo gallery (above-footer position) */} {renderLogoGallery("above-footer")} diff --git a/src/pages/Post.tsx b/src/pages/Post.tsx index 9faedd5..012f4c3 100644 --- a/src/pages/Post.tsx +++ b/src/pages/Post.tsx @@ -9,7 +9,7 @@ import { useState, useEffect } from "react"; // Site configuration const SITE_URL = "https://markdowncms.netlify.app"; -const SITE_NAME = "markdown sync site"; +const SITE_NAME = "markdown sync framework"; const DEFAULT_OG_IMAGE = "/images/og-default.svg"; export default function Post() { diff --git a/src/styles/global.css b/src/styles/global.css index 050f079..aa33c4f 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -1939,6 +1939,341 @@ body { overflow: hidden; } +/* ============================================ + GITHUB CONTRIBUTIONS GRAPH + Theme-aware contribution calendar + ============================================ */ +.github-contributions-container { + margin: 32px 0 48px; + padding: 16px 20px; + background-color: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 8px; +} + +.github-contributions-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 12px; + flex-wrap: wrap; + gap: 12px; +} + +.github-contributions-header-left { + display: flex; + flex-direction: column; + gap: 2px; +} + +.github-contributions-title { + font-size: var(--font-size-md); + font-weight: 500; + color: var(--text-primary); +} + +.github-contributions-count { + font-size: var(--font-size-xs); + color: var(--text-muted); +} + +.github-contributions-nav { + display: flex; + align-items: center; + gap: 8px; +} + +.github-nav-button { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + padding: 0; + background: transparent; + border: 1px solid var(--border-color); + border-radius: 6px; + color: var(--text-secondary); + cursor: pointer; + transition: all 0.15s ease; +} + +.github-nav-button:hover:not(:disabled) { + background-color: var(--bg-hover); + color: var(--text-primary); + border-color: var(--text-muted); +} + +.github-nav-button:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.github-year { + font-size: var(--font-size-md); + font-weight: 500; + color: var(--text-primary); + min-width: 48px; + text-align: center; +} + +/* Graph layout */ +.github-contributions-graph { + display: flex; + flex-direction: column; + gap: 4px; +} + +.github-contributions-months { + display: flex; + gap: 4px; +} + +.github-contributions-day-spacer { + width: 28px; + flex-shrink: 0; +} + +.github-contributions-month-labels { + display: grid; + grid-template-columns: repeat(53, 1fr); + flex: 1; + font-size: 10px; + color: var(--text-muted); +} + +.github-contributions-month { + white-space: nowrap; +} + +.github-contributions-body { + display: flex; + gap: 4px; +} + +.github-contributions-days { + display: flex; + flex-direction: column; + gap: 2px; + width: 28px; + flex-shrink: 0; +} + +.github-contributions-day-label { + height: 10px; + font-size: 9px; + color: var(--text-muted); + line-height: 10px; + text-align: right; + padding-right: 4px; +} + +.github-contributions-grid { + display: grid; + grid-template-rows: repeat(7, 1fr); + grid-auto-flow: column; + gap: 2px; + flex: 1; + overflow-x: auto; +} + +/* Contribution cells */ +.github-contribution-cell { + width: 10px; + height: 10px; + border-radius: 2px; + transition: filter 0.15s ease; +} + +.github-contributions-link { + display: block; + text-decoration: none; +} + +.github-contributions-link:hover .github-contribution-cell { + filter: brightness(1.15); +} + +/* Legend */ +.github-contributions-legend { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 4px; + margin-top: 8px; +} + +.github-contributions-legend-label { + font-size: 10px; + color: var(--text-muted); + margin: 0 4px; +} + +.github-contributions-legend .github-contribution-cell { + width: 10px; + height: 10px; +} + +/* Error state */ +.github-contributions-error { + padding: 24px; + text-align: center; + font-size: var(--font-size-sm); + color: var(--text-muted); +} + +/* Theme-specific contribution colors */ +/* Dark theme - GitHub green on dark */ +:root[data-theme="dark"] { + --contrib-empty: #161b22; + --contrib-level-1: #0e4429; + --contrib-level-2: #006d32; + --contrib-level-3: #26a641; + --contrib-level-4: #39d353; +} + +/* Light theme - GitHub green */ +:root[data-theme="light"] { + --contrib-empty: #ebedf0; + --contrib-level-1: #9be9a8; + --contrib-level-2: #40c463; + --contrib-level-3: #30a14e; + --contrib-level-4: #216e39; +} + +/* Tan theme - warm brown tones */ +:root[data-theme="tan"] { + --contrib-empty: #ebe7df; + --contrib-level-1: #d4c4a8; + --contrib-level-2: #b5986d; + --contrib-level-3: #8b7355; + --contrib-level-4: #5d4e37; +} + +/* Cloud theme - gray-blue tones */ +:root[data-theme="cloud"] { + --contrib-empty: #e0e0e0; + --contrib-level-1: #b0c4de; + --contrib-level-2: #7ba3c9; + --contrib-level-3: #4682b4; + --contrib-level-4: #2f5a7c; +} + +/* Apply colors to cells */ +.github-contribution-cell[data-level="empty"] { + background-color: transparent; +} + +.github-contribution-cell[data-level="0"] { + background-color: var(--contrib-empty); +} + +.github-contribution-cell[data-level="1"] { + background-color: var(--contrib-level-1); +} + +.github-contribution-cell[data-level="2"] { + background-color: var(--contrib-level-2); +} + +.github-contribution-cell[data-level="3"] { + background-color: var(--contrib-level-3); +} + +.github-contribution-cell[data-level="4"] { + background-color: var(--contrib-level-4); +} + +/* Theme-specific container shadows */ +:root[data-theme="dark"] .github-contributions-container { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} + +:root[data-theme="tan"] .github-contributions-container { + box-shadow: 0 2px 8px rgba(139, 115, 85, 0.1); +} + +:root[data-theme="cloud"] .github-contributions-container { + box-shadow: 0 2px 8px rgba(100, 116, 139, 0.1); +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .github-contributions-container { + margin: 24px 0 32px; + padding: 12px 14px; + } + + .github-contributions-header { + flex-direction: column; + align-items: flex-start; + gap: 8px; + } + + .github-contributions-nav { + align-self: flex-end; + } + + .github-contributions-day-spacer, + .github-contributions-days { + width: 20px; + } + + .github-contribution-cell { + width: 8px; + height: 8px; + } + + .github-contributions-legend .github-contribution-cell { + width: 8px; + height: 8px; + } + + .github-contributions-grid { + gap: 1px; + } + + .github-contributions-day-label { + font-size: 8px; + height: 8px; + line-height: 8px; + } + + .github-contributions-month-labels { + font-size: 9px; + } +} + +@media (max-width: 480px) { + .github-contributions-container { + margin: 20px 0 28px; + padding: 10px 12px; + } + + .github-contribution-cell { + width: 6px; + height: 6px; + } + + .github-contributions-legend .github-contribution-cell { + width: 6px; + height: 6px; + } + + .github-contributions-day-spacer, + .github-contributions-days { + display: none; + } + + .github-contributions-month-labels { + font-size: 8px; + } + + .github-contributions-legend-label { + font-size: 9px; + } +} + /* Logo marquee container */ .logo-marquee-container { margin: 48px 0; @@ -2047,6 +2382,34 @@ body { opacity: 0.9; } +/* Logo static grid mode (non-scrolling) */ +.logo-static-grid { + display: flex; + justify-content: center; + align-items: center; + gap: 48px; + flex-wrap: wrap; + padding: 0 24px; +} + +.logo-static-item { + display: flex; + align-items: center; + justify-content: center; +} + +@media (max-width: 768px) { + .logo-static-grid { + gap: 32px; + } +} + +@media (max-width: 480px) { + .logo-static-grid { + gap: 24px; + } +} + /* Tan theme adjustments for featured cards */ :root[data-theme="tan"] .featured-card { box-shadow: 0 2px 8px rgba(139, 115, 85, 0.08);