mirror of
https://github.com/waynesutton/markdown-site.git
synced 2026-01-11 20:08:57 +00:00
update: show iamge at top of blog post, updated monospace font and base font size, default font for write
This commit is contained in:
14
TASK.md
14
TASK.md
@@ -3,15 +3,25 @@
|
||||
## To Do
|
||||
|
||||
- [ ] Newsletter signup
|
||||
- [ ] Comments system
|
||||
- [ ] Draft preview mode
|
||||
|
||||
## Current Status
|
||||
|
||||
v1.34.0 ready. Blog page featured layout with hero post, featured row, and regular posts grid. Uses `blogFeatured` frontmatter field to control featured posts display.
|
||||
v1.35.0 ready. Added `showImageAtTop` frontmatter field to display images at the top of posts and pages above the header. Image appears full-width when enabled, otherwise only used for Open Graph and featured cards.
|
||||
|
||||
## Completed
|
||||
|
||||
- [x] showImageAtTop frontmatter field for posts and pages
|
||||
- [x] Added showImageAtTop optional boolean field to convex/schema.ts for posts and pages
|
||||
- [x] Updated scripts/sync-posts.ts to parse showImageAtTop from frontmatter
|
||||
- [x] Updated convex/posts.ts and convex/pages.ts queries and mutations to include showImageAtTop
|
||||
- [x] Updated src/pages/Post.tsx to conditionally render image at top when showImageAtTop: true
|
||||
- [x] Added CSS styles for .post-header-image and .post-header-image-img
|
||||
- [x] Updated src/pages/Write.tsx to include showImageAtTop in POST_FIELDS and PAGE_FIELDS
|
||||
- [x] Updated documentation: docs.md, how-to-publish.md, using-images-in-posts.md, files.md
|
||||
- [x] Image displays full-width above post header with rounded corners
|
||||
- [x] Default behavior: image only used for OG and featured cards when showImageAtTop not set
|
||||
|
||||
- [x] Blog page featured layout with hero post
|
||||
- [x] `blogFeatured` frontmatter field for posts to mark as featured on blog page
|
||||
- [x] `BlogHeroCard` component for the hero featured post (first blogFeatured post)
|
||||
|
||||
30
changelog.md
30
changelog.md
@@ -4,11 +4,39 @@ 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.35.0] - 2025-12-26
|
||||
|
||||
### Added
|
||||
|
||||
- `showImageAtTop` frontmatter field for posts and pages
|
||||
- Set `showImageAtTop: true` to display the `image` field at the top of the post/page above the header
|
||||
- Image displays full-width with rounded corners above the post header
|
||||
- Default behavior: if `showImageAtTop` is not set or `false`, image only used for Open Graph previews and featured card thumbnails
|
||||
- Works for both blog posts and static pages
|
||||
- Image appears above the post header when enabled
|
||||
|
||||
### Changed
|
||||
|
||||
- `convex/schema.ts`: Added `showImageAtTop` optional boolean field to posts and pages tables
|
||||
- `scripts/sync-posts.ts`: Updated to parse `showImageAtTop` from frontmatter for both posts and pages
|
||||
- `convex/posts.ts` and `convex/pages.ts`: Updated queries and mutations to include `showImageAtTop` field
|
||||
- `src/pages/Post.tsx`: Added conditional rendering to display image at top when `showImageAtTop: true`
|
||||
- `src/pages/Write.tsx`: Added `showImageAtTop` to POST_FIELDS and PAGE_FIELDS frontmatter reference
|
||||
- `src/styles/global.css`: Added `.post-header-image` and `.post-header-image-img` styles for header image display
|
||||
- Documentation updated: `content/pages/docs.md`, `content/blog/how-to-publish.md`, `content/blog/using-images-in-posts.md`, `files.md`
|
||||
|
||||
### Technical
|
||||
|
||||
- Header image displays with full-width responsive layout
|
||||
- Image appears above post header with 32px bottom margin
|
||||
- Rounded corners (8px border-radius) for modern appearance
|
||||
- Maintains aspect ratio with `object-fit: cover`
|
||||
|
||||
## [1.34.0] - 2025-12-26
|
||||
|
||||
### Added
|
||||
|
||||
- Blog page featured layout with hero post
|
||||
- Blog page featured layout with hero post
|
||||
- `blogFeatured` frontmatter field for posts to mark as featured on blog page
|
||||
- First `blogFeatured` post displays as hero card with landscape image, tags, date, title, excerpt, author info, and read more link
|
||||
- Remaining `blogFeatured` posts display in 2-column featured row with excerpts
|
||||
|
||||
@@ -76,20 +76,21 @@ readTime: "5 min read"
|
||||
---
|
||||
```
|
||||
|
||||
| Field | Required | What It Does |
|
||||
| --------------- | -------- | --------------------------------------------------- |
|
||||
| `title` | Yes | Displays as the post heading |
|
||||
| `description` | Yes | Shows in search results and sharing |
|
||||
| `date` | Yes | Publication date (YYYY-MM-DD) |
|
||||
| `slug` | Yes | Becomes the URL path |
|
||||
| `published` | Yes | Set `true` to show, `false` to hide |
|
||||
| `tags` | Yes | Topic labels for the post |
|
||||
| `readTime` | No | Estimated reading time |
|
||||
| `image` | No | OG image for social sharing and featured card thumb |
|
||||
| `featured` | No | Set `true` to show in featured section |
|
||||
| `featuredOrder` | No | Order in featured section (lower first) |
|
||||
| `excerpt` | No | Short description for card view |
|
||||
| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC |
|
||||
| Field | Required | What It Does |
|
||||
| ---------------- | -------- | ------------------------------------------------------------------------------------------ |
|
||||
| `title` | Yes | Displays as the post heading |
|
||||
| `description` | Yes | Shows in search results and sharing |
|
||||
| `date` | Yes | Publication date (YYYY-MM-DD) |
|
||||
| `slug` | Yes | Becomes the URL path |
|
||||
| `published` | Yes | Set `true` to show, `false` to hide |
|
||||
| `tags` | Yes | Topic labels for the post |
|
||||
| `readTime` | No | Estimated reading time |
|
||||
| `image` | No | OG image for social sharing and featured card thumb |
|
||||
| `showImageAtTop` | No | Set `true` to display the image at the top of the post above the header (default: `false`) |
|
||||
| `featured` | No | Set `true` to show in featured section |
|
||||
| `featuredOrder` | No | Order in featured section (lower first) |
|
||||
| `excerpt` | No | Short description for card view |
|
||||
| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC |
|
||||
|
||||
## Write Your Content
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ featuredOrder: 4
|
||||
tags: ["images", "tutorial", "markdown", "open-graph"]
|
||||
readTime: "4 min read"
|
||||
blogFeatured: true
|
||||
showImageAtTop: true
|
||||
authorName: "Markdown"
|
||||
authorImage: "/images/authors/markdown.png"
|
||||
image: "https://images.unsplash.com/photo-1499750310107-5fef28a66643?w=1200&h=630&fit=crop"
|
||||
@@ -20,20 +21,42 @@ This post demonstrates how to add images to your blog posts. You can use header
|
||||
|
||||
## Header/Open Graph Images
|
||||
|
||||
The `image` field in your frontmatter serves two purposes:
|
||||
The `image` field in your frontmatter serves multiple purposes:
|
||||
|
||||
1. **Open Graph image** for social media previews (Twitter, LinkedIn, Slack)
|
||||
2. **Thumbnail image** for featured section card view on the homepage
|
||||
3. **Header image** displayed at the top of the post/page (when `showImageAtTop: true`)
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: "Your Post Title"
|
||||
image: "https://images.unsplash.com/photo-1499750310107-5fef28a66643?w=1200&h=630&fit=crop"
|
||||
showImageAtTop: true
|
||||
---
|
||||
```
|
||||
|
||||
**Recommended dimensions:** 1200x630 pixels (1.91:1 ratio) for social sharing
|
||||
|
||||
## Displaying Image at Top of Post/Page
|
||||
|
||||
To display the `image` field at the top of your post or page (above the header), add `showImageAtTop: true` to your frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: "My Post"
|
||||
image: "https://images.unsplash.com/photo-1499750310107-5fef28a66643?w=1200&h=630&fit=crop"
|
||||
showImageAtTop: true
|
||||
---
|
||||
```
|
||||
|
||||
**Default behavior:** If `showImageAtTop` is not set or set to `false`, the image will only be used for Open Graph previews and featured card thumbnails, not displayed in the post content.
|
||||
|
||||
**Use cases:**
|
||||
|
||||
- Hero images for blog posts
|
||||
- Featured images that introduce the content
|
||||
- Visual headers for documentation pages
|
||||
|
||||
## Featured Section Thumbnails
|
||||
|
||||
When a post or page is marked as `featured: true`, the `image` field displays as a square thumbnail in the card view.
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
title: "Contact"
|
||||
slug: "contact"
|
||||
published: true
|
||||
layout: "sidebar"
|
||||
order: 4
|
||||
---
|
||||
|
||||
|
||||
@@ -112,6 +112,7 @@ Content here...
|
||||
| `tags` | Yes | Array of strings |
|
||||
| `readTime` | No | Display time estimate |
|
||||
| `image` | No | OG image and featured card thumbnail. See [Using Images in Blog Posts](/using-images-in-posts) for markdown and HTML syntax |
|
||||
| `showImageAtTop` | No | Set `true` to display the image at the top of the post above the header (default: `false`) |
|
||||
| `excerpt` | No | Short text for card view |
|
||||
| `featured` | No | `true` to show in featured section |
|
||||
| `featuredOrder` | No | Order in featured (lower = first) |
|
||||
@@ -143,6 +144,7 @@ Content here...
|
||||
| `showInNav` | No | Show in navigation menu (default: `true`) |
|
||||
| `excerpt` | No | Short text for card view |
|
||||
| `image` | No | Thumbnail for featured card view |
|
||||
| `showImageAtTop` | No | Set `true` to display the image at the top of the page above the header (default: `false`) |
|
||||
| `featured` | No | `true` to show in featured section |
|
||||
| `featuredOrder` | No | Order in featured (lower = first) |
|
||||
| `authorName` | No | Author display name shown next to date |
|
||||
|
||||
@@ -119,6 +119,7 @@ export const getPageBySlug = query({
|
||||
showInNav: v.optional(v.boolean()),
|
||||
excerpt: v.optional(v.string()),
|
||||
image: v.optional(v.string()),
|
||||
showImageAtTop: v.optional(v.boolean()),
|
||||
featured: v.optional(v.boolean()),
|
||||
featuredOrder: v.optional(v.number()),
|
||||
authorName: v.optional(v.string()),
|
||||
@@ -151,6 +152,7 @@ export const getPageBySlug = query({
|
||||
showInNav: page.showInNav,
|
||||
excerpt: page.excerpt,
|
||||
image: page.image,
|
||||
showImageAtTop: page.showImageAtTop,
|
||||
featured: page.featured,
|
||||
featuredOrder: page.featuredOrder,
|
||||
authorName: page.authorName,
|
||||
@@ -177,6 +179,7 @@ export const syncPagesPublic = mutation({
|
||||
showInNav: v.optional(v.boolean()),
|
||||
excerpt: v.optional(v.string()),
|
||||
image: v.optional(v.string()),
|
||||
showImageAtTop: v.optional(v.boolean()),
|
||||
featured: v.optional(v.boolean()),
|
||||
featuredOrder: v.optional(v.number()),
|
||||
authorName: v.optional(v.string()),
|
||||
@@ -220,6 +223,7 @@ export const syncPagesPublic = mutation({
|
||||
showInNav: page.showInNav,
|
||||
excerpt: page.excerpt,
|
||||
image: page.image,
|
||||
showImageAtTop: page.showImageAtTop,
|
||||
featured: page.featured,
|
||||
featuredOrder: page.featuredOrder,
|
||||
authorName: page.authorName,
|
||||
|
||||
@@ -169,6 +169,7 @@ export const getPostBySlug = query({
|
||||
tags: v.array(v.string()),
|
||||
readTime: v.optional(v.string()),
|
||||
image: v.optional(v.string()),
|
||||
showImageAtTop: v.optional(v.boolean()),
|
||||
excerpt: v.optional(v.string()),
|
||||
featured: v.optional(v.boolean()),
|
||||
featuredOrder: v.optional(v.number()),
|
||||
@@ -204,6 +205,7 @@ export const getPostBySlug = query({
|
||||
tags: post.tags,
|
||||
readTime: post.readTime,
|
||||
image: post.image,
|
||||
showImageAtTop: post.showImageAtTop,
|
||||
excerpt: post.excerpt,
|
||||
featured: post.featured,
|
||||
featuredOrder: post.featuredOrder,
|
||||
@@ -232,6 +234,7 @@ export const syncPosts = internalMutation({
|
||||
tags: v.array(v.string()),
|
||||
readTime: v.optional(v.string()),
|
||||
image: v.optional(v.string()),
|
||||
showImageAtTop: v.optional(v.boolean()),
|
||||
excerpt: v.optional(v.string()),
|
||||
featured: v.optional(v.boolean()),
|
||||
featuredOrder: v.optional(v.number()),
|
||||
@@ -278,6 +281,7 @@ export const syncPosts = internalMutation({
|
||||
tags: post.tags,
|
||||
readTime: post.readTime,
|
||||
image: post.image,
|
||||
showImageAtTop: post.showImageAtTop,
|
||||
excerpt: post.excerpt,
|
||||
featured: post.featured,
|
||||
featuredOrder: post.featuredOrder,
|
||||
@@ -328,6 +332,7 @@ export const syncPostsPublic = mutation({
|
||||
tags: v.array(v.string()),
|
||||
readTime: v.optional(v.string()),
|
||||
image: v.optional(v.string()),
|
||||
showImageAtTop: v.optional(v.boolean()),
|
||||
excerpt: v.optional(v.string()),
|
||||
featured: v.optional(v.boolean()),
|
||||
featuredOrder: v.optional(v.number()),
|
||||
@@ -374,6 +379,7 @@ export const syncPostsPublic = mutation({
|
||||
tags: post.tags,
|
||||
readTime: post.readTime,
|
||||
image: post.image,
|
||||
showImageAtTop: post.showImageAtTop,
|
||||
excerpt: post.excerpt,
|
||||
featured: post.featured,
|
||||
featuredOrder: post.featuredOrder,
|
||||
|
||||
@@ -13,6 +13,7 @@ export default defineSchema({
|
||||
tags: v.array(v.string()),
|
||||
readTime: v.optional(v.string()),
|
||||
image: v.optional(v.string()), // Header/OG image URL
|
||||
showImageAtTop: v.optional(v.boolean()), // Display image at top of post (default: false)
|
||||
excerpt: v.optional(v.string()), // Short excerpt for card view
|
||||
featured: v.optional(v.boolean()), // Show in featured section
|
||||
featuredOrder: v.optional(v.number()), // Order in featured section (lower = first)
|
||||
@@ -50,6 +51,7 @@ export default defineSchema({
|
||||
showInNav: v.optional(v.boolean()), // Show in navigation menu (default: true)
|
||||
excerpt: v.optional(v.string()), // Short excerpt for card view
|
||||
image: v.optional(v.string()), // Thumbnail/OG image URL for featured cards
|
||||
showImageAtTop: v.optional(v.boolean()), // Display image at top of page (default: false)
|
||||
featured: v.optional(v.boolean()), // Show in featured section
|
||||
featuredOrder: v.optional(v.number()), // Order in featured section (lower = first)
|
||||
authorName: v.optional(v.string()), // Author display name
|
||||
|
||||
3
files.md
3
files.md
@@ -141,6 +141,7 @@ Markdown files with frontmatter for blog posts. Each file becomes a blog post.
|
||||
| `tags` | Array of topic tags |
|
||||
| `readTime` | Estimated reading time |
|
||||
| `image` | Header/Open Graph image URL (optional) |
|
||||
| `showImageAtTop` | Display image at top of post above header (optional, default: false) |
|
||||
| `excerpt` | Short excerpt for card view (optional) |
|
||||
| `featured` | Show in featured section (optional) |
|
||||
| `featuredOrder` | Order in featured section (optional) |
|
||||
@@ -164,6 +165,8 @@ Markdown files for static pages like About, Projects, Contact, Changelog.
|
||||
| `order` | Display order in navigation (lower first) |
|
||||
| `showInNav` | Show in navigation menu (default: true) |
|
||||
| `excerpt` | Short excerpt for card view (optional) |
|
||||
| `image` | Thumbnail/OG image URL (optional) |
|
||||
| `showImageAtTop` | Display image at top of page above header (optional, default: false) |
|
||||
| `featured` | Show in featured section (optional) |
|
||||
| `featuredOrder` | Order in featured section (optional) |
|
||||
| `authorName` | Author display name (optional) |
|
||||
|
||||
@@ -108,6 +108,7 @@ Content here...
|
||||
| `tags` | Yes | Array of strings |
|
||||
| `readTime` | No | Display time estimate |
|
||||
| `image` | No | OG image and featured card thumbnail. See [Using Images in Blog Posts](/using-images-in-posts) for markdown and HTML syntax |
|
||||
| `showImageAtTop` | No | Set `true` to display the image at the top of the post above the header (default: `false`) |
|
||||
| `excerpt` | No | Short text for card view |
|
||||
| `featured` | No | `true` to show in featured section |
|
||||
| `featuredOrder` | No | Order in featured (lower = first) |
|
||||
@@ -139,6 +140,7 @@ Content here...
|
||||
| `showInNav` | No | Show in navigation menu (default: `true`) |
|
||||
| `excerpt` | No | Short text for card view |
|
||||
| `image` | No | Thumbnail for featured card view |
|
||||
| `showImageAtTop` | No | Set `true` to display the image at the top of the page above the header (default: `false`) |
|
||||
| `featured` | No | `true` to show in featured section |
|
||||
| `featuredOrder` | No | Order in featured (lower = first) |
|
||||
| `authorName` | No | Author display name shown next to date |
|
||||
|
||||
@@ -70,20 +70,21 @@ readTime: "5 min read"
|
||||
---
|
||||
```
|
||||
|
||||
| Field | Required | What It Does |
|
||||
| --------------- | -------- | --------------------------------------------------- |
|
||||
| `title` | Yes | Displays as the post heading |
|
||||
| `description` | Yes | Shows in search results and sharing |
|
||||
| `date` | Yes | Publication date (YYYY-MM-DD) |
|
||||
| `slug` | Yes | Becomes the URL path |
|
||||
| `published` | Yes | Set `true` to show, `false` to hide |
|
||||
| `tags` | Yes | Topic labels for the post |
|
||||
| `readTime` | No | Estimated reading time |
|
||||
| `image` | No | OG image for social sharing and featured card thumb |
|
||||
| `featured` | No | Set `true` to show in featured section |
|
||||
| `featuredOrder` | No | Order in featured section (lower first) |
|
||||
| `excerpt` | No | Short description for card view |
|
||||
| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC |
|
||||
| Field | Required | What It Does |
|
||||
| ---------------- | -------- | ------------------------------------------------------------------------------------------ |
|
||||
| `title` | Yes | Displays as the post heading |
|
||||
| `description` | Yes | Shows in search results and sharing |
|
||||
| `date` | Yes | Publication date (YYYY-MM-DD) |
|
||||
| `slug` | Yes | Becomes the URL path |
|
||||
| `published` | Yes | Set `true` to show, `false` to hide |
|
||||
| `tags` | Yes | Topic labels for the post |
|
||||
| `readTime` | No | Estimated reading time |
|
||||
| `image` | No | OG image for social sharing and featured card thumb |
|
||||
| `showImageAtTop` | No | Set `true` to display the image at the top of the post above the header (default: `false`) |
|
||||
| `featured` | No | Set `true` to show in featured section |
|
||||
| `featuredOrder` | No | Order in featured section (lower first) |
|
||||
| `excerpt` | No | Short description for card view |
|
||||
| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC |
|
||||
|
||||
## Write Your Content
|
||||
|
||||
|
||||
@@ -15,20 +15,42 @@ This post demonstrates how to add images to your blog posts. You can use header
|
||||
|
||||
## Header/Open Graph Images
|
||||
|
||||
The `image` field in your frontmatter serves two purposes:
|
||||
The `image` field in your frontmatter serves multiple purposes:
|
||||
|
||||
1. **Open Graph image** for social media previews (Twitter, LinkedIn, Slack)
|
||||
2. **Thumbnail image** for featured section card view on the homepage
|
||||
3. **Header image** displayed at the top of the post/page (when `showImageAtTop: true`)
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: "Your Post Title"
|
||||
image: "https://images.unsplash.com/photo-1499750310107-5fef28a66643?w=1200&h=630&fit=crop"
|
||||
showImageAtTop: true
|
||||
---
|
||||
```
|
||||
|
||||
**Recommended dimensions:** 1200x630 pixels (1.91:1 ratio) for social sharing
|
||||
|
||||
## Displaying Image at Top of Post/Page
|
||||
|
||||
To display the `image` field at the top of your post or page (above the header), add `showImageAtTop: true` to your frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: "My Post"
|
||||
image: "https://images.unsplash.com/photo-1499750310107-5fef28a66643?w=1200&h=630&fit=crop"
|
||||
showImageAtTop: true
|
||||
---
|
||||
```
|
||||
|
||||
**Default behavior:** If `showImageAtTop` is not set or set to `false`, the image will only be used for Open Graph previews and featured card thumbnails, not displayed in the post content.
|
||||
|
||||
**Use cases:**
|
||||
|
||||
- Hero images for blog posts
|
||||
- Featured images that introduce the content
|
||||
- Visual headers for documentation pages
|
||||
|
||||
## Featured Section Thumbnails
|
||||
|
||||
When a post or page is marked as `featured: true`, the `image` field displays as a square thumbnail in the card view.
|
||||
|
||||
@@ -31,6 +31,7 @@ interface PostFrontmatter {
|
||||
tags: string[];
|
||||
readTime?: string;
|
||||
image?: string; // Header/OG image URL
|
||||
showImageAtTop?: boolean; // Display image at top of post (default: false)
|
||||
excerpt?: string; // Short excerpt for card view
|
||||
featured?: boolean; // Show in featured section
|
||||
featuredOrder?: number; // Order in featured section (lower = first)
|
||||
@@ -52,6 +53,7 @@ interface ParsedPost {
|
||||
tags: string[];
|
||||
readTime?: string;
|
||||
image?: string; // Header/OG image URL
|
||||
showImageAtTop?: boolean; // Display image at top of post (default: false)
|
||||
excerpt?: string; // Short excerpt for card view
|
||||
featured?: boolean; // Show in featured section
|
||||
featuredOrder?: number; // Order in featured section (lower = first)
|
||||
@@ -74,6 +76,7 @@ interface PageFrontmatter {
|
||||
showInNav?: boolean; // Show in navigation menu (default: true)
|
||||
excerpt?: string; // Short excerpt for card view
|
||||
image?: string; // Thumbnail/OG image URL for featured cards
|
||||
showImageAtTop?: boolean; // Display image at top of page (default: false)
|
||||
featured?: boolean; // Show in featured section
|
||||
featuredOrder?: number; // Order in featured section (lower = first)
|
||||
authorName?: string; // Author display name
|
||||
@@ -93,6 +96,7 @@ interface ParsedPage {
|
||||
showInNav?: boolean; // Show in navigation menu (default: true)
|
||||
excerpt?: string; // Short excerpt for card view
|
||||
image?: string; // Thumbnail/OG image URL for featured cards
|
||||
showImageAtTop?: boolean; // Display image at top of page (default: false)
|
||||
featured?: boolean; // Show in featured section
|
||||
featuredOrder?: number; // Order in featured section (lower = first)
|
||||
authorName?: string; // Author display name
|
||||
@@ -135,6 +139,7 @@ function parseMarkdownFile(filePath: string): ParsedPost | null {
|
||||
tags: frontmatter.tags || [],
|
||||
readTime: frontmatter.readTime || calculateReadTime(content),
|
||||
image: frontmatter.image, // Header/OG image URL
|
||||
showImageAtTop: frontmatter.showImageAtTop, // Display image at top of post
|
||||
excerpt: frontmatter.excerpt, // Short excerpt for card view
|
||||
featured: frontmatter.featured, // Show in featured section
|
||||
featuredOrder: frontmatter.featuredOrder, // Order in featured section
|
||||
@@ -192,6 +197,7 @@ function parsePageFile(filePath: string): ParsedPage | null {
|
||||
showInNav: frontmatter.showInNav, // Show in navigation menu (default: true)
|
||||
excerpt: frontmatter.excerpt, // Short excerpt for card view
|
||||
image: frontmatter.image, // Thumbnail/OG image URL for featured cards
|
||||
showImageAtTop: frontmatter.showImageAtTop, // Display image at top of page
|
||||
featured: frontmatter.featured, // Show in featured section
|
||||
featuredOrder: frontmatter.featuredOrder, // Order in featured section
|
||||
authorName: frontmatter.authorName, // Author display name
|
||||
|
||||
@@ -54,8 +54,14 @@ export default function Blog() {
|
||||
siteConfig.footer.enabled && siteConfig.footer.showOnBlogPage;
|
||||
|
||||
// Split featured posts: first one is hero, rest go to featured row
|
||||
const heroPost = blogFeaturedPosts && blogFeaturedPosts.length > 0 ? blogFeaturedPosts[0] : null;
|
||||
const featuredRowPosts = blogFeaturedPosts && blogFeaturedPosts.length > 1 ? blogFeaturedPosts.slice(1) : [];
|
||||
const heroPost =
|
||||
blogFeaturedPosts && blogFeaturedPosts.length > 0
|
||||
? blogFeaturedPosts[0]
|
||||
: null;
|
||||
const featuredRowPosts =
|
||||
blogFeaturedPosts && blogFeaturedPosts.length > 1
|
||||
? blogFeaturedPosts.slice(1)
|
||||
: [];
|
||||
|
||||
// Get slugs of all featured posts for filtering
|
||||
const featuredSlugs = new Set(blogFeaturedPosts?.map((p) => p.slug) || []);
|
||||
@@ -76,14 +82,12 @@ export default function Blog() {
|
||||
|
||||
return (
|
||||
<div className={blogPageClass}>
|
||||
{/* Navigation with back button */}
|
||||
<nav className="post-nav">
|
||||
<button onClick={() => navigate("/")} className="back-button">
|
||||
{/* Navigation with back button commented out <button onClick={() => navigate("/")} className="back-button">
|
||||
<ArrowLeft size={16} />
|
||||
<span>Back</span>
|
||||
</button>
|
||||
</button>*/}
|
||||
</nav>
|
||||
|
||||
{/* Blog page header */}
|
||||
<header className="blog-header">
|
||||
<div className="blog-header-top">
|
||||
@@ -144,7 +148,6 @@ export default function Blog() {
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Hero featured post section (only in cards view) */}
|
||||
{showPosts && hasFeaturedContent && viewMode === "cards" && heroPost && (
|
||||
<section className="blog-hero-section">
|
||||
@@ -162,7 +165,6 @@ export default function Blog() {
|
||||
/>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Featured row: remaining featured posts in 2 columns (only in cards view) */}
|
||||
{showPosts && featuredRowPosts.length > 0 && viewMode === "cards" && (
|
||||
<section className="blog-featured-row">
|
||||
@@ -174,7 +176,6 @@ export default function Blog() {
|
||||
/>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Regular posts section: non-featured posts in 3 columns */}
|
||||
{showPosts && (
|
||||
<section className="blog-posts">
|
||||
@@ -192,7 +193,6 @@ export default function Blog() {
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Message when posts are disabled on blog page */}
|
||||
{!showPosts && (
|
||||
<p className="blog-disabled-message">
|
||||
@@ -200,7 +200,6 @@ export default function Blog() {
|
||||
<code>postsDisplay.showOnBlogPage</code> in siteConfig to enable.
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Footer section */}
|
||||
{showFooter && <Footer />}
|
||||
</div>
|
||||
|
||||
@@ -241,6 +241,16 @@ export default function Post({
|
||||
|
||||
{/* Main content */}
|
||||
<article className={`post-article ${hasAnySidebar ? "post-article-with-sidebar" : ""} ${hasOnlyRightSidebar ? "post-article-centered" : ""}`}>
|
||||
{/* Display image at top if showImageAtTop is true */}
|
||||
{page.showImageAtTop && page.image && (
|
||||
<div className="post-header-image">
|
||||
<img
|
||||
src={page.image}
|
||||
alt={page.title}
|
||||
className="post-header-image-img"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<header className="post-header">
|
||||
<div className="post-title-row">
|
||||
<h1 className="post-title">{page.title}</h1>
|
||||
@@ -374,6 +384,16 @@ export default function Post({
|
||||
)}
|
||||
|
||||
<article className={`post-article ${hasAnySidebar ? "post-article-with-sidebar" : ""} ${hasOnlyRightSidebar ? "post-article-centered" : ""}`}>
|
||||
{/* Display image at top if showImageAtTop is true */}
|
||||
{post.showImageAtTop && post.image && (
|
||||
<div className="post-header-image">
|
||||
<img
|
||||
src={post.image}
|
||||
alt={post.title}
|
||||
className="post-header-image-img"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<header className="post-header">
|
||||
<div className="post-title-row">
|
||||
<h1 className="post-title">{post.title}</h1>
|
||||
|
||||
@@ -32,6 +32,7 @@ const POST_FIELDS = [
|
||||
{ name: "tags", required: true, example: '["tag1", "tag2"]' },
|
||||
{ name: "readTime", required: false, example: '"5 min read"' },
|
||||
{ name: "image", required: false, example: '"/images/my-image.png"' },
|
||||
{ name: "showImageAtTop", required: false, example: "true" },
|
||||
{
|
||||
name: "excerpt",
|
||||
required: false,
|
||||
@@ -65,6 +66,7 @@ const PAGE_FIELDS = [
|
||||
{ name: "showInNav", required: false, example: "true" },
|
||||
{ name: "excerpt", required: false, example: '"Short description"' },
|
||||
{ name: "image", required: false, example: '"/images/thumbnail.png"' },
|
||||
{ name: "showImageAtTop", required: false, example: "true" },
|
||||
{ name: "featured", required: false, example: "true" },
|
||||
{ name: "featuredOrder", required: false, example: "1" },
|
||||
{ name: "authorName", required: false, example: '"Jane Doe"' },
|
||||
@@ -180,7 +182,7 @@ export default function Write() {
|
||||
const [content, setContent] = useState("");
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [copiedField, setCopiedField] = useState<string | null>(null);
|
||||
const [font, setFont] = useState<"serif" | "sans" | "monospace">("serif");
|
||||
const [font, setFont] = useState<"serif" | "sans" | "monospace">("sans");
|
||||
const [isAIChatMode, setIsAIChatMode] = useState(false);
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
@@ -210,7 +212,7 @@ export default function Write() {
|
||||
setContentType(savedType);
|
||||
}
|
||||
|
||||
// Use saved font preference, or fall back to global font, or default to serif
|
||||
// Use saved font preference, or fall back to global font, or default to sans
|
||||
if (
|
||||
savedFont &&
|
||||
(savedFont === "serif" ||
|
||||
@@ -218,9 +220,16 @@ export default function Write() {
|
||||
savedFont === "monospace")
|
||||
) {
|
||||
setFont(savedFont);
|
||||
} else {
|
||||
} else if (
|
||||
globalFont === "serif" ||
|
||||
globalFont === "sans" ||
|
||||
globalFont === "monospace"
|
||||
) {
|
||||
// Sync with global font on first load
|
||||
setFont(globalFont);
|
||||
} else {
|
||||
// Default to sans if no saved preference and global font is not valid
|
||||
setFont("sans");
|
||||
}
|
||||
}, [globalFont]);
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
--font-size-xs: 12px;
|
||||
--font-size-sm: 13px;
|
||||
--font-size-md: 16px;
|
||||
--font-size-base: 18px;
|
||||
--font-size-base: 16px;
|
||||
--font-size-lg: 17px;
|
||||
--font-size-xl: 18px;
|
||||
--font-size-2xl: 20px;
|
||||
@@ -55,7 +55,7 @@
|
||||
--font-size-post-description: var(--font-size-xl);
|
||||
|
||||
/* Blog content font sizes (article body) */
|
||||
--font-size-blog-content: var(--font-size-lg);
|
||||
--font-size-blog-content: var(--font-size-md);
|
||||
--font-size-blog-h1: var(--font-size-3xl);
|
||||
--font-size-blog-h2: var(--font-size-2xl);
|
||||
--font-size-blog-h3: var(--font-size-xl);
|
||||
@@ -77,7 +77,7 @@
|
||||
/* Footer and tags font sizes */
|
||||
--font-size-post-tag: var(--font-size-sm);
|
||||
--font-size-share-button: var(--font-size-md);
|
||||
--font-size-footer-text: 18px;
|
||||
--font-size-footer-text: var(--font-size-base);
|
||||
|
||||
/* Blog page font sizes */
|
||||
--font-size-blog-page-title: 2rem;
|
||||
@@ -876,6 +876,21 @@ body {
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
/* Header image displayed at top of post/page */
|
||||
.post-header-image {
|
||||
width: 100%;
|
||||
margin-bottom: 32px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.post-header-image-img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* Title row container - flex layout for title and CopyPageDropdown */
|
||||
.post-title-row {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user