Skip to content

Commit 136a1a3

Browse files
committed
Merge remote-tracking branch 'upstream/main' into smooth-theme-transition
2 parents 1aec579 + b3f511c commit 136a1a3

3 files changed

Lines changed: 350 additions & 48 deletions

File tree

src/css/custom.css

Lines changed: 120 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2054,73 +2054,145 @@ main>div:not(.m-0) {
20542054
margin-top: 1.5rem;
20552055
margin-bottom: 1.5rem;
20562056
}
2057+
}
20572058

2058-
.blog-carousel-section {
2059-
padding: 1.5rem 0 !important;
2060-
margin: 1.5rem 0 !important;
2061-
}
2059+
/* =====================================================
2060+
SECTION 15: BLOG POST PAGE — CENTER + TOC FIX
2061+
2062+
SAFE: Only targets .blog-wrapper (individual post page)
2063+
Remove any previous SECTION 15 block before adding this.
2064+
===================================================== */
20622065

2063-
/* Reduce section padding on mobile */
2064-
section,
2065-
[role="region"] {
2066-
padding: 1.5rem 0;
2067-
margin: 1.5rem 0;
2068-
}
2066+
/* Full width container — no artificial max-width cutting space */
2067+
.blog-wrapper .container {
2068+
max-width: 100% !important;
2069+
width: 100% !important;
2070+
padding-left: 1rem !important;
2071+
padding-right: 1rem !important;
20692072
}
20702073

2071-
/* Typography spacing for consistency */
2072-
h1:not(:first-child),
2073-
h2:not(:first-child),
2074-
h3:not(:first-child) {
2075-
margin-top: 2rem;
2076-
margin-bottom: 1rem;
2074+
/* Row: full width flex, no gaps, sidebars hug the edges */
2075+
.blog-wrapper .container>.row {
2076+
display: flex !important;
2077+
flex-wrap: nowrap !important;
2078+
align-items: flex-start !important;
2079+
width: 100% !important;
2080+
margin: 0 !important;
2081+
padding: 0 !important;
20772082
}
20782083

2079-
h1:first-child,
2080-
h2:first-child,
2081-
h3:first-child {
2082-
margin-top: 0;
2083-
margin-bottom: 1rem;
2084+
/* LEFT sidebar — Recent Posts, fixed width, left edge */
2085+
.blog-wrapper .container>.row>.col.col--3 {
2086+
flex: 0 0 240px !important;
2087+
width: 240px !important;
2088+
max-width: 240px !important;
2089+
padding-left: 0.5rem !important;
2090+
padding-right: 1.5rem !important;
20842091
}
20852092

2086-
/* --- CUSTOM MODERN SCROLLBAR --- */
2087-
2088-
::-webkit-scrollbar {
2089-
width: 6px !important;
2090-
height: 6px !important;
2093+
/* CENTER content — takes all remaining space between sidebars */
2094+
.blog-wrapper .container>.row>.col:not(.col--2):not(.col--3) {
2095+
flex: 1 1 auto !important;
2096+
min-width: 0 !important;
2097+
max-width: 100% !important;
2098+
/* Center the text content within this column */
2099+
padding-left: 3rem !important;
2100+
padding-right: 3rem !important;
2101+
}
2102+
2103+
/* RIGHT sidebar — TOC, wider so text is readable, right edge */
2104+
.blog-wrapper .container>.row>.col.col--2 {
2105+
flex: 0 0 280px !important;
2106+
/* ← expanded from ~220px to 280px */
2107+
width: 280px !important;
2108+
max-width: 280px !important;
2109+
padding-left: 1rem !important;
2110+
padding-right: 0.5rem !important;
20912111
}
20922112

2093-
::-webkit-scrollbar-track {
2094-
background: transparent !important;
2113+
/* TOC links: allow text to wrap properly instead of squishing */
2114+
.blog-wrapper .table-of-contents__link {
2115+
white-space: normal !important;
2116+
word-break: break-word !important;
2117+
line-height: 1.4 !important;
2118+
display: block !important;
2119+
padding: 2px 0 !important;
2120+
font-size: 0.85rem !important;
20952121
}
20962122

2097-
/* --- LIGHT THEME --- */
2098-
html[data-theme="light"] ::-webkit-scrollbar-thumb {
2099-
background-color: rgba(203, 213, 225, 1) !important;
2100-
border-radius: 10px !important;
2123+
/* TOC heading items — slightly bolder for hierarchy */
2124+
.blog-wrapper .table-of-contents li>.table-of-contents__link {
2125+
font-size: 0.875rem !important;
21012126
}
21022127

2103-
html[data-theme="light"] ::-webkit-scrollbar-thumb:hover {
2104-
background-color: rgba(148, 163, 184, 1) !important;
2128+
/* Comfortable reading styles */
2129+
.blog-post-page .markdown {
2130+
max-width: 100% !important;
2131+
line-height: 1.8 !important;
21052132
}
21062133

2107-
/* --- DARK THEME --- */
2108-
html[data-theme="dark"] ::-webkit-scrollbar-thumb {
2109-
background-color: rgba(51, 65, 85, 0.8) !important;
2110-
border-radius: 10px !important;
2134+
/* ── Large desktop (≥1400px) ── */
2135+
@media (min-width: 1400px) {
2136+
.blog-wrapper .container>.row>.col.col--3 {
2137+
flex: 0 0 260px !important;
2138+
width: 260px !important;
2139+
max-width: 260px !important;
2140+
}
2141+
2142+
.blog-wrapper .container>.row>.col.col--2 {
2143+
flex: 0 0 300px !important;
2144+
width: 300px !important;
2145+
max-width: 300px !important;
2146+
}
2147+
2148+
.blog-wrapper .container>.row>.col:not(.col--2):not(.col--3) {
2149+
padding-left: 4rem !important;
2150+
padding-right: 4rem !important;
2151+
}
21112152
}
21122153

2113-
html[data-theme="dark"] ::-webkit-scrollbar-thumb:hover {
2114-
background-color: #06b6d4 !important;
2154+
/* ── Tablet (997px–1199px) ── */
2155+
@media (max-width: 1199px) and (min-width: 997px) {
2156+
.blog-wrapper .container>.row>.col.col--3 {
2157+
flex: 0 0 200px !important;
2158+
width: 200px !important;
2159+
max-width: 200px !important;
2160+
}
2161+
2162+
.blog-wrapper .container>.row>.col.col--2 {
2163+
flex: 0 0 220px !important;
2164+
width: 220px !important;
2165+
max-width: 220px !important;
2166+
}
2167+
2168+
.blog-wrapper .container>.row>.col:not(.col--2):not(.col--3) {
2169+
padding-left: 2rem !important;
2170+
padding-right: 2rem !important;
2171+
}
21152172
}
21162173

2117-
/* --- FIREFOX CORRECTIONS --- */
2118-
html[data-theme="light"] {
2119-
scrollbar-width: thin !important;
2120-
scrollbar-color: #cbd5e1 transparent !important;
2174+
/* ── Mobile (≤996px): stack columns naturally ── */
2175+
@media (max-width: 996px) {
2176+
.blog-wrapper .container {
2177+
padding-left: 1rem !important;
2178+
padding-right: 1rem !important;
2179+
}
2180+
2181+
.blog-wrapper .container>.row {
2182+
flex-direction: column !important;
2183+
}
2184+
2185+
.blog-wrapper .container>.row>.col.col--3,
2186+
.blog-wrapper .container>.row>.col.col--2,
2187+
.blog-wrapper .container>.row>.col:not(.col--2):not(.col--3) {
2188+
flex: 1 1 auto !important;
2189+
width: 100% !important;
2190+
max-width: 100% !important;
2191+
padding-left: 0 !important;
2192+
padding-right: 0 !important;
2193+
}
21212194
}
21222195

2123-
html[data-theme="dark"] {
2124-
scrollbar-width: thin !important;
2125-
scrollbar-color: #334155 transparent !important;
2126-
}
2196+
/* =====================================================
2197+
END SECTION 15
2198+
===================================================== */
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import React from "react";
2+
import Link from "@docusaurus/Link";
3+
import { useBlogPost } from "@docusaurus/plugin-content-blog/client";
4+
import BlogPostItemFooterOriginal from "@theme-original/BlogPostItem/Footer";
5+
import type BlogPostItemFooterType from "@theme/BlogPostItem/Footer";
6+
import type { WrapperProps } from "@docusaurus/types";
7+
import { getAuthorProfile } from "@site/src/utils/authors";
8+
9+
import styles from "./styles.module.css";
10+
11+
type Props = WrapperProps<typeof BlogPostItemFooterType>;
12+
13+
function getGitHubUrl(author: {
14+
key?: string;
15+
url?: string;
16+
name?: string;
17+
}): string | undefined {
18+
if (author.key) {
19+
return getAuthorProfile(author.key).githubUrl;
20+
}
21+
22+
if (author.url && /github\.com\//i.test(author.url)) {
23+
return author.url.startsWith("http") ? author.url : `https://${author.url}`;
24+
}
25+
26+
return undefined;
27+
}
28+
29+
export default function BlogPostItemFooterWrapper(props: Props): JSX.Element {
30+
const { metadata, isBlogPostPage } = useBlogPost();
31+
const primaryAuthor = metadata.authors?.[0];
32+
33+
const profile = primaryAuthor?.key
34+
? getAuthorProfile(primaryAuthor.key)
35+
: undefined;
36+
37+
const authorName = primaryAuthor?.name || profile?.name;
38+
const authorAvatar = primaryAuthor?.imageURL || profile?.imageUrl;
39+
const githubUrl = primaryAuthor
40+
? getGitHubUrl({
41+
key: primaryAuthor.key,
42+
name: primaryAuthor.name,
43+
url: primaryAuthor.url,
44+
})
45+
: undefined;
46+
47+
const roundedReadTime = Math.max(1, Math.ceil(metadata.readingTime || 0));
48+
const readTimeText = `${roundedReadTime} min read`;
49+
const authorSummary =
50+
primaryAuthor?.description ||
51+
profile?.description ||
52+
primaryAuthor?.title ||
53+
profile?.title;
54+
const blogDate =
55+
metadata.formattedDate ||
56+
(metadata.date
57+
? new Date(metadata.date).toLocaleDateString("en-US", {
58+
year: "numeric",
59+
month: "short",
60+
day: "numeric",
61+
})
62+
: "");
63+
64+
const showAuthorCard = Boolean(isBlogPostPage && primaryAuthor && authorName);
65+
66+
return (
67+
<>
68+
<BlogPostItemFooterOriginal {...props} />
69+
{showAuthorCard && (
70+
<section className={styles.authorCard} aria-label="Post author details">
71+
<div className={styles.authorLeft}>
72+
{authorAvatar ? (
73+
<img
74+
className={styles.authorAvatar}
75+
src={authorAvatar}
76+
alt={`${authorName} profile picture`}
77+
loading="lazy"
78+
/>
79+
) : (
80+
<div className={styles.authorAvatarFallback} aria-hidden="true">
81+
{authorName?.charAt(0).toUpperCase()}
82+
</div>
83+
)}
84+
<div className={styles.authorIdentity}>
85+
<p className={styles.authorName}>{authorName}</p>
86+
{blogDate ? <p className={styles.authorDate}>{blogDate}</p> : null}
87+
{authorSummary ? (
88+
<p className={styles.authorSummary}>{authorSummary}</p>
89+
) : null}
90+
</div>
91+
</div>
92+
93+
<div className={styles.authorRight}>
94+
<p className={styles.readTime}>{readTimeText}</p>
95+
{githubUrl ? (
96+
<Link
97+
className={styles.githubButton}
98+
to={githubUrl}
99+
target="_blank"
100+
rel="noopener noreferrer"
101+
>
102+
GitHub Profile
103+
</Link>
104+
) : null}
105+
</div>
106+
</section>
107+
)}
108+
</>
109+
);
110+
}

0 commit comments

Comments
 (0)