feat: revamp blog reading page ui and reading experience#338
Conversation
Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
…thor cards, sidebar, TOC tooltip, responsive typography, and Keploy-themed design Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
|
Hi @amaan-bhati , |
Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
|
Hi @amaan-bhati |
amaan-bhati
left a comment
There was a problem hiding this comment.
Hey @dhananjay6561 I tried reviewing the pr locally, most of the things look good to me, but there are a few things that i think needs to be updated/imporoved:
- the tags need to be clickabble, clicking on each tag redirects to the blogs under that tag, you can check the current behaviour of tags on the prod, clicking on each tag redirects to the blogs under the tag.
- There seems to be an unwanted line behind the banner image of the blog, the line is tight behind the banner image in the header which i think might be unwanted here, please confirm if it was intended.
- please also confirm that the previous tags that were below the blog near the author and reviewer cards are not removed, they are just commented out.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 16 changed files in this pull request and generated 12 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| pre { | ||
| background-color: #222222 !important; | ||
| color: antiquewhite; | ||
| } | ||
|
|
||
| .footer-font{ | ||
| font-family: "__Inter_aaf875","__Inter_Fallback_aaf875", sans-serif; | ||
| /* ── Blog-scoped styles (post-content-wrapper) ── */ | ||
|
|
||
| .post-content-wrapper pre { | ||
| background-color: #1e1e2e; | ||
| color: #cdd6f4; | ||
| } |
There was a problem hiding this comment.
.post-content-wrapper pre is intended to restyle blog code blocks, but the global pre { background-color: ... !important; } rule above will override it, so the new colors won’t apply. Consider scoping/removing the global pre rule or removing the !important so the blog-scoped rule can take effect.
| let h2Count = 0; | ||
| let h3Count = 0; | ||
| let h4Count = 0; | ||
|
|
||
| return headings.map((item, index) => { | ||
| const sid = sanitizeStringForURL(item.id, true); | ||
| const isAct = sid === activeId; | ||
| const isH3Plus = item.type === "h3" || item.type === "h4"; | ||
| const isH4 = item.type === "h4"; | ||
|
|
||
| // Compute numbering | ||
| let number = ""; | ||
| if (item.type === "h2" || item.type === "h1") { | ||
| h2Count++; | ||
| h3Count = 0; | ||
| h4Count = 0; | ||
| number = `${h2Count}.`; | ||
| } else if (item.type === "h3") { | ||
| h3Count++; | ||
| h4Count = 0; | ||
| number = `${h2Count}.${h3Count}`; | ||
| } else if (item.type === "h4") { | ||
| h4Count++; | ||
| number = `${h2Count}.${h3Count}.${h4Count}`; | ||
| } | ||
|
|
There was a problem hiding this comment.
The number variable is computed for TOC items but never rendered/used. This is dead code and makes the TOC harder to maintain; either display the numbering or remove the counters/number computation.
| let h2Count = 0; | |
| let h3Count = 0; | |
| let h4Count = 0; | |
| return headings.map((item, index) => { | |
| const sid = sanitizeStringForURL(item.id, true); | |
| const isAct = sid === activeId; | |
| const isH3Plus = item.type === "h3" || item.type === "h4"; | |
| const isH4 = item.type === "h4"; | |
| // Compute numbering | |
| let number = ""; | |
| if (item.type === "h2" || item.type === "h1") { | |
| h2Count++; | |
| h3Count = 0; | |
| h4Count = 0; | |
| number = `${h2Count}.`; | |
| } else if (item.type === "h3") { | |
| h3Count++; | |
| h4Count = 0; | |
| number = `${h2Count}.${h3Count}`; | |
| } else if (item.type === "h4") { | |
| h4Count++; | |
| number = `${h2Count}.${h3Count}.${h4Count}`; | |
| } | |
| return headings.map((item, index) => { | |
| const sid = sanitizeStringForURL(item.id, true); | |
| const isAct = sid === activeId; | |
| const isH3Plus = item.type === "h3" || item.type === "h4"; | |
| const isH4 = item.type === "h4"; |
| blogwriter[0].name.split(" ")[0].toLowerCase() === | ||
| blogreviewer[0].name.toLowerCase(); |
There was a problem hiding this comment.
sameAuthor comparison looks incorrect: it compares the writer’s first name to the reviewer’s full lowercased name, so it will almost always be false (even when they are the same person). Please compare like-with-like (e.g., compare full names, or compare first names for both).
| blogwriter[0].name.split(" ")[0].toLowerCase() === | |
| blogreviewer[0].name.toLowerCase(); | |
| blogwriter[0].name.toLowerCase() === blogreviewer[0].name.toLowerCase(); |
| export default function PostBody({ | ||
| content, | ||
| authorName, | ||
| authorImageUrl, | ||
| authorDescription, | ||
| ReviewAuthorDetails, | ||
| slug | ||
| slug, | ||
| categories, | ||
| }: { | ||
| content: Post["content"]; | ||
| authorName: Post["ppmaAuthorName"]; | ||
| authorImageUrl: string; | ||
| authorDescription: string; | ||
| ReviewAuthorDetails: { edges: { node: { name: string; avatar: { url: string }; description: string } }[] }; | ||
| slug: string | string[] | undefined; | ||
| categories?: Post["categories"]; |
There was a problem hiding this comment.
The categories prop was added to PostBody but is never used in the component. Please remove it (and the corresponding props passed from the slug pages) or use it in the new sidebar/category UI.
| font-weight: 400; | ||
| /* Keeping standard bold, not increasing it */ |
There was a problem hiding this comment.
.content strong, .content b sets font-weight: 400, which removes the semantic emphasis that <strong>/<b> is expected to provide and can reduce readability/accessibility. Please use an actual bold weight (e.g., 600/700) or inherit the browser default bold styling.
| font-weight: 400; | |
| /* Keeping standard bold, not increasing it */ | |
| font-weight: 600; | |
| /* Use a semi-bold weight to preserve emphasis and accessibility */ |
| .post-content-wrapper .relative { | ||
| border-radius: 0.5rem; | ||
| overflow: hidden; | ||
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); | ||
| margin-bottom: 1.5rem; | ||
| } |
There was a problem hiding this comment.
.post-content-wrapper .relative is a very broad selector and will affect any element inside post content that uses the Tailwind relative class, making the styling fragile. It also adds a box-shadow even though the PR description calls out “strokes over shadows”; consider targeting a more specific wrapper/class and removing the shadow if that’s the intended design.
| const [coords, setCoords] = useState({ top: 0, left: 0 }); | ||
| const timeout = useRef<ReturnType<typeof setTimeout>>(null); | ||
| const wrapperRef = useRef<HTMLDivElement>(null); | ||
|
|
||
| const handleEnter = () => { | ||
| timeout.current = setTimeout(() => { | ||
| if (wrapperRef.current) { | ||
| const rect = wrapperRef.current.getBoundingClientRect(); | ||
| setCoords({ | ||
| top: rect.top + rect.height / 2, | ||
| left: rect.right + 12, | ||
| }); | ||
| } | ||
| setShow(true); | ||
| }, 50); | ||
| }; |
There was a problem hiding this comment.
timeout is initialized with null but the ref type doesn’t include null, which makes the code harder to reason about and will become an error if strictNullChecks is enabled later. Consider typing it as useRef<ReturnType<typeof setTimeout> | null>(null) and clearing any existing timeout before setting a new one to avoid multiple pending timers.
| /* Resolve avatar src (handle external URLs via proxy) */ | ||
| const resolvedSrc = | ||
| !imageUrl || imageUrl === "n/a" | ||
| ? "/blog/images/author.png" | ||
| : /^https?:\/\//i.test(imageUrl) && basePath | ||
| ? `${basePath}/api/proxy-image?url=${encodeURIComponent(imageUrl)}` | ||
| : imageUrl; |
There was a problem hiding this comment.
resolvedSrc only proxies external images when basePath is passed in, but current callers (e.g., PostBody) don’t provide it. This can break author avatars for external hosts not in next.config.js image domains. Consider deriving basePath via useRouter() inside AuthorCard (as ReviewingAuthor does) so external images reliably go through /api/proxy-image when needed.
| slug, | ||
| categories, | ||
| }: { |
There was a problem hiding this comment.
categories is accepted as a prop but doesn’t appear to be used anywhere in PostBody. Please remove it (and stop passing it from the slug pages) or wire it into the new sidebar/category UI so it isn’t dead API surface.
| return ( | ||
| <div key={index} className="relative mx-auto mb-4"> | ||
| <div key={index} className="rounded-lg overflow-hidden mb-6 border border-[#313244] shadow-md"> | ||
| {/* Header bar: language badge + copy button */} | ||
| <div className="flex items-center justify-between px-4 py-2 bg-[#181825] border-b border-[#313244]"> |
There was a problem hiding this comment.
The code-block wrapper adds shadow-md, but the PR description calls out “strokes over shadows throughout”. If the new design intent is truly no-shadows, consider removing the shadow here (and relying on border strokes only) for consistency.
Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
|
Hi @amaan-bhati |
amaan-bhati
left a comment
There was a problem hiding this comment.
Hey @dhananjay6561 Thanks for following up on the previous review, I tried reviewing your changes again locally, the clickable tags thing is fixed, The line behind the banner i still pending which i think should be covered in an another pr as early as possible before the release. Please also try to resolve the remaining copilot comments from this pr. We'd hence raise one more pr resolving these changes before the release, you can raise the pr once you are done with the integration testing page pr you have in hand.
Merging this pr for now, checks:
- UI is stable on desktop :
- UI is stable on smaller devices:
- Ran
npm run buildnothing breaks:

Blog Article Page — Complete UI/UX Revamp
Summary
Complete overhaul of the blog article page — new CSS Grid layout, two new components, revamped typography, author hover cards, portal-based TOC tooltips, and a Keploy-branded sidebar. Strokes over shadows throughout.
16 files changed · 1,499 insertions · 613 deletions
Changes
Layout
≥1440px; single column below-mt-16 md:-mt-20) in both slug pagespx-5 → sm:px-6 → lg:px-10 → xl:px-16 → 2xl:px-20New Components
AuthorCard.tsx— Replaces oldAuthorDescription+ReviewingAuthor. Clean card with colored accent bar (orange=Writer, purple=Reviewer), role badge, 80×80 avatar, 3-line description, "View All Posts →" CTA, optional LinkedIn link. No shadows, strokes only.BlogSidebar.tsx— Right sidebar (max 260px): Share icons (X, Facebook, LinkedIn, copy-link), Category pill, Keploy CTA banner (#FFF4EEbg,#FF914Dborder).PostHeaderAuthors (Rewritten)
w-60card with role badge, avatar, description, "View Profile →" button"7. June 2024"Table of Contents (Rewritten)
1440px; sticky desktop card (max-w-[320px]), collapsible dropdown on smaller screenstitle— 50ms delay, border stroke, no shadow, no clippingTypography & Styling
700via DOM mutation (useEffect+style.setProperty) to override WordPress inline styleslineHeight: 1.25,fontWeight: 800,clamp()responsive sizingline-height: 1.1 !importantglobal override fromindex.cssSlug Pages (technology + community)
authorImageUrl,authorDescription,categoriesOther
cover-image.tsx— rounded corners (rounded-xl)categories.tsx— styling consistency updatetestimonials.tsx— minor rendering fixnext.config.js— image domain config updateFiles Modified
components/AuthorCard.tsxcomponents/BlogSidebar.tsxcomponents/PostHeaderAuthors.tsxcomponents/TableContents.tsxcomponents/post-body.tsxcomponents/post-body.module.csscomponents/post-header.tsxcomponents/post-title.tsxcomponents/cover-image.tsxcomponents/categories.tsxcomponents/containerSlug.tsxcomponents/testimonials.tsxstyles/index.csspages/technology/[slug].tsxpages/community/[slug].tsxnext.config.jsScreenshots
Desktop (≥ 1440px)
Mobile (< 768px)
Tablet (768px – 1439px)
Dark Mode
Build Output (
npm run build)Testing
npm run buildpasses