refactor(home): redesign hackathon top bar with aurora gradient style#72
refactor(home): redesign hackathon top bar with aurora gradient style#72
Conversation
📝 WalkthroughWalkthrough主页新增英雄轮播组件,展示五条排序后的活动数据;导航栏文本添加防换行处理;引入配套样式模块定义轮播UI布局与响应式行为。 Changes
Sequence Diagram(s)sequenceDiagram
participant Page as 首页
participant HeroCarousel as HeroCarousel组件
participant API as Activity API
participant LarkImage as LarkImage
participant DOM as 浏览器
Page->>HeroCarousel: 挂载组件
activate HeroCarousel
HeroCarousel->>API: 获取活动数据
activate API
API-->>HeroCarousel: 返回活动列表
deactivate API
HeroCarousel->>HeroCarousel: 按 startTime 排序<br/>取前5条
HeroCarousel->>DOM: 初始化 ResizeObserver<br/>测量导航栏高度
activate DOM
DOM-->>HeroCarousel: 返回高度值
deactivate DOM
HeroCarousel->>HeroCarousel: 设置 CSS 变量<br/>--nav-height
loop 每条活动
HeroCarousel->>LarkImage: 加载活动图片
activate LarkImage
LarkImage-->>HeroCarousel: 图片加载完成
deactivate LarkImage
end
HeroCarousel->>DOM: 渲染轮播卡片<br/>含徽章、日期、位置、按钮
HeroCarousel->>DOM: 监听窗口resize事件
deactivate HeroCarousel
DOM-->>Page: 显示英雄轮播
Estimated code review effort🎯 3 (中等) | ⏱️ ~20 分钟 审查重点:
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ 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)
⚔️ Resolve merge conflicts
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. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pages/index.tsx`:
- Line 69: Replace the hardcoded event name inside the span (className
styles.hackathonTopBarEventName) with a call to the i18n t() function and add a
corresponding translation key (e.g. "hackathon.eventName") to your locale files;
update pages/index.tsx to use t('hackathon.eventName') (or the chosen key) so
the visible text is localized and ensure the new key exists in all supported
locales.
- Around line 57-84: The top-banner uses raw HTML elements and a hard-coded
event name; replace the <aside>/<div>/<a>/<button>/<span> usage with React
Bootstrap components (e.g., Alert, Card, Button/CloseButton) while preserving
behavior tied to isHackathonTopBarVisible, hackathonTopBarStyle,
HackathonTopBarLink and the closeHackathonTopBar handler, and move the literal
"Labor AI Hackathon 2026" into i18n as home_hackathon_top_bar_event_name,
rendering it via t('home_hackathon_top_bar_event_name') alongside the existing
t('home_hackathon_top_bar_title'), t('home_hackathon_top_bar_description') and
t('home_hackathon_top_bar_action'); ensure aria-label/title still use
t('home_hackathon_top_bar_aria_label') and t('home_hackathon_top_bar_close').
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: afcd89f5-d1e7-40d2-bfc2-16789a915968
⛔ Files ignored due to path filters (6)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml,!pnpm-lock.yamland included by nonestyles/Home.module.lessis excluded by none and included by nonestyles/globals.cssis excluded by none and included by nonetranslation/en-US.tsis excluded by none and included by nonetranslation/zh-CN.tsis excluded by none and included by nonetranslation/zh-TW.tsis excluded by none and included by none
📒 Files selected for processing (1)
pages/index.tsx
| home_hackathon_top_bar_aria_label: 'Labor AI Hackathon 2026 活动宣传', | ||
| home_hackathon_top_bar_badge: '活动招募中', | ||
| home_hackathon_top_bar_title: '打工人の终极反叛', | ||
| home_hackathon_top_bar_description: '48 小时,来这里创造未来', |
There was a problem hiding this comment.
There was a problem hiding this comment.
我尝试了代码复用,但是没有拿到 name 的数据,麻烦 @TechQuery 再帮我看一下代码
- Replace raw HTML with Alert, Alert.Link, CloseButton - Move complex styles from styled-jsx to PromoBar.module.less - Add Bootstrap utility classes for layout - Fetch activity name/link from Lark Bitable via ActivityModel - Keep localStorage persistence commented out
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/Activity/PromoBar.tsx`:
- Around line 41-50: 在 useEffect 中对异步加载添加卸载保护:在 useEffect 里创建一个标志(例如 let mounted
= true 或使用 AbortController 如果 ActivityModel.getOne 支持取消),在 getOne
调用前保留该标志/控制器,并在 Promise resolve 前检查 mounted,只有 mounted 为 true 时才调用 setActivity;在
useEffect 返回的 cleanup 函数将 mounted 设为 false(或调用
controller.abort())以阻止组件卸载后继续更新或消耗请求资源。确保引用的符号为 useEffect、ActivityModel.getOne 和
setActivity。
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4a3b4906-bb3f-4728-9d11-7adac39ad05a
📒 Files selected for processing (4)
components/Activity/PromoBar.module.lesscomponents/Activity/PromoBar.tsxcomponents/Navigator/MainNavigator.tsxpages/index.tsx
✅ Files skipped from review due to trivial changes (3)
- pages/index.tsx
- components/Activity/PromoBar.module.less
- components/Navigator/MainNavigator.tsx
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/Activity/HeroCarousel.module.less`:
- Around line 14-47: The stylelint error is caused by CSS Modules :global()
being treated as an unknown pseudo-class; update the linter config to allow it
by adding an exception for "global" to the selector-pseudo-class-no-unknown rule
(e.g., set "selector-pseudo-class-no-unknown": ["error", {
"ignorePseudoClasses": ["global"] }]) in .stylelintrc.json, or alternatively
enable Less parsing by adding customSyntax: "postcss-less" (and installing
postcss-less) so Less files and CSS Modules :global() are recognized; adjust the
config rather than changing components/Activity/HeroCarousel.module.less.
In `@components/Activity/HeroCarousel.tsx`:
- Around line 73-90: The useEffect in HeroCarousel (the async IIFE that
constructs ActivityModel, calls model.getAll(), filters/sorts/slices to
MAX_ITEMS and then calls setActivities) can update state after the component
unmounts; add an unmount guard: declare a local mounted/isActive flag before the
async work, check it before calling setActivities, and set the flag to false in
the effect cleanup returned function so the state update is skipped when the
component has been unmounted.
- Around line 103-107: Replace the native <section> root in HeroCarousel.tsx
with React Bootstrap's Container using as="section" to preserve semantics;
update the JSX root where the element with className={`${styles.heroCarousel}
position-relative`} aria-label={t('home_hackathon_top_bar_aria_label')}
style={heroStyle} is defined to use <Container as="section" ...> and the
matching closing tag, and add the Container import from 'react-bootstrap' if
missing so the component follows the project's React Bootstrap convention.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d51b74c7-7d3b-4b10-b791-0270372de013
⛔ Files ignored due to path filters (1)
tsconfig.jsonis excluded by none and included by none
📒 Files selected for processing (3)
components/Activity/HeroCarousel.module.lesscomponents/Activity/HeroCarousel.tsxpages/index.tsx
✅ Files skipped from review due to trivial changes (1)
- pages/index.tsx
| :global(.carousel-inner) { | ||
| min-height: inherit; | ||
| } | ||
|
|
||
| :global(.carousel-item) { | ||
| min-height: inherit; | ||
| } | ||
|
|
||
| :global(.carousel-indicators) { | ||
| right: auto; | ||
| bottom: 2rem; | ||
| left: 1rem; | ||
| justify-content: flex-start; | ||
| margin: 0; | ||
| } | ||
|
|
||
| :global(.carousel-indicators button) { | ||
| opacity: 0.9; | ||
| margin: 0 0.45rem 0 0; | ||
| border: 0; | ||
| border-radius: 999px; | ||
| background-color: rgb(255 255 255 / 65%); | ||
| width: 2.25rem; | ||
| height: 0.32rem; | ||
| } | ||
|
|
||
| :global(.carousel-control-prev), | ||
| :global(.carousel-control-next) { | ||
| opacity: 0.95; | ||
| width: 6rem; | ||
| } | ||
|
|
||
| :global(.carousel-control-prev-icon), | ||
| :global(.carousel-control-next-icon) { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== 查找 stylelint 配置文件 =="
fd -HI -i 'stylelint' .
fd -HI -i '.stylelintrc' .
echo
echo "== 检查是否已允许 :global 或配置了 less/css-modules 语法 =="
rg -n --iglob '*stylelint*' 'selector-pseudo-class-no-unknown|ignorePseudoClasses|global|customSyntax|postcss-less|css-modules'Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 267
🏁 Script executed:
cat .stylelintrc.jsonRepository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 1451
🏁 Script executed:
# Check if postcss-less is in package.json
rg -A 2 -B 2 'postcss-less' package.json
# Check for .stylelintignore
cat .stylelintignore 2>/dev/null || echo ".stylelintignore not found"
# Check package.json for stylelint and postcss dependencies
rg 'stylelint|postcss' package.json | head -20Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 118
🏁 Script executed:
# Check if HeroCarousel.module.less exists
ls -la components/Activity/HeroCarousel.module.less 2>/dev/null && echo "File exists" || echo "File not found"
# Check package.json for stylelint scripts and dependencies
head -200 package.json | grep -A 5 -B 5 'stylelint\|scripts'
# Search for any mentions of less or module in package.json
rg 'less|module' package.json | head -20Repository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 806
🏁 Script executed:
head -50 components/Activity/HeroCarousel.module.lessRepository: Open-Source-Bazaar/Open-Source-Bazaar.github.io
Length of output: 1032
stylelint 配置缺失 CSS Modules :global() 支持,需调整规则
components/Activity/HeroCarousel.module.less 的第 14-47 行和 123-137 行使用了 CSS Modules 的 :global() 选择器来样式化 Bootstrap carousel 组件。当前 .stylelintrc.json 扩展的是 stylelint-config-standard-scss,未配置 Less 语法支持或为 selector-pseudo-class-no-unknown 规则添加 ignorePseudoClasses: ['global'] 设置。这会导致 stylelint 将 :global() 视为未知伪类,触发 lint 错误并阻塞 CI。
建议在 .stylelintrc.json 中修改:
"rules": {
"selector-pseudo-class-no-unknown": ["error", { "ignorePseudoClasses": ["global"] }]
}或配置 Less 语法支持(安装 postcss-less 并在配置中添加 customSyntax)。
🧰 Tools
🪛 Stylelint (17.9.0)
[error] 14-14: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)
(selector-pseudo-class-no-unknown)
[error] 18-18: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)
(selector-pseudo-class-no-unknown)
[error] 22-22: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)
(selector-pseudo-class-no-unknown)
[error] 30-30: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)
(selector-pseudo-class-no-unknown)
[error] 40-40: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)
(selector-pseudo-class-no-unknown)
[error] 41-41: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)
(selector-pseudo-class-no-unknown)
[error] 46-46: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)
(selector-pseudo-class-no-unknown)
[error] 47-47: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)
(selector-pseudo-class-no-unknown)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/Activity/HeroCarousel.module.less` around lines 14 - 47, The
stylelint error is caused by CSS Modules :global() being treated as an unknown
pseudo-class; update the linter config to allow it by adding an exception for
"global" to the selector-pseudo-class-no-unknown rule (e.g., set
"selector-pseudo-class-no-unknown": ["error", { "ignorePseudoClasses":
["global"] }]) in .stylelintrc.json, or alternatively enable Less parsing by
adding customSyntax: "postcss-less" (and installing postcss-less) so Less files
and CSS Modules :global() are recognized; adjust the config rather than changing
components/Activity/HeroCarousel.module.less.
| useEffect(() => { | ||
| (async () => { | ||
| try { | ||
| const model = new ActivityModel(); | ||
| const data = await model.getAll(); | ||
| const latestActivities = data | ||
| .filter(({ name }) => Boolean(name)) | ||
| .sort( | ||
| ({ startTime: left }, { startTime: right }) => timestampOf(right) - timestampOf(left), | ||
| ) | ||
| .slice(0, MAX_ITEMS); | ||
|
|
||
| setActivities(latestActivities); | ||
| } catch (err) { | ||
| console.error('Failed to load activities:', err); | ||
| } | ||
| })(); | ||
| }, []); |
There was a problem hiding this comment.
为异步加载 effect 增加卸载保护,避免卸载后更新状态
Line 73-90 的异步请求完成后直接 setActivities,组件已卸载时仍可能触发状态更新。建议加取消标记。
建议修改
useEffect(() => {
+ let cancelled = false;
(async () => {
try {
const model = new ActivityModel();
const data = await model.getAll();
const latestActivities = data
.filter(({ name }) => Boolean(name))
.sort(
({ startTime: left }, { startTime: right }) => timestampOf(right) - timestampOf(left),
)
.slice(0, MAX_ITEMS);
- setActivities(latestActivities);
+ if (!cancelled) setActivities(latestActivities);
} catch (err) {
console.error('Failed to load activities:', err);
}
})();
+
+ return () => {
+ cancelled = true;
+ };
}, []);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/Activity/HeroCarousel.tsx` around lines 73 - 90, The useEffect in
HeroCarousel (the async IIFE that constructs ActivityModel, calls
model.getAll(), filters/sorts/slices to MAX_ITEMS and then calls setActivities)
can update state after the component unmounts; add an unmount guard: declare a
local mounted/isActive flag before the async work, check it before calling
setActivities, and set the flag to false in the effect cleanup returned function
so the state update is skipped when the component has been unmounted.
| <section | ||
| className={`${styles.heroCarousel} position-relative`} | ||
| aria-label={t('home_hackathon_top_bar_aria_label')} | ||
| style={heroStyle} | ||
| > |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
根节点建议改为 React Bootstrap 组件承载语义 section
Line 103 当前使用原生 <section>,与组件层统一使用 React Bootstrap 的规范不一致。可用 Container 并通过 as="section" 保留语义。
建议修改
- <section
- className={`${styles.heroCarousel} position-relative`}
- aria-label={t('home_hackathon_top_bar_aria_label')}
- style={heroStyle}
- >
+ <Container
+ as="section"
+ fluid
+ className={`${styles.heroCarousel} position-relative`}
+ aria-label={t('home_hackathon_top_bar_aria_label')}
+ style={heroStyle}
+ >
...
- </section>
+ </Container>As per coding guidelines, “{pages,components}/**/*.tsx: ALWAYS use React Bootstrap components instead of custom HTML elements in UI code”.
📝 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.
| <section | |
| className={`${styles.heroCarousel} position-relative`} | |
| aria-label={t('home_hackathon_top_bar_aria_label')} | |
| style={heroStyle} | |
| > | |
| <Container | |
| as="section" | |
| fluid | |
| className={`${styles.heroCarousel} position-relative`} | |
| aria-label={t('home_hackathon_top_bar_aria_label')} | |
| style={heroStyle} | |
| > | |
| {/* content */} | |
| </Container> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/Activity/HeroCarousel.tsx` around lines 103 - 107, Replace the
native <section> root in HeroCarousel.tsx with React Bootstrap's Container using
as="section" to preserve semantics; update the JSX root where the element with
className={`${styles.heroCarousel} position-relative`}
aria-label={t('home_hackathon_top_bar_aria_label')} style={heroStyle} is defined
to use <Container as="section" ...> and the matching closing tag, and add the
Container import from 'react-bootstrap' if missing so the component follows the
project's React Bootstrap convention.
Checklist(清单):
pnpm-lock.yaml有变动导致eslint.config.ts:29的@next/next插件类型不匹配Summary by CodeRabbit
发布说明
新功能
改进