diff --git a/TASK.md b/TASK.md index c80b194..091873b 100644 --- a/TASK.md +++ b/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) diff --git a/changelog.md b/changelog.md index c48bf8a..54ddcf0 100644 --- a/changelog.md +++ b/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 diff --git a/content/blog/how-to-publish.md b/content/blog/how-to-publish.md index 6bedd7a..fa88771 100644 --- a/content/blog/how-to-publish.md +++ b/content/blog/how-to-publish.md @@ -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 diff --git a/content/blog/using-images-in-posts.md b/content/blog/using-images-in-posts.md index 5ee4713..c03bd08 100644 --- a/content/blog/using-images-in-posts.md +++ b/content/blog/using-images-in-posts.md @@ -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. diff --git a/content/pages/contact.md b/content/pages/contact.md index 185a17f..a0b9fda 100644 --- a/content/pages/contact.md +++ b/content/pages/contact.md @@ -2,7 +2,6 @@ title: "Contact" slug: "contact" published: true -layout: "sidebar" order: 4 --- diff --git a/content/pages/docs.md b/content/pages/docs.md index 91700bd..602f4f1 100644 --- a/content/pages/docs.md +++ b/content/pages/docs.md @@ -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 | diff --git a/convex/pages.ts b/convex/pages.ts index 53c2d2f..821a31b 100644 --- a/convex/pages.ts +++ b/convex/pages.ts @@ -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, diff --git a/convex/posts.ts b/convex/posts.ts index 2488bd4..ef00afd 100644 --- a/convex/posts.ts +++ b/convex/posts.ts @@ -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, diff --git a/convex/schema.ts b/convex/schema.ts index 96bd1de..9c8942e 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -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 diff --git a/files.md b/files.md index 8040226..9d80c08 100644 --- a/files.md +++ b/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) | diff --git a/public/raw/docs.md b/public/raw/docs.md index fb78d54..e2ebabb 100644 --- a/public/raw/docs.md +++ b/public/raw/docs.md @@ -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 | diff --git a/public/raw/how-to-publish.md b/public/raw/how-to-publish.md index 0de9b0e..dd546c5 100644 --- a/public/raw/how-to-publish.md +++ b/public/raw/how-to-publish.md @@ -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 diff --git a/public/raw/using-images-in-posts.md b/public/raw/using-images-in-posts.md index 6401447..4860b40 100644 --- a/public/raw/using-images-in-posts.md +++ b/public/raw/using-images-in-posts.md @@ -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. diff --git a/scripts/sync-posts.ts b/scripts/sync-posts.ts index 9721d4f..58107f4 100644 --- a/scripts/sync-posts.ts +++ b/scripts/sync-posts.ts @@ -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 diff --git a/src/pages/Blog.tsx b/src/pages/Blog.tsx index e3d76fb..88d4386 100644 --- a/src/pages/Blog.tsx +++ b/src/pages/Blog.tsx @@ -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 (
- {/* Navigation with back button */} - {/* Blog page header */}
@@ -144,7 +148,6 @@ export default function Blog() { )}
- {/* Hero featured post section (only in cards view) */} {showPosts && hasFeaturedContent && viewMode === "cards" && heroPost && (
@@ -162,7 +165,6 @@ export default function Blog() { />
)} - {/* Featured row: remaining featured posts in 2 columns (only in cards view) */} {showPosts && featuredRowPosts.length > 0 && viewMode === "cards" && (
@@ -174,7 +176,6 @@ export default function Blog() { />
)} - {/* Regular posts section: non-featured posts in 3 columns */} {showPosts && (
@@ -192,7 +193,6 @@ export default function Blog() { )}
)} - {/* Message when posts are disabled on blog page */} {!showPosts && (

@@ -200,7 +200,6 @@ export default function Blog() { postsDisplay.showOnBlogPage in siteConfig to enable.

)} - {/* Footer section */} {showFooter &&
diff --git a/src/pages/Post.tsx b/src/pages/Post.tsx index 8137087..07e098a 100644 --- a/src/pages/Post.tsx +++ b/src/pages/Post.tsx @@ -241,6 +241,16 @@ export default function Post({ {/* Main content */}
+ {/* Display image at top if showImageAtTop is true */} + {page.showImageAtTop && page.image && ( +
+ {page.title} +
+ )}

{page.title}

@@ -374,6 +384,16 @@ export default function Post({ )}
+ {/* Display image at top if showImageAtTop is true */} + {post.showImageAtTop && post.image && ( +
+ {post.title} +
+ )}

{post.title}

diff --git a/src/pages/Write.tsx b/src/pages/Write.tsx index e17bdfb..338ce82 100644 --- a/src/pages/Write.tsx +++ b/src/pages/Write.tsx @@ -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(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(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]); diff --git a/src/styles/global.css b/src/styles/global.css index b8a960d..abd0baf 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -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;