Help Improvement 9 — Section Deep Links
Status: [To Build]
Purpose: Help icons on feature pages currently link to the topic root (e.g.
/help/blog). When the relevant information is in a specific section mid-page, the user has to scroll to find it. Adding anchor IDs to section headings enables?buttons to link directly to the relevant section.
What Changes
- Every section heading in
HelpPagegets anidattribute derived from the heading text. - On page load,
HelpPagescrolls to the#hashin the URL (if present). - Each heading renders a small
#link (visible on hover) for copy-to-clipboard navigation. HelpTriggerand inline helpLinks can append#section-idto their href.
Slug Generation
A pure slugify function converts heading text to an id-safe string:
// packages/ui/src/lib/slugify.ts
export function slugify(text: string): string {
return text
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, "") // remove non-alphanumeric
.trim()
.replace(/\s+/g, "-"); // spaces → hyphens
}
// Examples:
// "The approval workflow" → "the-approval-workflow"
// "Status guide" → "status-guide"
// "What are Blog Posts?" → "what-are-blog-posts"HelpPage Section Heading Render
For every section that has a heading field, add the id and hover anchor:
// packages/ui/src/HelpPage.tsx
import { slugify } from "./lib/slugify";
import { Link2 } from "lucide-react";
function SectionHeading({ text }: { text: string }) {
const id = slugify(text);
return (
<h2
id={id}
className="text-base font-semibold mb-3 flex items-center gap-2 group scroll-mt-6"
>
{text}
<a
href={`#${id}`}
className="opacity-0 group-hover:opacity-100 transition-opacity text-muted-foreground
hover:text-foreground"
aria-label={`Link to ${text}`}
>
<Link2 className="w-3.5 h-3.5" />
</a>
</h2>
);
}Replace all inline <h2>{section.heading}</h2> calls with <SectionHeading text={section.heading} />.
Scroll to Hash on Load
// packages/ui/src/HelpPage.tsx
"use client";
import { useEffect } from "react";
// Inside HelpPage component:
useEffect(() => {
const hash = window.location.hash.slice(1);
if (!hash) return;
const el = document.getElementById(hash);
if (el) {
// Small delay so the page has painted before scrolling
setTimeout(() => el.scrollIntoView({ behavior: "smooth", block: "start" }), 100);
}
}, [data.slug]); // re-run when slug changes (drawer navigation)Deep Link Usage in HelpTrigger
HelpTrigger gains an optional section prop:
// apps/dashboard/src/components/help/HelpTrigger.tsx
interface HelpTriggerProps {
slug: string;
section?: string; // heading text OR pre-computed slug
label?: string;
}
export function HelpTrigger({ slug, section, label }: HelpTriggerProps) {
const { open } = useHelpDrawer();
const hash = section ? `#${slugify(section)}` : "";
function handleClick() {
open(slug);
if (hash) {
// Scroll after drawer animation completes
setTimeout(() => {
const el = document.getElementById(hash.slice(1));
el?.scrollIntoView({ behavior: "smooth", block: "start" });
}, 300);
}
}
return (
<button onClick={handleClick} title={label ?? "Help"} ...>
<HelpCircle className="w-4 h-4" />
</button>
);
}Example usage — the ? icon next to the approval section of the blog page opens the drawer and scrolls to “The approval workflow”:
<HelpTrigger slug="blog" section="The approval workflow" label="Help: Blog Approvals" />Affected Files
| File | Change |
|---|---|
packages/ui/src/lib/slugify.ts | New utility |
packages/ui/src/HelpPage.tsx | Add id to section headings; SectionHeading component; scroll-to-hash on mount |
apps/dashboard/src/components/help/HelpTrigger.tsx | Add section prop + scroll-after-open |