diff --git a/sites/art/src/components/CoralComments.astro b/sites/art/src/components/CoralComments.astro index 308d5e9..2e70341 100644 --- a/sites/art/src/components/CoralComments.astro +++ b/sites/art/src/components/CoralComments.astro @@ -8,7 +8,7 @@ const { storyID, storyURL } = Astro.props; const coralRoot = "https://coral.neeldhara.cloud"; --- -
+
diff --git a/sites/art/src/components/sections/blog-post.tsx b/sites/art/src/components/sections/blog-post.tsx index cb7319b..21082ce 100644 --- a/sites/art/src/components/sections/blog-post.tsx +++ b/sites/art/src/components/sections/blog-post.tsx @@ -1,4 +1,14 @@ import type { ReactNode } from "react"; +import { format } from "date-fns"; + +const getReadingTime = (body: string | undefined) => { + const words = (body ?? "") + .replace(/```[\s\S]*?```/g, " ") + .replace(/<[^>]+>/g, " ") + .match(/\b[\w'-]+\b/g); + + return Math.max(1, Math.ceil((words?.length ?? 0) / 220)); +}; const BlogPost = ({ post, @@ -7,11 +17,22 @@ const BlogPost = ({ post: any; children: ReactNode; }) => { - const { title, image } = post.data; + const { title, image, pubDate } = post.data; + const readingTime = getReadingTime(post.body); return (
+
+
+ + {readingTime} min read +
+

{title}

+
+ {image ? (
{title} diff --git a/sites/art/src/styles/article.css b/sites/art/src/styles/article.css index 2fa5219..798a3b9 100644 --- a/sites/art/src/styles/article.css +++ b/sites/art/src/styles/article.css @@ -18,6 +18,10 @@ } .blog-post-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.65rem; color: hsl(var(--muted-foreground)); font-family: var(--font-mono); font-size: 0.75rem; @@ -25,6 +29,12 @@ text-transform: uppercase; } +.blog-post-meta span::before { + content: "/"; + margin-right: 0.65rem; + color: color-mix(in srgb, hsl(var(--muted-foreground)) 55%, transparent); +} + .blog-post-header .blog-post-title { margin-top: 0.65rem; color: hsl(var(--foreground)); @@ -314,7 +324,9 @@ } .article-content :where([data-footnotes] ol) { - padding-left: 1.25rem; + margin-left: 0; + padding-left: 0; + list-style-position: inside; } .article-content :where([data-footnotes] li + li) { @@ -322,6 +334,7 @@ } .article-content :where([data-footnotes] p) { + display: inline; margin: 0; } @@ -330,6 +343,17 @@ text-decoration: none; } +.blog-comments-shell { + width: 100%; + max-width: 64rem; + margin-inline: auto; + padding: 0 1.5rem 4.5rem; +} + +.blog-comments-shell #coral_thread { + width: 100%; +} + @media (min-width: 768px) { .blog-post-shell { padding-top: 4rem; diff --git a/sites/poetry/src/components/CoralComments.astro b/sites/poetry/src/components/CoralComments.astro index 308d5e9..2e70341 100644 --- a/sites/poetry/src/components/CoralComments.astro +++ b/sites/poetry/src/components/CoralComments.astro @@ -8,7 +8,7 @@ const { storyID, storyURL } = Astro.props; const coralRoot = "https://coral.neeldhara.cloud"; --- -
+
diff --git a/sites/poetry/src/components/sections/blog-post.tsx b/sites/poetry/src/components/sections/blog-post.tsx index 4cb7aa9..560e9f0 100644 --- a/sites/poetry/src/components/sections/blog-post.tsx +++ b/sites/poetry/src/components/sections/blog-post.tsx @@ -1,3 +1,14 @@ +import { format } from "date-fns"; + +const getReadingTime = (body: string | undefined) => { + const words = (body ?? "") + .replace(/```[\s\S]*?```/g, " ") + .replace(/<[^>]+>/g, " ") + .match(/\b[\w'-]+\b/g); + + return Math.max(1, Math.ceil((words?.length ?? 0) / 220)); +}; + const BlogPost = ({ post, children, @@ -5,12 +16,19 @@ const BlogPost = ({ post: any; children: React.ReactNode; }) => { - const { title } = post.data; + const { title, pubDate } = post.data; + const readingTime = getReadingTime(post.body); return (

{title}

+
+ + {readingTime} min read +
{children}
diff --git a/sites/poetry/src/styles/article.css b/sites/poetry/src/styles/article.css index 2fa5219..798a3b9 100644 --- a/sites/poetry/src/styles/article.css +++ b/sites/poetry/src/styles/article.css @@ -18,6 +18,10 @@ } .blog-post-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.65rem; color: hsl(var(--muted-foreground)); font-family: var(--font-mono); font-size: 0.75rem; @@ -25,6 +29,12 @@ text-transform: uppercase; } +.blog-post-meta span::before { + content: "/"; + margin-right: 0.65rem; + color: color-mix(in srgb, hsl(var(--muted-foreground)) 55%, transparent); +} + .blog-post-header .blog-post-title { margin-top: 0.65rem; color: hsl(var(--foreground)); @@ -314,7 +324,9 @@ } .article-content :where([data-footnotes] ol) { - padding-left: 1.25rem; + margin-left: 0; + padding-left: 0; + list-style-position: inside; } .article-content :where([data-footnotes] li + li) { @@ -322,6 +334,7 @@ } .article-content :where([data-footnotes] p) { + display: inline; margin: 0; } @@ -330,6 +343,17 @@ text-decoration: none; } +.blog-comments-shell { + width: 100%; + max-width: 64rem; + margin-inline: auto; + padding: 0 1.5rem 4.5rem; +} + +.blog-comments-shell #coral_thread { + width: 100%; +} + @media (min-width: 768px) { .blog-post-shell { padding-top: 4rem; diff --git a/sites/poetry/src/styles/global.css b/sites/poetry/src/styles/global.css index 0a757d9..f10239b 100644 --- a/sites/poetry/src/styles/global.css +++ b/sites/poetry/src/styles/global.css @@ -300,7 +300,16 @@ } .poetry-post-title { - @apply mb-8 text-2xl font-semibold md:mb-10 md:text-3xl; + @apply mb-3 text-2xl font-semibold md:text-3xl; + } + + .poetry-post-meta { + @apply mb-8 flex flex-wrap items-center justify-center gap-2 font-mono text-xs uppercase tracking-wide text-muted-foreground md:mb-10; + } + + .poetry-post-meta span::before { + content: "/"; + margin-right: 0.5rem; } .poetry-post-body { diff --git a/sites/puzzles/src/components/CoralComments.astro b/sites/puzzles/src/components/CoralComments.astro index 308d5e9..2e70341 100644 --- a/sites/puzzles/src/components/CoralComments.astro +++ b/sites/puzzles/src/components/CoralComments.astro @@ -8,7 +8,7 @@ const { storyID, storyURL } = Astro.props; const coralRoot = "https://coral.neeldhara.cloud"; --- -
+
diff --git a/sites/puzzles/src/components/sections/blog-post.tsx b/sites/puzzles/src/components/sections/blog-post.tsx index cb7319b..21082ce 100644 --- a/sites/puzzles/src/components/sections/blog-post.tsx +++ b/sites/puzzles/src/components/sections/blog-post.tsx @@ -1,4 +1,14 @@ import type { ReactNode } from "react"; +import { format } from "date-fns"; + +const getReadingTime = (body: string | undefined) => { + const words = (body ?? "") + .replace(/```[\s\S]*?```/g, " ") + .replace(/<[^>]+>/g, " ") + .match(/\b[\w'-]+\b/g); + + return Math.max(1, Math.ceil((words?.length ?? 0) / 220)); +}; const BlogPost = ({ post, @@ -7,11 +17,22 @@ const BlogPost = ({ post: any; children: ReactNode; }) => { - const { title, image } = post.data; + const { title, image, pubDate } = post.data; + const readingTime = getReadingTime(post.body); return (
+
+
+ + {readingTime} min read +
+

{title}

+
+ {image ? (
{title} diff --git a/sites/puzzles/src/styles/article.css b/sites/puzzles/src/styles/article.css index 2fa5219..798a3b9 100644 --- a/sites/puzzles/src/styles/article.css +++ b/sites/puzzles/src/styles/article.css @@ -18,6 +18,10 @@ } .blog-post-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.65rem; color: hsl(var(--muted-foreground)); font-family: var(--font-mono); font-size: 0.75rem; @@ -25,6 +29,12 @@ text-transform: uppercase; } +.blog-post-meta span::before { + content: "/"; + margin-right: 0.65rem; + color: color-mix(in srgb, hsl(var(--muted-foreground)) 55%, transparent); +} + .blog-post-header .blog-post-title { margin-top: 0.65rem; color: hsl(var(--foreground)); @@ -314,7 +324,9 @@ } .article-content :where([data-footnotes] ol) { - padding-left: 1.25rem; + margin-left: 0; + padding-left: 0; + list-style-position: inside; } .article-content :where([data-footnotes] li + li) { @@ -322,6 +334,7 @@ } .article-content :where([data-footnotes] p) { + display: inline; margin: 0; } @@ -330,6 +343,17 @@ text-decoration: none; } +.blog-comments-shell { + width: 100%; + max-width: 64rem; + margin-inline: auto; + padding: 0 1.5rem 4.5rem; +} + +.blog-comments-shell #coral_thread { + width: 100%; +} + @media (min-width: 768px) { .blog-post-shell { padding-top: 4rem; diff --git a/sites/reflections/src/components/CoralComments.astro b/sites/reflections/src/components/CoralComments.astro index 308d5e9..2e70341 100644 --- a/sites/reflections/src/components/CoralComments.astro +++ b/sites/reflections/src/components/CoralComments.astro @@ -8,7 +8,7 @@ const { storyID, storyURL } = Astro.props; const coralRoot = "https://coral.neeldhara.cloud"; --- -
+
diff --git a/sites/reflections/src/components/sections/blog-post.tsx b/sites/reflections/src/components/sections/blog-post.tsx index cb7319b..21082ce 100644 --- a/sites/reflections/src/components/sections/blog-post.tsx +++ b/sites/reflections/src/components/sections/blog-post.tsx @@ -1,4 +1,14 @@ import type { ReactNode } from "react"; +import { format } from "date-fns"; + +const getReadingTime = (body: string | undefined) => { + const words = (body ?? "") + .replace(/```[\s\S]*?```/g, " ") + .replace(/<[^>]+>/g, " ") + .match(/\b[\w'-]+\b/g); + + return Math.max(1, Math.ceil((words?.length ?? 0) / 220)); +}; const BlogPost = ({ post, @@ -7,11 +17,22 @@ const BlogPost = ({ post: any; children: ReactNode; }) => { - const { title, image } = post.data; + const { title, image, pubDate } = post.data; + const readingTime = getReadingTime(post.body); return (
+
+
+ + {readingTime} min read +
+

{title}

+
+ {image ? (
{title} diff --git a/sites/reflections/src/styles/article.css b/sites/reflections/src/styles/article.css index 2fa5219..798a3b9 100644 --- a/sites/reflections/src/styles/article.css +++ b/sites/reflections/src/styles/article.css @@ -18,6 +18,10 @@ } .blog-post-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.65rem; color: hsl(var(--muted-foreground)); font-family: var(--font-mono); font-size: 0.75rem; @@ -25,6 +29,12 @@ text-transform: uppercase; } +.blog-post-meta span::before { + content: "/"; + margin-right: 0.65rem; + color: color-mix(in srgb, hsl(var(--muted-foreground)) 55%, transparent); +} + .blog-post-header .blog-post-title { margin-top: 0.65rem; color: hsl(var(--foreground)); @@ -314,7 +324,9 @@ } .article-content :where([data-footnotes] ol) { - padding-left: 1.25rem; + margin-left: 0; + padding-left: 0; + list-style-position: inside; } .article-content :where([data-footnotes] li + li) { @@ -322,6 +334,7 @@ } .article-content :where([data-footnotes] p) { + display: inline; margin: 0; } @@ -330,6 +343,17 @@ text-decoration: none; } +.blog-comments-shell { + width: 100%; + max-width: 64rem; + margin-inline: auto; + padding: 0 1.5rem 4.5rem; +} + +.blog-comments-shell #coral_thread { + width: 100%; +} + @media (min-width: 768px) { .blog-post-shell { padding-top: 4rem; diff --git a/sites/research/src/components/CoralComments.astro b/sites/research/src/components/CoralComments.astro index 308d5e9..2e70341 100644 --- a/sites/research/src/components/CoralComments.astro +++ b/sites/research/src/components/CoralComments.astro @@ -8,7 +8,7 @@ const { storyID, storyURL } = Astro.props; const coralRoot = "https://coral.neeldhara.cloud"; --- -
+
diff --git a/sites/research/src/components/sections/blog-post.tsx b/sites/research/src/components/sections/blog-post.tsx index cb7319b..21082ce 100644 --- a/sites/research/src/components/sections/blog-post.tsx +++ b/sites/research/src/components/sections/blog-post.tsx @@ -1,4 +1,14 @@ import type { ReactNode } from "react"; +import { format } from "date-fns"; + +const getReadingTime = (body: string | undefined) => { + const words = (body ?? "") + .replace(/```[\s\S]*?```/g, " ") + .replace(/<[^>]+>/g, " ") + .match(/\b[\w'-]+\b/g); + + return Math.max(1, Math.ceil((words?.length ?? 0) / 220)); +}; const BlogPost = ({ post, @@ -7,11 +17,22 @@ const BlogPost = ({ post: any; children: ReactNode; }) => { - const { title, image } = post.data; + const { title, image, pubDate } = post.data; + const readingTime = getReadingTime(post.body); return (
+
+
+ + {readingTime} min read +
+

{title}

+
+ {image ? (
{title} diff --git a/sites/research/src/styles/article.css b/sites/research/src/styles/article.css index 2fa5219..798a3b9 100644 --- a/sites/research/src/styles/article.css +++ b/sites/research/src/styles/article.css @@ -18,6 +18,10 @@ } .blog-post-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.65rem; color: hsl(var(--muted-foreground)); font-family: var(--font-mono); font-size: 0.75rem; @@ -25,6 +29,12 @@ text-transform: uppercase; } +.blog-post-meta span::before { + content: "/"; + margin-right: 0.65rem; + color: color-mix(in srgb, hsl(var(--muted-foreground)) 55%, transparent); +} + .blog-post-header .blog-post-title { margin-top: 0.65rem; color: hsl(var(--foreground)); @@ -314,7 +324,9 @@ } .article-content :where([data-footnotes] ol) { - padding-left: 1.25rem; + margin-left: 0; + padding-left: 0; + list-style-position: inside; } .article-content :where([data-footnotes] li + li) { @@ -322,6 +334,7 @@ } .article-content :where([data-footnotes] p) { + display: inline; margin: 0; } @@ -330,6 +343,17 @@ text-decoration: none; } +.blog-comments-shell { + width: 100%; + max-width: 64rem; + margin-inline: auto; + padding: 0 1.5rem 4.5rem; +} + +.blog-comments-shell #coral_thread { + width: 100%; +} + @media (min-width: 768px) { .blog-post-shell { padding-top: 4rem; diff --git a/sites/reviews/src/components/CoralComments.astro b/sites/reviews/src/components/CoralComments.astro index 308d5e9..2e70341 100644 --- a/sites/reviews/src/components/CoralComments.astro +++ b/sites/reviews/src/components/CoralComments.astro @@ -8,7 +8,7 @@ const { storyID, storyURL } = Astro.props; const coralRoot = "https://coral.neeldhara.cloud"; --- -
+
diff --git a/sites/reviews/src/components/sections/blog-post.tsx b/sites/reviews/src/components/sections/blog-post.tsx index cb7319b..21082ce 100644 --- a/sites/reviews/src/components/sections/blog-post.tsx +++ b/sites/reviews/src/components/sections/blog-post.tsx @@ -1,4 +1,14 @@ import type { ReactNode } from "react"; +import { format } from "date-fns"; + +const getReadingTime = (body: string | undefined) => { + const words = (body ?? "") + .replace(/```[\s\S]*?```/g, " ") + .replace(/<[^>]+>/g, " ") + .match(/\b[\w'-]+\b/g); + + return Math.max(1, Math.ceil((words?.length ?? 0) / 220)); +}; const BlogPost = ({ post, @@ -7,11 +17,22 @@ const BlogPost = ({ post: any; children: ReactNode; }) => { - const { title, image } = post.data; + const { title, image, pubDate } = post.data; + const readingTime = getReadingTime(post.body); return (
+
+
+ + {readingTime} min read +
+

{title}

+
+ {image ? (
{title} diff --git a/sites/reviews/src/styles/article.css b/sites/reviews/src/styles/article.css index 2fa5219..798a3b9 100644 --- a/sites/reviews/src/styles/article.css +++ b/sites/reviews/src/styles/article.css @@ -18,6 +18,10 @@ } .blog-post-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.65rem; color: hsl(var(--muted-foreground)); font-family: var(--font-mono); font-size: 0.75rem; @@ -25,6 +29,12 @@ text-transform: uppercase; } +.blog-post-meta span::before { + content: "/"; + margin-right: 0.65rem; + color: color-mix(in srgb, hsl(var(--muted-foreground)) 55%, transparent); +} + .blog-post-header .blog-post-title { margin-top: 0.65rem; color: hsl(var(--foreground)); @@ -314,7 +324,9 @@ } .article-content :where([data-footnotes] ol) { - padding-left: 1.25rem; + margin-left: 0; + padding-left: 0; + list-style-position: inside; } .article-content :where([data-footnotes] li + li) { @@ -322,6 +334,7 @@ } .article-content :where([data-footnotes] p) { + display: inline; margin: 0; } @@ -330,6 +343,17 @@ text-decoration: none; } +.blog-comments-shell { + width: 100%; + max-width: 64rem; + margin-inline: auto; + padding: 0 1.5rem 4.5rem; +} + +.blog-comments-shell #coral_thread { + width: 100%; +} + @media (min-width: 768px) { .blog-post-shell { padding-top: 4rem; diff --git a/sites/vibes/src/components/CoralComments.astro b/sites/vibes/src/components/CoralComments.astro index 308d5e9..2e70341 100644 --- a/sites/vibes/src/components/CoralComments.astro +++ b/sites/vibes/src/components/CoralComments.astro @@ -8,7 +8,7 @@ const { storyID, storyURL } = Astro.props; const coralRoot = "https://coral.neeldhara.cloud"; --- -
+
diff --git a/sites/vibes/src/components/sections/blog-post.tsx b/sites/vibes/src/components/sections/blog-post.tsx index cb7319b..21082ce 100644 --- a/sites/vibes/src/components/sections/blog-post.tsx +++ b/sites/vibes/src/components/sections/blog-post.tsx @@ -1,4 +1,14 @@ import type { ReactNode } from "react"; +import { format } from "date-fns"; + +const getReadingTime = (body: string | undefined) => { + const words = (body ?? "") + .replace(/```[\s\S]*?```/g, " ") + .replace(/<[^>]+>/g, " ") + .match(/\b[\w'-]+\b/g); + + return Math.max(1, Math.ceil((words?.length ?? 0) / 220)); +}; const BlogPost = ({ post, @@ -7,11 +17,22 @@ const BlogPost = ({ post: any; children: ReactNode; }) => { - const { title, image } = post.data; + const { title, image, pubDate } = post.data; + const readingTime = getReadingTime(post.body); return (
+
+
+ + {readingTime} min read +
+

{title}

+
+ {image ? (
{title} diff --git a/sites/vibes/src/styles/article.css b/sites/vibes/src/styles/article.css index 2fa5219..798a3b9 100644 --- a/sites/vibes/src/styles/article.css +++ b/sites/vibes/src/styles/article.css @@ -18,6 +18,10 @@ } .blog-post-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.65rem; color: hsl(var(--muted-foreground)); font-family: var(--font-mono); font-size: 0.75rem; @@ -25,6 +29,12 @@ text-transform: uppercase; } +.blog-post-meta span::before { + content: "/"; + margin-right: 0.65rem; + color: color-mix(in srgb, hsl(var(--muted-foreground)) 55%, transparent); +} + .blog-post-header .blog-post-title { margin-top: 0.65rem; color: hsl(var(--foreground)); @@ -314,7 +324,9 @@ } .article-content :where([data-footnotes] ol) { - padding-left: 1.25rem; + margin-left: 0; + padding-left: 0; + list-style-position: inside; } .article-content :where([data-footnotes] li + li) { @@ -322,6 +334,7 @@ } .article-content :where([data-footnotes] p) { + display: inline; margin: 0; } @@ -330,6 +343,17 @@ text-decoration: none; } +.blog-comments-shell { + width: 100%; + max-width: 64rem; + margin-inline: auto; + padding: 0 1.5rem 4.5rem; +} + +.blog-comments-shell #coral_thread { + width: 100%; +} + @media (min-width: 768px) { .blog-post-shell { padding-top: 4rem; diff --git a/src/components/sections/blog-post.tsx b/src/components/sections/blog-post.tsx index 598cdb0..21082ce 100644 --- a/src/components/sections/blog-post.tsx +++ b/src/components/sections/blog-post.tsx @@ -1,87 +1,47 @@ -import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; +import type { ReactNode } from "react"; import { format } from "date-fns"; -import { Calendar, Clock, ArrowLeft, User } from "lucide-react"; + +const getReadingTime = (body: string | undefined) => { + const words = (body ?? "") + .replace(/```[\s\S]*?```/g, " ") + .replace(/<[^>]+>/g, " ") + .match(/\b[\w'-]+\b/g); + + return Math.max(1, Math.ceil((words?.length ?? 0) / 220)); +}; const BlogPost = ({ post, children, }: { post: any; - children: React.ReactNode; + children: ReactNode; }) => { - const { title, authorName, image, pubDate, description, authorImage } = - post.data; + const { title, image, pubDate } = post.data; + const readingTime = getReadingTime(post.body); + return ( - <> - {/* Hero section with gradient background and post info */} -
-
-
-

- {title} -

-

- {description} -

+
+
+
+
+ + {readingTime} min read
+

{title}

+
-
- {/* Author info */} - {authorImage ? ( -
-
- ) : ( -
-
- -
-
- - {authorName || "Anonymous"} - - Author -
-
- )} + {image ? ( +
+ {title} +
+ ) : null} - {/* Date info */} -
-
- - {format(pubDate, "MMMM d, yyyy")} -
- -
- - 5 min read -
-
-
-
-
- - {/* Featured image */} - {image && ( -
-
- {title} -
-
- )} - - {/* Article content */} -
-
- {children} -
-
- +
{children}
+
+
); }; diff --git a/src/styles/article.css b/src/styles/article.css index 2fa5219..798a3b9 100644 --- a/src/styles/article.css +++ b/src/styles/article.css @@ -18,6 +18,10 @@ } .blog-post-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.65rem; color: hsl(var(--muted-foreground)); font-family: var(--font-mono); font-size: 0.75rem; @@ -25,6 +29,12 @@ text-transform: uppercase; } +.blog-post-meta span::before { + content: "/"; + margin-right: 0.65rem; + color: color-mix(in srgb, hsl(var(--muted-foreground)) 55%, transparent); +} + .blog-post-header .blog-post-title { margin-top: 0.65rem; color: hsl(var(--foreground)); @@ -314,7 +324,9 @@ } .article-content :where([data-footnotes] ol) { - padding-left: 1.25rem; + margin-left: 0; + padding-left: 0; + list-style-position: inside; } .article-content :where([data-footnotes] li + li) { @@ -322,6 +334,7 @@ } .article-content :where([data-footnotes] p) { + display: inline; margin: 0; } @@ -330,6 +343,17 @@ text-decoration: none; } +.blog-comments-shell { + width: 100%; + max-width: 64rem; + margin-inline: auto; + padding: 0 1.5rem 4.5rem; +} + +.blog-comments-shell #coral_thread { + width: 100%; +} + @media (min-width: 768px) { .blog-post-shell { padding-top: 4rem;