Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions src/components/Sections/Blog.css
Original file line number Diff line number Diff line change
Expand Up @@ -1088,20 +1088,30 @@
margin: 0;
}

.toc-item-wrap {
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.toc-item-wrap:last-child {
border-bottom: none;
}

.toc-item {
display: flex;
align-items: flex-start;
gap: 0.75rem;
width: 100%;
padding: 0.75rem 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
margin: 0;
background: none;
border: none;
cursor: pointer;
text-align: left;
font: inherit;
color: inherit;
transition: all 0.2s;
}

.toc-item:last-child {
border-bottom: none;
}

.toc-item:hover {
padding-left: 0.5rem;
}
Expand All @@ -1114,6 +1124,12 @@
color: #ffffff;
}

.toc-item:focus-visible {
outline: 2px solid rgba(69, 89, 220, 0.6);
outline-offset: 2px;
border-radius: 4px;
}

.toc-number {
font-size: 0.75rem;
font-weight: 700;
Expand All @@ -1140,6 +1156,7 @@

.content-section {
margin-bottom: 3rem;
scroll-margin-top: 100px;
}
Comment on lines 1157 to 1160
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scroll-margin-top: 100px duplicates the top: 100px offset used for the sticky sidebar. To avoid these drifting out of sync when the header/sticky offset changes, consider extracting this value into a single CSS custom property and reusing it in both places.

Copilot uses AI. Check for mistakes.

.section-heading {
Expand Down
71 changes: 40 additions & 31 deletions src/components/Sections/BlogDetail.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,34 +62,33 @@ const BlogDetail = () => {
);
}

// ✅ Normalized content
const content = {
toc: blog.content?.toc ?? [
"Introduction",
"Overview",
"Key Points",
"Analysis",
"Conclusion",
],
sections: blog.content?.sections ?? [
{
heading: "Introduction",
text:
blog.excerpt ||
"This article provides insights into the latest cryptocurrency trends.",
},
{
heading: "Overview",
text: "This article is currently being prepared with detailed analysis and insights.",
},
],
// Sections are the single source of truth for body + TOC (avoids toc/section count mismatches)
const sections =
blog.content?.sections?.length > 0
? blog.content.sections
: [
{
heading: "Introduction",
text:
blog.excerpt ||
"This article provides insights into the latest cryptocurrency trends.",
},
{
heading: "Overview",
text:
"This article is currently being prepared with detailed analysis and insights.",
},
];

const scrollToSection = (index) => {
document
.getElementById(`blog-section-${index}`)
Comment on lines +83 to +85
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Section IDs and React keys are derived from the array index (blog-section-${i} / key={i}). This isn’t stable if sections are inserted/reordered (and makes it harder to support deep-linking). Consider using a deterministic id per section (e.g., slugified heading or an explicit id field in blog.content.sections) and use that for both the DOM id and the list key/scroll target.

Suggested change
const scrollToSection = (index) => {
document
.getElementById(`blog-section-${index}`)
// Create a deterministic, heading-based id for each section
const createSectionId = (heading) => {
if (!heading || typeof heading !== "string") {
return "blog-section";
}
const slug = heading
.toLowerCase()
.trim()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
return `blog-section-${slug || "section"}`;
};
const sectionIds = sections.map((section) => createSectionId(section.heading));
const scrollToSection = (index) => {
const sectionId = sectionIds[index];
if (!sectionId) {
return;
}
document
.getElementById(sectionId)

Copilot uses AI. Check for mistakes.
?.scrollIntoView({ behavior: "smooth", block: "start" });
};

// Demo views
const views = Math.floor(Math.random() * 2000) + 500;

// Handlers
// Handlers
const handleBookmark = async () => {
if (!currentUser) {
toast.error("Please login to bookmark articles");
Expand Down Expand Up @@ -197,12 +196,18 @@ const BlogDetail = () => {
<div className="sidebar-sticky">
<h3 className="sidebar-title">Contents</h3>
<ul className="blog-toc">
{content.toc.map((item, i) => (
<li key={i} className="toc-item">
<span className="toc-number">
{String(i + 1).padStart(2, "0")}
</span>
<span className="toc-text">{item}</span>
{sections.map((section, i) => (
<li key={i} className="toc-item-wrap">
<button
type="button"
className="toc-item"
onClick={() => scrollToSection(i)}
>
<span className="toc-number">
{String(i + 1).padStart(2, "0")}
</span>
<span className="toc-text">{section.heading}</span>
</button>
</li>
))}
</ul>
Expand All @@ -215,8 +220,12 @@ const BlogDetail = () => {
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
>
{content.sections.map((section, i) => (
<div key={i} className="content-section">
{sections.map((section, i) => (
<div
key={i}
id={`blog-section-${i}`}
className="content-section"
>
<h2 className="section-heading">{section.heading}</h2>
<p className="section-text">{section.text}</p>
</div>
Expand Down
35 changes: 32 additions & 3 deletions src/data/blogData.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export const generateBlogPosts = () => {
isFeatured: true,
gradient: "linear-gradient(135deg, #4559DC30, #22c55e20)",
content: {
toc: ["Market Overview", "On-chain Metrics", "Supply Dynamics", "Price Action Analysis", "Forward Outlook"],
sections: [
{
heading: "Market Overview",
Expand All @@ -56,6 +55,14 @@ export const generateBlogPosts = () => {
{
heading: "Supply Dynamics",
text: "The percentage of supply held in profit has recovered to 85%, indicating most holders remain in profit despite recent volatility. Realized capitalization growth suggests organic capital inflow rather than speculative trading."
},
{
heading: "Price Action Analysis",
text: "Bitcoin is testing key structural levels as volume profiles show accumulation near current prices. The Vector models highlight a narrowing range that often precedes a directional expansion."
},
{
heading: "Forward Outlook",
text: "Institutional flows and holder behavior suggest patience is warranted; the next major move will likely align with macro liquidity and ETF flow trends. We continue to monitor exchange balances and realized cap for confirmation signals."
}
]
}
Expand All @@ -73,7 +80,6 @@ export const generateBlogPosts = () => {
isFeatured: true,
gradient: "linear-gradient(135deg, #22c55e30, #9d4edd20)",
content: {
toc: ["Weekly Summary", "Exchange Flows", "Miner Activity", "Network Health", "Trading Volume"],
sections: [
{
heading: "Weekly Summary",
Expand All @@ -82,6 +88,18 @@ export const generateBlogPosts = () => {
{
heading: "Exchange Flows",
text: "Major exchanges recorded significant outflows, particularly from institutional custody solutions. This suggests accumulation by long-term investors despite uncertain price action."
},
{
heading: "Miner Activity",
text: "Hash rate remains near all-time highs while miner revenue holds steady post-difficulty adjustment. Selling pressure from miners has been orderly relative to prior cycles."
},
{
heading: "Network Health",
text: "Block times and mempool congestion are within normal ranges. Node count and geographic distribution continue to support a resilient network topology."
},
{
heading: "Trading Volume",
text: "Spot volumes are stable week-over-week; derivatives open interest declined slightly as leverage resets. On-chain transfer volume remains consistent with consolidation rather than distribution."
}
]
}
Expand All @@ -99,7 +117,6 @@ export const generateBlogPosts = () => {
isFeatured: true,
gradient: "linear-gradient(135deg, #9d4edd30, #f59e0b20)",
content: {
toc: ["Volatility Analysis", "Options Positioning", "Liquidity Conditions", "Market Sentiment", "Risk Assessment"],
sections: [
{
heading: "Volatility Analysis",
Expand All @@ -108,6 +125,18 @@ export const generateBlogPosts = () => {
{
heading: "Options Positioning",
text: "Dealer gamma exposure is turning positive near current price levels, creating potential for accelerated moves should key technical levels break."
},
{
heading: "Liquidity Conditions",
text: "Order book depth has improved versus late December, though weekend liquidity remains thinner. Stablecoin balances on exchanges provide a modest buffer for large clips."
},
{
heading: "Market Sentiment",
text: "Funding rates are neutral to slightly positive; social sentiment indicators show cautious optimism without euphoria typical of late-cycle tops."
},
{
heading: "Risk Assessment",
text: "Key risks include macro surprises and sudden deleveraging in perpetual markets. Position sizing and defined risk remain prudent given compressed volatility regimes."
}
]
}
Expand Down
Loading