style: refine mdx prose math and images

This commit is contained in:
opencode
2026-06-03 11:30:13 -05:00
parent d223c5f512
commit f2b88f1130
3 changed files with 175 additions and 23 deletions

View File

@@ -175,6 +175,148 @@
padding: 2rem;
}
/* === Local prose styles (Tailwind Typography plugin is not installed) === */
.prose {
color: var(--color-ink);
font-size: 1rem;
line-height: 1.75;
overflow-wrap: break-word;
}
.prose :where(h1, h2, h3, h4, h5, h6) {
color: var(--color-ink);
scroll-margin-top: 96px;
}
.prose :where(h1) { margin: 2.75rem 0 1rem; }
.prose :where(h2) { margin: 3rem 0 1rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--color-border); }
.prose :where(h3) { margin: 2.25rem 0 0.75rem; }
.prose :where(h4, h5, h6) { margin: 1.75rem 0 0.5rem; }
.prose :where(h1, h2, h3, h4, h5, h6):first-child { margin-top: 0; }
.prose :where(p) {
margin: 1.25rem 0;
color: var(--color-ink);
}
.prose :where(a) {
color: var(--color-accent);
text-decoration-thickness: 1px;
}
.prose :where(ul, ol) {
margin: 1.5rem 0;
padding-left: 1.5rem;
}
.prose :where(li) { margin: 0.45rem 0; padding-left: 0.2rem; }
.prose :where(li)::marker { color: var(--color-accent); }
.prose :where(li > p) { margin: 0.5rem 0; }
.prose :where(blockquote) {
margin: 2rem 0;
border-left: 4px solid color-mix(in oklch, var(--color-accent) 45%, var(--color-border));
border-radius: 0 1rem 1rem 0;
background: color-mix(in oklch, var(--color-surface) 70%, transparent);
padding: 0.75rem 1rem 0.75rem 1.25rem;
color: var(--color-ink-soft);
}
.prose :where(blockquote p) { margin: 0.5rem 0; }
.prose :where(.callout, [data-callout]) {
margin: 2rem 0;
border: 1px solid color-mix(in oklch, var(--color-accent) 28%, var(--color-border));
border-radius: 1rem;
background: color-mix(in oklch, var(--color-accent) 8%, var(--color-surface));
padding: 1rem;
}
.prose :where(details) {
margin: 2rem 0;
border: 1px solid var(--color-border);
border-radius: 1rem;
background: color-mix(in oklch, var(--color-surface) 82%, var(--color-canvas));
padding: 1rem 1.125rem;
}
.prose :where(summary) {
cursor: pointer;
color: var(--color-ink);
font-weight: 700;
}
.prose :where(details[open] summary) { margin-bottom: 0.75rem; }
.prose :where(details > :last-child) { margin-bottom: 0; }
.prose :where(figure) { margin: 2.5rem 0; }
.prose :where(img) {
display: block;
max-width: min(100%, 56rem);
height: auto;
margin: 2rem auto;
border: 1px solid var(--color-border);
border-radius: 1rem;
background: var(--color-surface);
box-shadow: var(--shadow-card);
}
.prose :where(p:has(> img:only-child)) {
margin: 2.5rem 0 1rem;
}
.prose :where(p:has(> img:only-child) + em),
.prose :where(p:has(> img:only-child) + strong) {
display: block;
margin-top: -0.5rem;
text-align: center;
color: var(--color-ink-soft);
font-size: 0.875rem;
line-height: 1.5;
}
.prose :where(figcaption) {
margin-top: 0.75rem;
color: var(--color-ink-soft);
font-size: 0.875rem;
line-height: 1.5;
text-align: center;
}
.prose :where(table) {
width: 100%;
min-width: 40rem;
border-collapse: collapse;
color: var(--color-ink);
}
.prose :where(th, td) {
border: 1px solid var(--color-border);
padding: 0.75rem 0.875rem;
text-align: left;
vertical-align: top;
}
.prose :where(th) {
background: var(--color-surface);
color: var(--color-ink);
font-weight: 700;
}
.prose :where(tbody tr:nth-child(even)) {
background: color-mix(in oklch, var(--color-surface) 55%, transparent);
}
.prose :where(pre) {
max-width: 100%;
overflow-x: auto;
}
.prose :where(:not(pre) > code) {
color: var(--color-ink);
}
/* === Code Blocks: Borderless, VS Code Style, Line Numbers === */
[data-rehype-pretty-code-figure] {
@apply relative overflow-hidden rounded-[var(--radius-code)];
@@ -325,23 +467,24 @@ html.dark code[data-theme*=" "] span {
font-size: 1.1em !important;
}
.katex-display {
overflow: visible;
overflow-x: auto;
overflow-y: hidden;
max-width: 100%;
margin: 1.5em 0;
margin: 1.75em 0;
padding: 0.25rem 0 0.6rem;
text-align: center;
-webkit-overflow-scrolling: touch;
}
.katex-display > .katex {
display: block;
display: inline-block;
min-width: max-content;
text-align: center;
white-space: normal;
white-space: nowrap;
}
@media (max-width: 640px) {
.katex-display > .katex {
transform: scale(0.85);
transform-origin: center top;
}
.katex-display {
margin: 0.8em 0;
margin: 1.25em 0;
text-align: left;
}
}
.prose p + .katex-display { margin-top: 0.5em; }

View File

@@ -37,7 +37,7 @@ export default async function PostPage({ params }: { params: Promise<{ slug: str
<ReadingProgress />
<div className="max-w-4xl mx-auto px-6 py-16">
<div className="grid grid-cols-1 lg:grid-cols-[1fr_200px] gap-8">
<article>
<article className="min-w-0">
<header className="mb-12">
<time className="font-mono text-sm text-ink-soft" dateTime={post.date}>{new Date(post.date).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })}</time>
<h1 className="heading-xl text-ink mt-3 mb-2">
@@ -50,9 +50,11 @@ export default async function PostPage({ params }: { params: Promise<{ slug: str
{post.coverImage && (
<img
src={post.coverImage}
alt={`Cover image for ${post.title}`}
className="w-full h-64 object-cover rounded-xl my-6"
loading="lazy"
alt={`Featured image for article: ${post.title}`}
className="my-8 aspect-[16/9] w-full rounded-2xl border border-border bg-surface object-cover shadow-card"
loading="eager"
decoding="async"
fetchPriority="high"
/>
)}
{post.tags.length > 0 && (
@@ -65,7 +67,7 @@ export default async function PostPage({ params }: { params: Promise<{ slug: str
</div>
)}
</header>
<div className="prose prose-lg max-w-none">
<div className="prose prose-lg max-w-none min-w-0">
<PostContent />
</div>
</article>

View File

@@ -30,12 +30,12 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
<h6 className="text-sm font-medium uppercase tracking-wider text-ink-soft mt-6 mb-2" {...props}>{children}</h6>
),
p: ({ children }) => (
<p className="leading-7 [&:not(:first-child)]:mt-6 text-ink">
<p className="text-ink leading-7 [&:not(:first-child)]:mt-6">
{children}
</p>
),
blockquote: ({ children }) => (
<blockquote className="border-l-4 border-border pl-4 italic text-ink-soft my-6">
<blockquote className="my-6 border-l-4 border-accent/40 bg-surface/60 py-1 pl-5 pr-4 italic text-ink-soft">
{children}
</blockquote>
),
@@ -75,11 +75,18 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
</pre>
),
img: ({ src, alt, ...rest }) => (
<img src={src} alt={alt} className="my-8 rounded-xl w-full" {...rest} />
<img
src={src}
alt={alt ?? ''}
className="mx-auto my-8 h-auto max-h-[80vh] w-auto max-w-full rounded-2xl border border-border bg-surface object-contain shadow-card"
loading="lazy"
decoding="async"
{...rest}
/>
),
table: ({ children }) => (
<div className="my-6 overflow-x-auto">
<table className="w-full border-collapse text-sm">
<div className="my-8 overflow-x-auto rounded-xl border border-border bg-canvas">
<table className="w-full min-w-[40rem] border-collapse text-sm">
{children}
</table>
</div>
@@ -91,8 +98,8 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
li: (props) => <li className="leading-7" {...props} />,
// Figures and captions
figure: (props) => <figure className="my-8" {...props} />,
figcaption: (props) => <figcaption className="text-center text-sm text-ink-soft mt-2" {...props} />,
figure: (props) => <figure className="my-10" {...props} />,
figcaption: (props) => <figcaption className="mx-auto mt-3 max-w-2xl text-center text-sm leading-6 text-ink-soft" {...props} />,
// Subscripts and superscripts
sup: (props) => <sup className="text-xs" {...props} />,
@@ -104,7 +111,7 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
),
// Collapsible sections
details: (props) => <details className="my-4 rounded-lg border border-border p-4" {...props} />,
summary: (props) => <summary className="cursor-pointer font-semibold" {...props} />,
details: (props) => <details className="my-6 rounded-xl border border-border bg-surface/70 p-4 shadow-card" {...props} />,
summary: (props) => <summary className="cursor-pointer font-semibold text-ink" {...props} />,
}
}