Skip to content

Conversation

@ProfessorSeb
Copy link
Collaborator

No description provided.

Signed-off-by: Sebastian Maniak <sebastian@maniak.io>
Signed-off-by: Sebastian Maniak <sebastian@maniak.io>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request adds a floating hackathon promotion banner to the website. The banner advertises "MCP_HACK//26", a virtual hackathon running from Feb 2 – Mar 1, 2026, with $5,000 in prizes. The banner features a cyberpunk-themed design with animated glowing effects, scanlines, and flickering text. Users can dismiss the banner, and the dismissal state is stored in sessionStorage.

Changes:

  • Added a new HackathonBanner component with animated styling and dismissible functionality
  • Integrated the banner into the root layout to display site-wide
  • Updated a navigation description and sitemap dates, and bumped a CloudFlare deployment dependency

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/components/hackathon-banner.tsx New component implementing a fixed-position promotional banner with cyberpunk styling, animations, and sessionStorage-based dismissal
src/app/layout.tsx Imported and rendered the HackathonBanner component in the root layout
src/config/navigation.json Expanded "docs" to "documentation" in kmcp API reference description (unrelated to banner)
public/sitemap.xml Updated all lastmod dates from 2026-01-12 to 2026-01-29
package.json Updated @opennextjs/cloudflare from 1.6.5 to 1.14.8 (unrelated to banner)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +11 to +22
const wasDismissed = sessionStorage.getItem('hackathon-banner-dismissed');
if (wasDismissed) {
setDismissed(true);
} else {
setVisible(true);
}
}, []);

const handleDismiss = () => {
setDismissed(true);
setVisible(false);
sessionStorage.setItem('hackathon-banner-dismissed', 'true');
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

Accessing sessionStorage directly in useEffect can cause hydration errors in Next.js since sessionStorage is undefined during server-side rendering. While the initial state is set to false which prevents rendering issues, it's better practice to wrap the sessionStorage access in a check for the window object or use a try-catch block to handle potential SSR errors more gracefully.

Suggested change
const wasDismissed = sessionStorage.getItem('hackathon-banner-dismissed');
if (wasDismissed) {
setDismissed(true);
} else {
setVisible(true);
}
}, []);
const handleDismiss = () => {
setDismissed(true);
setVisible(false);
sessionStorage.setItem('hackathon-banner-dismissed', 'true');
try {
if (typeof window === 'undefined' || !window.sessionStorage) {
return;
}
const wasDismissed = window.sessionStorage.getItem('hackathon-banner-dismissed');
if (wasDismissed) {
setDismissed(true);
} else {
setVisible(true);
}
} catch {
// If sessionStorage is unavailable or throws, fall back to default visibility.
}
}, []);
const handleDismiss = () => {
setDismissed(true);
setVisible(false);
try {
if (typeof window !== 'undefined' && window.sessionStorage) {
window.sessionStorage.setItem('hackathon-banner-dismissed', 'true');
}
} catch {
// Ignore storage errors; dismissal still applies for this session.
}

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +75
@keyframes cyber-glow {
0%, 100% {
box-shadow: 0 0 15px rgba(0, 255, 255, 0.4),
0 0 30px rgba(0, 255, 255, 0.1),
inset 0 0 20px rgba(0, 255, 255, 0.05);
}
50% {
box-shadow: 0 0 25px rgba(0, 255, 255, 0.6),
0 0 50px rgba(0, 255, 255, 0.2),
inset 0 0 30px rgba(0, 255, 255, 0.1);
}
}
@keyframes scanline {
0% { transform: translateY(-100%); }
100% { transform: translateY(100%); }
}
@keyframes flicker {
0%, 95%, 100% { opacity: 1; }
96% { opacity: 0.8; }
97% { opacity: 1; }
98% { opacity: 0.6; }
}
.hackathon-float {
animation: cyber-glow 3s ease-in-out infinite;
}
.hackathon-float::after {
content: '';
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: linear-gradient(transparent 50%, rgba(0, 255, 255, 0.03) 50%);
background-size: 100% 4px;
pointer-events: none;
}
.hackathon-scanline {
position: absolute;
top: 0; left: 0; right: 0;
height: 8px;
background: linear-gradient(to bottom, transparent, rgba(0, 255, 255, 0.1), transparent);
animation: scanline 3s linear infinite;
pointer-events: none;
}
.hackathon-title {
animation: flicker 4s infinite;
text-shadow: 0 0 10px rgba(0, 255, 255, 0.8),
0 0 20px rgba(0, 255, 255, 0.4);
}
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

The banner animations (cyber-glow, scanline, flicker) run continuously even when not visible, which could impact performance. Consider adding 'prefers-reduced-motion' media queries to respect user accessibility preferences and disable animations for users who have motion sensitivity. This can be done by wrapping the animations in a '@media (prefers-reduced-motion: no-preference)' query.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +22
const wasDismissed = sessionStorage.getItem('hackathon-banner-dismissed');
if (wasDismissed) {
setDismissed(true);
} else {
setVisible(true);
}
}, []);

const handleDismiss = () => {
setDismissed(true);
setVisible(false);
sessionStorage.setItem('hackathon-banner-dismissed', 'true');
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

The banner uses sessionStorage which means it will reappear every time the user opens a new browser session. For a promotional banner that users dismiss, localStorage might be more appropriate to remember the dismissal across sessions. Alternatively, if the intent is to show the banner in each session, this should be documented in a comment to make the behavior explicit.

Copilot uses AI. Check for mistakes.
"title": "API docs",
"href": "/docs/kmcp/reference/api-ref",
"description": "kmcp API reference docs"
"description": "kmcp API reference documentation"
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

This change from "kmcp API reference docs" to "kmcp API reference documentation" appears unrelated to adding a hackathon banner. While this is a minor improvement in consistency (using the full word "documentation"), it should either be in a separate PR or the PR title/description should mention this change.

Copilot uses AI. Check for mistakes.
Comment on lines +97 to +131
// Virtual Hackathon
</div>
<div
className="hackathon-title text-xl font-bold mb-2 font-mono"
style={{ color: '#00ffff' }}
>
MCP_HACK//26
</div>
<p
className="text-xs leading-relaxed mb-4"
style={{ color: 'rgba(200, 210, 255, 0.7)' }}
>
Build with <span style={{ color: '#00ffff' }}>MCP</span> &amp;{' '}
<span style={{ color: '#ff00ff' }}>AI Agents</span>.<br />
$5,000 in prizes. Feb 2 &ndash; Mar 1, 2026.
</p>
<a
href="https://aihackathon.dev/"
target="_blank"
rel="noopener noreferrer"
className="inline-block w-full text-center text-sm font-bold rounded px-4 py-2.5 transition-all duration-200 uppercase tracking-widest hover:shadow-[0_0_20px_rgba(0,255,255,0.5)]"
style={{
background: 'linear-gradient(90deg, #00ffff, #00ccff)',
color: '#0a0e1a',
letterSpacing: '0.1em',
}}
onMouseOver={(e) => {
e.currentTarget.style.background = 'linear-gradient(90deg, #00ffff, #ff00ff)';
}}
onMouseOut={(e) => {
e.currentTarget.style.background = 'linear-gradient(90deg, #00ffff, #00ccff)';
}}
>
Register Now &rarr;
</a>
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

The banner content has hardcoded values including dates, URLs, and prize amounts. Consider extracting these to constants or a configuration object at the top of the file for easier updates when the hackathon details change or when creating similar banners in the future.

Copilot uses AI. Check for mistakes.
"devDependencies": {
"@eslint/eslintrc": "^3",
"@opennextjs/cloudflare": "^1.6.5",
"@opennextjs/cloudflare": "^1.14.8",
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

The @opennextjs/cloudflare package is being updated from 1.6.5 to 1.14.8, which spans 8 minor versions. This change appears unrelated to adding a hackathon banner and should ideally be in a separate PR. Additionally, ensure that this update has been tested with the Cloudflare deployment workflow (build:worker, deploy:worker) and that there are no breaking changes in the intermediate versions that could affect the build or deployment process.

Copilot uses AI. Check for mistakes.
}}
onMouseOut={(e) => {
e.currentTarget.style.background = 'linear-gradient(90deg, #00ffff, #00ccff)';
}}
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

The inline onMouseOver and onMouseOut handlers change the background gradient on hover. While this works for mouse users, keyboard users navigating with Tab won't see this visual feedback. Consider adding corresponding onFocus and onBlur handlers with the same logic to ensure keyboard accessibility.

Suggested change
}}
}}
onFocus={(e) => {
e.currentTarget.style.background = 'linear-gradient(90deg, #00ffff, #ff00ff)';
}}
onBlur={(e) => {
e.currentTarget.style.background = 'linear-gradient(90deg, #00ffff, #00ccff)';
}}

Copilot uses AI. Check for mistakes.
}
`}</style>
<div
className="hackathon-float fixed top-24 right-6 z-50 max-w-xs rounded-xl overflow-hidden transition-all duration-300"
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

The fixed positioning with 'top-24' and 'right-6' may cause issues on mobile devices where the banner could overlap with content or extend beyond the viewport. Consider adding responsive positioning classes like 'md:top-24 md:right-6 top-4 right-4' and potentially adjusting the max-width for smaller screens to ensure the banner remains accessible and doesn't obstruct important content.

Suggested change
className="hackathon-float fixed top-24 right-6 z-50 max-w-xs rounded-xl overflow-hidden transition-all duration-300"
className="hackathon-float fixed top-4 right-4 md:top-24 md:right-6 z-50 max-w-[90vw] md:max-w-xs rounded-xl overflow-hidden transition-all duration-300"

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Mobile view renders like this:
image

Copy link
Collaborator

Choose a reason for hiding this comment

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

the chat-proposed solution floats over the top menubar, so not sure it's better:

image

@ProfessorSeb ProfessorSeb merged commit 7e3ba38 into main Jan 29, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants