Skip to content

Commit ad364d2

Browse files
committed
[fix] Improve docs copy button behavior and code spacing
- Fixed copy button positioning relative to heading instead of container - Made copy button invisible on mobile until heading is tapped - Increased horizontal padding for inline code elements (mx-1 → mx-2) - Enhanced spacing for code blocks in headings with more padding and margin - Improved mobile layout by removing DOM elements when not needed 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
1 parent c874dcb commit ad364d2

File tree

2 files changed

+98
-39
lines changed

2 files changed

+98
-39
lines changed

web/src/components/docs/mdx/mdx-components.tsx

Lines changed: 69 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const createHeadingWithCopyLink = (
3131
...props
3232
}: HTMLAttributes<HTMLHeadingElement>) => {
3333
const [copied, setCopied] = useState(false)
34-
const [showCopyButton, setShowCopyButton] = useState(false)
34+
const [showMobileBadge, setShowMobileBadge] = useState(false)
3535
const isMobile = useIsMobile()
3636

3737
useEffect(() => {
@@ -42,14 +42,14 @@ const createHeadingWithCopyLink = (
4242
return undefined
4343
}, [copied])
4444

45-
// Auto-hide copy button on mobile after 3 seconds
45+
// Auto-hide mobile badge after 3 seconds
4646
useEffect(() => {
47-
if (isMobile && showCopyButton) {
48-
const timer = setTimeout(() => setShowCopyButton(false), 1_500)
47+
if (isMobile && showMobileBadge) {
48+
const timer = setTimeout(() => setShowMobileBadge(false), 3000)
4949
return () => clearTimeout(timer)
5050
}
5151
return undefined
52-
}, [isMobile, showCopyButton])
52+
}, [isMobile, showMobileBadge])
5353

5454
const title = children?.toString()
5555

@@ -77,7 +77,7 @@ const createHeadingWithCopyLink = (
7777
<HeadingComponent
7878
{...props}
7979
className={cn(
80-
'group relative hover:cursor-pointer hover:underline scroll-m-20',
80+
'hover:cursor-pointer hover:underline scroll-m-20',
8181
defaultClasses,
8282
className
8383
)}
@@ -102,48 +102,78 @@ const createHeadingWithCopyLink = (
102102
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' })
103103
}
104104

105-
// On mobile, toggle copy button visibility when title is tapped
105+
// On mobile, show floating badge when title is tapped
106106
if (isMobile) {
107-
setShowCopyButton(!showCopyButton)
107+
setShowMobileBadge(true)
108108
}
109109
}
110110

111-
// Determine button visibility based on device type
112-
const buttonVisibilityClass = isMobile
113-
? showCopyButton
114-
? 'opacity-100'
115-
: 'opacity-0'
116-
: 'xs:opacity-100 xl:opacity-0 group-hover:opacity-100'
111+
// Extract margin classes from defaultClasses for container
112+
const marginClasses =
113+
defaultClasses
114+
.match(
115+
/(?:^|\s)(mt-\S+|mb-\S+|my-\S+|m-\S+|first:mt-\S+|first:mb-\S+)(?=\s|$)/g
116+
)
117+
?.join(' ') || ''
118+
const textClasses = defaultClasses
119+
.replace(
120+
/(?:^|\s)(?:mt-\S+|mb-\S+|my-\S+|m-\S+|first:mt-\S+|first:mb-\S+)(?=\s|$)/g,
121+
''
122+
)
123+
.trim()
117124

118125
return (
119-
<div className="group">
120-
<HeadingComponent
121-
{...props}
122-
id={id}
123-
className={cn(
124-
'hover:cursor-pointer hover:underline scroll-m-20 inline-flex items-center gap-2',
125-
defaultClasses,
126-
className
127-
)}
128-
onClick={handleClick}
129-
>
130-
{children}
131-
<button
132-
onClick={handleCopy}
126+
<div className={cn(marginClasses)}>
127+
<div className="group inline-flex items-baseline">
128+
<HeadingComponent
129+
{...props}
130+
id={id}
133131
className={cn(
134-
buttonVisibilityClass,
135-
'p-1.5 rounded-md bg-muted/50 hover:bg-muted border border-border/50 hover:border-border transition-all duration-200 ease-in-out inline-flex items-center justify-center shadow-sm hover:shadow-md',
136-
isMobile ? 'min-h-[44px] min-w-[44px]' : 'h-auto w-auto'
132+
'relative hover:cursor-pointer hover:underline scroll-m-20',
133+
textClasses,
134+
className
137135
)}
138-
aria-label="Copy link to section"
136+
onClick={handleClick}
139137
>
140-
{copied ? (
141-
<Check className="text-green-500 h-4 w-4" />
142-
) : (
143-
<Link className="h-4 w-4 text-muted-foreground hover:text-foreground" />
138+
{children}
139+
{/* Mobile floating badge */}
140+
{isMobile && showMobileBadge && (
141+
<div className="absolute -top-12 left-0 z-10">
142+
<button
143+
onClick={handleCopy}
144+
className="bg-background border border-border rounded-lg px-3 py-2 shadow-lg flex items-center gap-2 animate-in fade-in-0 slide-in-from-bottom-2 duration-200 hover:bg-muted transition-colors cursor-pointer"
145+
aria-label="Copy link to section"
146+
>
147+
<div className="flex items-center justify-center p-1.5 rounded-md bg-muted/50 border border-border/50">
148+
{copied ? (
149+
<Check className="h-3 w-3 text-green-500" />
150+
) : (
151+
<Link className="h-3 w-3 text-muted-foreground" />
152+
)}
153+
</div>
154+
<span className="text-sm font-medium">
155+
{copied ? 'Copied!' : 'Copy link'}
156+
</span>
157+
</button>
158+
</div>
144159
)}
145-
</button>
146-
</HeadingComponent>
160+
</HeadingComponent>
161+
162+
{/* Desktop copy button right next to heading */}
163+
{!isMobile && (
164+
<button
165+
onClick={handleCopy}
166+
className="flex-shrink-0 ml-2 opacity-0 group-hover:opacity-100 transition-all duration-200 p-1.5 rounded-md bg-muted/50 hover:bg-muted border border-border/50 hover:border-border flex items-center justify-center shadow-sm hover:shadow-md"
167+
aria-label="Copy link to section"
168+
>
169+
{copied ? (
170+
<Check className="h-3 w-3 text-green-500" />
171+
) : (
172+
<Link className="h-3 w-3 text-muted-foreground hover:text-foreground" />
173+
)}
174+
</button>
175+
)}
176+
</div>
147177
</div>
148178
)
149179
}
@@ -247,7 +277,7 @@ const components = {
247277
code: ({ className, ...props }: HTMLAttributes<HTMLElement>) => (
248278
<code
249279
className={cn(
250-
'relative rounded px-[0.3rem] py-[0.2rem] font-mono text-sm bg-muted',
280+
'relative rounded px-[0.3rem] py-[0.2rem] mx-2 font-mono text-sm bg-muted',
251281
className
252282
)}
253283
{...props}

web/src/styles/globals.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,32 @@
161161
scrollbar-width: thin;
162162
scrollbar-color: hsl(var(--border) / 0.6) transparent;
163163
}
164+
165+
/* Code blocks within headings - lighter styling to prevent mobile layout issues */
166+
:where(h1, h2, h3, h4, h5, h6) code {
167+
@apply border-0 text-current;
168+
padding: 0.15em 0.75em;
169+
margin: 0 0.4em;
170+
font-size: 0.9em;
171+
font-weight: inherit;
172+
border-radius: 2px;
173+
background-color: rgba(255, 255, 255, 0.1);
174+
}
175+
176+
/* Ensure proper text wrapping in headings on mobile */
177+
@media (max-width: 767px) {
178+
:where(h1, h2, h3, h4, h5, h6) {
179+
/* Use flexbox for robust wrapping */
180+
display: inline-flex;
181+
flex-wrap: wrap;
182+
align-items: baseline;
183+
line-height: 1; /* Minimal line spacing */
184+
gap: 0; /* Remove gap between items */
185+
margin-top: 0rem; /* Reduce top margin */
186+
}
187+
188+
/* Keep code snippets from breaking on mobile */
189+
:where(h1, h2, h3, h4, h5, h6) code {
190+
white-space: nowrap;
191+
}
192+
}

0 commit comments

Comments
 (0)