Skip to content

Commit 2da6c44

Browse files
authored
Merge pull request #252 from dotCMS/making-blogs-responsive
some surgery to make blog posts responsive
2 parents 168044d + 229801d commit 2da6c44

10 files changed

Lines changed: 73 additions & 23 deletions

File tree

app/blog/[slug]/page.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,8 @@ export default async function BlogPage({ params }) {
168168
<Header />
169169
<JsonLd post={post} hostname={hostname} />
170170

171-
<div className="flex flex-1">
172-
<main className="flex-1">
171+
<div className="flex min-h-0 min-w-0 w-full flex-1 flex-col">
172+
<main className="min-w-0 w-full flex-1">
173173
<BlogDetailComponent post={post} />
174174
</main>
175175
</div>

app/globals.css

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,36 @@
133133
hyphens: none;
134134
white-space: normal;
135135
}
136+
137+
/* Prose / block editor (blogs, etc.): keep wide content inside the viewport */
138+
.prose table {
139+
max-width: 100%;
140+
}
141+
.prose th,
142+
.prose td {
143+
overflow-wrap: break-word;
144+
word-break: normal;
145+
hyphens: auto;
146+
}
147+
.prose td code,
148+
.prose th code {
149+
word-break: break-word;
150+
overflow-wrap: break-word;
151+
hyphens: none;
152+
white-space: normal;
153+
}
154+
.prose img,
155+
.prose video {
156+
max-width: 100%;
157+
height: auto;
158+
}
159+
.prose iframe {
160+
max-width: 100%;
161+
}
162+
.prose pre {
163+
max-width: 100%;
164+
overflow-x: auto;
165+
}
136166
}
137167

138168

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"use client";
2+
3+
import { DotCMSBlockEditorRenderer } from "@dotcms/react";
4+
5+
/**
6+
* Block editor must run on the client — @dotcms/react bundles TinyMCE, which breaks
7+
* when the learn course page is evaluated as a Server Component during `next build`.
8+
*/
9+
export default function CourseIntroduction({ blocks }) {
10+
return (
11+
<DotCMSBlockEditorRenderer
12+
className="prose dark:prose-invert"
13+
blocks={blocks}
14+
/>
15+
);
16+
}

app/learn/[slug]/page.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getCourseDetail } from "@/services/courses/getCourse";
2-
import { DotCMSBlockEditorRenderer } from "@dotcms/react";
32
import ChapterFooter from "./ChapterFooter";
3+
import CourseIntroduction from "./CourseIntroduction";
44
import { notFound } from "next/navigation";
55

66
export default async function CoursePage({ params }) {
@@ -12,10 +12,7 @@ export default async function CoursePage({ params }) {
1212
<>
1313
<p className="text-sm text-white/50 mb-2">Introduction</p>
1414
<h1 className="text-4xl font-bold mb-8">{course.title}</h1>
15-
<DotCMSBlockEditorRenderer
16-
className="prose dark:prose-invert"
17-
blocks={course.introduction.json}
18-
/>
15+
<CourseIntroduction blocks={course.introduction.json} />
1916
<ChapterFooter courseSlug={slug} currentIndex={-1} chapters={course.chapters} />
2017
</>
2118
);

components/MarkdownContent.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -302,14 +302,13 @@ const MarkdownContent: React.FC<MarkdownContentProps> = ({ content, className, d
302302
},
303303
hr: () => <hr className="border-t border-border mb-6" />,
304304

305-
img: ({ src, alt, ...props }: any) => (
306-
<img
307-
src={src}
308-
alt={alt}
309-
className="inline rounded-lg max-w-full h-auto"
310-
{...props}
311-
/>
312-
),
305+
img: ({ src, alt, ...props }: any) =>
306+
React.createElement('img', {
307+
src,
308+
alt,
309+
className: 'inline rounded-lg max-w-full h-auto',
310+
...props,
311+
}),
313312

314313
video: ({ node, ...props }: any) => {
315314
if (!node?.children) return null;

components/blogs/blog-component.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default function BlogComponent(props: BlogProps) {
1212
const customRenderers: any = {};
1313

1414
return (
15-
<div className="prose dark:prose-invert mb-6 sm:mb-8 break-words whitespace-normal overflow-hidden max-w-none">
15+
<div className="prose dark:prose-invert mb-6 sm:mb-8 max-w-none min-w-0 w-full overflow-x-auto break-words whitespace-normal prose-img:max-w-full prose-pre:max-w-full prose-pre:overflow-x-auto">
1616
<DotBlockEditor blocks={body?.json || body} customRenderers={customRenderers} />
1717
</div>
1818
);

components/blogs/blog-detail.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,21 @@ export default function BlogDetailComponent({ post }) {
1414
const customRenderers = {};
1515

1616
return (
17-
<div className="container mx-auto">
17+
<div className="container mx-auto w-full min-w-0 max-w-full">
1818
{/* Main Content Grid */}
19-
<div className="flex gap-4 py-8 ">
19+
<div className="flex w-full min-w-0 flex-col gap-4 py-8 xl:flex-row">
2020

2121

2222
{/* Main Content */}
23-
<article className="flex-1 px-4 max-w-4xl">
23+
<article className="w-full min-w-0 max-w-4xl flex-1 px-4">
2424

2525
<DetailHeader post={post} />
2626

2727
<BlogComponent body={post.body.json} />
2828
</article>
2929

3030
{/* Right Sidebar */}
31-
<div className="w-64 shrink-0 hidden xl:block">
31+
<div className="hidden w-64 shrink-0 xl:block">
3232
<div className="sticky top-16">
3333
<Authors authors={post.author} />
3434
<OnThisPage />

components/content-types/feature-card.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ interface FeatureCardProps {
2424

2525
imageUrl?: string;
2626
externalLink?:boolean;
27+
/** e.g. `v=2` — appended as a cache-busting query on the resolved image URL */
28+
imageCacheBust?: string;
2729
}
2830

2931
export default function FeatureCard({
@@ -37,11 +39,14 @@ export default function FeatureCard({
3739
links = [],
3840
useIconOnly = false,
3941
imageUrl,
40-
externalLink = false
42+
externalLink = false,
43+
imageCacheBust,
4144
}: FeatureCardProps) {
4245

4346
const myHref = href ? href : "#";
4447
const imageUrlAlt = imageIdentifier && (imageIdentifier.startsWith('http') || imageIdentifier.startsWith('/dA/')) ? imageIdentifier : `${Config.CDNHost}/dA/${imageIdentifier}/`;
48+
const withImageBust = (url: string) =>
49+
url && imageCacheBust ? `${url}${url.includes("?") ? "&" : "?"}${imageCacheBust}` : url;
4550

4651
return (
4752
<div className="space-y-4">
@@ -75,7 +80,7 @@ export default function FeatureCard({
7580
<div className="mt-auto flex justify-center items-center w-full">
7681
{imageUrl ? (
7782
<Image
78-
src={`${imageUrl}`}
83+
src={withImageBust(`${imageUrl}`)}
7984
alt={`${title} illustration`}
8085
width={200}
8186
height={200}
@@ -96,7 +101,7 @@ export default function FeatureCard({
96101
</div>
97102
) : (
98103
<Image
99-
src={`${imageUrlAlt}`}
104+
src={withImageBust(`${imageUrlAlt}`)}
100105
alt={`${title} illustration`}
101106
width={400}
102107
height={150}

components/content-types/hero.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export default function Hero(props: HeroProps) {
6868
title={card1?.title}
6969
description={card1?.description}
7070
imageIdentifier={card1?.titleImage?.idPath || ""}
71+
imageCacheBust="v=2"
7172
color="[#a21caf]"
7273
count={0}
7374
links={developerLinks}

components/navigation/NavTree.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ const NavTree = React.memo(
9595
// Restore scroll position immediately
9696
navElement.scrollTop = savedScroll;
9797
setIsInitialSetupComplete(true);
98+
// savedScroll is updated on every scroll; listing it would re-run this effect constantly.
99+
// eslint-disable-next-line react-hooks/exhaustive-deps -- intentional: mount + isMobile only
98100
}, [isMobile]);
99101

100102
// Helper function to find active link with flexible matching

0 commit comments

Comments
 (0)