update: show iamge at top of blog post, updated monospace font and base font size, default font for write

This commit is contained in:
Wayne Sutton
2025-12-26 18:13:06 -08:00
parent b35dd17332
commit 3dcdb69041
18 changed files with 203 additions and 51 deletions

14
TASK.md
View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -2,7 +2,6 @@
title: "Contact"
slug: "contact"
published: true
layout: "sidebar"
order: 4
---

View File

@@ -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 |

View File

@@ -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,

View File

@@ -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,

View File

@@ -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

View File

@@ -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) |

View File

@@ -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 |

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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]);

View File

@@ -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;