style: refine mdx prose math and images
This commit is contained in:
161
app/globals.css
161
app/globals.css
@@ -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; }
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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} />,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user