Skip to content

Commit 02a66e1

Browse files
Improve mobile responsiveness and fix layout issues
- Fix mobile footer positioning with proper sticky behavior and smooth transitions - Add horizontal scrolling for wide code blocks to prevent page overflow on small screens - Default code blocks without language specification to 'text' for consistent rendering - Fix mobile layout for profile section headers to stack vertically on small screens - Add overflow handling to docs prose content for better mobile experience 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
1 parent 2a45876 commit 02a66e1

File tree

7 files changed

+108
-76
lines changed

7 files changed

+108
-76
lines changed

web/src/app/docs/[category]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ interface CategoryPageProps {
6969

7070
const DocPage = ({ doc }: { doc: Doc }) => {
7171
return (
72-
<article className="prose dark:prose-invert prose-compact [&_h1]:scroll-mt-24 [&_h2]:scroll-mt-24 [&_h3]:scroll-mt-24">
72+
<article className="prose dark:prose-invert prose-compact [&_h1]:scroll-mt-24 [&_h2]:scroll-mt-24 [&_h3]:scroll-mt-24 max-w-none overflow-x-auto">
7373
<Mdx code={doc.body.code} />
7474

7575
{React.createElement(

web/src/app/docs/layout.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,25 @@ export default function DocsLayout({
1717
const [open, setOpen] = useState(false)
1818
const [showTopFade, setShowTopFade] = useState(false)
1919
const [showBottomFade, setShowBottomFade] = useState(false)
20+
const [isFixed, setIsFixed] = useState(false)
2021
const sidebarRef = useRef<HTMLDivElement>(null)
2122

23+
// Track scroll position to determine if sidebar should be fixed
24+
useEffect(() => {
25+
const handleScroll = () => {
26+
// The header with logo is 4rem (64px) tall
27+
// Fix the sidebar when the user scrolls past the header
28+
if (window.scrollY > 64) {
29+
setIsFixed(true)
30+
} else {
31+
setIsFixed(false)
32+
}
33+
}
34+
35+
window.addEventListener('scroll', handleScroll)
36+
return () => window.removeEventListener('scroll', handleScroll)
37+
}, [])
38+
2239
// New: Smoothly scroll to hash target on back/forward navigation
2340
useEffect(() => {
2441
const handleHashChange = () => {
@@ -60,7 +77,11 @@ export default function DocsLayout({
6077
<div className="pt-8">
6178
<div className="container flex md:space-x-8 overflow-x-hidden">
6279
<div className="hidden lg:block w-64 shrink-0">
63-
<div className="fixed top-24 w-64 h-[calc(100vh-12rem)] z-40">
80+
<div
81+
className={`w-64 z-40 transition-all duration-200 ease-in-out ${
82+
isFixed ? 'fixed top-16 h-[calc(100vh-4rem)]' : 'relative'
83+
}`}
84+
>
6485
{/* Dynamic gradient fade indicators */}
6586
{showTopFade && (
6687
<div className="absolute top-0 left-0 right-0 h-6 bg-gradient-to-b from-background to-transparent pointer-events-none z-10 rounded-t-lg transition-opacity duration-200" />

web/src/app/profile/components/profile-section.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,21 @@ export function ProfileSection({
2323
<>
2424
{(title || description || headerActions) && (
2525
<div className="mb-6">
26-
<div className="flex items-start justify-between">
26+
<div className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
2727
<div className="space-y-1">
2828
{title && (
29-
<h2 className="text-2xl font-semibold tracking-tight">{title}</h2>
29+
<h2 className="text-2xl font-semibold tracking-tight">
30+
{title}
31+
</h2>
3032
)}
3133
{description && (
3234
<p className="text-muted-foreground">{description}</p>
3335
)}
3436
</div>
3537
{headerActions && (
36-
<div className="flex items-center gap-2">{headerActions}</div>
38+
<div className="flex items-center gap-2 md:ml-4">
39+
{headerActions}
40+
</div>
3741
)}
3842
</div>
3943
</div>
@@ -57,9 +61,5 @@ export function ProfileSection({
5761
)
5862
}
5963

60-
return (
61-
<div className={cn('space-y-6', className)}>
62-
{content}
63-
</div>
64-
)
65-
}
64+
return <div className={cn('space-y-6', className)}>{content}</div>
65+
}

web/src/app/profile/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export default function ProfilePage() {
119119
</div>
120120
</div>
121121
</div>
122-
<main className="flex-1 min-w-0">
122+
<main className="flex-1 min-w-0 pb-20 lg:pb-0">
123123
<div className="mb-6">
124124
<h1 className="text-2xl font-semibold">{activeTitle}</h1>
125125
</div>
@@ -128,7 +128,7 @@ export default function ProfilePage() {
128128
</div>
129129

130130
{/* Mobile navigation */}
131-
<div className="flex items-center lg:hidden sticky bottom-0 z-50 bg-background/80 backdrop-blur-sm container p-4 rounded-t-lg border-t">
131+
<div className="flex items-center lg:hidden fixed bottom-0 left-0 right-0 z-50 bg-background/80 backdrop-blur-sm container p-4 rounded-t-lg border-t transition-all duration-300 ease-in-out transform translate-y-0">
132132
<Sheet
133133
open={open}
134134
onOpenChange={(isOpen) => {

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ export function CodeDemo({ children, language, rawContent }: CodeDemoProps) {
8484
}
8585

8686
return (
87-
<div className="rounded-lg border bg-muted/30 px-4 w-full max-w-80 md:max-w-full my-3 transition-all group hover:bg-muted/40 overflow-hidden">
88-
<div className="flex items-center justify-between h-6 mt-0.5 mb-0.5">
87+
<div className="rounded-lg border bg-muted/30 w-full max-w-80 md:max-w-full my-3 transition-all group hover:bg-muted/40 overflow-x-auto">
88+
<div className="flex items-center justify-between h-6 mt-0.5 mb-0.5 px-4">
8989
<div className="text-[10px] text-muted-foreground/40 font-mono tracking-wide">
9090
{language.toLowerCase()}
9191
</div>
@@ -101,10 +101,12 @@ export function CodeDemo({ children, language, rawContent }: CodeDemoProps) {
101101
)}
102102
</button>
103103
</div>
104-
{language && <Separator className="bg-border/20 mb-0.5" />}
105-
<pre className="text-[13px] leading-relaxed py-1 bg-transparent text-foreground/90 rounded-lg overflow-x-auto scrollbar-thin scrollbar-thumb-muted-foreground/10 scrollbar-track-transparent">
106-
<code className="font-mono">{childrenContent}</code>
107-
</pre>
104+
{language && <Separator className="bg-border/20 mb-0.5 mx-4" />}
105+
<div className="px-4">
106+
<pre className="text-[13px] leading-relaxed py-1 bg-transparent text-foreground/90 rounded-lg scrollbar-thin scrollbar-thumb-muted-foreground/10 scrollbar-track-transparent">
107+
<code className="font-mono">{childrenContent}</code>
108+
</pre>
109+
</div>
108110
</div>
109111
)
110112
}

web/src/components/footer.tsx

Lines changed: 62 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,41 @@ import { LinkedInInsightTag } from 'nextjs-linkedin-insight-tag'
88
import { Separator } from '@/components/ui/separator'
99
import { siteConfig } from '@/lib/constant'
1010

11+
type LinkInfo = { text: string; href: string; target?: string }
12+
13+
const siteLinks: LinkInfo[] = [
14+
{ text: 'Home', href: '/' },
15+
{ text: 'Docs', href: '/docs', target: '_blank' },
16+
{ text: 'News', href: 'https://news.codebuff.com', target: '_blank' },
17+
{ text: 'Pricing', href: '/pricing' },
18+
{ text: 'Usage', href: '/usage' },
19+
]
20+
21+
const legalLinks: LinkInfo[] = [
22+
{ text: 'Privacy Policy', href: '/privacy-policy' },
23+
{ text: 'Terms of Service', href: '/terms-of-service' },
24+
]
25+
26+
const communityLinks: LinkInfo[] = [
27+
{ text: 'Discord', href: 'https://discord.gg/mcWTGjgTj3', target: '_blank' },
28+
]
29+
30+
const authLinks: LinkInfo[] = [{ text: 'Login', href: '/login' }]
31+
32+
const publicPaths = [
33+
...authLinks,
34+
...legalLinks,
35+
...siteLinks.filter((link) => link.href !== '/docs'),
36+
]
37+
.map((link) => link.href)
38+
.filter((href) => !href.startsWith('http'))
39+
1140
export const Footer = () => {
1241
const pathname = usePathname()
13-
if (pathname.startsWith('/docs')) {
14-
return <></>
42+
const isPublicPage = publicPaths.includes(pathname)
43+
44+
if (!isPublicPage) {
45+
return null
1546
}
1647

1748
return (
@@ -36,71 +67,49 @@ export const Footer = () => {
3667
<div>
3768
<h3 className="font-semibold mb-4">Site</h3>
3869
<nav className="flex flex-col space-y-2">
39-
<Link
40-
href="/"
41-
className="text-muted-foreground hover:text-primary"
42-
>
43-
Home
44-
</Link>
45-
<Link
46-
href="/docs"
47-
target="_blank"
48-
className="text-muted-foreground hover:text-primary"
49-
>
50-
Docs
51-
</Link>
52-
<Link
53-
href="https://news.codebuff.com"
54-
target="_blank"
55-
className="text-muted-foreground hover:text-primary"
56-
>
57-
News
58-
</Link>
59-
<Link
60-
href="/pricing"
61-
className="text-muted-foreground hover:text-primary"
62-
>
63-
Pricing
64-
</Link>
65-
<Link
66-
href="/usage"
67-
className="text-muted-foreground hover:text-primary"
68-
>
69-
Usage
70-
</Link>
70+
{siteLinks.map((link) => (
71+
<Link
72+
key={link.href}
73+
href={link.href}
74+
target={link.target}
75+
className="text-muted-foreground hover:text-primary"
76+
>
77+
{link.text}
78+
</Link>
79+
))}
7180
</nav>
7281
</div>
7382

7483
{/* Legal */}
7584
<div>
7685
<h3 className="font-semibold mb-4">Legal</h3>
7786
<nav className="flex flex-col space-y-2">
78-
<Link
79-
href="/privacy-policy"
80-
className="text-muted-foreground hover:text-primary"
81-
>
82-
Privacy Policy
83-
</Link>
84-
<Link
85-
href="/terms-of-service"
86-
className="text-muted-foreground hover:text-primary"
87-
>
88-
Terms of Service
89-
</Link>
87+
{legalLinks.map((link) => (
88+
<Link
89+
key={link.href}
90+
href={link.href}
91+
className="text-muted-foreground hover:text-primary"
92+
>
93+
{link.text}
94+
</Link>
95+
))}
9096
</nav>
9197
</div>
9298

9399
{/* Community */}
94100
<div>
95101
<h3 className="font-semibold mb-4">Community</h3>
96102
<nav className="flex flex-col space-y-2">
97-
<Link
98-
href="https://discord.gg/mcWTGjgTj3"
99-
target="_blank"
100-
className="text-muted-foreground hover:text-primary"
101-
>
102-
Discord
103-
</Link>
103+
{communityLinks.map((link) => (
104+
<Link
105+
key={link.href}
106+
href={link.href}
107+
target={link.target}
108+
className="text-muted-foreground hover:text-primary"
109+
>
110+
{link.text}
111+
</Link>
112+
))}
104113
</nav>
105114
</div>
106115
</div>

web/src/lib/remark-code-to-codedemo.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type { Plugin } from 'unified'
88
* and replaces them with an <CodeDemo language="lang">...</CodeDemo> MDX node,
99
* preserving multi-line formatting.
1010
*
11-
* If no language is specified (plain ``` block), it leaves the original code block unchanged.
11+
* If no language is specified (plain ``` block), it defaults to 'text' language.
1212
*/
1313
export const remarkCodeToCodeDemo = function remarkCodeToCodeDemo(): Plugin<
1414
any[],
@@ -18,8 +18,8 @@ export const remarkCodeToCodeDemo = function remarkCodeToCodeDemo(): Plugin<
1818
visit(tree, 'code', (node: Code, index, parent: any) => {
1919
if (!parent || typeof index !== 'number') return
2020

21-
// Skip transformation if no language is specified
22-
if (!node.lang) return
21+
// Default to 'text' if no language is specified
22+
const language = node.lang || 'text'
2323

2424
// Build an MDX JSX node representing <CodeDemo language="lang" rawContent="...">...</CodeDemo>
2525
const codeDemoNode: any = {
@@ -29,7 +29,7 @@ export const remarkCodeToCodeDemo = function remarkCodeToCodeDemo(): Plugin<
2929
{
3030
type: 'mdxJsxAttribute',
3131
name: 'language',
32-
value: node.lang,
32+
value: language,
3333
},
3434
{
3535
type: 'mdxJsxAttribute',

0 commit comments

Comments
 (0)