Skip to content

feat(ui): 改进 UI 组件审美和一致性 (P0)#2

Open
lora-sys wants to merge 1 commit intomainfrom
feat/ui-design-improvements
Open

feat(ui): 改进 UI 组件审美和一致性 (P0)#2
lora-sys wants to merge 1 commit intomainfrom
feat/ui-design-improvements

Conversation

@lora-sys
Copy link
Copy Markdown
Owner

@lora-sys lora-sys commented Mar 28, 2026

🎨 PR 概述

本次 PR 完成了前端 UI 组件的 P0 优先级审美改进,提升了整体视觉一致性和用户体验。


✅ 变更内容

1. 统一圆角规范

  • 将所有卡片组件的圆角从 rounded-lg 统一为 rounded-xl
  • 影响的组件:
    • Card 基础组件
    • FilterCarousel 筛选标签
    • VideoGridCard 视频网格卡片
    • VideoRowCard 视频行卡片
    • PlaylistCard 播放列表卡片
    • SystemPlaylistCard 系统播放列表卡片

2. 添加 Hover 交互效果

为所有卡片组件添加了统一的交互动画:

transition-all duration-200 hover:shadow-lg hover:-translate-y-0.5
  • 鼠标悬停时显示阴影效果
  • 轻微向上位移增强立体感
  • 200ms 过渡动画保证流畅体验

3. 优化颜色对比度

调整 globals.css 中的颜色变量以符合 WCAG 可访问性标准:

  • 浅色模式:--muted-foreground: 0 0% 42% (从 45.1% 调整)
  • 深色模式:--muted-foreground: 0 0% 60% (从 63.9% 调整)

4. 字体层级优化

  • 视频标题从 font-medium 改为 font-semibold
  • 增强标题视觉权重,提升可读性

5. Bug 修复

  • 修复类型导入错误 (VideoGetManyutputVideoGetManyOutput)
  • 修复 ESLint 未使用变量警告
  • 为 SearchInput 添加 Suspense 边界修复构建错误

📸 视觉效果对比

方面 改进前 改进后
圆角 rounded-lg (8px) rounded-xl (12px)
阴影 hover:shadow-lg
动画 200ms 位移 + 阴影
字体 font-medium font-semibold

🧪 测试

  • 构建通过 (npm run build)
  • TypeScript 类型检查通过
  • ESLint 检查通过

📁 修改的文件

app/globals.css
components/ui/card.tsx
components/ui/filter-carousel.tsx
components/ui/infinite-grid.tsx
moubles/videos/ui/components/video-grid-card.tsx
moubles/videos/ui/components/video-row-card.tsx
moubles/playlists/ui/components/playlist-card.tsx
moubles/playlists/ui/components/system-playlist-card.tsx
app/(home)/users/[userId]/page.tsx
moubles/home/ui/components/home-navbar/search-input.tsx
app/(home)/user/content/page.tsx

📌 相关链接

  • 分支:feat/ui-design-improvements

Summary by CodeRabbit

  • Style

    • Updated border radius styling across components for a more refined appearance.
    • Adjusted color values for improved text contrast.
    • Enhanced card hover effects with smooth transitions and shadow effects.
    • Updated typography weights and sizes for better visual hierarchy.
  • Bug Fixes

    • Corrected internal type naming inconsistencies.
  • Refactor

    • Improved search input component loading performance with better async handling.

P0 优先级改进:
- 统一所有卡片组件圆角为 rounded-xl (之前混用 rounded-lg)
- 为视频卡片和播放列表卡片添加 hover 阴影和位移动画效果
- 优化字体层级,视频标题使用 font-semibold
- 调整 muted-foreground 颜色对比度以符合 WCAG 标准

修复问题:
- 修复类型导入错误 (VideoGetManyutput -> VideoGetManyOutput)
- 修复 ESLint 未使用变量警告
- 为 SearchInput 添加 Suspense 边界修复构建错误
- 添加 force-dynamic 导出修复 user/content 页面

影响范围:
- globals.css (颜色变量)
- Card, FilterCarousel 基础组件
- VideoGridCard, VideoRowCard 视频卡片
- PlaylistCard, SystemPlaylistCard 播放列表卡片

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 28, 2026

📝 Walkthrough

Walkthrough

The pull request introduces styling refinements across multiple UI components (updating border radius from rounded-lg to rounded-xl), adjusts CSS theme variables for the muted-foreground color, restructures the SearchInput component to use a Suspense boundary, corrects a type name from VideoGetManyutput to VideoGetManyOutput, adds hover/transition effects to card components, updates Next.js rendering configuration, and cleans up error handling syntax.

Changes

Cohort / File(s) Summary
Next.js Route Configuration
app/(home)/user/content/page.tsx
Added export const dynamic = "force-dynamic"; to force dynamic rendering behavior for the user content page.
Error Handling Cleanup
app/(home)/users/[userId]/page.tsx
Removed unused error parameter from catch block in TRPC prefetch error handling; control flow unchanged.
CSS Theme Variables
app/globals.css
Updated --muted-foreground custom property values in both :root and .dark themes: light theme 45.1%42%, dark theme 63.9%60%.
Border Radius & Corner Updates
components/ui/card.tsx, components/ui/filter-carousel.tsx, moubles/playlists/ui/components/playlist-card.tsx, moubles/playlists/ui/components/system-playlist-card.tsx, moubles/videos/ui/components/video-grid-card.tsx, moubles/videos/ui/components/video-row-card.tsx
Updated component styling from rounded-lg / rounded-md to rounded-xl for card thumbnails and badges, increasing visual corner prominence.
Component Restructuring with Suspense
moubles/home/ui/components/home-navbar/search-input.tsx
Refactored SearchInput to wrap content in a Suspense boundary with a styled placeholder fallback; extracted original implementation into non-exported SearchInputContent component. No functional logic changes to search behavior.
Type Correction & Enhanced Interactivity
components/ui/infinite-grid.tsx, moubles/videos/ui/components/video-grid-card.tsx, moubles/videos/ui/components/video-row-card.tsx
Fixed type name VideoGetManyutputVideoGetManyOutput in video card components. Added transition and hover effects (transition-all duration-200 hover:shadow-lg hover:-translate-y-0.5) to card containers. Enhanced typography emphasis (font-mediumfont-semibold) in video titles. Updated error boundary to destructure only resetErrorBoundary.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 With whiskers twitching, corners rounded bright,
From lg to xl, the design takes flight!
Suspense now frames the search, a smoother way,
While shadows dance on hover—hip hooray!
Types corrected, themes refined with care,
Fresh interactions floating through the air! 🎨✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is vague and lacks specificity. It uses a generic term '改进 UI 组件审美和一致性' (improve UI component aesthetics and consistency) without clearly conveying the primary changes or scope. Consider making the title more specific by highlighting key changes like 'unify border radius to rounded-xl and add hover effects' or breaking it into more concrete language that clearly describes the main modifications.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ui-design-improvements

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/ui/infinite-grid.tsx (1)

122-122: ⚠️ Potential issue | 🟡 Minor

Corrupted character in comment.

The comment contains a Unicode replacement character (�). This should be fixed.

🔧 Proposed fix
-// 包装器组件,用于处理 Suspense �� ErrorBoundary
+// 包装器组件,用于处理 Suspense 和 ErrorBoundary
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/infinite-grid.tsx` at line 122, Fix the corrupted Unicode
character in the comment that currently reads "包装器组件,用于处理 Suspense ��
ErrorBoundary" by replacing the replacement character with the intended
character (e.g., "和") so the comment becomes "包装器组件,用于处理 Suspense 和
ErrorBoundary"; update the comment near the InfiniteGrid wrapper component (the
wrapper that handles Suspense and ErrorBoundary) to ensure no other Unicode
replacement characters remain.
🧹 Nitpick comments (5)
app/(home)/users/[userId]/page.tsx (1)

33-35: Consider logging or differentiating error types.

The catch clause correctly removes the unused error binding. However, catching all errors and redirecting to notFound() may obscure genuine bugs (e.g., network timeouts, database errors). Consider logging the error for observability before calling notFound().

💡 Optional: Add error logging
-  } catch {
+  } catch (error) {
+    console.error('Failed to load user page:', error);
     notFound();
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(home)/users/[userId]/page.tsx around lines 33 - 35, The catch block
currently swallows all errors and calls notFound(); update the catch to capture
the error (e.g., catch (err)) and log it to your application's logger or console
before calling notFound() so operational errors are observable; locate the catch
around the data-fetching in the page component and add a log call referencing
the captured error, then call notFound() as before.
components/ui/filter-carousel.tsx (1)

51-56: Pre-existing: Gradient backgrounds may not work in dark mode.

The fade gradients use hardcoded from-white and to-transparent, which will appear incorrectly in dark mode. Consider using theme-aware colors like from-background.

💡 Optional: Theme-aware gradient
-          "absolute left-12 top-0 bottom-0 w-12 z-10 bg-gradient-to-r from-white to-transparent pointer-events-none",
+          "absolute left-12 top-0 bottom-0 w-12 z-10 bg-gradient-to-r from-background to-transparent pointer-events-none",

Also applies to: 111-116

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/filter-carousel.tsx` around lines 51 - 56, The gradient uses
hardcoded light-theme colors which break in dark mode; update the gradient
classNames in the filter-carousel component (the left gradient div that uses
"from-white to-transparent" and the corresponding right gradient div around the
second occurrence) to use theme-aware tokens such as "from-background
to-transparent" (or the project's equivalent background token) so the fade
respects dark mode; locate the two divs that build the absolute gradient
overlays (they reference current and use cn(..., current === 1 && "hidden")) and
replace the hardcoded color tokens with the theme-aware ones.
components/ui/infinite-grid.tsx (1)

134-145: Consider logging the actual error for debugging.

While removing the unused error parameter satisfies ESLint, the actual error information is discarded. For observability, consider logging it before displaying the fallback UI.

💡 Optional: Log error for debugging
-        fallbackRender={({ resetErrorBoundary }) => (
+        fallbackRender={({ error, resetErrorBoundary }) => {
+          console.error('InfiniteGrid error:', error);
+          return (
           <div className="flex flex-col items-center justify-center py-20 text-muted-foreground">
             <p className="text-lg">{errorMessage}</p>
             <button
               onClick={resetErrorBoundary}
               className="mt-4 text-sm text-blue-500 hover:underline"
             >
               Try again
             </button>
           </div>
-        )}
+          );
+        }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/infinite-grid.tsx` around lines 134 - 145, The fallbackRender
currently discards the ErrorBoundary error parameter; update the fallbackRender
signature used in ErrorBoundary to accept the error (e.g. ({ error,
resetErrorBoundary })) and log the error before rendering the UI — for example
call console.error(error) or your app logger — then continue to show the same UI
using errorMessage and resetErrorBoundary so debugging info is preserved while
keeping the user-facing fallback unchanged.
moubles/videos/ui/components/video-row-card.tsx (2)

22-32: Typo in variable name: videoRowCardVarinats should be videoRowCardVariants.

The cva variable has a typo ("Varinats" instead of "Variants"). While this doesn't affect functionality, it impacts code searchability and consistency.

Proposed fix
-const videoRowCardVarinats = cva("group flex min-w-0 transition-all duration-200 hover:shadow-lg hover:-translate-y-0.5",{
+const videoRowCardVariants = cva("group flex min-w-0 transition-all duration-200 hover:shadow-lg hover:-translate-y-0.5",{

Also update usages at lines 59 and 136.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@moubles/videos/ui/components/video-row-card.tsx` around lines 22 - 32, Rename
the cva variable videoRowCardVarinats to videoRowCardVariants to fix the typo
and maintain consistency; update the declaration (const videoRowCardVarinats
...) and all usages of that identifier within this file (e.g., the places
currently referencing videoRowCardVarinats around the card render and className
application) to videoRowCardVariants so references match the new name.

22-22: Consider removing hover:-translate-y-0.5 from row cards.

While hover effects work well on grid cards, applying hover:-translate-y-0.5 to row-layout cards may cause visual jitter when users scan a vertical list. The translate effect can shift adjacent rows and create a disjointed experience, especially in compact mode where items are closer together.

Consider keeping transition-all duration-200 hover:shadow-lg but removing the translate:

Proposed adjustment
-const videoRowCardVarinats = cva("group flex min-w-0 transition-all duration-200 hover:shadow-lg hover:-translate-y-0.5",{
+const videoRowCardVarinats = cva("group flex min-w-0 transition-all duration-200 hover:shadow-lg",{
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@moubles/videos/ui/components/video-row-card.tsx` at line 22, The row-card
class variable videoRowCardVarinats currently includes a vertical translate on
hover ("hover:-translate-y-0.5") which causes visual jitter in list/row layouts;
update the cva call in videoRowCardVarinats to remove "hover:-translate-y-0.5"
while preserving "transition-all duration-200 hover:shadow-lg" so the hover
still animates shadow without shifting adjacent rows.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@moubles/home/ui/components/home-navbar/search-input.tsx`:
- Around line 70-76: The Suspense fallback in SearchInput currently renders a
single left-rounded rectangle that doesn't match the actual SearchInputContent
(which is a pill-shaped input with a right-side button); update the fallback
passed to Suspense so the skeleton visually matches the real component—either
replace the current element with a full pill (use rounded-full / full-width
pill) or render a two-part skeleton that mirrors the input plus button split
(left input skeleton + right button skeleton) so loading state and final UI
align.

---

Outside diff comments:
In `@components/ui/infinite-grid.tsx`:
- Line 122: Fix the corrupted Unicode character in the comment that currently
reads "包装器组件,用于处理 Suspense �� ErrorBoundary" by replacing the replacement
character with the intended character (e.g., "和") so the comment becomes
"包装器组件,用于处理 Suspense 和 ErrorBoundary"; update the comment near the InfiniteGrid
wrapper component (the wrapper that handles Suspense and ErrorBoundary) to
ensure no other Unicode replacement characters remain.

---

Nitpick comments:
In `@app/`(home)/users/[userId]/page.tsx:
- Around line 33-35: The catch block currently swallows all errors and calls
notFound(); update the catch to capture the error (e.g., catch (err)) and log it
to your application's logger or console before calling notFound() so operational
errors are observable; locate the catch around the data-fetching in the page
component and add a log call referencing the captured error, then call
notFound() as before.

In `@components/ui/filter-carousel.tsx`:
- Around line 51-56: The gradient uses hardcoded light-theme colors which break
in dark mode; update the gradient classNames in the filter-carousel component
(the left gradient div that uses "from-white to-transparent" and the
corresponding right gradient div around the second occurrence) to use
theme-aware tokens such as "from-background to-transparent" (or the project's
equivalent background token) so the fade respects dark mode; locate the two divs
that build the absolute gradient overlays (they reference current and use
cn(..., current === 1 && "hidden")) and replace the hardcoded color tokens with
the theme-aware ones.

In `@components/ui/infinite-grid.tsx`:
- Around line 134-145: The fallbackRender currently discards the ErrorBoundary
error parameter; update the fallbackRender signature used in ErrorBoundary to
accept the error (e.g. ({ error, resetErrorBoundary })) and log the error before
rendering the UI — for example call console.error(error) or your app logger —
then continue to show the same UI using errorMessage and resetErrorBoundary so
debugging info is preserved while keeping the user-facing fallback unchanged.

In `@moubles/videos/ui/components/video-row-card.tsx`:
- Around line 22-32: Rename the cva variable videoRowCardVarinats to
videoRowCardVariants to fix the typo and maintain consistency; update the
declaration (const videoRowCardVarinats ...) and all usages of that identifier
within this file (e.g., the places currently referencing videoRowCardVarinats
around the card render and className application) to videoRowCardVariants so
references match the new name.
- Line 22: The row-card class variable videoRowCardVarinats currently includes a
vertical translate on hover ("hover:-translate-y-0.5") which causes visual
jitter in list/row layouts; update the cva call in videoRowCardVarinats to
remove "hover:-translate-y-0.5" while preserving "transition-all duration-200
hover:shadow-lg" so the hover still animates shadow without shifting adjacent
rows.
🪄 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: ed9ab34f-abf1-435f-a838-5693c7290008

📥 Commits

Reviewing files that changed from the base of the PR and between 249884b and 9e2ee05.

📒 Files selected for processing (11)
  • app/(home)/user/content/page.tsx
  • app/(home)/users/[userId]/page.tsx
  • app/globals.css
  • components/ui/card.tsx
  • components/ui/filter-carousel.tsx
  • components/ui/infinite-grid.tsx
  • moubles/home/ui/components/home-navbar/search-input.tsx
  • moubles/playlists/ui/components/playlist-card.tsx
  • moubles/playlists/ui/components/system-playlist-card.tsx
  • moubles/videos/ui/components/video-grid-card.tsx
  • moubles/videos/ui/components/video-row-card.tsx

Comment on lines +70 to 76
export const SearchInput = () => {
return (
<Suspense fallback={<div className="w-full max-w-[600px] h-10 bg-muted rounded-l-full" />}>
<SearchInputContent />
</Suspense>
);
}; No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fallback styling doesn't match the actual component.

The fallback uses rounded-l-full but the actual search bar has both a left-rounded input and a right-rounded button. This will cause a visual mismatch during loading:

  1. Fallback is a single rounded-left rectangle
  2. Actual component is a pill-shaped form with input + button

Consider matching the full pill shape or using a more representative skeleton:

Proposed fix for consistent fallback
-    <Suspense fallback={<div className="w-full max-w-[600px] h-10 bg-muted rounded-l-full" />}>
+    <Suspense fallback={<div className="w-full max-w-[600px] h-10 bg-muted rounded-full" />}>

Or for a more accurate representation matching the input+button split:

-    <Suspense fallback={<div className="w-full max-w-[600px] h-10 bg-muted rounded-l-full" />}>
+    <Suspense fallback={
+      <div className="flex w-full max-w-[600px]">
+        <div className="flex-1 h-10 bg-muted rounded-l-full border" />
+        <div className="w-14 h-10 bg-gray-100 rounded-r-full border border-l-0" />
+      </div>
+    }>
📝 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.

Suggested change
export const SearchInput = () => {
return (
<Suspense fallback={<div className="w-full max-w-[600px] h-10 bg-muted rounded-l-full" />}>
<SearchInputContent />
</Suspense>
);
};
export const SearchInput = () => {
return (
<Suspense fallback={<div className="w-full max-w-[600px] h-10 bg-muted rounded-full" />}>
<SearchInputContent />
</Suspense>
);
};
Suggested change
export const SearchInput = () => {
return (
<Suspense fallback={<div className="w-full max-w-[600px] h-10 bg-muted rounded-l-full" />}>
<SearchInputContent />
</Suspense>
);
};
export const SearchInput = () => {
return (
<Suspense fallback={
<div className="flex w-full max-w-[600px]">
<div className="flex-1 h-10 bg-muted rounded-l-full border" />
<div className="w-14 h-10 bg-gray-100 rounded-r-full border border-l-0" />
</div>
}>
<SearchInputContent />
</Suspense>
);
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@moubles/home/ui/components/home-navbar/search-input.tsx` around lines 70 -
76, The Suspense fallback in SearchInput currently renders a single left-rounded
rectangle that doesn't match the actual SearchInputContent (which is a
pill-shaped input with a right-side button); update the fallback passed to
Suspense so the skeleton visually matches the real component—either replace the
current element with a full pill (use rounded-full / full-width pill) or render
a two-part skeleton that mirrors the input plus button split (left input
skeleton + right button skeleton) so loading state and final UI align.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant