Skip to content

Commit 9d37619

Browse files
committed
mobile partners
1 parent b23d2f8 commit 9d37619

File tree

4 files changed

+229
-113
lines changed

4 files changed

+229
-113
lines changed

AGENTS.md

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -156,34 +156,41 @@ Since `listRoles` is wrapped in `createServerFn`, TanStack Start will properly h
156156

157157
## Development & Build Commands
158158

159-
### Use `build` for Testing Build Output
159+
### Don't Build After Every Change
160160

161-
**The `dev` command does not end - it runs indefinitely in watch mode.**
161+
**Do not run builds after every change, especially for visual changes.**
162162

163-
When agents need to test build output or verify that the project builds successfully, use the `build` command instead of `dev`. The `build` command will complete and exit, making it suitable for automated testing and verification.
163+
This is a visual website, not a library. Assume changes work unless the user reports otherwise. Running builds after every change wastes time and context.
164164

165-
### Testing Limitations
165+
### Debugging Visual Issues
166166

167-
**Agents cannot run end-to-end tests without a headless browser.**
167+
When the user reports something doesn't work or look right:
168168

169-
This is a TanStack Start application that requires a browser environment to fully test. Agents can:
169+
1. Use the Playwright MCP to view the page and debug visually
170+
2. Use builds (`pnpm build`) only when investigating build/bundler issues
171+
3. Use TypeScript compilation (`pnpm tsc --noEmit`) for type errors
170172

171-
- ✅ Run TypeScript compilation (`pnpm tsc --noEmit`) to check for type errors
172-
- ✅ Run the build command (`pnpm build`) to verify the project builds successfully
173-
- ✅ Inspect build output and generated files
173+
### Use `build` for Build-Specific Issues
174174

175-
Agents cannot:
175+
**The `dev` command does not end, it runs indefinitely in watch mode.**
176176

177-
- ❌ Start the dev server and interact with the application (no headless browser)
178-
- ❌ Test UI functionality or user interactions
179-
- ❌ Verify runtime behavior in the browser
180-
- ❌ Test API endpoints that require browser context
177+
Only use `build` when:
181178

182-
For runtime testing and verification, developers should:
179+
- Investigating bundler or build-time errors
180+
- Verifying production output
181+
- The user specifically asks to verify the build
183182

184-
1. Review the code changes
185-
2. Start the dev server manually (`pnpm dev`)
186-
3. Test the functionality in a browser
183+
### Testing with Playwright
184+
185+
**Use the Playwright MCP for visual debugging and verification.**
186+
187+
When debugging issues or verifying visual changes work correctly:
188+
189+
- Navigate to the relevant page using Playwright
190+
- Take snapshots or screenshots to verify the UI
191+
- Interact with elements to test functionality
192+
193+
This is the preferred method for verifying visual changes since this is a visual site.
187194

188195
## UI Style Guide 2026
189196

src/components/DocsLayout.tsx

Lines changed: 109 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,102 @@ import { FrameworkSelect, useCurrentFramework } from './FrameworkSelect'
1919
import { VersionSelect } from './VersionSelect'
2020
import { Card } from './Card'
2121

22+
// Mobile partners strip - inline in the docs toggle bar
23+
function MobilePartnersStrip({
24+
partners,
25+
onLabelClick,
26+
}: {
27+
partners: Array<{
28+
name: string
29+
href: string
30+
image: Parameters<typeof PartnerImage>[0]['config']
31+
}>
32+
onLabelClick?: () => void
33+
}) {
34+
const innerRef = React.useRef<HTMLDivElement>(null)
35+
const [isHovered, setIsHovered] = React.useState(false)
36+
37+
React.useEffect(() => {
38+
const inner = innerRef.current
39+
if (!inner) return
40+
41+
let animationId: number
42+
let timeoutId: ReturnType<typeof setTimeout>
43+
let scrollPosition = 0
44+
const scrollSpeed = 0.08 // pixels per frame - very slow
45+
const startDelay = 4000 // wait 4 seconds before starting
46+
47+
const animate = () => {
48+
if (!isHovered && inner) {
49+
scrollPosition += scrollSpeed
50+
// Reset when we've scrolled past the first set
51+
if (scrollPosition >= inner.scrollWidth / 2) {
52+
scrollPosition = 0
53+
}
54+
inner.style.transform = `translateX(${-scrollPosition}px)`
55+
}
56+
animationId = requestAnimationFrame(animate)
57+
}
58+
59+
timeoutId = setTimeout(() => {
60+
animationId = requestAnimationFrame(animate)
61+
}, startDelay)
62+
63+
return () => {
64+
clearTimeout(timeoutId)
65+
cancelAnimationFrame(animationId)
66+
}
67+
}, [isHovered])
68+
69+
return (
70+
<div
71+
className="flex-1 flex items-center gap-2 min-w-0"
72+
onClick={(e) => e.preventDefault()}
73+
>
74+
<button
75+
type="button"
76+
className="text-[9px] uppercase tracking-wide font-medium text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-400 shrink-0"
77+
onClick={(e) => {
78+
e.stopPropagation()
79+
onLabelClick?.()
80+
}}
81+
>
82+
Partners
83+
</button>
84+
<div
85+
className="relative flex-1 overflow-hidden min-w-0"
86+
onMouseEnter={() => setIsHovered(true)}
87+
onMouseLeave={() => setIsHovered(false)}
88+
onTouchStart={() => setIsHovered(true)}
89+
onTouchEnd={() => setIsHovered(false)}
90+
>
91+
<div className="overflow-hidden">
92+
<div
93+
ref={innerRef}
94+
className="flex items-center gap-4 w-max py-1 will-change-transform"
95+
>
96+
{/* Duplicate partners for seamless loop */}
97+
{[...partners, ...partners].map((partner, i) => (
98+
<a
99+
key={`${partner.name}-${i}`}
100+
href={partner.href}
101+
target="_blank"
102+
rel="noreferrer"
103+
className="shrink-0 flex items-center opacity-50 hover:opacity-100 transition-opacity"
104+
onClick={(e) => e.stopPropagation()}
105+
>
106+
<div className="h-4 flex items-center [&_img]:h-full [&_img]:w-auto [&_div]:h-full">
107+
<PartnerImage config={partner.image} alt={partner.name} />
108+
</div>
109+
</a>
110+
))}
111+
</div>
112+
</div>
113+
</div>
114+
</div>
115+
)
116+
}
117+
22118
// Component for the collapsed menu strip showing box indicators
23119
// Minimap style: boxes with flex height filling available vertical space
24120
function DocsMenuStrip({
@@ -496,11 +592,21 @@ export function DocsLayout({
496592
id="docs-details"
497593
className="border-b border-gray-500/20"
498594
>
499-
<summary className="py-2 px-4 flex gap-2 items-center justify-between">
500-
<div className="flex-1 flex gap-4 items-center">
595+
<summary className="py-2 px-4 flex gap-2 items-center">
596+
<div className="flex gap-2 items-center shrink-0 pr-2">
501597
<Menu className="cursor-pointer" />
502-
Documentation
598+
Docs
503599
</div>
600+
<div className="w-px h-6 bg-gray-300 dark:bg-gray-600 shrink-0" />
601+
<MobilePartnersStrip
602+
partners={activePartners}
603+
onLabelClick={() => {
604+
const details = detailsRef.current as HTMLDetailsElement | null
605+
if (details) {
606+
details.open = !details.open
607+
}
608+
}}
609+
/>
504610
</summary>
505611
<div className="flex flex-col gap-4 p-4 overflow-y-auto border-t border-gray-500/20 bg-white/20 text-lg dark:bg-black/20">
506612
<div className="flex flex-col gap-1">

src/components/TrustedByMarquee.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ export function TrustedByMarquee({
3636
Trusted in Production by
3737
</div>
3838
<div className="relative w-full">
39+
{/* Left fade */}
40+
<div className="absolute left-0 top-0 bottom-0 w-16 bg-gradient-to-r from-white dark:from-black to-transparent z-10 pointer-events-none" />
41+
{/* Right fade */}
42+
<div className="absolute right-0 top-0 bottom-0 w-16 bg-gradient-to-l from-white dark:from-black to-transparent z-10 pointer-events-none" />
3943
<div
4044
className="flex gap-8 items-center whitespace-nowrap will-change-transform animate-[marquee_linear_infinite]"
4145
style={{

0 commit comments

Comments
 (0)