feature: Social footer icons in header navigation

This commit is contained in:
Wayne Sutton
2026-01-01 16:06:36 -08:00
parent 2858b6149b
commit 4cfbb2588a
20 changed files with 216 additions and 16 deletions

View File

@@ -955,7 +955,7 @@ Set `showFooter: false` in post/page frontmatter to hide footer on specific page
## Social Footer Configuration ## Social Footer Configuration
Display social icons and copyright information below the main footer. Display social icons and copyright information below the main footer. Icons can also appear in the header.
### In fork-config.json ### In fork-config.json
@@ -967,6 +967,7 @@ Display social icons and copyright information below the main footer.
"showOnPosts": true, "showOnPosts": true,
"showOnPages": true, "showOnPages": true,
"showOnBlogPage": true, "showOnBlogPage": true,
"showInHeader": true,
"socialLinks": [ "socialLinks": [
{ {
"platform": "github", "platform": "github",
@@ -996,6 +997,7 @@ socialFooter: {
showOnPosts: true, showOnPosts: true,
showOnPages: true, showOnPages: true,
showOnBlogPage: true, showOnBlogPage: true,
showInHeader: true, // Show social icons in header (left of search icon)
socialLinks: [ socialLinks: [
{ platform: "github", url: "https://github.com/username" }, { platform: "github", url: "https://github.com/username" },
{ platform: "twitter", url: "https://x.com/handle" }, { platform: "twitter", url: "https://x.com/handle" },
@@ -1010,6 +1012,10 @@ socialFooter: {
**Supported Platforms:** github, twitter, linkedin, instagram, youtube, tiktok, discord, website **Supported Platforms:** github, twitter, linkedin, instagram, youtube, tiktok, discord, website
**Header Social Icons:**
When `showInHeader: true`, social icons appear in the navigation header to the left of the search icon on desktop. This provides additional visibility for your social links while maintaining the footer placement.
**Frontmatter Override:** **Frontmatter Override:**
Set `showSocialFooter: false` in post/page frontmatter to hide social footer on specific pages. Set `showSocialFooter: false` in post/page frontmatter to hide social footer on specific pages.

14
TASK.md
View File

@@ -2,15 +2,25 @@
## To Do ## To Do
- [ ] site confg add header icons
- [ ] fix site confg link - [ ] fix site confg link
- [ ] dashboard agent nanobanno
- [ ] npm package
## Current Status ## Current Status
v2.4.0 ready. YouTube and Twitter/X embed support with domain whitelisting. v2.5.0 ready. Social footer icons can now display in header navigation.
## Completed ## Completed
- [x] Social footer icons in header navigation
- [x] Added `showInHeader` option to `siteConfig.socialFooter` config
- [x] Exported `platformIcons` from SocialFooter.tsx for reuse
- [x] Updated Layout.tsx to render social icons in header (left of search)
- [x] Added CSS styles for `.header-social-links` and `.header-social-link`
- [x] Added showInHeader to configure-fork.ts for automated setup
- [x] Updated FORK_CONFIG.md, fork-config.json.example, docs.md, setup-guide.md
- [x] YouTube and Twitter/X embed support with domain whitelisting - [x] YouTube and Twitter/X embed support with domain whitelisting
- [x] Added `ALLOWED_IFRAME_DOMAINS` constant for whitelisted domains (YouTube, Twitter/X) - [x] Added `ALLOWED_IFRAME_DOMAINS` constant for whitelisted domains (YouTube, Twitter/X)
- [x] Added `iframe` to sanitize schema with allowed attributes - [x] Added `iframe` to sanitize schema with allowed attributes

View File

@@ -4,6 +4,26 @@ 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/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [2.5.0] - 2026-01-01
### Added
- Social footer icons in header navigation
- New `showInHeader` option in `siteConfig.socialFooter` to display social icons in the header
- Social icons appear left of the search icon on desktop viewports
- Uses same icons and links as the social footer component
- Configurable via siteConfig, FORK_CONFIG.md, and fork-config.json
- Disabled by default (set `showInHeader: true` to enable)
### Technical
- Exported `platformIcons` from `SocialFooter.tsx` for reuse in Layout component
- Added social icon rendering in `Layout.tsx` header controls
- Added `.header-social-links` and `.header-social-link` CSS styles in `global.css`
- Updated `SocialFooterConfig` interface with `showInHeader: boolean`
- Added socialFooter support to `configure-fork.ts` script
- Updated documentation: FORK_CONFIG.md, fork-config.json.example, docs.md, setup-guide.md
## [2.4.0] - 2026-01-01 ## [2.4.0] - 2026-01-01
### Added ### Added

View File

@@ -10,6 +10,28 @@ layout: "sidebar"
All notable changes to this project. All notable changes to this project.
![](https://img.shields.io/badge/License-MIT-yellow.svg) ![](https://img.shields.io/badge/License-MIT-yellow.svg)
## v2.5.0
Released January 1, 2026
**Social footer icons in header navigation**
- New `showInHeader` option in `siteConfig.socialFooter` to display social icons in the header
- Social icons appear left of the search icon on desktop viewports
- Uses same icons and links as the social footer component
- Configurable via siteConfig, FORK_CONFIG.md, and fork-config.json
- Set `showInHeader: true` in socialFooter config to enable
**Technical details:**
- Exported `platformIcons` from `src/components/SocialFooter.tsx` for reuse
- Added social icon rendering in `src/components/Layout.tsx` header controls
- Added `.header-social-links` and `.header-social-link` CSS styles
- Updated `SocialFooterConfig` interface with `showInHeader: boolean`
- Added socialFooter support to `scripts/configure-fork.ts`
Updated files: `src/config/siteConfig.ts`, `src/components/SocialFooter.tsx`, `src/components/Layout.tsx`, `src/styles/global.css`, `scripts/configure-fork.ts`, `FORK_CONFIG.md`, `fork-config.json.example`, `public/raw/docs.md`, `public/raw/setup-guide.md`, `files.md`, `TASK.md`, `changelog.md`, `content/pages/changelog-page.md`
## v2.4.0 ## v2.4.0
Released January 1, 2026 Released January 1, 2026

View File

@@ -4,7 +4,8 @@ slug: "docs"
published: true published: true
order: 0 order: 0
layout: "sidebar" layout: "sidebar"
aiChat: true aiChat: false
rightSidebar: true
showFooter: true showFooter: true
--- ---

View File

@@ -56,7 +56,7 @@ A brief description of each file in the codebase.
| File | Description | | File | Description |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Layout.tsx` | Page wrapper with logo in header (top-left), search button, theme toggle, mobile menu (left-aligned on mobile), and scroll-to-top. Combines Blog link, hardcoded nav items, and markdown pages for navigation. Logo reads from siteConfig.innerPageLogo | | `Layout.tsx` | Page wrapper with logo in header (top-left), search button, theme toggle, mobile menu (left-aligned on mobile), and scroll-to-top. Combines Blog link, hardcoded nav items, and markdown pages for navigation. Logo reads from siteConfig.innerPageLogo. Displays social icons in header (left of search) when siteConfig.socialFooter.showInHeader is true. |
| `ThemeToggle.tsx` | Theme switcher (dark/light/tan/cloud) | | `ThemeToggle.tsx` | Theme switcher (dark/light/tan/cloud) |
| `PostList.tsx` | Year-grouped blog post list or card grid (supports list/cards view modes, columns prop for 2/3 column grids, showExcerpts prop to control excerpt visibility) | | `PostList.tsx` | Year-grouped blog post list or card grid (supports list/cards view modes, columns prop for 2/3 column grids, showExcerpts prop to control excerpt visibility) |
| `BlogHeroCard.tsx` | Hero card component for the first blogFeatured post on blog page. Displays landscape image, tags, date, title, excerpt, author info, and read more link | | `BlogHeroCard.tsx` | Hero card component for the first blogFeatured post on blog page. Displays landscape image, tags, date, title, excerpt, author info, and read more link |
@@ -75,7 +75,7 @@ A brief description of each file in the codebase.
| `AIChatView.tsx` | AI chat interface component (Agent) using Anthropic Claude API. Supports per-page chat history, page content context, markdown rendering, and copy functionality. Used in Write page (replaces textarea when enabled) and optionally in RightSidebar. Requires ANTHROPIC_API_KEY environment variable in Convex. System prompt configurable via CLAUDE_PROMPT_STYLE, CLAUDE_PROMPT_COMMUNITY, CLAUDE_PROMPT_RULES, or CLAUDE_SYSTEM_PROMPT environment variables. Includes error handling for missing API keys. | | `AIChatView.tsx` | AI chat interface component (Agent) using Anthropic Claude API. Supports per-page chat history, page content context, markdown rendering, and copy functionality. Used in Write page (replaces textarea when enabled) and optionally in RightSidebar. Requires ANTHROPIC_API_KEY environment variable in Convex. System prompt configurable via CLAUDE_PROMPT_STYLE, CLAUDE_PROMPT_COMMUNITY, CLAUDE_PROMPT_RULES, or CLAUDE_SYSTEM_PROMPT environment variables. Includes error handling for missing API keys. |
| `NewsletterSignup.tsx` | Newsletter signup form component for email-only subscriptions. Displays configurable title/description, validates email, and submits to Convex. Shows on home, blog page, and posts based on siteConfig.newsletter settings. Supports frontmatter override via newsletter: true/false. Includes honeypot field for bot protection. | | `NewsletterSignup.tsx` | Newsletter signup form component for email-only subscriptions. Displays configurable title/description, validates email, and submits to Convex. Shows on home, blog page, and posts based on siteConfig.newsletter settings. Supports frontmatter override via newsletter: true/false. Includes honeypot field for bot protection. |
| `ContactForm.tsx` | Contact form component with name, email, and message fields. Displays when contactForm: true in frontmatter. Submits to Convex which sends email via AgentMail to configured recipient. Requires AGENTMAIL_API_KEY and AGENTMAIL_INBOX environment variables. Includes honeypot field for bot protection. | | `ContactForm.tsx` | Contact form component with name, email, and message fields. Displays when contactForm: true in frontmatter. Submits to Convex which sends email via AgentMail to configured recipient. Requires AGENTMAIL_API_KEY and AGENTMAIL_INBOX environment variables. Includes honeypot field for bot protection. |
| `SocialFooter.tsx` | Social footer component with social icons on left (GitHub, Twitter/X, LinkedIn, Instagram, YouTube, TikTok, Discord, Website) and copyright on right. Configurable via siteConfig.socialFooter. Shows below main footer on homepage, blog posts, and pages. Supports frontmatter override via showSocialFooter: true/false. Auto-updates copyright year. | | `SocialFooter.tsx` | Social footer component with social icons on left (GitHub, Twitter/X, LinkedIn, Instagram, YouTube, TikTok, Discord, Website) and copyright on right. Configurable via siteConfig.socialFooter. Shows below main footer on homepage, blog posts, and pages. Supports frontmatter override via showSocialFooter: true/false. Auto-updates copyright year. Exports `platformIcons` for reuse in header. |
### Context (`src/context/`) ### Context (`src/context/`)

View File

@@ -80,6 +80,7 @@
"showOnPosts": true, "showOnPosts": true,
"showOnPages": true, "showOnPages": true,
"showOnBlogPage": true, "showOnBlogPage": true,
"showInHeader": true,
"socialLinks": [ "socialLinks": [
{ {
"platform": "github", "platform": "github",

View File

@@ -2,7 +2,7 @@
--- ---
Type: page Type: page
Date: 2026-01-01 Date: 2026-01-02
--- ---
An open-source publishing framework built for AI agents and developers to ship websites, docs, or blogs.. Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents. Built on Convex and Netlify. An open-source publishing framework built for AI agents and developers to ship websites, docs, or blogs.. Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents. Built on Convex and Netlify.

View File

@@ -2,12 +2,34 @@
--- ---
Type: page Type: page
Date: 2026-01-01 Date: 2026-01-02
--- ---
All notable changes to this project. All notable changes to this project.
![](https://img.shields.io/badge/License-MIT-yellow.svg) ![](https://img.shields.io/badge/License-MIT-yellow.svg)
## v2.5.0
Released January 1, 2026
**Social footer icons in header navigation**
- New `showInHeader` option in `siteConfig.socialFooter` to display social icons in the header
- Social icons appear left of the search icon on desktop viewports
- Uses same icons and links as the social footer component
- Configurable via siteConfig, FORK_CONFIG.md, and fork-config.json
- Set `showInHeader: true` in socialFooter config to enable
**Technical details:**
- Exported `platformIcons` from `src/components/SocialFooter.tsx` for reuse
- Added social icon rendering in `src/components/Layout.tsx` header controls
- Added `.header-social-links` and `.header-social-link` CSS styles
- Updated `SocialFooterConfig` interface with `showInHeader: boolean`
- Added socialFooter support to `scripts/configure-fork.ts`
Updated files: `src/config/siteConfig.ts`, `src/components/SocialFooter.tsx`, `src/components/Layout.tsx`, `src/styles/global.css`, `scripts/configure-fork.ts`, `FORK_CONFIG.md`, `fork-config.json.example`, `public/raw/docs.md`, `public/raw/setup-guide.md`, `files.md`, `TASK.md`, `changelog.md`, `content/pages/changelog-page.md`
## v2.4.0 ## v2.4.0
Released January 1, 2026 Released January 1, 2026

View File

@@ -2,7 +2,7 @@
--- ---
Type: page Type: page
Date: 2026-01-01 Date: 2026-01-02
--- ---
You found the contact page. Nice You found the contact page. Nice

View File

@@ -2,7 +2,7 @@
--- ---
Type: page Type: page
Date: 2026-01-01 Date: 2026-01-02
--- ---
## Getting Started ## Getting Started

View File

@@ -2,7 +2,7 @@
--- ---
Type: page Type: page
Date: 2026-01-01 Date: 2026-01-02
--- ---
Built with [Convex](https://convex.dev) for real-time sync and deployed on [Netlify](https://netlify.com). Read the [project on GitHub](https://github.com/waynesutton/markdown-site) to fork and deploy your own. View [real-time site stats](/stats). Built with [Convex](https://convex.dev) for real-time sync and deployed on [Netlify](https://netlify.com). Read the [project on GitHub](https://github.com/waynesutton/markdown-site) to fork and deploy your own. View [real-time site stats](/stats).

View File

@@ -2,7 +2,7 @@
--- ---
Type: page Type: page
Date: 2026-01-01 Date: 2026-01-02
--- ---
An open-source publishing framework built for AI agents and developers to ship **[docs](/docs)**, or **[blogs](/blog)** or **[websites](/)**. An open-source publishing framework built for AI agents and developers to ship **[docs](/docs)**, or **[blogs](/blog)** or **[websites](/)**.

View File

@@ -2,7 +2,7 @@
--- ---
Type: page Type: page
Date: 2026-01-01 Date: 2026-01-02
--- ---
# Newsletter Demo Page # Newsletter Demo Page

View File

@@ -2,7 +2,7 @@
--- ---
Type: page Type: page
Date: 2026-01-01 Date: 2026-01-02
--- ---
This markdown framework 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.

View File

@@ -83,6 +83,22 @@ interface ForkConfig {
slug?: string; slug?: string;
originalHomeRoute?: string; originalHomeRoute?: string;
}; };
socialFooter?: {
enabled?: boolean;
showOnHomepage?: boolean;
showOnPosts?: boolean;
showOnPages?: boolean;
showOnBlogPage?: boolean;
showInHeader?: boolean;
socialLinks?: Array<{
platform: string;
url: string;
}>;
copyright?: {
siteName?: string;
showYear?: boolean;
};
};
} }
// Get project root directory // Get project root directory
@@ -318,6 +334,58 @@ function updateSiteConfig(config: ForkConfig): void {
`contentPath: "${gitHubRepoContentPath}", // Path to raw markdown files`, `contentPath: "${gitHubRepoContentPath}", // Path to raw markdown files`,
); );
// Update socialFooter if specified
if (config.socialFooter) {
if (config.socialFooter.enabled !== undefined) {
content = content.replace(
/socialFooter: \{[\s\S]*?enabled: (?:true|false),\s*\/\/ Global toggle for social footer/,
`socialFooter: {\n enabled: ${config.socialFooter.enabled}, // Global toggle for social footer`,
);
}
if (config.socialFooter.showOnHomepage !== undefined) {
content = content.replace(
/showOnHomepage: (?:true|false),\s*\/\/ Show social footer on homepage/,
`showOnHomepage: ${config.socialFooter.showOnHomepage}, // Show social footer on homepage`,
);
}
if (config.socialFooter.showOnPosts !== undefined) {
content = content.replace(
/showOnPosts: (?:true|false),\s*\/\/ Default: show social footer on blog posts/,
`showOnPosts: ${config.socialFooter.showOnPosts}, // Default: show social footer on blog posts`,
);
}
if (config.socialFooter.showOnPages !== undefined) {
content = content.replace(
/showOnPages: (?:true|false),\s*\/\/ Default: show social footer on static pages/,
`showOnPages: ${config.socialFooter.showOnPages}, // Default: show social footer on static pages`,
);
}
if (config.socialFooter.showOnBlogPage !== undefined) {
content = content.replace(
/showOnBlogPage: (?:true|false),\s*\/\/ Show social footer on \/blog page/,
`showOnBlogPage: ${config.socialFooter.showOnBlogPage}, // Show social footer on /blog page`,
);
}
if (config.socialFooter.showInHeader !== undefined) {
content = content.replace(
/showInHeader: (?:true|false),\s*\/\/ Show social icons in header/,
`showInHeader: ${config.socialFooter.showInHeader}, // Show social icons in header`,
);
}
if (config.socialFooter.copyright?.siteName) {
content = content.replace(
/siteName: ['"].*?['"],\s*\/\/ Update with your site\/company name/,
`siteName: "${config.socialFooter.copyright.siteName}", // Update with your site/company name`,
);
}
if (config.socialFooter.copyright?.showYear !== undefined) {
content = content.replace(
/showYear: (?:true|false),\s*\/\/ Auto-updates to current year/,
`showYear: ${config.socialFooter.copyright.showYear}, // Auto-updates to current year`,
);
}
}
fs.writeFileSync(filePath, content, "utf-8"); fs.writeFileSync(filePath, content, "utf-8");
console.log(` Updated: src/config/siteConfig.ts`); console.log(` Updated: src/config/siteConfig.ts`);
} }

View File

@@ -10,6 +10,7 @@ import MobileMenu, { HamburgerButton } from "./MobileMenu";
import ScrollToTop, { ScrollToTopConfig } from "./ScrollToTop"; import ScrollToTop, { ScrollToTopConfig } from "./ScrollToTop";
import { useSidebarOptional } from "../context/SidebarContext"; import { useSidebarOptional } from "../context/SidebarContext";
import siteConfig from "../config/siteConfig"; import siteConfig from "../config/siteConfig";
import { platformIcons } from "./SocialFooter";
// Scroll-to-top configuration - enabled by default // Scroll-to-top configuration - enabled by default
// Customize threshold (pixels) to control when button appears // Customize threshold (pixels) to control when button appears
@@ -190,6 +191,26 @@ export default function Layout({ children }: LayoutProps) {
{/* Desktop search and theme (visible on desktop only) */} {/* Desktop search and theme (visible on desktop only) */}
<div className="desktop-controls desktop-only"> <div className="desktop-controls desktop-only">
{/* Social icons in header (if enabled) */}
{siteConfig.socialFooter?.enabled && siteConfig.socialFooter?.showInHeader && (
<div className="header-social-links">
{siteConfig.socialFooter.socialLinks.map((link) => {
const IconComponent = platformIcons[link.platform];
return (
<a
key={link.platform}
href={link.url}
target="_blank"
rel="noopener noreferrer"
className="header-social-link"
aria-label={`Follow on ${link.platform}`}
>
<IconComponent size={18} weight="regular" />
</a>
);
})}
</div>
)}
{/* Search button with icon */} {/* Search button with icon */}
<button <button
onClick={openSearch} onClick={openSearch}

View File

@@ -9,10 +9,12 @@ import {
TiktokLogo, TiktokLogo,
DiscordLogo, DiscordLogo,
Globe, Globe,
type Icon,
} from "@phosphor-icons/react"; } from "@phosphor-icons/react";
// Map platform names to Phosphor icons // Map platform names to Phosphor icons
const platformIcons: Record<SocialLink["platform"], React.ComponentType<{ size?: number; weight?: "regular" | "bold" | "fill" }>> = { // Exported for reuse in header social icons
export const platformIcons: Record<SocialLink["platform"], Icon> = {
github: GithubLogo, github: GithubLogo,
twitter: TwitterLogo, twitter: TwitterLogo,
linkedin: LinkedinLogo, linkedin: LinkedinLogo,

View File

@@ -226,6 +226,7 @@ export interface SocialFooterConfig {
showOnPosts: boolean; // Default: show social footer on blog posts showOnPosts: boolean; // Default: show social footer on blog posts
showOnPages: boolean; // Default: show social footer on static pages showOnPages: boolean; // Default: show social footer on static pages
showOnBlogPage: boolean; // Show social footer on /blog page showOnBlogPage: boolean; // Show social footer on /blog page
showInHeader: boolean; // Show social icons in header (left of search icon)
socialLinks: SocialLink[]; // Array of social links to display socialLinks: SocialLink[]; // Array of social links to display
copyright: { copyright: {
siteName: string; // Site name or company name displayed in copyright siteName: string; // Site name or company name displayed in copyright
@@ -491,7 +492,7 @@ export const siteConfig: SiteConfig = {
}, },
// Footer configuration // Footer configuration
// Footer content is loaded from content/pages/footer.md (synced via npm run sync) // Footer content is loaded from content/pages/footer.md (synced via npm run sync)
// Use showFooter: false in frontmatter to hide footer on specific posts/pages // Use showFooter: false in frontmatter to hide footer on specific posts/pages
footer: { footer: {
enabled: true, // Global toggle for footer enabled: true, // Global toggle for footer
@@ -568,6 +569,7 @@ export const siteConfig: SiteConfig = {
showOnPosts: true, // Default: show social footer on blog posts showOnPosts: true, // Default: show social footer on blog posts
showOnPages: true, // Default: show social footer on static pages showOnPages: true, // Default: show social footer on static pages
showOnBlogPage: true, // Show social footer on /blog page showOnBlogPage: true, // Show social footer on /blog page
showInHeader: true, // Show social icons in header (left of search icon)
socialLinks: [ socialLinks: [
{ {
platform: "github", platform: "github",

View File

@@ -391,6 +391,31 @@ body {
gap: 8px; gap: 8px;
} }
/* Header social links (shown left of search when enabled) */
.header-social-links {
display: flex;
align-items: center;
gap: 4px;
margin-right: 8px;
padding-right: 8px;
border-right: 1px solid var(--border-color);
}
.header-social-link {
display: flex;
align-items: center;
justify-content: center;
color: var(--text-secondary);
padding: 4px;
border-radius: 4px;
transition: color 0.2s ease, background-color 0.2s ease;
}
.header-social-link:hover {
color: var(--text-primary);
background-color: var(--hover-bg);
}
/* Page navigation links (About, Projects, Contact, etc.) */ /* Page navigation links (About, Projects, Contact, etc.) */
.page-nav { .page-nav {
display: flex; display: flex;