From bbdb1ef82802b7e5830a6f9733b0d247cd39aae7 Mon Sep 17 00:00:00 2001
From: enscribe <jason@enscribe.dev>
Date: Fri, 21 Mar 2025 20:03:30 -0700
Subject: [PATCH] feat: dedicated typography file, remove tailwind-typography

---
 README.md                        |  31 ++++----
 astro.config.ts                  |   4 +-
 package.json                     |   1 -
 src/components/BlogCard.astro    |   2 +-
 src/components/Head.astro        |   2 +-
 src/components/ProjectCard.astro |   2 +-
 src/components/SocialIcons.astro |   2 +-
 src/pages/404.astro              |   2 +-
 src/pages/about.astro            |   2 +-
 src/pages/authors/[...id].astro  |   2 +-
 src/pages/authors/index.astro    |   2 +-
 src/pages/blog/[...id].astro     |   2 +-
 src/pages/blog/[...page].astro   |   2 +-
 src/pages/index.astro            |  28 +++-----
 src/pages/tags/[...id].astro     |   2 +-
 src/styles/global.css            |  40 +----------
 src/styles/katex.css             |   1 -
 src/styles/typography.css        | 120 +++++++++++++++++++++++++++++++
 18 files changed, 160 insertions(+), 87 deletions(-)
 delete mode 100644 src/styles/katex.css
 create mode 100644 src/styles/typography.css

diff --git a/README.md b/README.md
index 190fbae..7722f3d 100644
--- a/README.md
+++ b/README.md
@@ -124,20 +124,23 @@ Colors are defined in `src/styles/global.css` in [<abbr title="Hue, Saturation,
 
 ```css
 :root {
-  --background: hsl(0 0% 100%);
-  --foreground: hsl(0 0% 3.9%);
-  --primary: hsl(0 0% 9%);
-  --primary-foreground: hsl(0 0% 98%);
-  --secondary: hsl(0 0% 80.1%);
-  --secondary-foreground: hsl(0 0% 9%);
-  --muted: hsl(0 0% 80.1%);
-  --muted-foreground: hsl(0 0% 45.1%);
-  --accent: hsl(0 0% 80.1%);
-  --accent-foreground: hsl(0 0% 9%);
-  --destructive: hsl(0 84.2% 60.2%);
-  --destructive-foreground: hsl(0 0% 98%);
-  --border: hsl(0 0% 89.8%);
-  --ring: hsl(0 0% 3.9%);
+  --background: oklch(1 0 0);
+  --foreground: oklch(0.145 0 0);
+  --card: oklch(1 0 0);
+  --card-foreground: oklch(0.145 0 0);
+  --popover: oklch(1 0 0);
+  --popover-foreground: oklch(0.145 0 0);
+  --primary: oklch(0.205 0 0);
+  --primary-foreground: oklch(0.985 0 0);
+  --secondary: oklch(0.97 0 0);
+  --secondary-foreground: oklch(0.205 0 0);
+  --muted: oklch(0.97 0 0);
+  --muted-foreground: oklch(0.556 0 0);
+  --accent: oklch(0.97 0 0);
+  --accent-foreground: oklch(0.205 0 0);
+  --destructive: oklch(0.577 0.245 27.325);
+  --border: oklch(0.922 0 0);
+  --ring: oklch(0.708 0 0);
 }
 
 .dark {
diff --git a/astro.config.ts b/astro.config.ts
index 6d7ecd6..a205e20 100644
--- a/astro.config.ts
+++ b/astro.config.ts
@@ -9,7 +9,7 @@ import expressiveCode from 'astro-expressive-code'
 import { rehypeHeadingIds } from '@astrojs/markdown-remark'
 import rehypeExternalLinks from 'rehype-external-links'
 import rehypeKatex from 'rehype-katex'
-import sectionize from '@hbsnow/rehype-sectionize'
+// import sectionize from '@hbsnow/rehype-sectionize'
 import remarkEmoji from 'remark-emoji'
 import remarkMath from 'remark-math'
 import remarkToc from 'remark-toc'
@@ -89,7 +89,7 @@ export default defineConfig({
       ],
       rehypeHeadingIds,
       rehypeKatex,
-      sectionize,
+      // sectionize,
     ],
     remarkPlugins: [remarkToc, remarkMath, remarkEmoji],
   },
diff --git a/package.json b/package.json
index 8434288..021fc74 100644
--- a/package.json
+++ b/package.json
@@ -53,7 +53,6 @@
     "typescript": "^5.7.3"
   },
   "devDependencies": {
-    "@tailwindcss/typography": "^0.5.16",
     "prettier": "^3.5.1",
     "prettier-plugin-astro": "^0.14.1",
     "prettier-plugin-astro-organize-imports": "^0.4.11",
diff --git a/src/components/BlogCard.astro b/src/components/BlogCard.astro
index 8093675..eb1ac7d 100644
--- a/src/components/BlogCard.astro
+++ b/src/components/BlogCard.astro
@@ -23,7 +23,7 @@ const authors = await parseAuthors(entry.data.authors ?? [])
 ---
 
 <div
-  class="not-prose hover:bg-secondary/50 rounded-xl border p-4 transition-colors duration-300 ease-in-out"
+  class="hover:bg-secondary/50 rounded-xl border p-4 transition-colors duration-300 ease-in-out"
 >
   <Link
     href={`/${entry.collection}/${entry.id}`}
diff --git a/src/components/Head.astro b/src/components/Head.astro
index d0acc9a..9afab73 100644
--- a/src/components/Head.astro
+++ b/src/components/Head.astro
@@ -1,6 +1,6 @@
 ---
 import '../styles/global.css'
-import '../styles/katex.css'
+import '../styles/typography.css'
 
 import { SITE } from '@/consts'
 import { ClientRouter } from 'astro:transitions'
diff --git a/src/components/ProjectCard.astro b/src/components/ProjectCard.astro
index 78c88ae..b4e8e07 100644
--- a/src/components/ProjectCard.astro
+++ b/src/components/ProjectCard.astro
@@ -12,7 +12,7 @@ const { project } = Astro.props
 ---
 
 <div
-  class="not-prose hover:bg-secondary/50 rounded-xl border p-4 transition-colors duration-300 ease-in-out"
+  class="hover:bg-secondary/50 rounded-xl border p-4 transition-colors duration-300 ease-in-out"
 >
   <Link
     href={project.data.link}
diff --git a/src/components/SocialIcons.astro b/src/components/SocialIcons.astro
index 7ce7571..46d0622 100644
--- a/src/components/SocialIcons.astro
+++ b/src/components/SocialIcons.astro
@@ -29,7 +29,7 @@ const getSocialLink = ({ href, label }: SocialLink) => ({
 })
 ---
 
-<ul class={cn('not-prose flex flex-wrap gap-2', className)} role="list">
+<ul class={cn('flex flex-wrap gap-2', className)} role="list">
   {
     links.map((link) => {
       const { href, ariaLabel, iconName } = getSocialLink(link)
diff --git a/src/pages/404.astro b/src/pages/404.astro
index ea1fa17..6ba66f8 100644
--- a/src/pages/404.astro
+++ b/src/pages/404.astro
@@ -17,7 +17,7 @@ import { cn } from '@/lib/utils'
     >
       <div class="max-w-md">
         <h1 class="mb-4 text-3xl font-medium">404: Page not found</h1>
-        <p class="prose prose-neutral dark:prose-invert">
+        <p class="prose">
           Oops! The page you're looking for doesn't exist.
         </p>
       </div>
diff --git a/src/pages/about.astro b/src/pages/about.astro
index 1ad4bda..6600f27 100644
--- a/src/pages/about.astro
+++ b/src/pages/about.astro
@@ -16,7 +16,7 @@ const projects = await getCollection('projects')
 
     <section>
       <div class="min-w-full">
-        <div class="prose prose-neutral dark:prose-invert mb-8">
+        <div class="prose mb-8">
           <p class="mb-4">
             astro-erudite is an opinionated, no-frills static blogging template
             that prioritizes simplicity and performance, built with <Link
diff --git a/src/pages/authors/[...id].astro b/src/pages/authors/[...id].astro
index 237b738..9ba3470 100644
--- a/src/pages/authors/[...id].astro
+++ b/src/pages/authors/[...id].astro
@@ -47,7 +47,7 @@ const authorPosts = allPosts
       <h2 class="text-2xl font-medium">Posts by {author.data.name}</h2>
       {
         authorPosts.length > 0 ? (
-          <ul class="not-prose flex flex-col gap-4">
+          <ul class="flex flex-col gap-4">
             {authorPosts.map((post) => (
               <li>
                 <BlogCard entry={post} />
diff --git a/src/pages/authors/index.astro b/src/pages/authors/index.astro
index b110c5c..cbb5902 100644
--- a/src/pages/authors/index.astro
+++ b/src/pages/authors/index.astro
@@ -13,7 +13,7 @@ const authors = await getCollection('authors')
     <Breadcrumbs items={[{ label: 'Authors', icon: 'lucide:users' }]} />
     {
       authors.length > 0 ? (
-        <ul class="not-prose flex flex-col gap-4">
+        <ul class="flex flex-col gap-4">
           {authors.map((author) => (
             <li>
               <AuthorCard author={author} />
diff --git a/src/pages/blog/[...id].astro b/src/pages/blog/[...id].astro
index 96c7681..db32c77 100644
--- a/src/pages/blog/[...id].astro
+++ b/src/pages/blog/[...id].astro
@@ -153,7 +153,7 @@ const authors = await parseAuthors(post.data.authors ?? [])
     {headings.length > 0 && <TableOfContents headings={headings} />}
 
     <article
-      class="prose prose-neutral dark:prose-invert col-start-2 max-w-none"
+      class="prose col-start-2 max-w-none"
     >
       <Content />
     </article>
diff --git a/src/pages/blog/[...page].astro b/src/pages/blog/[...page].astro
index 4145bf6..dcf116b 100644
--- a/src/pages/blog/[...page].astro
+++ b/src/pages/blog/[...page].astro
@@ -48,7 +48,7 @@ const years = Object.keys(postsByYear).sort((a, b) => parseInt(b) - parseInt(a))
         years.map((year) => (
           <section class="flex flex-col gap-y-4">
             <div class="font-medium">{year}</div>
-            <ul class="not-prose flex flex-col gap-4">
+            <ul class="flex flex-col gap-4">
               {postsByYear[year].map((post) => (
                 <li>
                   <BlogCard entry={post} />
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 122226c..5053282 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -3,13 +3,6 @@ import BlogCard from '@/components/BlogCard.astro'
 import Container from '@/components/Container.astro'
 import Link from '@/components/Link.astro'
 import { buttonVariants } from '@/components/ui/button'
-import {
-  Card,
-  CardContent,
-  CardDescription,
-  CardHeader,
-  CardTitle,
-} from '@/components/ui/card'
 import { SITE } from '@/consts'
 import Layout from '@/layouts/Layout.astro'
 import { getCollection } from 'astro:content'
@@ -23,15 +16,12 @@ const blog = (await getCollection('blog'))
 <Layout title="Home" description={SITE.DESCRIPTION}>
   <Container class="flex flex-col gap-y-6">
     <section>
-      <Card>
-        <CardHeader>
-          <CardTitle className="text-3xl">er·u·dite</CardTitle>
-          <CardDescription
-            >/ˈer(y)əˌdīt/ &bull; <span class="font-medium">adjective</span
-            ></CardDescription
-          >
-        </CardHeader>
-        <CardContent>
+      <div class="rounded-lg border">
+        <div class="flex flex-col space-y-1.5 p-6">
+          <h3 class="text-3xl font-medium leading-none">er·u·dite</h3>
+          <p class="text-sm text-muted-foreground">/ˈer(y)əˌdīt/ &bull; <span class="font-medium">adjective</span></p>
+        </div>
+        <div class="p-6 pt-0">
           <p class="text-muted-foreground mb-2 text-sm">
             astro-erudite is an opinionated, no-frills static blogging template
             built with <Link
@@ -69,12 +59,12 @@ const blog = (await getCollection('blog'))
               underline>The State of Static Blogs in 2024</Link
             >.
           </p>
-        </CardContent>
-      </Card>
+        </div>
+      </div>
     </section>
     <section class="flex flex-col gap-y-4">
       <h2 class="text-2xl font-medium">Latest posts</h2>
-      <ul class="not-prose flex flex-col gap-y-4">
+      <ul class="flex flex-col gap-y-4">
         {
           blog.map((post) => (
             <li>
diff --git a/src/pages/tags/[...id].astro b/src/pages/tags/[...id].astro
index d075e24..01fa0e0 100644
--- a/src/pages/tags/[...id].astro
+++ b/src/pages/tags/[...id].astro
@@ -56,7 +56,7 @@ export async function getStaticPaths() {
         posts.map((post) => (
           <section class="flex flex-col gap-y-4">
             <div>
-              <ul class="not-prose flex flex-col gap-4">
+              <ul class="flex flex-col gap-4">
                 <li>
                   <BlogCard entry={post} />
                 </li>
diff --git a/src/styles/global.css b/src/styles/global.css
index 95bc524..3e37ce2 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -1,5 +1,5 @@
+@import 'https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css';
 @import 'tailwindcss';
-@plugin '@tailwindcss/typography';
 
 @custom-variant dark (&:is(.dark *));
 
@@ -53,13 +53,8 @@
 }
 
 :root {
-  --radius: 0.625rem;
   --background: oklch(1 0 0);
   --foreground: oklch(0.145 0 0);
-  --card: oklch(1 0 0);
-  --card-foreground: oklch(0.145 0 0);
-  --popover: oklch(1 0 0);
-  --popover-foreground: oklch(0.145 0 0);
   --primary: oklch(0.205 0 0);
   --primary-foreground: oklch(0.985 0 0);
   --secondary: oklch(0.97 0 0);
@@ -76,10 +71,6 @@
 .dark {
   --background: oklch(0.145 0 0);
   --foreground: oklch(0.985 0 0);
-  --card: oklch(0.205 0 0);
-  --card-foreground: oklch(0.985 0 0);
-  --popover: oklch(0.205 0 0);
-  --popover-foreground: oklch(0.985 0 0);
   --primary: oklch(0.922 0 0);
   --primary-foreground: oklch(0.205 0 0);
   --secondary: oklch(0.269 0 0);
@@ -120,32 +111,3 @@
     @apply transition-none!;
   }
 }
-
-@layer components {
-  article {
-    @apply prose-headings:scroll-mt-20 prose-headings:break-words [&>section]:first:prose-headings:mt-0!;
-    @apply prose-headings:font-medium! prose-code:font-medium!;
-    @apply prose-p:break-words;
-    @apply prose-a:break-words! prose-a:decoration-muted-foreground! prose-a:underline-offset-[3px] prose-a:transition-colors prose-a:hover:decoration-foreground!;
-    @apply prose-pre:px-0! prose-img:mx-auto;
-
-    .katex-display {
-      @apply overflow-x-auto overflow-y-hidden py-4;
-    }
-
-    /* Removes background from <mark> elements */
-    mark {
-      @apply bg-transparent;
-    }
-
-    /* Adjacent expressive-code blocks */
-    div.expressive-code:has(+ div.expressive-code) {
-      @apply mb-4;
-    }
-
-    /* Inline code */
-    :not(pre) > code {
-      @apply bg-muted/50 relative rounded-sm px-[0.3rem] py-[0.2rem] font-mono text-sm font-medium before:content-none! after:content-none!;
-    }
-  }
-}
diff --git a/src/styles/katex.css b/src/styles/katex.css
deleted file mode 100644
index 9c1e538..0000000
--- a/src/styles/katex.css
+++ /dev/null
@@ -1 +0,0 @@
-@import 'https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css';
diff --git a/src/styles/typography.css b/src/styles/typography.css
new file mode 100644
index 0000000..37ce1a1
--- /dev/null
+++ b/src/styles/typography.css
@@ -0,0 +1,120 @@
+@reference './global.css';
+
+@layer components {
+  .prose {
+    @apply text-foreground text-base leading-8 [&>*]:first:mt-0 [&>*]:last:mb-0;
+
+    p {
+      @apply text-foreground/80 my-5 leading-7 [&:not(:first-child)]:mt-5;
+    }
+
+    h1 {
+      @apply text-foreground mt-0 mb-6 scroll-m-20 text-4xl leading-tight font-medium;
+    }
+
+    h2 {
+      @apply text-foreground mt-8 mb-4 scroll-m-20 text-2xl leading-tight font-medium;
+    }
+
+    h3 {
+      @apply text-foreground mt-6 mb-4 scroll-m-20 text-xl leading-snug font-medium;
+    }
+
+    h4 {
+      @apply text-foreground mt-6 mb-3 scroll-m-20 text-lg leading-normal font-medium;
+    }
+
+    h5 {
+      @apply text-foreground mt-5 mb-3 scroll-m-20 leading-normal font-medium;
+    }
+
+    h6 {
+      @apply text-foreground mt-5 mb-3 scroll-m-20 leading-normal font-medium;
+    }
+
+    a {
+      @apply text-foreground decoration-muted-foreground hover:decoration-foreground font-medium break-words underline underline-offset-[3px] transition-colors;
+    }
+
+    strong {
+      @apply text-foreground font-medium;
+    }
+
+    ul {
+      @apply marker:text-foreground/30 my-5 ml-6 list-disc [&>li]:mt-4;
+    }
+
+    ol {
+      @apply marker:text-foreground/30 my-5 ml-6 list-decimal [&>li]:mt-4;
+      @apply [&[type='A']]:list-[upper-alpha] [&[type='I']]:list-[upper-roman] [&[type='a']]:list-[lower-alpha] [&[type='i']]:list-[lower-roman];
+    }
+
+    li {
+      @apply text-foreground/80 pl-2 leading-7 [&>p]:my-0;
+    }
+
+    ul ul,
+    ol ol,
+    ul ol,
+    ol ul {
+      @apply marker:text-foreground/30 my-2 ml-6;
+    }
+
+    code {
+      @apply bg-muted/50 text-foreground relative rounded-sm px-[0.3rem] py-[0.2rem] text-sm font-medium;
+    }
+
+    .expressive-code {
+      @apply my-6;
+    }
+
+    blockquote {
+      @apply my-6 border-l-2 pl-6 text-sm;
+    }
+
+    hr {
+      @apply border-border my-8 border-t;
+    }
+
+    table {
+      @apply my-8 w-full text-sm;
+    }
+
+    thead {
+      @apply border-muted-foreground/30 border-b;
+    }
+
+    th {
+      @apply border px-4 py-2 text-left font-medium [&[align=center]]:text-center [&[align=right]]:text-right;
+    }
+
+    tbody tr {
+      @apply border-muted-foreground/20 even:bg-muted/50 border-b;
+    }
+
+    td {
+      @apply border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right;
+    }
+
+    img,
+    video {
+      @apply my-8;
+    }
+
+    figure {
+      @apply my-8;
+    }
+
+    figcaption {
+      @apply text-muted-foreground mt-3 text-sm;
+    }
+
+    kbd {
+      @apply text-foreground bg-muted border-border rounded-md border px-2 py-1 text-xs font-medium shadow-sm;
+    }
+
+    .katex-display {
+      @apply my-6 overflow-x-auto overflow-y-hidden;
+    }
+  }
+}