-
-
Notifications
You must be signed in to change notification settings - Fork 1k
feat: adding casestudies, blogs, tsc, roadmap, events, ambassadors page, tools page and community page #4553
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: website-ui
Are you sure you want to change the base?
Conversation
✅ Deploy Preview for asyncapi-website ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThis PR implements extensive website modernization including a Code of Conduct 3.0 update, dark mode support across UI components, new community-focused components (FeatureCard, TSCMemberCard, ToolingCard, UpcomingEventsSection), major page refactors with pagination/filtering (blog, case studies, community, TSC, ambassadors), new icon components, and configuration updates for community meetings and events. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Page as Blog/Case Studies Page
participant Filter as Filter Component
participant State as Page State
participant Render as Render
User->>Page: Visit page / click filter
Page->>Filter: Pass onFilter callback
User->>Filter: Select filter option
Filter->>Page: onFilter(selected)
Page->>State: Update filters, reset pagination
State->>Page: Re-compute filtered/paginated posts
Page->>Render: Re-render with currentPosts
Render->>User: Display filtered page 1
User->>Page: Click next page / select tab
Page->>State: Update currentPage/activeTab
State->>Page: Compute page slice
Page->>Render: Render new slice
Render->>User: Display new page content
sequenceDiagram
participant ToolingsShowcase
participant ToolData as Tool Selection State
participant ToolingCard
participant UI as Rendered Card
ToolingsShowcase->>ToolData: Mount with first tool selected
User->>ToolingsShowcase: Click tool tag
ToolingsShowcase->>ToolData: Set isShuffling = true
ToolData->>ToolingCard: Pass isShuffling prop
ToolingCard->>UI: Apply rotation/translation animation
ToolingsShowcase->>ToolingsShowcase: After 300ms timeout
ToolingsShowcase->>ToolData: Update selected tool + isShuffling = false
ToolData->>ToolingCard: Pass new tool data
ToolingCard->>UI: Render new tool, clear animation
UI->>User: Display selected tool
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Areas requiring extra attention:
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
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 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## website-ui #4553 +/- ##
==============================================
Coverage ? 100.00%
==============================================
Files ? 22
Lines ? 778
Branches ? 144
==============================================
Hits ? 778
Misses ? 0
Partials ? 0 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@Shriya-Chauhan Starting to review this, will complete the review by tomorrow. |
|
On load, the dark mode should appear instantly instead of showing a transition from light mode: Screen.Recording.2025-11-13.224339.mp4 |
|
The font colour of the author's name in the case study and the title of the TOC should be changed to something lighter in the dark mode. Also, the colour of the left border of the TOC is very sharp as compared to background in the dark mode, please change it.
ref: https://deploy-preview-4553--asyncapi-website.netlify.app/casestudies/adeogroup |
|
The go-to page dropdown on the case studies page is not working: Screen.Recording.2025-11-13.225426.mp4 |
|
The pagination "go to page" dropdown should not go out of the screen, keep the height restricted to 3-4 options. Also, increase the width of the box and the dropdown to make the content fit correctly.
ref: https://deploy-preview-4553--asyncapi-website.netlify.app/blog |
|
On the roadmap page, the collapsible arrow and background colors are too similar in dark mode, making them hard to see.
ref: https://deploy-preview-4553--asyncapi-website.netlify.app/roadmap |
|
In the Events & Update Page, the event title text and background colors are too similar in dark mode, making them hard to read.
ref: https://deploy-preview-4553--asyncapi-website.netlify.app/community/events-and-updates |
|
The Subscribe to Newsletter section is incomplete on the "Ambassadors" page. Ideally, you should use a single "Subscribe to Newsletter" component on all pages to avoid inconsistencies.
ref: https://deploy-preview-4553--asyncapi-website.netlify.app/community/ambassadors |
|
The community Dashboard page is incomplete for dark mode (headings, Hot Topics, Good First Issues) topics.
ref: https://deploy-preview-4553--asyncapi-website.netlify.app/community/dashboard |
Signed-off-by: Shriya-Chauhan <[email protected]>
Signed-off-by: Shriya-Chauhan <[email protected]>
Signed-off-by: Shriya-Chauhan <[email protected]>
Signed-off-by: Shriya-Chauhan <[email protected]>
Signed-off-by: Shriya-Chauhan <[email protected]>
Signed-off-by: Shriya-Chauhan <[email protected]>
Signed-off-by: Shriya-Chauhan <[email protected]>
Signed-off-by: Shriya-Chauhan <[email protected]>
3e4a7e0 to
2f5d5b7
Compare
Signed-off-by: Shriya-Chauhan <[email protected]>
|
@CodeRabbit fullreview |
|
@devilkiller-ag: I'll conduct a full review of the PR. The review will cover all the changes including the newly added pages (Case Studies, Blogs, TSC, Roadmap, Events, Ambassadors, Tools, and Community) and will take into account the various styling and functionality issues you've already identified in your comments, particularly around dark mode compatibility, pagination, and incomplete sections. 🧠 Learnings used✅ Actions performedFull review triggered. |
That will take a lot of time. I have shared the per-page visual earlier. For the code part, I have triggered a full review by Coderabbit. In addition to that, I will go through all the files one by one.
The other PR was merged. Have you taken the latest pull from the base and master branches? |
yes |
|
i have started making changes
I have started working on the changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 20
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (7)
components/AlgoliaSearch.tsx (1)
288-318: MemoizeactionKeyto prevent unnecessary effect re-runs.Adding
actionKeyto the dependency array at Line 318 is correct in principle, butactionKeyis computed fromgetActionKey()on every render (Line 288), creating a new object reference each time. This causes the effect to run on every render, even though the underlying platform information never changes.Apply this diff to memoize
actionKey:+import React, { ..., useMemo, ... } from 'react'; export function SearchButton({ children, indexName = INDEX_NAME, ...props }: ISearchButtonProps) { const { onOpen, onInput } = useContext(SearchContext); const [Children, setChildren] = useState<string | React.ReactNode>(''); const searchButtonRef = useRef<HTMLButtonElement>(null); - const actionKey = getActionKey(); + const actionKey = useMemo(() => getActionKey(), []);components/Calendar.tsx (2)
56-61: Add dark mode text colors for event items.The event title and date text lack dark mode styling, which can cause poor contrast in dark mode. This aligns with the reported issue about event title text/background contrast being too similar in dark mode.
Apply this diff to add dark mode text colors:
<div className='grow text-left sm:mt-0 sm:pl-6'> - <h2 className='title-font font-medium text-gray-900 hover:text-gray-500'>{event.title}</h2> - <p className='text-gray-600'> + <h2 className='title-font font-medium text-gray-900 dark:text-gray-100 hover:text-gray-500 dark:hover:text-gray-400'>{event.title}</h2> + <p className='text-gray-600 dark:text-gray-400'> {moment(event.date).local().format('LLLL')} UTC {moment(event.date).local().format('Z')} </p>
67-67: Add dark mode text color for "no meetings" message.The message text lacks dark mode styling, which can cause poor contrast in dark mode.
Apply this diff:
- {!eventsExist && <div className='font-bold text-gray-700 lg:pb-8'>{t('calendar.noMeetingsMessage')}</div>} + {!eventsExist && <div className='font-bold text-gray-700 dark:text-gray-300 lg:pb-8'>{t('calendar.noMeetingsMessage')}</div>}components/tools/ToolsDashboard.tsx (1)
153-167: Guard against missing hash targets in tools hash‑scroll effectWith the dependency array updated to
[toolsList], this effect will rerun on filter changes, which is good. However,const element = toolsList[elementID]?.elementRef!;can still beundefinedfor an unknown hash, and the subsequentif (element.current)will then throw. Suggest a small guard:- if (hash) { - const elementID = decodeURIComponent(hash.slice(1)); - const element = toolsList[elementID]?.elementRef!; - - if (element.current) { + if (hash) { + const elementID = decodeURIComponent(hash.slice(1)); + const element = toolsList[elementID]?.elementRef; + + if (element?.current) { document.documentElement.style.scrollPaddingTop = '6rem'; element.current.scrollIntoView({ behavior: 'smooth' }); document.documentElement.style.scrollPaddingTop = '0'; }This keeps behavior the same when the hash is valid, while avoiding crashes on unexpected hashes.
components/navigation/EventPostItem.tsx (1)
23-44: Avoid brittle date comparison and hard‑coded event metadata
- Lines [23–44]: Comparing
currentDateandpostDate.format()as strings mixes local time with a hard‑coded UTC suffix and relies on lexicographic ordering. This can misclassify events as past/future around time‑zone boundaries. Prefer moment’s date comparison directly onmomentinstances, e.g.:- const localTime = moment().format('YYYY-MM-DD'); // store localTime - const currentDate = `${localTime}T00:00:00.000Z`; + const now = moment(); let active = true; const postDate = moment(post.date); @@ - } else if (currentDate > postDate.format()) { - active = false; - } + } else if (postDate.isBefore(now, 'day')) { + active = false; + }
- Lines [28–34]:
title.includes('community' | 'conference' | 'workshop')is case‑sensitive, so titles like “Community Meetup” won’t match. Normalizing once helps:const normalizedTitle = title.toLowerCase(); if (normalizedTitle.includes('community')) { … }
Lines [50–60]: The
"150 attending"copy is hard‑coded; ifIEventexposes attendance or similar metadata, it should come frompostor be removed to avoid misleading users.Lines [66–75]: For dark mode, consider adding
dark:text-white/dark:text-gray-300to the heading and date/time text, to avoid low contrast against dark backgrounds.Also applies to: 48-87
components/tools/ToolsCard.tsx (1)
31-49: Avoid stalevisiblestate inside delayed tooltip handlers (and confirm truncation assumptions).In the Open Source/Commercial pill,
onMouseEnter/onMouseLeaveusesetTimeoutwithsetVisible({ ...visible, desc: true/false }). Becausevisibleis captured when the handler runs, any changes tovisiblemade in the meantime (e.g., fromCardDatausing the same state) can be overwritten when the timeout fires.To avoid stale state, use functional updates:
- <div - className='...' - onMouseEnter={() => - setTimeout(() => { - if (!visible.desc) setVisible({ ...visible, desc: true }); - }, 400) - } - > + <div + className='...' + onMouseEnter={() => + setTimeout(() => { + setVisible((prev) => + prev.desc ? prev : { ...prev, desc: true } + ); + }, 400) + } + > @@ - onMouseLeave={() => - setTimeout(() => { - if (visible.desc) setVisible({ ...visible, desc: false }); - }, 300) - } + onMouseLeave={() => + setTimeout(() => { + setVisible((prev) => + prev.desc ? { ...prev, desc: false } : prev + ); + }, 300) + }Also,
isTruncatedis currently computed only once on mount (useEffect(..., [])). IftoolData.descriptioncan change for a mounted card or the layout size changes significantly, consider adding dependencies or recalculating when needed. If the card is strictly static, the mount-only check is fine.Also applies to: 60-73
components/tools/Filters.tsx (1)
69-105: Remove duplicatetechsfilter and fixrouter.pushquery parameter format.In
handleApplyFilters(lines 84–91), thecheckedTechnologyfilter is set twice. Remove one of these duplicate blocks.Additionally, at lines 97–100,
router.pushis called withquery: searchParams.toString(). The object form ofrouter.pushexpectsqueryto be an object map of keys to values, not a string. Pass an object instead:
query: { pricing: checkPaid !== 'all' ? checkPaid : undefined, ... }(include only defined filters), or- Use the string form:
router.push('/tools?' + searchParams.toString(), undefined, { shallow: true }).
♻️ Duplicate comments (2)
components/icons/Lightning.tsx (1)
4-4: Add TypeScript type annotation for props.Same issue as in
components/icons/Terminal.tsx: theclassNameparameter lacks an explicit type annotation.Apply this diff:
+interface IconLightningProps { + className?: string; +} + /** * @description Icon component for Lightning */ -export default function IconLightning({ className = '' }) { +export default function IconLightning({ className = '' }: IconLightningProps) { return (config/meetings.json (1)
9-22: Replace placeholder calendar links with real event data.The AsyncAPI Design WG Meeting and Governance Board Meeting entries also contain placeholder
calLinkvalues (eid=sample2andeid=sample3respectively) and generic issue URLs.These placeholder values must be replaced with actual Google Calendar event IDs and specific GitHub issue URLs before deployment.
🧹 Nitpick comments (33)
components/icons/Clipboard.tsx (2)
4-4: Add TypeScript prop types.The file has a
.tsxextension but the function lacks TypeScript type annotations. Consider adding proper types for the props to improve type safety and developer experience.Apply this diff to add TypeScript types:
-export default function IconClipboard({ className = '' }) { +export default function IconClipboard({ className = '' }: { className?: string }) {
6-13: Add accessibility attributes to the SVG icon.The SVG element lacks accessibility attributes, which can make it difficult for screen reader users to understand its purpose. Consider adding an
aria-labelor a<title>element to provide an accessible name for the icon.Apply this diff to add an aria-label:
- <svg className={className} fill='currentColor' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'> + <svg className={className} fill='currentColor' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' aria-label='Clipboard icon'> <path d='M9 2a1 1 0 000 2h2a1 1 0 100-2H9z' />Alternatively, add a
<title>element inside the SVG:export default function IconClipboard({ className = '' }: { className?: string }) { return ( <svg className={className} fill='currentColor' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' role='img'> <title>Clipboard icon</title> <path d='M9 2a1 1 0 000 2h2a1 1 0 100-2H9z' /> <path fillRule='evenodd' d='M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm3 4a1 1 0 000 2h.01a1 1 0 100-2H7zm3 0a1 1 0 000 2h3a1 1 0 100-2h-3zm-3 4a1 1 0 100 2h.01a1 1 0 100-2H7zm3 0a1 1 0 100 2h3a1 1 0 100-2h-3z' clipRule='evenodd' /> </svg> ); }components/icons/UsersGroup.tsx (1)
4-10: Add TypeScript type annotation for the className parameter.The
classNameparameter lacks an explicit type annotation. For consistency and type safety, specify the type asstring.Apply this diff:
-export default function IconUsersGroup({ className = '' }) { +export default function IconUsersGroup({ className = '' }: { className?: string }) {components/icons/Book.tsx (1)
4-15: Add TypeScript type annotation for the className parameter.For type safety and consistency with TypeScript best practices, add an explicit type annotation for the
classNameparameter.Apply this diff:
-export default function IconBook({ className = '' }) { +export default function IconBook({ className = '' }: { className?: string }) {components/icons/Video.tsx (1)
4-10: Add TypeScript type annotation for the className parameter.For type safety and consistency with TypeScript best practices, add an explicit type annotation for the
classNameparameter.Apply this diff:
-export default function IconVideo({ className = '' }) { +export default function IconVideo({ className = '' }: { className?: string }) {components/icons/Document.tsx (1)
4-4: Add TypeScript interface for props to improve type safety.The component accepts a
classNameprop but lacks a TypeScript interface definition. This is a common pattern across the new icon components in this PR (Document, CodeBrackets, Gear).Apply this diff to add type safety:
+interface IconDocumentProps { + className?: string; +} + /** * @description Icon component for Document */ -export default function IconDocument({ className = '' }) { +export default function IconDocument({ className = '' }: IconDocumentProps) { return (Consider creating a shared
IconPropsinterface in a types file if this pattern repeats across all icon components.components/icons/CodeBrackets.tsx (1)
4-4: Add TypeScript interface for props to improve type safety.Similar to the
IconDocumentcomponent, this icon lacks a TypeScript interface for its props.Apply this diff:
+interface IconCodeBracketsProps { + className?: string; +} + /** * @description Icon component for Code Brackets */ -export default function IconCodeBrackets({ className = '' }) { +export default function IconCodeBrackets({ className = '' }: IconCodeBracketsProps) { return (components/form/Select.tsx (1)
34-34: Consider extracting long className string for maintainability.The dark mode classes are correctly implemented, but the className string is becoming quite long (200+ characters), making it harder to read and maintain.
Consider extracting the classes into a constant or using a utility like
clsxfor better readability:+const selectClasses = [ + 'form-select h-full pl-2 pr-8 inline-flex justify-center rounded-md', + 'border border-gray-300 dark:border-gray-700 shadow-sm px-4 py-2', + 'bg-white dark:bg-dark-card text-sm font-medium', + 'text-gray-700 dark:text-gray-300', + 'hover:bg-gray-50 dark:hover:bg-dark-background', + 'focus:outline-none focus:ring-2 focus:ring-offset-2', + 'focus:ring-offset-gray-100 dark:focus:ring-offset-dark-background', + 'focus:ring-primary-500' +].join(' '); + export default function Select({ className = '', onChange = () => {}, options, selected }: SelectProps) { // ... return ( <select // ... - className={twMerge( - `form-select h-full pl-2 pr-8 inline-flex justify-center rounded-md border border-gray-300 dark:border-gray-700 shadow-sm px-4 py-2 bg-white dark:bg-dark-card text-sm font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-dark-background focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 dark:focus:ring-offset-dark-background focus:ring-primary-500 ${className}` - )} + className={twMerge(`${selectClasses} ${className}`)}components/icons/Gear.tsx (1)
4-4: Add TypeScript interface for props to improve type safety.Consistent with the other icon components in this PR, this component would benefit from a TypeScript interface for its props.
Apply this diff:
+interface IconGearProps { + className?: string; +} + /** * @description Icon component for Gear */ -export default function IconGear({ className = '' }) { +export default function IconGear({ className = '' }: IconGearProps) { return (Alternatively, if you apply the suggestion to create a shared
IconPropsinterface mentioned in theIconDocumentreview, you can reuse it here.components/tools/Checkbox.tsx (1)
27-27: Consider extracting the className logic for improved readability.The className string is quite long and complex with multiple conditionals and dark mode variants, making it harder to read and maintain.
Consider extracting the className logic:
+ const baseClasses = `border ${borderColor} dark:border-secondary-500 ${bgColor} dark:bg-gray-800 ${textColor} dark:text-secondary-400 p-1 pb-0 rounded-2xl flex gap-1 cursor-pointer items-start transition-all duration-200 hover:shadow-md`; + const checkedClasses = checked ? `${checkedStateBgColor} dark:bg-secondary-700 ${checkedStateTextColor} dark:text-white` : ''; + return ( <div - className={twMerge( - `border ${borderColor} dark:border-secondary-500 ${bgColor} dark:bg-gray-800 ${textColor} dark:text-secondary-400 p-1 pb-0 rounded-2xl flex gap-1 cursor-pointer items-start transition-all duration-200 hover:shadow-md ${checked ? `${checkedStateBgColor} dark:bg-secondary-700 ${checkedStateTextColor} dark:text-white` : ''}` - )} + className={twMerge(`${baseClasses} ${checkedClasses}`)}components/roadmap/GoalCardRoadmapPage.tsx (1)
18-18: Improve icon contrast in dark mode.The icon uses
text-gray-900for both light and dark modes. In dark mode, dark text on a dark background (even with the correcteddark:bg-secondary-700) may have insufficient contrast.Apply this diff to ensure proper contrast:
- <Icon className='w-8 h-8 text-gray-900 dark:text-gray-900' /> + <Icon className='w-8 h-8 text-gray-900 dark:text-white' />components/tools/CategoryDropdown.tsx (1)
21-21: LGTM with optional readability suggestion.The dark mode styling is comprehensive with proper contrast for background, borders, and shadows. The long className could be split into logical groups for better readability, but this is a minor nitpick.
Optionally, consider grouping related classes:
+ const containerClasses = [ + 'absolute z-10 h-60 w-52 origin-top-right overflow-y-auto', + 'rounded-lg shadow-xl dark:shadow-2xl', + 'bg-white dark:bg-dark-card', + 'ring-1 ring-black/5 dark:ring-gray-700', + 'border border-gray-200 dark:border-gray-700', + 'focus:outline-none lg:w-56' + ].join(' '); + <div - className='absolute z-10 h-60 w-52 origin-top-right overflow-y-auto rounded-lg bg-white dark:bg-dark-card shadow-xl dark:shadow-2xl ring-1 ring-black/5 dark:ring-gray-700 focus:outline-none lg:w-56 border border-gray-200 dark:border-gray-700' + className={containerClasses}components/icons/Terminal.tsx (1)
4-4: Add TypeScript type annotation for props.The
classNameparameter lacks an explicit type annotation, relying on TypeScript's implicit typing. For better type safety and maintainability, define an interface for the component props.Apply this diff:
+interface IconTerminalProps { + className?: string; +} + /** * @description Icon component for Terminal */ -export default function IconTerminal({ className = '' }) { +export default function IconTerminal({ className = '' }: IconTerminalProps) { return (components/newsroom/NewsroomYoutube.tsx (1)
16-17: Consider making the video count configurable.The component hard-codes the number of videos to display at 3. While this aligns with the grid layout (lg:grid-cols-3), making this configurable through props would improve reusability.
Example refactor:
interface NewsroomYoutubeProps { className?: string; + maxVideos?: number; } -export default function NewsroomYoutube({ className = '' }: NewsroomYoutubeProps) { +export default function NewsroomYoutube({ className = '', maxVideos = 3 }: NewsroomYoutubeProps) { // Only show the first 3 videos - const videos = videoData.slice(0, 3); + const videos = videoData.slice(0, maxVideos);components/community/ToolingCard.tsx (2)
19-34: Extract magic numbers to constants for maintainability.The stacked card backgrounds use multiple hard-coded positioning and rotation values that make the component difficult to maintain and adjust. Consider extracting these values to named constants or configuration objects.
Example refactor:
+const CARD_STACK_CONFIG = { + back: { rotate: -12, rotateShuffling: -18, top: -15, left: -45, zIndex: 1 }, + middle: { rotate: -7, rotateShuffling: -13, top: -10, left: -25, zIndex: 2 } +}; + export default function ToolingCard({ name, description, badge, language, link, isShuffling }: ToolingCardProps) { const CardContent = ( <div className='relative max-w-xl w-full'> - {/* Stacked Cards Behind - Positioned on LEFT side */} <div - className={`absolute w-full h-full bg-gradient-to-br from-purple-200 to-purple-300 - dark:from-purple-800/60 dark:to-purple-700/50 rounded-3xl shadow-xl border-4 - border-purple-400 dark:border-purple-600 transition-all duration-300 ${ - isShuffling ? 'transform -rotate-[18deg] translate-x-[-10px]' : 'transform -rotate-[12deg]' - }`} - style={{ top: '-15px', left: '-45px', zIndex: 1 }} + className={`absolute w-full h-full bg-gradient-to-br from-purple-200 to-purple-300 + dark:from-purple-800/60 dark:to-purple-700/50 rounded-3xl shadow-xl border-4 + border-purple-400 dark:border-purple-600 transition-all duration-300`} + style={{ + transform: `rotate(${isShuffling ? CARD_STACK_CONFIG.back.rotateShuffling : CARD_STACK_CONFIG.back.rotate}deg)`, + top: `${CARD_STACK_CONFIG.back.top}px`, + left: `${CARD_STACK_CONFIG.back.left}px`, + zIndex: CARD_STACK_CONFIG.back.zIndex + }} />
37-37: Consider overflow handling for long content.The card has a fixed height (
h-[520px]) and the description usesflex-grow. If the description text is very long, it could overflow the fixed-height container without any indication or scroll behavior.Consider adding overflow handling:
- <div className='relative bg-white dark:bg-dark-card rounded-3xl p-10 shadow-2xl dark:shadow-primary-500/20 overflow-hidden border-4 border-primary-500 dark:border-primary-400 h-[520px] transition-all hover:translate-y-[-4px] hover:shadow-3xl cursor-pointer z-10'> + <div className='relative bg-white dark:bg-dark-card rounded-3xl p-10 shadow-2xl dark:shadow-primary-500/20 overflow-hidden border-4 border-primary-500 dark:border-primary-400 h-[520px] transition-all hover:translate-y-[-4px] hover:shadow-3xl cursor-pointer z-10 flex flex-col'>And on the description:
- <p className='text-gray-700 dark:text-gray-300 mb-6 leading-relaxed text-base flex-grow'>{description}</p> + <p className='text-gray-700 dark:text-gray-300 mb-6 leading-relaxed text-base flex-grow overflow-y-auto'>{description}</p>Also applies to: 97-97
components/community/TSCMemberCard.tsx (1)
62-94: Consider extracting social icons to separate components.The inline SVG icons for GitHub, Twitter, and LinkedIn could be extracted into reusable icon components (similar to other icons in the codebase like
IconPlus,IconPlay). This would improve maintainability and consistency.For example, create
components/icons/GitHub.tsx,components/icons/Twitter.tsx, andcomponents/icons/LinkedIn.tsx, then import and use them:import IconGitHub from '../icons/GitHub'; import IconTwitter from '../icons/Twitter'; import IconLinkedIn from '../icons/LinkedIn'; // Then in the JSX: <IconGitHub className='h-5 w-5' /> <IconTwitter className='h-5 w-5' /> <IconLinkedIn className='h-5 w-5' />components/community/ToolingsShowcase.tsx (1)
23-32: Consider minor robustness and accessibility tweaks for the tooling selectorOverall component looks solid and the shuffle effect is a nice touch. A few small improvements you might consider:
currentTool = toolsData[selectedTool]assumesselectedToolwill always exist intoolsData. Today it’s true because state is only set from known tools, but a future refactor or data-driven config could break this. A simple fallback keeps this safe:- const currentTool = toolsData[selectedTool]; + const currentTool = toolsData[selectedTool] ?? toolsData['java-asyncapi']; + if (!currentTool) return null;
setTimeoutinhandleToolChangeis never cleared. It’s short-lived (300ms), so this is low-risk, but if you want to be defensive you could store the timeout id in auseRefand clear it on unmount / before scheduling a new one.- For the tool buttons, exposing selection to assistive tech would help accessibility. Adding
aria-pressed={selectedTool === tool.name}(oraria-current="true"on the active one) would make the “currently selected tool” state clearer.Also applies to: 97-115, 147-161, 164-181
components/community/UpcomingEventsSection.tsx (2)
16-25: Optimize events initialization and guardwindow.scrollToTwo small improvements here:
useState(getEvents(meetings))callsgetEventson every render even though React only uses the first result. Switching to a lazy initializer avoids redundant work:- const [events] = useState(getEvents(meetings)); + const [events] = useState(() => getEvents(meetings));
window.scrollTois fine in the browser, but defending against non-DOM environments (tests, Storybook, SSR edge cases) is cheap:- const goToPage = (pageNumber: number) => { - setCurrentPage(pageNumber); - // Scroll to top of events section - window.scrollTo({ top: 0, behavior: 'smooth' }); - }; + const goToPage = (pageNumber: number) => { + setCurrentPage(pageNumber); + // Scroll to top of events section + if (typeof window !== 'undefined') { + window.scrollTo({ top: 0, behavior: 'smooth' }); + } + };Also applies to: 26-30
32-37: Improve pagination accessibility and use stable keys for eventsA couple of small tweaks would make this pagination more robust and accessible:
- The Prev/Next buttons currently render only
<and>text. Screen readers will announce “less than”/“greater than”, which is not ideal. Adding ARIA labels keeps the visuals while improving semantics:- <button + <button key='prev' onClick={() => goToPage(currentPage - 1)} disabled={currentPage === 1} className='...' data-testid='Pagination-Prev' + aria-label='Previous page' > < </button> ... - <button + <button key='next' onClick={() => goToPage(currentPage + 1)} disabled={currentPage === totalPages} className='...' data-testid='Pagination-Next' + aria-label='Next page' > > </button>
- For the events grid, using
indexas the React key is fragile if the events array ever reorders or changes length. IfEventexposes something stable (e.g.idorslug), prefer that:- {currentEvents.map((event: Event, index: number) => { - return <EventPostItem key={index} id={event.title} post={event} />; - })} + {currentEvents.map((event: Event) => { + const key = event.id ?? event.title; + return <EventPostItem key={key} id={event.title} post={event} />; + })}Also applies to: 42-53, 76-92, 94-113, 115-127, 159-162, 166-187
pages/tools/index.tsx (1)
19-21: Tools hero/layout looks good; addrelfor the external TextLinkThe new hero and page wrapper integrate nicely with dark mode and keep the ToolsDashboard wiring intact. One small hardening point:
- The
TextLinkto the GitHub “Tool Documentation” opens in a new tab viatarget='_blank'. UnlessTextLinkinternally addsrel="noopener noreferrer", it’s safer to include it explicitly to avoidwindow.openerissues:- <TextLink - href='https://github.com/asyncapi/community/blob/master/new-tool-documentation.md' - target='_blank' + <TextLink + href='https://github.com/asyncapi/community/blob/master/new-tool-documentation.md' + target='_blank' + rel='noopener noreferrer' className='...' >Also applies to: 22-33, 55-78
pages/community/index.tsx (1)
117-141: Align heading levels with page semantics for better accessibilityYou now have several prominent headings on this page:
- “AsyncAPI Slack” (HeadingLevel.h1)
- “AsyncAPI Community Meetings & Events” (HeadingLevel.h2)
- “Ambassador Programs” (HeadingLevel.h2)
- “Our Goals, Financials, and Merchandising” (HeadingLevel.h1)
Depending on what the
Herocomponent renders, this may result in multipleh1elements on a single page. HTML5 allows this, but for accessibility and SEO it’s often clearer to keep a single logical H1 and use H2/H3 for subsequent sections.Consider:
- Keeping the main page title/hero as
h1.- Demoting section titles like “AsyncAPI Slack” and “Our Goals, Financials, and Merchandising” to
HeadingLevel.h2(or h3 where appropriate), while keeping visual size viatypeStyle.Example:
- <Heading - level={HeadingLevel.h1} + <Heading + level={HeadingLevel.h2} typeStyle={HeadingTypeStyle.xl} className='text-gray-900 dark:text-dark-heading font-bold' > AsyncAPI Slack </Heading> ... - <Heading - level={HeadingLevel.h1} + <Heading + level={HeadingLevel.h2} typeStyle={HeadingTypeStyle.lg} className='text-gray-900 dark:text-dark-heading font-bold mb-12 text-center lg:text-left' > Our Goals, Financials, and Merchandising </Heading>This preserves the visual hierarchy while clarifying the semantic structure.
Also applies to: 156-181, 196-239
components/community/Card.tsx (1)
84-107: Large card dark‑mode styling is solid; consider minor class cleanupThe large card’s conditional dark border and background plus dark text on heading/description align well with the rest of the palette, especially the
bg === 'bg-code-editor-dark'exception. One small nit:${!bg && 'border-[#ad20e2] dark:border-primary-400'}will stringifyfalseinto the class list whenbgis truthy; consider using a helper orbg ? '' : '...'for cleaner output.components/layout/BlogLayout.tsx (1)
63-77: Check author link color consistency with previous hydration constraintsThe author list now conditionally renders
<a>elements withclassName='dark:text-gray-100'inside a parent<p>that already setstext-gray-900 dark:text-gray-100. Functionally this is fine and avoids nestedLinks, but you might want to explicitly mirror the light-mode color on the anchors (e.g., also settext-gray-900) for consistent styling and to keep behavior uniform with other author renderers that previously ran into hydration issues. Based on learnings.components/tools/ToolsDashboard.tsx (1)
184-205: Filter/category/search bar dark‑mode and interaction polish look goodThe updated classes on the filter button, category dropdown trigger, and search bar (dark borders/backgrounds,
font-medium, hover/scale/transition states, and improved focus styles) address the previously noted dark-mode issues, including search icon visibility (dark:opacity-80) and “Clear Filters” readability (dark:text-gray-400+ hover variants). Behavior stays the same, just more usable in dark mode.Also applies to: 217-221, 227-243
pages/blog/index.tsx (2)
38-49: Filtering, tabs, and pagination state management looks consistentThe combination of
activeTab,currentPage, andpostsPerPage, plus the two effects that resetcurrentPageandactiveTabon filter changes, is coherent and avoids stale page indices across viewport changes and filter toggles. ThehasDropdownFiltersguard to give dropdown filters precedence over tabs is also clear and easy to reason about.If you want to simplify a bit, you can likely drop the
setActiveTab('All Posts')insideonFilter(Lines [42–47]) and rely solely on theuseEffectwatchingrouter.query(Lines [117–121]) to keep tab state in sync with dropdown filters.Also applies to: 72-92, 94-123
217-241: Refine desktop pagination “Go to page” UX / a11y
Lines [299–310]: The
<select>is functional but on long lists the native dropdown can feel cramped and may overflow horizontally on smaller viewports. Consider:
- Adding a minimum width (e.g.
min-w-[80px]) so the value and chevron never overlap.- If you need to limit visible options (3–4) as per earlier UX feedback, you may want to follow the custom dropdown pattern you implemented on the Case Studies page instead of a native
<select>.For accessibility, wiring the “Go to page” label to the select via
id/htmlForor addingaria-labeldirectly on the<select>will make the control clearer to screen readers.Also applies to: 251-311
pages/roadmap.tsx (1)
515-521: Avoid placeholderhref='#'on hero TextLinkThe “Any kind of APIs”
TextLink(Lines [515–521]) currently points tohref='#', which just jumps to the top and doesn’t give users more context.Consider either:
- Pointing it to a relevant internal section (e.g., a specs/vision section anchor), or
- Rendering it as non‑link styled text if there is no target yet.
pages/community/ambassadors/index.tsx (2)
180-195: Add a descriptivetitleto the YouTube iframe for accessibilityThe embedded video iframe (Lines [183–189]) lacks a
titleattribute, which screen readers rely on to describe the content.Consider adding a concise title, e.g.:
- <iframe - src='https://www.youtube.com/embed/3rg_7hIb9PQ' + <iframe + title='AsyncAPI Ambassador Program overview' + src='https://www.youtube.com/embed/3rg_7hIb9PQ'
262-281: Double‑check ambassador profile link pathLines [269–280]:
Linkuseshref={ambassadors/${ambassador.github}}withasset to the same relative string. Depending on your routing setup, this may resolve to/ambassadors/[github]rather than/community/ambassadors/[github], or vice versa.If the detail page lives under
/community/ambassadors/[github].tsx, it’s safer to use an absolute path like:<Link href={`/community/ambassadors/${ambassador.github}`}>Please verify the generated URLs in the deploy preview to ensure they match the actual ambassador profile route.
pages/casestudies/index.tsx (1)
222-257: Polish adopters table row clickability and pagination dropdown UX
Lines [225–257]: Each adopter row uses
cursor-pointer, but only the “Learn More” button inside theLinkis actually interactive. Either:
- Make the whole row clickable by wrapping the row in
Link, or- Drop
cursor-pointerso the pointer cursor doesn’t imply a larger hit area than exists.Lines [248–253]:
href={adopter.resources[0]?.link || '#'}means adopters without resources will still render a CTA that navigates to#. Consider conditionally hiding or disabling the button when no resources are available.Lines [315–357]: The custom “Go to page” dropdown nicely addresses the previous overflow issue (
max-h-48, scrollable list). For a11y, you might also add:
aria-haspopup='listbox'andaria-expanded={isDropdownOpen}on the trigger button.role='listbox'/role='option'on the menu and items, if you want full screen‑reader semantics.Also applies to: 260-359
pages/community/tsc.tsx (1)
288-390: Pagination logic is overly complex and can render redundant ellipses.The current set of conditionals for page numbers/ellipses (
currentPage > 2 && currentPage < totalPages - 1,currentPage < totalPages && currentPage > 1, plus separate...blocks) can lead to multiple ellipses and omit intermediate pages in some states, making the control harder to reason about and test.Given TSC member counts are relatively small, a simpler approach would be to:
- Render all page numbers in a loop and highlight the active one, or
- Reuse the same pagination component/logic you use on blog/case-studies to keep behavior consistent.
Example of a simpler pattern:
- {/* Page Numbers */} - {currentPage > 1 && ( - <button ...>1</button> - )} - ... - {currentPage === totalPages && ( - <button ...>{totalPages}</button> - )} + {/* Page Numbers */} + {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => ( + <button + key={page} + onClick={() => setCurrentPage(page)} + className={ + page === currentPage + ? 'w-10 h-10 flex items-center justify-center rounded-md bg-primary-500 text-white font-medium' + : 'w-10 h-10 flex items-center justify-center rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-dark-card hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors' + } + aria-current={page === currentPage ? 'page' : undefined} + > + {page} + </button> + ))}You can then decide whether ellipses are even necessary here. Also, to address earlier feedback about “Go to page” dropdown overflow, you may want to add a
max-handoverflow-y-autoon the<select>to constrain visible options.components/tools/Filters.tsx (1)
207-207: Use vertical scrolling for dropdown content instead ofoverflow-x-auto.The language, technology, and category dropdown panels are declared with
overflow-x-auto, but their content lists grow vertically. For long lists, vertical scrolling is what you want; horizontal scrolling is unlikely to ever be needed and may have no effect.You can make the dropdowns more robust by switching to vertical overflow with a max height, e.g.:
- <div className='w-auto overflow-x-auto rounded-b-lg border border-gray-400 dark:border-gray-600 bg-gray-200 dark:bg-gray-700 duration-150'> + <div className='w-auto max-h-60 overflow-y-auto rounded-b-lg border border-gray-400 dark:border-gray-600 bg-gray-200 dark:bg-gray-700 duration-150'>(and similarly for the technology and category dropdown containers).
Also applies to: 248-248, 287-287
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
public/img/social/woman.jpgis excluded by!**/*.jpg
📒 Files selected for processing (65)
CODE_OF_CONDUCT.md(1 hunks)components/AlgoliaSearch.tsx(1 hunks)components/Calendar.tsx(1 hunks)components/CaseStudyCard.tsx(2 hunks)components/community/Card.tsx(2 hunks)components/community/FeatureCard.tsx(1 hunks)components/community/Header.tsx(2 hunks)components/community/HomeCard.tsx(1 hunks)components/community/TSCMemberCard.tsx(1 hunks)components/community/ToolingCard.tsx(1 hunks)components/community/ToolingsShowcase.tsx(1 hunks)components/community/UpcomingEventsSection.tsx(1 hunks)components/dashboard/table/Filters.tsx(1 hunks)components/form/Select.tsx(1 hunks)components/icons/Book.tsx(1 hunks)components/icons/Clipboard.tsx(1 hunks)components/icons/CodeBrackets.tsx(1 hunks)components/icons/Document.tsx(1 hunks)components/icons/Gear.tsx(1 hunks)components/icons/LightBulb.tsx(1 hunks)components/icons/Lightning.tsx(1 hunks)components/icons/Microphone.tsx(1 hunks)components/icons/Newsroom.tsx(1 hunks)components/icons/Play.tsx(1 hunks)components/icons/Plus.tsx(1 hunks)components/icons/Star.tsx(1 hunks)components/icons/Terminal.tsx(1 hunks)components/icons/UsersGroup.tsx(1 hunks)components/icons/Video.tsx(1 hunks)components/layout/BlogLayout.tsx(2 hunks)components/layout/GenericLayout.tsx(1 hunks)components/navigation/BlogPostItem.tsx(2 hunks)components/navigation/DocsNav.tsx(1 hunks)components/navigation/EventPostItem.tsx(1 hunks)components/navigation/Filter.tsx(1 hunks)components/navigation/communityItems.tsx(1 hunks)components/newsroom/NewsroomBlogPosts.tsx(2 hunks)components/newsroom/NewsroomSection.tsx(1 hunks)components/newsroom/NewsroomYoutube.tsx(1 hunks)components/newsroom/YouTubeCard.tsx(1 hunks)components/roadmap/GoalCardRoadmapPage.tsx(1 hunks)components/roadmap/RoadmapColumn.tsx(2 hunks)components/roadmap/RoadmapItem.tsx(2 hunks)components/roadmap/RoadmapPill.tsx(2 hunks)components/tools/CategoryDropdown.tsx(2 hunks)components/tools/Checkbox.tsx(1 hunks)components/tools/Filters.tsx(9 hunks)components/tools/FiltersDisplay.tsx(1 hunks)components/tools/Toggle.tsx(1 hunks)components/tools/ToolsCard.tsx(8 hunks)components/tools/ToolsDashboard.tsx(4 hunks)components/tools/ToolsList.tsx(1 hunks)config/meetings.json(1 hunks)next-env.d.ts(1 hunks)pages/blog/index.tsx(3 hunks)pages/casestudies/index.tsx(2 hunks)pages/community/ambassadors/index.tsx(3 hunks)pages/community/events-and-updates.tsx(1 hunks)pages/community/index.tsx(2 hunks)pages/community/tsc.tsx(1 hunks)pages/roadmap.tsx(4 hunks)pages/tools/index.tsx(1 hunks)styles/globals.css(4 hunks)types/components/navigation/BlogPostType.ts(1 hunks)types/pages/community/Community.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
Learnt from: asyncapi-bot
Repo: asyncapi/website PR: 0
File: :0-0
Timestamp: 2025-02-18T12:07:42.211Z
Learning: The following PR commands are supported in the asyncapi/website repository:
- `/please-take-a-look` or `/ptal`: Requests attention from reviewers who haven't reviewed the PR
- `/ready-to-merge` or `/rtm`: Triggers automerge when all conditions are met
- `/do-not-merge` or `/dnm`: Blocks automerge even if all conditions are met
- `/autoupdate` or `/au`: Adds autoupdate label to keep PR in sync with target branch
- `/update` or `/u`: One-time update of PR with latest changes from target branch
📚 Learning: 2024-10-11T11:17:32.246Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/navigation/BlogPostItem.tsx:80-87
Timestamp: 2024-10-11T11:17:32.246Z
Learning: In the `BlogPostItem` component, the parent `Link` wraps the entire content, so inner `Link` components around the title and excerpt are unnecessary.
Applied to files:
components/newsroom/NewsroomBlogPosts.tsxcomponents/navigation/EventPostItem.tsxcomponents/layout/BlogLayout.tsxcomponents/newsroom/YouTubeCard.tsxcomponents/navigation/BlogPostItem.tsx
📚 Learning: 2024-10-11T11:32:30.226Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/navigation/BlogPostItem.tsx:95-119
Timestamp: 2024-10-11T11:32:30.226Z
Learning: In the `BlogPostItem` component (`components/navigation/BlogPostItem.tsx`), nesting `<a>` tags inside the parent `Link` component leads to hydration issues; therefore, we should avoid nesting `<a>` tags inside `Link` components in this component.
Applied to files:
components/newsroom/NewsroomBlogPosts.tsxcomponents/community/Card.tsxpages/blog/index.tsxcomponents/navigation/EventPostItem.tsxcomponents/CaseStudyCard.tsxpages/casestudies/index.tsxcomponents/layout/BlogLayout.tsxcomponents/newsroom/YouTubeCard.tsxcomponents/navigation/BlogPostItem.tsx
📚 Learning: 2024-10-11T10:46:58.882Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/Avatar.tsx:35-44
Timestamp: 2024-10-11T10:46:58.882Z
Learning: In Next.js, when avoiding nested `<a>` tags due to hydration issues, use accessible non-link elements like `<button>` or `<span>` with appropriate roles, `tabIndex`, and event handlers to maintain accessibility and SEO.
Applied to files:
components/community/Card.tsxcomponents/newsroom/NewsroomSection.tsxpages/community/ambassadors/index.tsxcomponents/CaseStudyCard.tsxpages/casestudies/index.tsxcomponents/navigation/BlogPostItem.tsx
📚 Learning: 2024-10-11T07:38:35.745Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/newsroom/FeaturedBlogPost.tsx:90-92
Timestamp: 2024-10-11T07:38:35.745Z
Learning: In Next.js, nested `<a>` tags cause hydration issues due to invalid HTML. To fix this, avoid nesting `<a>` tags by replacing inner `<a>` tags with non-interactive elements like `<span>`, and use click handlers to maintain interactivity if needed.
Applied to files:
components/community/Card.tsxpages/community/ambassadors/index.tsxcomponents/CaseStudyCard.tsxpages/casestudies/index.tsxcomponents/roadmap/RoadmapPill.tsxcomponents/navigation/BlogPostItem.tsx
📚 Learning: 2025-05-09T17:35:57.171Z
Learnt from: TRohit20
Repo: asyncapi/website PR: 4107
File: markdown/docs/tools/studio/architecture.md:23-23
Timestamp: 2025-05-09T17:35:57.171Z
Learning: In the AsyncAPI Studio architecture documentation, "Layer Breakdown" is intentionally structured as an H3 heading (subsection) because it provides additional detail about the layered architecture pattern introduced earlier, rather than being a standalone main section.
Applied to files:
components/community/Header.tsx
📚 Learning: 2024-11-11T21:30:32.478Z
Learnt from: amanbhoria
Repo: asyncapi/website PR: 3373
File: components/AlgoliaSearch.tsx:313-313
Timestamp: 2024-11-11T21:30:32.478Z
Learning: In the `SearchButton` component within `components/AlgoliaSearch.tsx`, if the component re-renders on every button click and the `useEffect` runs accordingly, adding dependencies to the dependency array might not be necessary.
Applied to files:
components/navigation/DocsNav.tsxcomponents/dashboard/table/Filters.tsxcomponents/AlgoliaSearch.tsxcomponents/navigation/Filter.tsx
📚 Learning: 2025-01-18T08:44:43.614Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3423
File: tests/index.test.js:2-7
Timestamp: 2025-01-18T08:44:43.614Z
Learning: In the AsyncAPI website project, JavaScript test files must include the .ts extension when importing TypeScript files (e.g., `require('../scripts/build-rss.ts')`). This is enforced by the project's configuration which uses `moduleResolution: "bundler"` in tsconfig.json and TypeScript-aware ESLint plugins. The .ts extensions are required even though the files are imported using CommonJS require statements.
Applied to files:
next-env.d.ts
📚 Learning: 2025-04-20T16:05:16.482Z
Learnt from: anshgoyalevil
Repo: asyncapi/website PR: 3950
File: scripts/utils/check-locales.ts:122-129
Timestamp: 2025-04-20T16:05:16.482Z
Learning: In the AsyncAPI website project, Next.js throws errors at runtime when locale files are missing, making additional validation for missing files unnecessary in the check-locales script.
Applied to files:
next-env.d.ts
📚 Learning: 2025-01-18T08:44:43.614Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3423
File: tests/index.test.js:2-7
Timestamp: 2025-01-18T08:44:43.614Z
Learning: In the AsyncAPI website project, JavaScript test files must include the .ts extension when importing TypeScript files (e.g., `require('../scripts/build-rss.ts')`). This is a project-specific requirement enforced by the linting rules and build setup, even though it differs from typical Node.js behavior.
Applied to files:
next-env.d.ts
📚 Learning: 2025-06-03T06:48:09.304Z
Learnt from: iambami
Repo: asyncapi/website PR: 4111
File: markdown/blog/community-spotlight-aayush.md:3-3
Timestamp: 2025-06-03T06:48:09.304Z
Learning: For AsyncAPI community spotlight blog posts, the frontmatter date should reflect the actual publication date, not the month being highlighted in the spotlight. For example, a "May Community Spotlight" can be published in June with a June date in the frontmatter.
Applied to files:
components/navigation/EventPostItem.tsx
📚 Learning: 2025-06-10T06:27:25.419Z
Learnt from: aminoxix
Repo: asyncapi/website PR: 4127
File: components/layout/CommunityLayout.tsx:179-179
Timestamp: 2025-06-10T06:27:25.419Z
Learning: In AsyncAPI's community data structure, ambassadors and TSC members are guaranteed to have GitHub accounts. The `github` field is required in both `Ambassador` and `Tsc` interfaces, and the data processing in `addAdditionalUserInfo()` converts GitHub usernames to full URLs, ensuring safe string operations when extracting usernames from URLs.
Applied to files:
types/pages/community/Community.ts
📚 Learning: 2024-10-11T07:27:53.362Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/newsroom/FeaturedBlogPost.tsx:90-92
Timestamp: 2024-10-11T07:27:53.362Z
Learning: Using anchor tags for author names in 'FeaturedBlogPost' component leads to hydration issues on the website.
Applied to files:
components/layout/BlogLayout.tsxcomponents/navigation/BlogPostItem.tsx
🪛 LanguageTool
CODE_OF_CONDUCT.md
[style] ~31-~31: ‘on the basis of’ might be wordy. Consider a shorter alternative.
Context: ...rizing anyone’s personality or behavior on the basis of immutable identities or traits. 4. **Se...
(EN_WORDINESS_PREMIUM_ON_THE_BASIS_OF)
[style] ~53-~53: In some texts, you can shorten this phrase. Consider using “email us”.
Context: ...f_conduct). - Or, if you’d prefer, just send us an email at [email protected]. Community...
(SEND_PRP_AN_EMAIL)
[style] ~55-~55: Consider a more concise word here.
Context: ...rioritizing safety and confidentiality. In order to honor these values, enforcement actions...
(IN_ORDER_TO_PREMIUM)
[style] ~81-~81: ‘in keeping with’ might be wordy. Consider a shorter alternative.
Context: ...s to use their discretion and judgment, in keeping with the best interests of our community. ...
(EN_WORDINESS_PREMIUM_IN_KEEPING_WITH)
🪛 markdownlint-cli2 (0.18.1)
CODE_OF_CONDUCT.md
53-53: Bare URL used
(MD034, no-bare-urls)
|
|
||
| - Join our [AsyncAPI Slack](https://asyncapi.com/slack-invite) and share your report in the `#coc` channel. | ||
| - Reach out directly to any member of the [Code of Conduct Committee](https://github.com/orgs/asyncapi/teams/code_of_conduct). | ||
| - Or, if you’d prefer, just send us an email at **[email protected]**. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Format email address as a proper markdown link.
The email address should be formatted as a link to comply with markdown best practices and improve user experience.
Apply this diff:
-- Or, if you'd prefer, just send us an email at **[email protected]**.
+- Or, if you'd prefer, just send us an email at [[email protected]](mailto:[email protected]).📝 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.
| - Or, if you’d prefer, just send us an email at **[email protected]**. | |
| - Or, if you'd prefer, just send us an email at [[email protected]](mailto:[email protected]). |
🧰 Tools
🪛 LanguageTool
[style] ~53-~53: In some texts, you can shorten this phrase. Consider using “email us”.
Context: ...f_conduct). - Or, if you’d prefer, just send us an email at [email protected]. Community...
(SEND_PRP_AN_EMAIL)
🪛 markdownlint-cli2 (0.18.1)
53-53: Bare URL used
(MD034, no-bare-urls)
🤖 Prompt for AI Agents
In CODE_OF_CONDUCT.md around line 53, the email address is plain text; change it
to a proper Markdown mailto link by replacing the text with
[[email protected]](mailto:[email protected]) so it renders as a clickable
email link in Markdown.
| <div className='absolute -bottom-40 -left-32 w-[700px] h-[500px]'> | ||
| <svg viewBox='0 0 500 500' xmlns='http://www.w3.org/2000/svg' className='w-full h-full'> | ||
| <defs> | ||
| <linearGradient id='blueGradient' x1='20%' y1='80%' x2='80%' y2='20%'> | ||
| <stop | ||
| offset='0%' | ||
| className='text-primary-500 dark:text-primary-600' | ||
| stopColor='currentColor' | ||
| stopOpacity='0.9' | ||
| /> | ||
| <stop | ||
| offset='40%' | ||
| className='text-blue-400 dark:text-blue-500' | ||
| stopColor='currentColor' | ||
| stopOpacity='0.85' | ||
| /> | ||
| <stop | ||
| offset='100%' | ||
| className='text-cyan-400 dark:text-cyan-600' | ||
| stopColor='currentColor' | ||
| stopOpacity='0.8' | ||
| /> | ||
| </linearGradient> | ||
| </defs> | ||
| {/* Fan/Petal shape spreading from bottom left */} | ||
| <path | ||
| fill='url(#blueGradient)' | ||
| d='M 50 450 Q 150 350 280 320 Q 400 300 480 340 Q 500 380 470 420 | ||
| Q 440 460 380 480 Q 280 500 180 490 Q 100 480 50 450 Z | ||
| M 60 440 Q 140 380 240 360 Q 320 350 380 380 | ||
| L 320 420 Q 240 400 160 430 Z | ||
| M 80 430 Q 150 390 220 380 Q 280 375 320 395 | ||
| L 280 420 Q 220 410 160 425 Z' | ||
| /> | ||
| </svg> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add accessibility attributes to decorative SVG.
The large decorative SVG element (fan/petal shape) should be marked as presentational to prevent screen readers from attempting to parse it.
Apply this diff:
<div className='absolute -bottom-40 -left-32 w-[700px] h-[500px]'>
- <svg viewBox='0 0 500 500' xmlns='http://www.w3.org/2000/svg' className='w-full h-full'>
+ <svg viewBox='0 0 500 500' xmlns='http://www.w3.org/2000/svg' className='w-full h-full' aria-hidden='true' role='presentation'>
<defs>📝 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.
| <div className='absolute -bottom-40 -left-32 w-[700px] h-[500px]'> | |
| <svg viewBox='0 0 500 500' xmlns='http://www.w3.org/2000/svg' className='w-full h-full'> | |
| <defs> | |
| <linearGradient id='blueGradient' x1='20%' y1='80%' x2='80%' y2='20%'> | |
| <stop | |
| offset='0%' | |
| className='text-primary-500 dark:text-primary-600' | |
| stopColor='currentColor' | |
| stopOpacity='0.9' | |
| /> | |
| <stop | |
| offset='40%' | |
| className='text-blue-400 dark:text-blue-500' | |
| stopColor='currentColor' | |
| stopOpacity='0.85' | |
| /> | |
| <stop | |
| offset='100%' | |
| className='text-cyan-400 dark:text-cyan-600' | |
| stopColor='currentColor' | |
| stopOpacity='0.8' | |
| /> | |
| </linearGradient> | |
| </defs> | |
| {/* Fan/Petal shape spreading from bottom left */} | |
| <path | |
| fill='url(#blueGradient)' | |
| d='M 50 450 Q 150 350 280 320 Q 400 300 480 340 Q 500 380 470 420 | |
| Q 440 460 380 480 Q 280 500 180 490 Q 100 480 50 450 Z | |
| M 60 440 Q 140 380 240 360 Q 320 350 380 380 | |
| L 320 420 Q 240 400 160 430 Z | |
| M 80 430 Q 150 390 220 380 Q 280 375 320 395 | |
| L 280 420 Q 220 410 160 425 Z' | |
| /> | |
| </svg> | |
| </div> | |
| <div className='absolute -bottom-40 -left-32 w-[700px] h-[500px]'> | |
| <svg viewBox='0 0 500 500' xmlns='http://www.w3.org/2000/svg' className='w-full h-full' aria-hidden='true' role='presentation'> | |
| <defs> | |
| <linearGradient id='blueGradient' x1='20%' y1='80%' x2='80%' y2='20%'> | |
| <stop | |
| offset='0%' | |
| className='text-primary-500 dark:text-primary-600' | |
| stopColor='currentColor' | |
| stopOpacity='0.9' | |
| /> | |
| <stop | |
| offset='40%' | |
| className='text-blue-400 dark:text-blue-500' | |
| stopColor='currentColor' | |
| stopOpacity='0.85' | |
| /> | |
| <stop | |
| offset='100%' | |
| className='text-cyan-400 dark:text-cyan-600' | |
| stopColor='currentColor' | |
| stopOpacity='0.8' | |
| /> | |
| </linearGradient> | |
| </defs> | |
| {/* Fan/Petal shape spreading from bottom left */} | |
| <path | |
| fill='url(#blueGradient)' | |
| d='M 50 450 Q 150 350 280 320 Q 400 300 480 340 Q 500 380 470 420 | |
| Q 440 460 380 480 Q 280 500 180 490 Q 100 480 50 450 Z | |
| M 60 440 Q 140 380 240 360 Q 320 350 380 380 | |
| L 320 420 Q 240 400 160 430 Z | |
| M 80 430 Q 150 390 220 380 Q 280 375 320 395 | |
| L 280 420 Q 220 410 160 425 Z' | |
| /> | |
| </svg> | |
| </div> |
🤖 Prompt for AI Agents
In components/community/ToolingCard.tsx around lines 46 to 81, the decorative
SVG should be marked presentational so screen readers ignore it; update the
<svg> element to include accessibility attributes (e.g., role="presentation",
aria-hidden="true", and focusable="false") so it is excluded from the
accessibility tree and not keyboard-focusable while leaving visual output
unchanged.
| <div | ||
| key={member.github} | ||
| className='bg-white dark:bg-dark-card rounded-xl border border-gray-200 dark:border-gray-700 p-6 hover:shadow-lg transition-shadow' | ||
| > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove key prop from component root.
The key prop on line 15 should be applied by the parent component when rendering a list of TSCMemberCard components, not within the component itself. React keys are used by the parent to identify elements in a collection.
Apply this diff:
return (
<div
- key={member.github}
className='bg-white dark:bg-dark-card rounded-xl border border-gray-200 dark:border-gray-700 p-6 hover:shadow-lg transition-shadow'
>📝 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.
| <div | |
| key={member.github} | |
| className='bg-white dark:bg-dark-card rounded-xl border border-gray-200 dark:border-gray-700 p-6 hover:shadow-lg transition-shadow' | |
| > | |
| <div | |
| className='bg-white dark:bg-dark-card rounded-xl border border-gray-200 dark:border-gray-700 p-6 hover:shadow-lg transition-shadow' | |
| > |
🤖 Prompt for AI Agents
In components/community/TSCMemberCard.tsx around lines 14 to 17, the root div
currently includes a key={member.github} which should be removed because keys
must be set by the parent when rendering lists; delete the key prop from the
component's root element and ensure any list rendering of TSCMemberCard in the
parent passes key={member.github} (or another stable identifier) instead.
|
|
||
| {member.reposList && member.reposList.length > 0 && ( | ||
| <div className='mb-4'> | ||
| <p className='text-xs text-gray-500 dark:text-gray-500 mb-2'>Maintainer of:</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Verify contrast for "Maintainer of:" label in dark mode.
The label uses text-gray-500 in both light and dark modes. In dark mode, gray-500 may not provide sufficient contrast against the dark background. Consider using a lighter shade for dark mode.
Apply this diff to improve dark mode contrast:
- <p className='text-xs text-gray-500 dark:text-gray-500 mb-2'>Maintainer of:</p>
+ <p className='text-xs text-gray-500 dark:text-gray-400 mb-2'>Maintainer of:</p>📝 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.
| <p className='text-xs text-gray-500 dark:text-gray-500 mb-2'>Maintainer of:</p> | |
| <p className='text-xs text-gray-500 dark:text-gray-400 mb-2'>Maintainer of:</p> |
🤖 Prompt for AI Agents
In components/community/TSCMemberCard.tsx around line 35, the label uses the
same text-gray-500 for both light and dark modes which may fail contrast in dark
mode; update the className to keep text-gray-500 for light but add a lighter
dark variant (for example dark:text-gray-300 or dark:text-gray-400) so the label
is readable on dark backgrounds, then verify the change with the app in dark
mode and ensure it meets contrast requirements.
| <div className='flex gap-3 pt-4 border-t border-gray-200 dark:border-gray-700'> | ||
| {member.githubUrl && ( | ||
| <a | ||
| href={member.githubUrl} | ||
| target='_blank' | ||
| rel='noreferrer' | ||
| className='text-gray-600 dark:text-gray-400 hover:!text-primary-500 dark:hover:!text-primary-500 transition-colors' | ||
| > | ||
| <svg className='h-5 w-5' fill='currentColor' viewBox='0 0 20 20'> | ||
| <path | ||
| fillRule='evenodd' | ||
| d='M10 0C4.477 0 0 4.484 0 10.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0110 4.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.203 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.942.359.31.678.921.678 1.856 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0020 10.017C20 4.484 15.522 0 10 0z' | ||
| clipRule='evenodd' | ||
| /> | ||
| </svg> | ||
| </a> | ||
| )} | ||
| {member.twitterUrl && ( | ||
| <a | ||
| href={member.twitterUrl} | ||
| target='_blank' | ||
| rel='noreferrer' | ||
| className='text-gray-600 dark:text-gray-400 hover:!text-primary-500 dark:hover:!text-primary-500 transition-colors' | ||
| > | ||
| <svg className='h-5 w-5' fill='currentColor' viewBox='0 0 20 20'> | ||
| <path d='M6.29 18.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0020 3.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.073 4.073 0 01.8 7.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 010 16.407a11.616 11.616 0 006.29 1.84' /> | ||
| </svg> | ||
| </a> | ||
| )} | ||
| {member.linkedinUrl && ( | ||
| <a | ||
| href={member.linkedinUrl} | ||
| target='_blank' | ||
| rel='noreferrer' | ||
| className='text-gray-600 dark:text-gray-400 hover:!text-primary-500 dark:hover:!text-primary-500 transition-colors' | ||
| > | ||
| <svg className='h-5 w-5' fill='currentColor' viewBox='0 0 20 20'> | ||
| <path d='M16.338 16.338H13.67V12.16c0-.995-.017-2.277-1.387-2.277-1.39 0-1.601 1.086-1.601 2.207v4.248H8.014v-8.59h2.559v1.174h.037c.356-.675 1.227-1.387 2.526-1.387 2.703 0 3.203 1.778 3.203 4.092v4.711zM5.005 6.575a1.548 1.548 0 11-.003-3.096 1.548 1.548 0 01.003 3.096zm-1.337 9.763H6.34v-8.59H3.667v8.59zM17.668 1H2.328C1.595 1 1 1.581 1 2.298v15.403C1 18.418 1.595 19 2.328 19h15.34c.734 0 1.332-.582 1.332-1.299V2.298C19 1.581 18.402 1 17.668 1z' /> | ||
| </svg> | ||
| </a> | ||
| )} | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add accessibility labels to social links.
The social links lack aria-label attributes, which would improve screen reader accessibility by providing clear labels for each link type.
Apply this diff to add aria-labels:
{member.githubUrl && (
<a
href={member.githubUrl}
target='_blank'
rel='noreferrer'
+ aria-label='GitHub profile'
className='text-gray-600 dark:text-gray-400 hover:!text-primary-500 dark:hover:!text-primary-500 transition-colors'
>
<svg className='h-5 w-5' fill='currentColor' viewBox='0 0 20 20'> {member.twitterUrl && (
<a
href={member.twitterUrl}
target='_blank'
rel='noreferrer'
+ aria-label='Twitter profile'
className='text-gray-600 dark:text-gray-400 hover:!text-primary-500 dark:hover:!text-primary-500 transition-colors'
>
<svg className='h-5 w-5' fill='currentColor' viewBox='0 0 20 20'> {member.linkedinUrl && (
<a
href={member.linkedinUrl}
target='_blank'
rel='noreferrer'
+ aria-label='LinkedIn profile'
className='text-gray-600 dark:text-gray-400 hover:!text-primary-500 dark:hover:!text-primary-500 transition-colors'
>
<svg className='h-5 w-5' fill='currentColor' viewBox='0 0 20 20'>🤖 Prompt for AI Agents
In components/community/TSCMemberCard.tsx around lines 54 to 95, the social
anchor links are missing aria-labels; add descriptive aria-label attributes to
each <a> so screen readers can announce them (e.g. aria-label={`${member.name ||
'Member'} on GitHub`}, aria-label={`${member.name || 'Member'} on Twitter`},
aria-label={`${member.name || 'Member'} on LinkedIn`}); keep existing
target/rel/className intact and ensure the fallback when member.name is
undefined is used to avoid runtime errors.
| {/* Upcoming Events Section */} | ||
| <div className='bg-secondary-200 rounded-3xl dark:bg-dark-card'> | ||
| <Container wide className='py-20'> | ||
| <div className='text-center'> | ||
| <Heading level={HeadingLevel.h2} typeStyle={HeadingTypeStyle.lg} className='mb-4 text-gray-900'> | ||
| Upcoming Events | ||
| </Heading> | ||
| <Paragraph | ||
| typeStyle={ParagraphTypeStyle.md} | ||
| className='mx-auto mb-12 max-w-2xl dark:text-gray-400 text-gray-600' | ||
| > | ||
| Don't miss out on these amazing community gatherings | ||
| </Paragraph> | ||
| </div> | ||
|
|
||
| <div className='mt-12 lg:flex lg:flex-row-reverse lg:items-center lg:gap-12'> | ||
| <section className='mt-10 lg:mt-0 lg:flex-1'> | ||
| <Calendar size={2} /> | ||
| </section> | ||
| <section className='mt-10 lg:mt-0 lg:max-w-xl lg:text-left'> | ||
| <div> | ||
| <Heading level={HeadingLevel.h3} typeStyle={HeadingTypeStyle.mdSemibold} className='text-gray-900'> | ||
| Join AsyncAPI meetings | ||
| </Heading> | ||
| <Paragraph typeStyle={ParagraphTypeStyle.md} className='mt-4 dark:text-gray-400 text-gray-600'> | ||
| We meet every week to discuss the future of AsyncAPI and event-driven architectures. Join us to share | ||
| your ideas, ask questions, and connect with the community.{' '} | ||
| <TextLink href='/community/meetings' className='text-indigo-600 hover:text-indigo-700'> | ||
| Learn more about our meetings | ||
| </TextLink> | ||
| </Paragraph> | ||
| <ul className='mt-8 flex flex-wrap justify-center gap-3 lg:justify-start'> | ||
| <li> | ||
| <GoogleCalendarButton href='https://calendar.google.com/calendar/u/3?cid=Y19xOXRzZWlnbG9tZHNqNm5qdWh2YnB0czExY0Bncm91cC5jYWxlbmRhci5nb29nbGUuY29t' /> | ||
| </li> | ||
| <li> | ||
| <SubscribeButton href='/community/meetings' /> | ||
| </li> | ||
| <li> | ||
| <ICSFileButton href='https://calendar.google.com/calendar/ical/c_q9tseiglomdsj6njuhvbpts11c%40group.calendar.google.com/public/basic.ics' /> | ||
| </li> | ||
| </ul> | ||
| </div> | ||
| </section> | ||
| </div> | ||
| </Container> | ||
| </div> | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix dark‑mode contrast in “Upcoming Events” section
This block uses bg-secondary-200 rounded-3xl dark:bg-dark-card, but key text still forces light-mode colors only:
- Heading “Upcoming Events” (Line 123) uses
text-gray-900with nodark:override on a dark card. - Subheading “Join AsyncAPI meetings” (Line 140) likewise uses
text-gray-900. - The inline
TextLink(Line 146) keepstext-indigo-600 hover:text-indigo-700only.
In dark mode those colors will be low-contrast against dark:bg-dark-card, especially the heading and link. Consider adding dark variants, e.g.:
- <Heading ... className='mb-4 text-gray-900'>
+ <Heading ... className='mb-4 text-gray-900 dark:text-dark-heading'>
- <Heading ... className='text-gray-900'>
+ <Heading ... className='text-gray-900 dark:text-dark-heading'>
- <TextLink ... className='text-indigo-600 hover:text-indigo-700'>
+ <TextLink ... className='text-indigo-400 hover:text-indigo-300 dark:text-indigo-300 dark:hover:text-indigo-200'>This should address the previously reported dark-mode readability issues for events titles/links.
📝 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.
| {/* Upcoming Events Section */} | |
| <div className='bg-secondary-200 rounded-3xl dark:bg-dark-card'> | |
| <Container wide className='py-20'> | |
| <div className='text-center'> | |
| <Heading level={HeadingLevel.h2} typeStyle={HeadingTypeStyle.lg} className='mb-4 text-gray-900'> | |
| Upcoming Events | |
| </Heading> | |
| <Paragraph | |
| typeStyle={ParagraphTypeStyle.md} | |
| className='mx-auto mb-12 max-w-2xl dark:text-gray-400 text-gray-600' | |
| > | |
| Don't miss out on these amazing community gatherings | |
| </Paragraph> | |
| </div> | |
| <div className='mt-12 lg:flex lg:flex-row-reverse lg:items-center lg:gap-12'> | |
| <section className='mt-10 lg:mt-0 lg:flex-1'> | |
| <Calendar size={2} /> | |
| </section> | |
| <section className='mt-10 lg:mt-0 lg:max-w-xl lg:text-left'> | |
| <div> | |
| <Heading level={HeadingLevel.h3} typeStyle={HeadingTypeStyle.mdSemibold} className='text-gray-900'> | |
| Join AsyncAPI meetings | |
| </Heading> | |
| <Paragraph typeStyle={ParagraphTypeStyle.md} className='mt-4 dark:text-gray-400 text-gray-600'> | |
| We meet every week to discuss the future of AsyncAPI and event-driven architectures. Join us to share | |
| your ideas, ask questions, and connect with the community.{' '} | |
| <TextLink href='/community/meetings' className='text-indigo-600 hover:text-indigo-700'> | |
| Learn more about our meetings | |
| </TextLink> | |
| </Paragraph> | |
| <ul className='mt-8 flex flex-wrap justify-center gap-3 lg:justify-start'> | |
| <li> | |
| <GoogleCalendarButton href='https://calendar.google.com/calendar/u/3?cid=Y19xOXRzZWlnbG9tZHNqNm5qdWh2YnB0czExY0Bncm91cC5jYWxlbmRhci5nb29nbGUuY29t' /> | |
| </li> | |
| <li> | |
| <SubscribeButton href='/community/meetings' /> | |
| </li> | |
| <li> | |
| <ICSFileButton href='https://calendar.google.com/calendar/ical/c_q9tseiglomdsj6njuhvbpts11c%40group.calendar.google.com/public/basic.ics' /> | |
| </li> | |
| </ul> | |
| </div> | |
| </section> | |
| </div> | |
| </Container> | |
| </div> | |
| {/* Upcoming Events Section */} | |
| <div className='bg-secondary-200 rounded-3xl dark:bg-dark-card'> | |
| <Container wide className='py-20'> | |
| <div className='text-center'> | |
| <Heading level={HeadingLevel.h2} typeStyle={HeadingTypeStyle.lg} className='mb-4 text-gray-900 dark:text-dark-heading'> | |
| Upcoming Events | |
| </Heading> | |
| <Paragraph | |
| typeStyle={ParagraphTypeStyle.md} | |
| className='mx-auto mb-12 max-w-2xl dark:text-gray-400 text-gray-600' | |
| > | |
| Don't miss out on these amazing community gatherings | |
| </Paragraph> | |
| </div> | |
| <div className='mt-12 lg:flex lg:flex-row-reverse lg:items-center lg:gap-12'> | |
| <section className='mt-10 lg:mt-0 lg:flex-1'> | |
| <Calendar size={2} /> | |
| </section> | |
| <section className='mt-10 lg:mt-0 lg:max-w-xl lg:text-left'> | |
| <div> | |
| <Heading level={HeadingLevel.h3} typeStyle={HeadingTypeStyle.mdSemibold} className='text-gray-900 dark:text-dark-heading'> | |
| Join AsyncAPI meetings | |
| </Heading> | |
| <Paragraph typeStyle={ParagraphTypeStyle.md} className='mt-4 dark:text-gray-400 text-gray-600'> | |
| We meet every week to discuss the future of AsyncAPI and event-driven architectures. Join us to share | |
| your ideas, ask questions, and connect with the community.{' '} | |
| <TextLink href='/community/meetings' className='text-indigo-600 hover:text-indigo-700 dark:text-indigo-300 dark:hover:text-indigo-200'> | |
| Learn more about our meetings | |
| </TextLink> | |
| </Paragraph> | |
| <ul className='mt-8 flex flex-wrap justify-center gap-3 lg:justify-start'> | |
| <li> | |
| <GoogleCalendarButton href='https://calendar.google.com/calendar/u/3?cid=Y19xOXRzZWlnbG9tZHNqNm5qdWh2YnB0czExY0Bncm91cC5jYWxlbmRhci5nb29nbGUuY29t' /> | |
| </li> | |
| <li> | |
| <SubscribeButton href='/community/meetings' /> | |
| </li> | |
| <li> | |
| <ICSFileButton href='https://calendar.google.com/calendar/ical/c_q9tseiglomdsj6njuhvbpts11c%40group.calendar.google.com/public/basic.ics' /> | |
| </li> | |
| </ul> | |
| </div> | |
| </section> | |
| </div> | |
| </Container> | |
| </div> |
🤖 Prompt for AI Agents
pages/community/events-and-updates.tsx lines 119-166: headings and link lack
dark-mode color variants causing poor contrast on dark:bg-dark-card; update the
Heading at line 123 and the Heading at line 140 to include dark: text classes
(e.g., replace or augment text-gray-900 with text-gray-900 dark:text-gray-100 or
dark:text-gray-50), and update the inline TextLink at line 146 to include dark
variants for normal and hover states (e.g., text-indigo-600
hover:text-indigo-700 dark:text-indigo-400 hover:dark:text-indigo-300); ensure
any other hardcoded light colors in this block are given appropriate dark:
counterparts to restore sufficient contrast.
| {/* From the Blogs Section */} | ||
| <div className='bg-white dark:bg-dark-background'> | ||
| <Container wide className='py-20' data-testid='EventsAndUpdates-Blogs'> | ||
| <div className='mb-12 flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center'> | ||
| <div> | ||
| <Heading level={HeadingLevel.h2} typeStyle={HeadingTypeStyle.lg} className='mb-2 text-gray-900'> | ||
| From the Blogs | ||
| </Heading> | ||
| <Paragraph typeStyle={ParagraphTypeStyle.md} className='text-gray-600'> | ||
| Stay connected with the pulse of our community | ||
| </Paragraph> | ||
| </div> | ||
| <Button | ||
| text='Read All Posts' | ||
| href='/blog' | ||
| className='rounded-lg bg-indigo-600 px-8 py-3 font-semibold text-white shadow-lg hover:bg-indigo-700' | ||
| bgClassName='bg-indigo-600 hover:bg-indigo-700' | ||
| /> | ||
| </div> | ||
| <div className='mt-12'> | ||
| <NewsroomBlogPosts /> | ||
| </div> | ||
| </Container> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also adjust dark‑mode text for “From the Blogs” section
This section uses bg-white dark:bg-dark-background, but:
- The H2 (Line 172) is
text-gray-900only. - The paragraph (Line 175) is
text-gray-600only.
In dark mode that will be muted against the dark background. Suggest adding dark variants similar to other sections:
- className='mb-2 text-gray-900'
+ className='mb-2 text-gray-900 dark:text-dark-heading'
- <Paragraph ... className='text-gray-600'>
+ <Paragraph ... className='text-gray-600 dark:text-gray-400'>This keeps blog titles/summary readable in dark mode.
📝 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.
| {/* From the Blogs Section */} | |
| <div className='bg-white dark:bg-dark-background'> | |
| <Container wide className='py-20' data-testid='EventsAndUpdates-Blogs'> | |
| <div className='mb-12 flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center'> | |
| <div> | |
| <Heading level={HeadingLevel.h2} typeStyle={HeadingTypeStyle.lg} className='mb-2 text-gray-900'> | |
| From the Blogs | |
| </Heading> | |
| <Paragraph typeStyle={ParagraphTypeStyle.md} className='text-gray-600'> | |
| Stay connected with the pulse of our community | |
| </Paragraph> | |
| </div> | |
| <Button | |
| text='Read All Posts' | |
| href='/blog' | |
| className='rounded-lg bg-indigo-600 px-8 py-3 font-semibold text-white shadow-lg hover:bg-indigo-700' | |
| bgClassName='bg-indigo-600 hover:bg-indigo-700' | |
| /> | |
| </div> | |
| <div className='mt-12'> | |
| <NewsroomBlogPosts /> | |
| </div> | |
| </Container> | |
| </div> | |
| {/* From the Blogs Section */} | |
| <div className='bg-white dark:bg-dark-background'> | |
| <Container wide className='py-20' data-testid='EventsAndUpdates-Blogs'> | |
| <div className='mb-12 flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center'> | |
| <div> | |
| <Heading level={HeadingLevel.h2} typeStyle={HeadingTypeStyle.lg} className='mb-2 text-gray-900 dark:text-dark-heading'> | |
| From the Blogs | |
| </Heading> | |
| <Paragraph typeStyle={ParagraphTypeStyle.md} className='text-gray-600 dark:text-gray-400'> | |
| Stay connected with the pulse of our community | |
| </Paragraph> | |
| </div> | |
| <Button | |
| text='Read All Posts' | |
| href='/blog' | |
| className='rounded-lg bg-indigo-600 px-8 py-3 font-semibold text-white shadow-lg hover:bg-indigo-700' | |
| bgClassName='bg-indigo-600 hover:bg-indigo-700' | |
| /> | |
| </div> | |
| <div className='mt-12'> | |
| <NewsroomBlogPosts /> | |
| </div> | |
| </Container> | |
| </div> |
🤖 Prompt for AI Agents
pages/community/events-and-updates.tsx around lines 167 to 190: the "From the
Blogs" H2 and Paragraph only specify light-mode text colors (text-gray-900 and
text-gray-600) which will be hard to read against the dark background; update
the Heading to include a dark-mode class (e.g., add dark:text-white or
dark:text-gray-100) and update the Paragraph to include a darker-mode variant
(e.g., dark:text-gray-300 or similar) to match other sections so both title and
summary remain readable in dark mode.
| const [currentPage, setCurrentPage] = useState(1); | ||
| const [searchTerm, setSearchTerm] = useState(''); | ||
| const [filterType, setFilterType] = useState<'all' | 'maintainer' | 'available' | 'company'>('all'); | ||
| const membersPerPage = 9; | ||
|
|
||
| // Filter members | ||
| const filteredMembers = tscMembers.filter((member) => { | ||
| const matchesSearch = | ||
| member.name.toLowerCase().includes(searchTerm.toLowerCase()) || | ||
| (member.github && member.github.toLowerCase().includes(searchTerm.toLowerCase())); | ||
|
|
||
| const matchesFilter = | ||
| filterType === 'all' || | ||
| (filterType === 'maintainer' && member.repos && member.repos.length > 0) || | ||
| (filterType === 'available' && member.availableForHire) || | ||
| (filterType === 'company' && member.company); | ||
|
|
||
| return matchesSearch && matchesFilter; | ||
| }); | ||
|
|
||
| const totalPages = Math.ceil(filteredMembers.length / membersPerPage); | ||
| const indexOfLastMember = currentPage * membersPerPage; | ||
| const indexOfFirstMember = indexOfLastMember - membersPerPage; | ||
| const currentMembers = filteredMembers.slice(indexOfFirstMember, indexOfLastMember); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reset pagination when filters/search change and align search placeholder.
If a user is on a higher page and then changes the search term or filter, filteredMembers.length can drop so that currentPage > totalPages. In that case the grid renders no members while the “Showing X of Y” text still updates, which is confusing.
Also, the placeholder says “Search members by role or name...”, but the filter only checks name and github, not a role field.
Consider:
- Resetting or clamping
currentPagewhenever filters/search change, e.g. by setting page to1inside the searchonChangeand each filter button handler. - Or, adding a
useEffectafterfilteredMembers:
+ useEffect(() => {
+ setCurrentPage(1);
+ }, [searchTerm, filterType, filteredMembers.length]);- Updating the placeholder to match behavior, e.g. “Search members by name or GitHub handle...”.
- Optionally, show a small “No members found for this filter” message when
filteredMembers.length === 0.
Also applies to: 222-231, 277-279
🤖 Prompt for AI Agents
In pages/community/tsc.tsx around lines 64 to 88 (also apply similar changes at
222-231 and 277-279), currentPage can become greater than totalPages after
search/filter changes causing an empty grid; update the code to clamp or reset
pagination whenever searchTerm or filterType (or filteredMembers) change—add a
useEffect that sets currentPage to 1 if totalPages === 0 or currentPage >
totalPages (or setCurrentPage(1) on search/filter handlers), update the search
input placeholder to “Search members by name or GitHub handle...” to match the
actual fields searched, and optionally render a “No members found for this
filter” message when filteredMembers.length === 0.
| icon={IconGear} | ||
| title='400% Community Growth' | ||
| description="AsyncAPI thrives on community. Expanding contributions, sponsorships, and | ||
| engagement ensures continued success. Let's grow together." | ||
| /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix invalid multi‑line description string in GoalCardRoadmapPage
Lines [549–553]: description is passed as a double‑quoted string spanning multiple lines, which is invalid TSX/JS syntax and will fail to compile. It also contains "Let's" which requires either double quotes or escaping if you switch to single quotes.
You can fix it by keeping it on a single line or by using a JSX fragment:
- <GoalCardRoadmapPage
- icon={IconGear}
- title='400% Community Growth'
- description="AsyncAPI thrives on community. Expanding contributions, sponsorships, and
- engagement ensures continued success. Let's grow together."
- />
+ <GoalCardRoadmapPage
+ icon={IconGear}
+ title='400% Community Growth'
+ description="AsyncAPI thrives on community. Expanding contributions, sponsorships, and engagement ensures continued success. Let's grow together."
+ />(or wrap the text in <>{'...'}</> if you prefer JSX content).
📝 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.
| icon={IconGear} | |
| title='400% Community Growth' | |
| description="AsyncAPI thrives on community. Expanding contributions, sponsorships, and | |
| engagement ensures continued success. Let's grow together." | |
| /> | |
| icon={IconGear} | |
| title='400% Community Growth' | |
| description="AsyncAPI thrives on community. Expanding contributions, sponsorships, and engagement ensures continued success. Let's grow together." | |
| /> |
🤖 Prompt for AI Agents
In pages/roadmap.tsx around lines 549 to 553, the description prop is written as
a multi-line double-quoted string which is invalid TSX and will fail to compile
(and contains "Let's" which needs proper quoting/escaping); replace the
multi-line quoted string with either a single-line string literal, a JSX
fragment containing the text, or a JS string expression with proper
quoting/escaping (e.g., use a single-line description="..." or
description={<>{'...'} </>} or description={'...'} with the apostrophe escaped)
so the prop is valid TSX and compiles.
| githubUrl?: string; | ||
| twitterUrl?: string; | ||
| linkedinUrl?: string; | ||
| reposList?: { name: string; url: string }[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify the relationship between github and githubUrl fields.
The Tsc interface now contains both github (line 44, required) and githubUrl (line 53, optional). Based on learnings, the github field contains usernames that are converted to URLs by addAdditionalUserInfo(). Please clarify:
- Is
githubUrlthe processed version ofgithub? - Are both fields needed, or is there potential duplication?
- Should consumers use
githubUrlwhen available and fall back to constructing fromgithub?
Run the following script to check how these fields are used:
Based on learnings
🏁 Script executed:
#!/bin/bash
# Description: Check usage of github and githubUrl fields in Tsc interface
# Search for Tsc interface usage patterns
echo "=== Searching for Tsc type usage ==="
rg -n "Tsc" --type=ts --type=tsx -C3
echo -e "\n=== Searching for githubUrl field access ==="
rg -n "\.githubUrl" --type=ts --type=tsx -C3
echo -e "\n=== Searching for github field access in TSC context ==="
rg -n "tsc.*\.github|member\.github" --type=ts --type=tsx -C3
echo -e "\n=== Check addAdditionalUserInfo implementation ==="
rg -n "addAdditionalUserInfo" --type=ts --type=tsx -C5Length of output: 304
---
🏁 Script executed:
```shell
#!/bin/bash
# Description: Check usage of github and githubUrl fields in Tsc interface
# Search for Tsc interface usage patterns
echo "=== Searching for Tsc type usage ==="
rg -n "Tsc" --type=ts -C3
echo -e "\n=== Searching for githubUrl field access ==="
rg -n "\.githubUrl" -g "*.ts" -g "*.tsx" -C3
echo -e "\n=== Searching for github field access in context ==="
rg -n "\.github\b" -g "*.ts" -g "*.tsx" -C2
echo -e "\n=== Check addAdditionalUserInfo implementation ==="
rg -n "addAdditionalUserInfo" -g "*.ts" -g "*.tsx" -C5
echo -e "\n=== Search for Tsc interface definition ==="
rg -n "interface Tsc" -g "*.ts" -g "*.tsx" -A15
Length of output: 28653
Fix CommunityLayout.tsx to create githubUrl instead of mutating github field.
A critical type safety issue was found: CommunityLayout.tsx (line 48) mutates the github field to become a full URL (userData.github = https://www.github.com/${userData.github}`), which violates the type contract. The Tsc interface defines github as a required string intended to be a username.
The other implementations—pages/community/tsc.tsx (line 31) and pages/community/ambassadors/index.tsx (line 37)—correctly handle this by creating a separate githubUrl field while keeping github as the username.
Changes needed in components/layout/CommunityLayout.tsx:
- Line 48: Replace mutation of
githubwith creation ofgithubUrlfield - Line 59: Update avatar URL to use
githubUrlinstead ofgithub - Line 187: Extract username from
githubdirectly (since it's kept as username) - Line 208: Update to use
githubUrlfor the href
Alternatively, if CommunityLayout.tsx intentionally converts github to URL for display purposes, the Tsc interface and component contracts need updating to reflect this consistently.
🤖 Prompt for AI Agents
In types/pages/community/Community.ts around lines 53-56, CommunityLayout.tsx is
mutating the required github username field into a full URL which breaks the Tsc
type contract; update components/layout/CommunityLayout.tsx so that at line 48
it does not overwrite userData.github but instead creates userData.githubUrl =
`https://www.github.com/${userData.github}`, change the avatar construction at
line 59 to use githubUrl, ensure any username extraction at line 187 reads
userData.github (the username), and update the link href at line 208 to use
githubUrl; alternatively, if the intent is to store a URL, update the Tsc type
to reflect github?: string | url and adjust all usages consistently.
Signed-off-by: Shriya-Chauhan <[email protected]>
fixed |
Signed-off-by: Shriya-Chauhan <[email protected]>
Signed-off-by: Shriya-Chauhan <[email protected]>










This PR introduces several new pages and updates across the site to enhance navigation and improve overall user experience.
🔧 Changes Included
Added new pages:
Fixes and updates:
Fixed mobile and desktop layout issues
Updated heading sizes for better hierarchy and consistency
Redesigned Roadmaps page
Updated Ambassadors page content and layout
Improved Blog desktop design
Added new graphics and assets where needed
related issues : #3669
Summary by CodeRabbit
New Features
Bug Fixes & Improvements
Documentation