fix(avbeat): 3 homepage bugs — invisible AI sidebar titles, dark tagline on blue header, literal <p> in excerpts
(A) src/components/AISuggestedArticles.tsx :186 + :287 — 'AI Suggested Articles' card titles
used text-[#e0e0e0] on now-white card surfaces, making the title invisible until
group-hover flipped it to #1D4ED8. Both occurrences changed to text-[#0F172A].
(ArticleFeed.tsx:627 and NewsPageClient.tsx:276 were already navy from the
prior contrast-fix deploy and cover both pagination + infinite-scroll variants.)
(B) src/components/AvBeatLogo.tsx :99 — header tagline 'Inside the Future of Pro AV'
was rendered in --brand-text-muted (#475569 slate) on a now-blue nav (#1D4ED8),
near-invisible. Changed to rgba(255,255,255,0.85) so it reads against the blue
while sitting visually subordinate to the wordmark above it.
(C) src/lib/articles/excerpt.ts — cleanExcerpt now strips HTML tags (<p>, <br>, <em>, etc.)
and decodes common entities ( , &, <, >, ", ', ') at
render time, then collapses whitespace before the existing trailing-junk/last-word
logic runs. Display-layer only — stored excerpts are not mutated, matching the
'fix at the DISPLAY layer' instruction.
This commit is contained in:
@@ -183,7 +183,7 @@ export default function AISuggestedArticles({ variant = 'compact' }: Props) {
|
||||
<p className="text-[#1D4ED8] font-body text-[9px] font-bold uppercase tracking-wider mb-0.5">
|
||||
{article.category}
|
||||
</p>
|
||||
<p className="font-heading text-[#e0e0e0] text-xs font-semibold leading-snug line-clamp-2 group-hover:text-[#1D4ED8] transition-colors">
|
||||
<p className="font-heading text-[#0F172A] text-xs font-semibold leading-snug line-clamp-2 group-hover:text-[#1D4ED8] transition-colors">
|
||||
{article.title}
|
||||
</p>
|
||||
</div>
|
||||
@@ -284,7 +284,7 @@ export default function AISuggestedArticles({ variant = 'compact' }: Props) {
|
||||
{article.category}
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="font-heading text-[#e0e0e0] text-sm font-semibold leading-snug line-clamp-2 group-hover:text-[#1D4ED8] transition-colors">
|
||||
<h3 className="font-heading text-[#0F172A] text-sm font-semibold leading-snug line-clamp-2 group-hover:text-[#1D4ED8] transition-colors">
|
||||
{article.title}
|
||||
</h3>
|
||||
<p className="text-[#555] font-body text-[11px] mt-1">{article.readTime}</p>
|
||||
|
||||
@@ -96,7 +96,7 @@ export default function AvBeatLogo({
|
||||
fontWeight: 500,
|
||||
fontSize: 11,
|
||||
letterSpacing: "0.04em",
|
||||
color: "var(--brand-text-muted, #475569)",
|
||||
color: "rgba(255, 255, 255, 0.85)",
|
||||
lineHeight: 1.2,
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
|
||||
@@ -1,16 +1,31 @@
|
||||
// Render-time excerpt sanitizer. Some imported / AI-generated excerpts were
|
||||
// hard-truncated at a fixed byte count, leaving sentences clipped mid-word
|
||||
// (e.g. "Since 1976, the co"). Until the upstream excerpt generator gets the
|
||||
// same treatment, this helper makes any clipped excerpt visually clean:
|
||||
// strip trailing junk, snap to the last complete word, and append an
|
||||
// ellipsis only when the excerpt isn't already a complete sentence.
|
||||
// (e.g. "Since 1976, the co"). Others arrive with raw HTML still in the
|
||||
// string (e.g. "<p>In today's fast-paced..."), which renders as visible
|
||||
// "<p>" text on the card. This helper handles both at render time without
|
||||
// mutating stored data.
|
||||
|
||||
const SENTENCE_END = /[.!?…]['")\]]?$/;
|
||||
const TRAILING_JUNK = /[\s,;:—–\-—_]+$/;
|
||||
|
||||
export function cleanExcerpt(raw: string | null | undefined): string {
|
||||
if (!raw) return "";
|
||||
let s = raw.replace(/ /g, " ").trim();
|
||||
// Strip any HTML tags first — excerpts are rendered as plain text, so
|
||||
// <p>, <br>, <em>, etc. should be removed rather than escaped.
|
||||
// Replace tags with a space so adjacent words don't collide when
|
||||
// formerly-block-level boundaries are removed.
|
||||
let s = raw.replace(/<[^>]*>/g, " ");
|
||||
// Decode the common entities that show up in WP/AI-imported excerpts.
|
||||
s = s
|
||||
.replace(/ /g, " ")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/'/g, "'");
|
||||
// Collapse multi-whitespace runs left behind by tag removal.
|
||||
s = s.replace(/\s+/g, " ").trim();
|
||||
if (!s) return "";
|
||||
if (SENTENCE_END.test(s)) return s;
|
||||
// Drop the trailing partial token.
|
||||
|
||||
Reference in New Issue
Block a user