feat(docs): add Next.js-style landing page to the docs site#107
Conversation
Adds spec for replacing the docs home placeholder with a Next.js-style dark-only marketing landing page featuring hero, trust strip, three pillars, an 8-cell bento grid, how-it-works flow, and final CTA. Uses pure CSS keyframes + inline SVG animations, EN/CN i18n, and the existing Fumadocs HomeLayout.
… tiles, balanced headings
…ng, body line-clamp engages
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR implements a complete landing page for the ServerBee docs featuring a dark-mode-only, bilingual (English/Chinese) layout with scoped CSS animations, seven content sections, thirteen micro-animation components, and full i18n support. ChangesLanding Page Feature
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (10)
apps/docs/src/components/landing/animations/data-stream.tsx (1)
32-40: 💤 Low valueConsider using explicit
aria-hiddenvalue.Line 35 uses
aria-hiddenwithout an explicit value. While this works in React (treated astrue), the recommended practice is to usearia-hidden="true"oraria-hidden={true}for clarity and consistency.♻️ Suggested improvement
<span - aria-hidden + aria-hidden="true" className={`stream-particle absolute top-1/2 h-1.5 w-3 -translate-y-1/2 rounded-full ${colorClass}`} style={{ animationDelay: delay, animationDirection: reverse ? 'reverse' : 'normal' }} />🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/docs/src/components/landing/animations/data-stream.tsx` around lines 32 - 40, The Particle component uses a bare aria-hidden attribute which relies on JSX truthiness; update the JSX in function Particle to set aria-hidden explicitly (e.g., aria-hidden="true" or aria-hidden={true}) on the span so accessibility intent is clear and consistent with other components.apps/docs/src/components/landing/animations/color-ring.tsx (1)
2-3: ⚡ Quick winMove gradient calculation outside render path.
The
stopsarray andgradientstring are recalculated on every render, even though they never change. Extractstopsas a module constant and either computegradientonce at module scope or memoize it withuseMemo.⚡ Proposed fix to optimize gradient calculation
+const COLOR_STOPS = ['#ffb300', '#4cc9f0', '#22c55e', '#a855f7', '#ef4444', '#ffb300'] +const CONIC_GRADIENT = COLOR_STOPS.map((c, i) => `${c} ${(i / (COLOR_STOPS.length - 1)) * 360}deg`).join(', ') + export function ColorRingAnim() { - const stops = ['#ffb300', '#4cc9f0', '#22c55e', '#a855f7', '#ef4444', '#ffb300'] - const gradient = stops.map((c, i) => `${c} ${(i / (stops.length - 1)) * 360}deg`).join(', ') return ( <div aria-label="Animated demo of theme customization" className="flex h-full items-center justify-center" role="img" > <div className="relative"> - <div className="orbit-anim h-28 w-28 rounded-full" style={{ background: `conic-gradient(${gradient})` }} /> + <div className="orbit-anim h-28 w-28 rounded-full" style={{ background: `conic-gradient(${CONIC_GRADIENT})` }} />🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/docs/src/components/landing/animations/color-ring.tsx` around lines 2 - 3, The stops array and gradient string are being recalculated on every render; move stops to a module-level constant (e.g., const STOPS = ['#ffb300', ...]) and compute gradient once at module scope (e.g., const GRADIENT = STOPS.map(...).join(', ')) or, if you prefer to keep it near the component, wrap gradient computation in useMemo inside the ColorRing component so stops/gradient are not recomputed on each render; update any references from stops/gradient to the new STOPS/GRADIENT or memoized variable.apps/docs/src/components/landing/animations/alert-bell.tsx (1)
4-4: ⚡ Quick winMove static channels array outside component.
The
channelsarray is recreated on every render. Since it never changes, extract it as a module-level constant to avoid unnecessary allocations.⚡ Proposed fix to optimize array allocation
import { Bell } from 'lucide-react' +const ALERT_CHANNELS = ['Webhook', 'Telegram', 'Bark', 'Email', 'APNs'] + export function AlertBellAnim() { - const channels = ['Webhook', 'Telegram', 'Bark', 'Email', 'APNs'] return ( <div aria-label="Animated demo of multi-channel alerts" className="flex h-full flex-col items-center justify-center gap-3" role="img" > <div className="relative"> <Bell className="bell-shake h-9 w-9 text-amber-300" /> <span className="absolute -top-0.5 -right-0.5 h-2.5 w-2.5 rounded-full bg-red-500 ring-2 ring-zinc-950" /> </div> <div className="flex flex-wrap justify-center gap-1.5"> - {channels.map((c, i) => ( + {ALERT_CHANNELS.map((c, i) => (🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/docs/src/components/landing/animations/alert-bell.tsx` at line 4, The channels array is being recreated on every render inside the component; extract const channels = ['Webhook','Telegram','Bark','Email','APNs'] to a module-level constant (move it outside the AlertBell component) so it is allocated once, and update any references inside the AlertBell component to use that module-level identifier.apps/docs/src/components/landing/animations/monitor-dots.tsx (1)
2-8: ⚡ Quick winMove static probes array outside component.
The
probesarray and its objects are recreated on every render. Since this data never changes, extract it as a module-level constant to prevent unnecessary allocations.⚡ Proposed fix to optimize array allocation
+const MONITOR_PROBES = [ + { name: 'SSL', meta: 'expires in 73d' }, + { name: 'DNS', meta: 'A · CNAME' }, + { name: 'HTTP', meta: '200 · keyword OK' }, + { name: 'TCP', meta: ':443 · 18 ms' }, + { name: 'WHOIS', meta: 'renews 2027-04' } +] + export function MonitorDotsAnim() { - const probes = [ - { name: 'SSL', meta: 'expires in 73d' }, - { name: 'DNS', meta: 'A · CNAME' }, - { name: 'HTTP', meta: '200 · keyword OK' }, - { name: 'TCP', meta: ':443 · 18 ms' }, - { name: 'WHOIS', meta: 'renews 2027-04' } - ] return ( <div aria-label="Animated demo of service monitors" className="grid h-full grid-cols-1 gap-1.5 font-mono text-xs sm:grid-cols-2" role="img" > - {probes.map((p, i) => ( + {MONITOR_PROBES.map((p, i) => (🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/docs/src/components/landing/animations/monitor-dots.tsx` around lines 2 - 8, The probes array is recreated on every render; extract it as a module-level constant (e.g., const PROBES = [...]) outside the component in monitor-dots.tsx, replace the in-component "probes" usage with that constant, and ensure any references inside the component (map, lookup) now point to PROBES to avoid repeated allocations.apps/docs/src/components/landing/sections/hero.tsx (1)
23-23: ⚡ Quick winUse self-closing JSX syntax with space.
The
<br/>tag should follow JSX convention with a space before the closing slash.♻️ Proposed fix
- <br /> + <br />🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/docs/src/components/landing/sections/hero.tsx` at line 23, Replace the self-closing line break JSX tag `<br/>` with the space-before-slash form `<br />` in the Hero component JSX (locate the `<br/>` occurrence in apps/docs/src/components/landing/sections/hero.tsx) to follow JSX conventions; update the single tag instance used in the component render/return so it uses the spaced self-closing syntax.apps/docs/src/components/landing/sections/bento.tsx (1)
52-52: 💤 Low valueConsider using a className utility for cleaner concatenation.
The template literal for className concatenation works but could be cleaner with a utility like
clsxor acn()helper function, especially if more conditional classes are added later.💡 Alternative approach
If you add
clsxor have acn()utility:className={cn( "group flex flex-col gap-4 overflow-hidden rounded-2xl border border-white/10 bg-white/[0.02] p-5", "transition hover:border-amber-400/30 hover:bg-white/[0.04]", span )}Or keep the current approach if this is the only dynamic class.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/docs/src/components/landing/sections/bento.tsx` at line 52, The current JSX uses a template literal to build the className on the element (the long string including "group flex flex-col ..." concatenated with the span variable); switch this to a className utility for cleaner concatenation and easier conditional classes by replacing the template literal with a call to your project's helper (e.g., cn(...) or clsx(...)) passing the static class strings and the span variable as separate arguments; update the element in apps/docs/src/components/landing/sections/bento.tsx that currently sets className={`... ${span}`} to use cn(...) or clsx(...) so future conditional classes are simpler to add.apps/docs/src/components/landing/sections/trust-strip.tsx (1)
21-21: ⚡ Quick winProvide explicit boolean value for
aria-hidden.While React accepts boolean attributes without values, it's better practice to provide an explicit value for screen reader compatibility.
♻️ Proposed fix
- <Icon aria-hidden className="h-5 w-5 text-amber-400" /> + <Icon aria-hidden="true" className="h-5 w-5 text-amber-400" />🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/docs/src/components/landing/sections/trust-strip.tsx` at line 21, The Icon usage currently has a bare boolean attribute; update the Icon element to use an explicit boolean value for accessibility by changing aria-hidden to aria-hidden={true} (or {false} if the icon conveys meaningful information) on the Icon component so screen readers handle it correctly.docs/superpowers/plans/2026-05-15-docs-landing-page.md (2)
588-588: 💤 Low valueRemove redundant identity transform.
The
patternTransform="translate(0 0)"is an identity transform that has no effect. It can be safely removed to simplify the code.♻️ Proposed fix
- <pattern id="hex" width="40" height="46" patternUnits="userSpaceOnUse" patternTransform="translate(0 0)"> + <pattern id="hex" width="40" height="46" patternUnits="userSpaceOnUse">🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/superpowers/plans/2026-05-15-docs-landing-page.md` at line 588, Remove the redundant identity transform from the SVG pattern by deleting the patternTransform="translate(0 0)" attribute on the <pattern id="hex" width="40" height="46" patternUnits="userSpaceOnUse"> element; leaving the rest of the tag and its attributes unchanged simplifies the markup without altering rendering.
1088-1098: 💤 Low valueClarify or document the color selection logic.
Line 1094 uses
i % 7 === 5to determine amber vs green dot colors. With 18 total dots, this results in only 2 amber dots (at indices 5 and 12), which may not represent a realistic "packet loss" pattern.Consider documenting the intended pattern or using a more explicit array/set to define which indices should be amber to make the visualization intent clearer.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/superpowers/plans/2026-05-15-docs-landing-page.md` around lines 1088 - 1098, The current color selection uses the expression i % 7 === 5 inside the Array.from({ length: 18 }).map to pick amber vs green for the dots, which yields only indices 5 and 12 as amber and is unclear; replace that modulo heuristic with an explicit definition (e.g., a constant array or Set like amberIndices = new Set([/* indices */]) or namedPattern) and use amberIndices.has(i) in the map, or at minimum add a clear inline comment above the className="h-2 rounded-sm fade-cycle" render explaining the intended visual pattern so the purpose of the color distribution is explicit and easier to modify.docs/superpowers/specs/2026-05-15-docs-landing-page-design.md (1)
192-198: ⚡ Quick winClarify reduced-motion implementation strategy.
Line 197 states that
prefers-reduced-motion: reducewill "pause every keyframe animation" usinganimation-play-state: paused. This approach can leave animations in an incomplete or visually confusing state (e.g., a typewriter animation paused atwidth: 0would be invisible).Consider specifying that reduced-motion should either:
- Set
animation: noneand apply static end-state styles, or- Use
animation-duration: 0.001msto jump to the final frame immediatelyThis ensures users with motion sensitivity see complete, legible content rather than mid-animation frozen states.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/superpowers/specs/2026-05-15-docs-landing-page-design.md` around lines 192 - 198, The reduced-motion note currently says to pause animations via prefers-reduced-motion: reduce using animation-play-state: paused which can freeze content mid-animation (e.g., a typewriter keyframe); update the spec to instead specify one of two implementations: either set animation: none and provide explicit static end-state styles for components using keyframes (mentioning typewriter, carousel, and animated demo regions) so content is fully visible, or set animation-duration: 0.001ms (or equivalent) under prefers-reduced-motion to jump to the final keyframe instantly; reference the prefers-reduced-motion rule, animation-play-state, animation: none, and animation-duration when describing the change so implementers know which CSS properties to alter.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/docs/src/components/landing/animations/mini-dashboard.tsx`:
- Around line 38-63: The Ring component currently ignores the value prop when
rendering progress; calculate the circle circumference (2 * Math.PI * 34) and
compute an offset = circumference * (1 - value/100) and apply it to the animated
circle (the element with class "ring-anim") via strokeDashoffset (or by setting
a CSS custom property consumed by .ring-anim) instead of leaving a fixed visual
for all values; update the local dash/circumference calculation and set
strokeDasharray/strokeDashoffset on the circle in Ring to reflect the value
prop.
In `@docs/superpowers/plans/2026-05-15-docs-landing-page.md`:
- Around line 194-202: The reduced-motion media query currently forces tiny
durations which causes flicker; update the rule for the .serverbee-landing
selectors inside `@media` (prefers-reduced-motion: reduce) to fully disable
animations and transitions instead of using 0.001ms: set animation: none and
transition: none (remove or stop using animation-duration: 0.001ms and
transition-duration: 0.001ms and you can remove animation-iteration-count) so
all animations and transitions on .serverbee-landing, .serverbee-landing
*::before and .serverbee-landing *::after are effectively disabled for
reduced-motion users.
- Around line 27-61: The fenced code block containing the file structure diagram
in the docs landing page markdown is missing a language specifier; fix it by
changing the opening fence from ``` to ```text (so the block is explicitly
marked as plain text) in the 2026-05-15-docs-landing-page.md file where the
apps/docs/src/ tree is shown.
In `@docs/superpowers/specs/2026-05-15-docs-landing-page-design.md`:
- Around line 98-100: The fenced code block containing the install command lacks
a language specifier; update the block around the line with "curl -fsSL
https://serverbee.app/install.sh | sh" to use a shell/bash language identifier
(e.g., change the opening ``` to ```bash) so Markdown renderers highlight it
correctly.
- Around line 50-60: The fenced ASCII-art grid block lacks a language specifier;
update the opening fence (the triple backticks before the box diagram) to
include a language tag such as text or ascii (e.g., change ``` to ```text) so
Markdown renders it correctly and meets docs standards; ensure the closing fence
remains as three backticks and do not alter the ASCII content inside the block.
---
Nitpick comments:
In `@apps/docs/src/components/landing/animations/alert-bell.tsx`:
- Line 4: The channels array is being recreated on every render inside the
component; extract const channels = ['Webhook','Telegram','Bark','Email','APNs']
to a module-level constant (move it outside the AlertBell component) so it is
allocated once, and update any references inside the AlertBell component to use
that module-level identifier.
In `@apps/docs/src/components/landing/animations/color-ring.tsx`:
- Around line 2-3: The stops array and gradient string are being recalculated on
every render; move stops to a module-level constant (e.g., const STOPS =
['#ffb300', ...]) and compute gradient once at module scope (e.g., const
GRADIENT = STOPS.map(...).join(', ')) or, if you prefer to keep it near the
component, wrap gradient computation in useMemo inside the ColorRing component
so stops/gradient are not recomputed on each render; update any references from
stops/gradient to the new STOPS/GRADIENT or memoized variable.
In `@apps/docs/src/components/landing/animations/data-stream.tsx`:
- Around line 32-40: The Particle component uses a bare aria-hidden attribute
which relies on JSX truthiness; update the JSX in function Particle to set
aria-hidden explicitly (e.g., aria-hidden="true" or aria-hidden={true}) on the
span so accessibility intent is clear and consistent with other components.
In `@apps/docs/src/components/landing/animations/monitor-dots.tsx`:
- Around line 2-8: The probes array is recreated on every render; extract it as
a module-level constant (e.g., const PROBES = [...]) outside the component in
monitor-dots.tsx, replace the in-component "probes" usage with that constant,
and ensure any references inside the component (map, lookup) now point to PROBES
to avoid repeated allocations.
In `@apps/docs/src/components/landing/sections/bento.tsx`:
- Line 52: The current JSX uses a template literal to build the className on the
element (the long string including "group flex flex-col ..." concatenated with
the span variable); switch this to a className utility for cleaner concatenation
and easier conditional classes by replacing the template literal with a call to
your project's helper (e.g., cn(...) or clsx(...)) passing the static class
strings and the span variable as separate arguments; update the element in
apps/docs/src/components/landing/sections/bento.tsx that currently sets
className={`... ${span}`} to use cn(...) or clsx(...) so future conditional
classes are simpler to add.
In `@apps/docs/src/components/landing/sections/hero.tsx`:
- Line 23: Replace the self-closing line break JSX tag `<br/>` with the
space-before-slash form `<br />` in the Hero component JSX (locate the `<br/>`
occurrence in apps/docs/src/components/landing/sections/hero.tsx) to follow JSX
conventions; update the single tag instance used in the component render/return
so it uses the spaced self-closing syntax.
In `@apps/docs/src/components/landing/sections/trust-strip.tsx`:
- Line 21: The Icon usage currently has a bare boolean attribute; update the
Icon element to use an explicit boolean value for accessibility by changing
aria-hidden to aria-hidden={true} (or {false} if the icon conveys meaningful
information) on the Icon component so screen readers handle it correctly.
In `@docs/superpowers/plans/2026-05-15-docs-landing-page.md`:
- Line 588: Remove the redundant identity transform from the SVG pattern by
deleting the patternTransform="translate(0 0)" attribute on the <pattern
id="hex" width="40" height="46" patternUnits="userSpaceOnUse"> element; leaving
the rest of the tag and its attributes unchanged simplifies the markup without
altering rendering.
- Around line 1088-1098: The current color selection uses the expression i % 7
=== 5 inside the Array.from({ length: 18 }).map to pick amber vs green for the
dots, which yields only indices 5 and 12 as amber and is unclear; replace that
modulo heuristic with an explicit definition (e.g., a constant array or Set like
amberIndices = new Set([/* indices */]) or namedPattern) and use
amberIndices.has(i) in the map, or at minimum add a clear inline comment above
the className="h-2 rounded-sm fade-cycle" render explaining the intended visual
pattern so the purpose of the color distribution is explicit and easier to
modify.
In `@docs/superpowers/specs/2026-05-15-docs-landing-page-design.md`:
- Around line 192-198: The reduced-motion note currently says to pause
animations via prefers-reduced-motion: reduce using animation-play-state: paused
which can freeze content mid-animation (e.g., a typewriter keyframe); update the
spec to instead specify one of two implementations: either set animation: none
and provide explicit static end-state styles for components using keyframes
(mentioning typewriter, carousel, and animated demo regions) so content is fully
visible, or set animation-duration: 0.001ms (or equivalent) under
prefers-reduced-motion to jump to the final keyframe instantly; reference the
prefers-reduced-motion rule, animation-play-state, animation: none, and
animation-duration when describing the change so implementers know which CSS
properties to alter.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8418b149-d2ca-42ba-98e4-e9781a2d64f6
📒 Files selected for processing (30)
apps/docs/src/components/landing/animations/alert-bell.tsxapps/docs/src/components/landing/animations/color-ring.tsxapps/docs/src/components/landing/animations/data-stream.tsxapps/docs/src/components/landing/animations/docker-stack.tsxapps/docs/src/components/landing/animations/file-tree.tsxapps/docs/src/components/landing/animations/install-binary.tsxapps/docs/src/components/landing/animations/light-band.tsxapps/docs/src/components/landing/animations/mini-dashboard.tsxapps/docs/src/components/landing/animations/monitor-dots.tsxapps/docs/src/components/landing/animations/orbit-icons.tsxapps/docs/src/components/landing/animations/ping-chart.tsxapps/docs/src/components/landing/animations/terminal-demo.tsxapps/docs/src/components/landing/animations/upgrade-loop.tsxapps/docs/src/components/landing/index.tsxapps/docs/src/components/landing/primitives/code-copy.tsxapps/docs/src/components/landing/primitives/gradient-heading.tsxapps/docs/src/components/landing/primitives/hex-background.tsxapps/docs/src/components/landing/primitives/section.tsxapps/docs/src/components/landing/sections/bento.tsxapps/docs/src/components/landing/sections/final-cta.tsxapps/docs/src/components/landing/sections/hero.tsxapps/docs/src/components/landing/sections/how-it-works.tsxapps/docs/src/components/landing/sections/pillars.tsxapps/docs/src/components/landing/sections/trust-strip.tsxapps/docs/src/components/landing/translations.tsapps/docs/src/routes/$lang/index.tsxapps/docs/src/styles/app.cssapps/docs/src/styles/landing.cssdocs/superpowers/plans/2026-05-15-docs-landing-page.mddocs/superpowers/specs/2026-05-15-docs-landing-page-design.md
| function Ring({ label, value, color }: { label: string; value: number; color: string }) { | ||
| const dash = 220 | ||
| return ( | ||
| <div className="flex items-center gap-3 rounded-xl bg-white/[0.03] p-3"> | ||
| <svg aria-hidden="true" focusable="false" height="60" viewBox="0 0 80 80" width="60"> | ||
| <circle cx="40" cy="40" fill="none" r="34" stroke="rgba(255,255,255,0.08)" strokeWidth="8" /> | ||
| <circle | ||
| className="ring-anim" | ||
| cx="40" | ||
| cy="40" | ||
| fill="none" | ||
| r="34" | ||
| stroke={color} | ||
| strokeDasharray={dash} | ||
| strokeLinecap="round" | ||
| strokeWidth="8" | ||
| transform="rotate(-90 40 40)" | ||
| /> | ||
| </svg> | ||
| <div> | ||
| <div className="font-mono text-xs text-zinc-400">{label}</div> | ||
| <div className="font-semibold text-xl text-zinc-100">{value}%</div> | ||
| </div> | ||
| </div> | ||
| ) | ||
| } |
There was a problem hiding this comment.
Ring progress visualization doesn't use the value prop.
The Ring component accepts a value prop (e.g., 42, 61) but doesn't calculate strokeDashoffset to reflect that value. Currently, strokeDasharray is fixed at 220 and no strokeDashoffset is set, meaning both rings will display identically regardless of their different values.
For a proper progress ring, calculate and apply the offset:
const circumference = 2 * Math.PI * 34 // ≈ 213.6
const offset = circumference * (1 - value / 100)Then add strokeDashoffset={offset} to the animated circle, or apply it via the .ring-anim CSS class using a CSS custom property.
🔧 Proposed fix
function Ring({ label, value, color }: { label: string; value: number; color: string }) {
- const dash = 220
+ const radius = 34
+ const circumference = 2 * Math.PI * radius
+ const offset = circumference * (1 - value / 100)
return (
<div className="flex items-center gap-3 rounded-xl bg-white/[0.03] p-3">
<svg aria-hidden="true" focusable="false" height="60" viewBox="0 0 80 80" width="60">
<circle cx="40" cy="40" fill="none" r="34" stroke="rgba(255,255,255,0.08)" strokeWidth="8" />
<circle
className="ring-anim"
cx="40"
cy="40"
fill="none"
- r="34"
+ r={radius}
stroke={color}
- strokeDasharray={dash}
+ strokeDasharray={circumference}
+ strokeDashoffset={offset}
strokeLinecap="round"
strokeWidth="8"
transform="rotate(-90 40 40)"
/>
</svg>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/docs/src/components/landing/animations/mini-dashboard.tsx` around lines
38 - 63, The Ring component currently ignores the value prop when rendering
progress; calculate the circle circumference (2 * Math.PI * 34) and compute an
offset = circumference * (1 - value/100) and apply it to the animated circle
(the element with class "ring-anim") via strokeDashoffset (or by setting a CSS
custom property consumed by .ring-anim) instead of leaving a fixed visual for
all values; update the local dash/circumference calculation and set
strokeDasharray/strokeDashoffset on the circle in Ring to reflect the value
prop.
| ``` | ||
| apps/docs/src/ | ||
| routes/$lang/index.tsx # REWRITE (existed) | ||
| styles/landing.css # NEW — keyframes, scoped utilities | ||
| styles/app.css # MODIFY — import landing.css | ||
| components/landing/ | ||
| index.tsx # NEW — composes all sections | ||
| translations.ts # NEW — en/cn strings + install command constant | ||
| primitives/ | ||
| section.tsx # NEW | ||
| gradient-heading.tsx # NEW | ||
| code-copy.tsx # NEW | ||
| hex-background.tsx # NEW | ||
| sections/ | ||
| hero.tsx # NEW | ||
| trust-strip.tsx # NEW | ||
| pillars.tsx # NEW | ||
| bento.tsx # NEW | ||
| how-it-works.tsx # NEW | ||
| final-cta.tsx # NEW | ||
| animations/ | ||
| mini-dashboard.tsx # NEW | ||
| install-binary.tsx # NEW | ||
| data-stream.tsx # NEW | ||
| orbit-icons.tsx # NEW | ||
| ping-chart.tsx # NEW | ||
| terminal-demo.tsx # NEW | ||
| file-tree.tsx # NEW | ||
| docker-stack.tsx # NEW | ||
| color-ring.tsx # NEW | ||
| alert-bell.tsx # NEW | ||
| monitor-dots.tsx # NEW | ||
| upgrade-loop.tsx # NEW | ||
| light-band.tsx # NEW | ||
| ``` |
There was a problem hiding this comment.
Add language specifier to fenced code block.
The file structure diagram should specify a language identifier (use text or leave empty for plain text) to comply with Markdown best practices.
📝 Proposed fix
-```
+```text
apps/docs/src/
routes/$lang/index.tsx # REWRITE (existed)🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 27-27: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/superpowers/plans/2026-05-15-docs-landing-page.md` around lines 27 - 61,
The fenced code block containing the file structure diagram in the docs landing
page markdown is missing a language specifier; fix it by changing the opening
fence from ``` to ```text (so the block is explicitly marked as plain text) in
the 2026-05-15-docs-landing-page.md file where the apps/docs/src/ tree is shown.
| @media (prefers-reduced-motion: reduce) { | ||
| .serverbee-landing *, | ||
| .serverbee-landing *::before, | ||
| .serverbee-landing *::after { | ||
| animation-duration: 0.001ms !important; | ||
| animation-iteration-count: 1 !important; | ||
| transition-duration: 0.001ms !important; | ||
| } | ||
| } |
There was a problem hiding this comment.
Fix reduced-motion implementation to prevent animation flickering.
Lines 198-199 set animation-duration: 0.001ms !important for reduced motion. This can cause flickering as animations rapidly complete. The better approach is to disable animations entirely or set duration to 0s.
Additionally, setting transition-duration: 0.001ms can cause layout shifts. Consider using transition: none instead.
♻️ Proposed fix
`@media` (prefers-reduced-motion: reduce) {
.serverbee-landing *,
.serverbee-landing *::before,
.serverbee-landing *::after {
- animation-duration: 0.001ms !important;
+ animation: none !important;
animation-iteration-count: 1 !important;
- transition-duration: 0.001ms !important;
+ transition: none !important;
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @media (prefers-reduced-motion: reduce) { | |
| .serverbee-landing *, | |
| .serverbee-landing *::before, | |
| .serverbee-landing *::after { | |
| animation-duration: 0.001ms !important; | |
| animation-iteration-count: 1 !important; | |
| transition-duration: 0.001ms !important; | |
| } | |
| } | |
| `@media` (prefers-reduced-motion: reduce) { | |
| .serverbee-landing *, | |
| .serverbee-landing *::before, | |
| .serverbee-landing *::after { | |
| animation: none !important; | |
| animation-iteration-count: 1 !important; | |
| transition: none !important; | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/superpowers/plans/2026-05-15-docs-landing-page.md` around lines 194 -
202, The reduced-motion media query currently forces tiny durations which causes
flicker; update the rule for the .serverbee-landing selectors inside `@media`
(prefers-reduced-motion: reduce) to fully disable animations and transitions
instead of using 0.001ms: set animation: none and transition: none (remove or
stop using animation-duration: 0.001ms and transition-duration: 0.001ms and you
can remove animation-iteration-count) so all animations and transitions on
.serverbee-landing, .serverbee-landing *::before and .serverbee-landing *::after
are effectively disabled for reduced-motion users.
| ``` | ||
| ┌──────────────────────────┬──────────────┬──────────────┐ | ||
| │ Network quality (2x2) │ Themes 1x1 │ Alerts 1x1 │ | ||
| │ ├──────────────┼──────────────┤ | ||
| │ │ Service monitors 1x2 │ | ||
| ├──────────────────────────┼──────────────┴──────────────┤ | ||
| │ Web Terminal (2x2) │ File Manager 1x2 │ | ||
| │ ├──────────────┬──────────────┤ | ||
| │ │ Docker 1x1 │ Auto-upgrade │ | ||
| └──────────────────────────┴──────────────┴──────────────┘ | ||
| ``` |
There was a problem hiding this comment.
Add language specifier to fenced code block.
The ASCII art grid visualization should specify a language identifier (use text or ascii) to improve Markdown rendering and comply with documentation standards.
📝 Proposed fix
-```
+```text
┌──────────────────────────┬──────────────┬──────────────┐
│ Network quality (2x2) │ Themes 1x1 │ Alerts 1x1 │📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ``` | |
| ┌──────────────────────────┬──────────────┬──────────────┐ | |
| │ Network quality (2x2) │ Themes 1x1 │ Alerts 1x1 │ | |
| │ ├──────────────┼──────────────┤ | |
| │ │ Service monitors 1x2 │ | |
| ├──────────────────────────┼──────────────┴──────────────┤ | |
| │ Web Terminal (2x2) │ File Manager 1x2 │ | |
| │ ├──────────────┬──────────────┤ | |
| │ │ Docker 1x1 │ Auto-upgrade │ | |
| └──────────────────────────┴──────────────┴──────────────┘ | |
| ``` |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 50-50: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/superpowers/specs/2026-05-15-docs-landing-page-design.md` around lines
50 - 60, The fenced ASCII-art grid block lacks a language specifier; update the
opening fence (the triple backticks before the box diagram) to include a
language tag such as text or ascii (e.g., change ``` to ```text) so Markdown
renders it correctly and meets docs standards; ensure the closing fence remains
as three backticks and do not alter the ASCII content inside the block.
| ``` | ||
| curl -fsSL https://serverbee.app/install.sh | sh | ||
| ``` |
There was a problem hiding this comment.
Add language specifier to fenced code block.
The install command code block should specify a language identifier (use bash or shell) to improve Markdown rendering.
📝 Proposed fix
-```
+```bash
curl -fsSL https://serverbee.app/install.sh | sh
```📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ``` | |
| curl -fsSL https://serverbee.app/install.sh | sh | |
| ``` |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 98-98: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/superpowers/specs/2026-05-15-docs-landing-page-design.md` around lines
98 - 100, The fenced code block containing the install command lacks a language
specifier; update the block around the line with "curl -fsSL
https://serverbee.app/install.sh | sh" to use a shell/bash language identifier
(e.g., change the opening ``` to ```bash) so Markdown renderers highlight it
correctly.
Summary
/$lang) with a dark-only, Next.js-style marketing landing page rendered inside the existing FumadocsHomeLayout.translations.ts; no new runtime deps..serverbee-landingso the docs reader is unaffected; landing CSS budget kept small.prefers-reduced-motionfallback,aria-label/role="img"on every animated demo, visible focus rings; fully responsive and SSR-safe.Notable polish
Test plan
bun run typecheckpasses (apps/docs)bun x ultracite check srcpasses (apps/docs)bun run build(production SSR build) succeeds/enand/cnrender every section with translated copy/en/docs/quick-start) still show the theme toggleprefers-reduced-motion: reduceshows no motion and no broken layoutSummary by CodeRabbit