Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/app/[locale]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getTranslations } from 'next-intl/server';
import HeroSection from '@/components/shared/HeroSection';
import HeroSection from '@/components/home/HeroSection';

export async function generateMetadata({
params,
Expand Down
186 changes: 157 additions & 29 deletions frontend/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--animate-float-soft: float-soft 8s ease-in-out infinite;
--animate-pulse-soft: pulse-soft 2.8s ease-in-out infinite;
--animate-wave-clip: wave-clip 5s ease-in-out infinite;
}

@theme {
Expand Down Expand Up @@ -154,51 +153,183 @@
user-select: none; /* Standard */
}

@keyframes float-soft {
@keyframes progress {
from {
width: 100%;
}
to {
width: 0%;
}
}

@keyframes shrink {
from {
transform: scaleX(1);
}
to {
transform: scaleX(0);
}
}

@keyframes wave-clip {
0%,
100% {
transform: translateY(8px) scale(1);
clip-path: polygon(
0% 50%,
15% 48%,
32% 52%,
54% 60%,
70% 62%,
84% 60%,
100% 55%,
100% 100%,
0% 100%
);
}
50% {
transform: translateY(-16px) scale(1.04);
clip-path: polygon(
0% 65%,
16% 70%,
34% 72%,
51% 68%,
67% 58%,
84% 52%,
100% 48%,
100% 100%,
0% 100%
);
}
}

@keyframes pulse-soft {
0%,
@keyframes slide-up-gradient {
0% {
clip-path: inset(100% 0 0 0);
}
100% {
opacity: 0.2;
transform: scale(1);
clip-path: inset(0 0 0 0);
}
}

@keyframes wave-slide-up {
0% {
clip-path: polygon(
0% 100%,
10% 100%,
20% 100%,
30% 100%,
40% 100%,
50% 100%,
60% 100%,
70% 100%,
80% 100%,
90% 100%,
100% 100%,
100% 100%,
0% 100%
);
}
50% {
opacity: 0.4;
transform: scale(1.08);
clip-path: polygon(
0% 60%,
10% 50%,
20% 45%,
30% 48%,
40% 55%,
50% 50%,
60% 45%,
70% 48%,
80% 52%,
90% 50%,
100% 55%,
100% 100%,
0% 100%
);
}
100% {
clip-path: polygon(
0% 0%,
10% 0%,
20% 0%,
30% 0%,
40% 0%,
50% 0%,
60% 0%,
70% 0%,
80% 0%,
90% 0%,
100% 0%,
100% 100%,
0% 100%
);
}
}

@keyframes progress {
from {
width: 100%;
@keyframes text-fade-in {
0% {
opacity: 0;
transform: translateY(4px);
}
to {
width: 0%;
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes shimmer {

@keyframes card-breathe {
0% {
background-position: 200% 0;
transform: translate(0, 0) scale(1) rotate(var(--card-rotate, 0deg));
}
50% {
transform: translate(var(--card-x, 0), var(--card-y, 0)) scale(1.05)
rotate(calc(var(--card-rotate, 0deg) + var(--card-rotate-offset, 0deg)));
}
100% {
background-position: -200% 0;
transform: translate(0, 0) scale(1) rotate(var(--card-rotate, 0deg));
}
}

@keyframes shrink {
from {
transform: scaleX(1);
@layer utilities {
.wave-text-gradient {
animation: wave-clip 5s ease-in-out infinite;
}
to {
transform: scaleX(0);

.animate-slide-up-gradient {
animation: slide-up-gradient 1.2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

.animate-wave-slide-up {
animation: wave-slide-up 1.2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

.animate-text-fade-in {
animation: text-fade-in 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

.animate-card-breathe {
animation: card-breathe 6s ease-in-out infinite;
will-change: transform;
}

.left-4 .animate-card-breathe,
.md\:left-6 .animate-card-breathe,
.lg\:left-8 .animate-card-breathe,
.xl\:left-12 .animate-card-breathe {
--card-x: -30px;
--card-y: -25px;
--card-rotate: -10deg;
--card-rotate-offset: -3deg;
animation-delay: 0s;
}

.right-4 .animate-card-breathe,
.md\:right-6 .animate-card-breathe,
.lg\:right-8 .animate-card-breathe,
.xl\:right-12 .animate-card-breathe {
--card-x: 30px;
--card-y: 25px;
--card-rotate: 8deg;
--card-rotate-offset: 3deg;
animation-delay: 0.8s;
}
}

Expand All @@ -218,13 +349,11 @@
--color-ring: var(--foreground);

--card: var(--background);
/* CTA button: light theme (section is black) -> pink bg + white text */
/* CTA button: light theme (section is black) -> pink bg + white text */
--shop-cta-bg: #ff2d55;
--shop-cta-fg: oklch(0.985 0 0);

}


.dark .shop-scope {
/* dark: shop accent = magenta */
--accent: var(--accent-primary);
Expand All @@ -241,10 +370,9 @@
/* keep borders closer to previous shop look, derived (no hex) */
--border: color-mix(in oklab, var(--foreground) 18%, var(--background));
--input: color-mix(in oklab, var(--foreground) 18%, var(--background));
/* CTA button: dark theme (section becomes white) -> black bg + white text */
/* CTA button: dark theme (section becomes white) -> black bg + white text */
--shop-cta-bg: var(--background);
--shop-cta-fg: var(--foreground);

}

/* about-community */
Expand Down
6 changes: 1 addition & 5 deletions frontend/components/header/MainSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,7 @@ export function MainSwitcher({
}

return (
<main
className={
isQa || isHome ? 'mx-auto min-h-[80vh]' : 'mx-auto px-6 min-h-[80vh]'
}
>
<main className={isQa || isHome ? 'mx-auto' : 'mx-auto px-6 min-h-[80vh]'}>
{children}
</main>
);
Expand Down
30 changes: 30 additions & 0 deletions frontend/components/home/CodeCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { ReactNode } from 'react';

interface CodeCardProps {
fileName: string;
snippet: ReactNode;
className?: string;
}

export function CodeCard({ fileName, snippet, className }: CodeCardProps) {
return (
<div
className={`pointer-events-none absolute hidden md:block ${className}`}
aria-hidden="true"
>
<div className="rounded-2xl border border-border bg-card/60 backdrop-blur-xl shadow-xl dark:shadow-black/50 px-5 py-4 min-w-[230px] animate-card-breathe">
<div className="flex items-center justify-between mb-3 text-[10px] text-muted-foreground">
<div className="flex items-center gap-1.5">
<span className="h-2 w-2 rounded-full bg-red-400/90" />
<span className="h-2 w-2 rounded-full bg-yellow-400/90" />
<span className="h-2 w-2 rounded-full bg-green-400/90" />
</div>
<span className="font-medium">{fileName}</span>
</div>
<code className="text-[11px] whitespace-pre leading-relaxed font-mono text-foreground/90">
{snippet}
</code>
</div>
</div>
);
}
19 changes: 19 additions & 0 deletions frontend/components/home/HeroBackground.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export function HeroBackground() {
return (
<>
<div className="pointer-events-none absolute inset-0 opacity-60">
<div className="absolute -top-32 left-1/2 h-96 w-[36rem] -translate-x-1/2 rounded-full bg-[var(--accent-primary)]/20 blur-3xl" />
<div className="absolute bottom-[-12rem] left-1/4 h-[22rem] w-[22rem] rounded-full bg-[var(--accent-hover)]/15 blur-3xl" />
<div className="absolute bottom-[-10rem] right-0 h-[26rem] w-[26rem] rounded-full bg-[var(--accent-primary)]/25 blur-3xl" />
</div>

<div className="pointer-events-none absolute inset-0 opacity-40 dark:opacity-60">
<span className="absolute left-[10%] top-[18%] h-1 w-1 rounded-full bg-[var(--accent-primary)]" />
<span className="absolute left-[35%] top-[8%] h-1 w-1 rounded-full bg-[var(--accent-hover)]" />
<span className="absolute left-[70%] top-[16%] h-1 w-1 rounded-full bg-[var(--accent-primary)]" />
<span className="absolute left-[80%] top-[40%] h-1 w-1 rounded-full bg-[var(--accent-hover)]" />
<span className="absolute left-[18%] top-[60%] h-1 w-1 rounded-full bg-[var(--accent-primary)]" />
</div>
</>
);
}
56 changes: 56 additions & 0 deletions frontend/components/home/HeroCodeCards.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { CodeCard } from './CodeCard';

export function HeroCodeCards() {
return (
<>
<CodeCard
fileName="arrays.ts"
className="left-4 md:left-10 lg:left-8 xl:left-12 -top-2 md:-top-12 lg:-top-8 xl:-top-12 rotate-[-10deg]"
snippet={
<>
<span className="text-[var(--accent-primary)]">type</span> Arr1 = [
<span className="text-emerald-500 dark:text-emerald-400">
&apos;a&apos;
</span>
,{' '}
<span className="text-emerald-500 dark:text-emerald-400">
&apos;b&apos;
</span>
,{' '}
<span className="text-emerald-500 dark:text-emerald-400">
&apos;c&apos;
</span>
]{'\n'}
<span className="text-[var(--accent-primary)]">type</span> Arr2 = [
<span className="text-amber-500 dark:text-amber-400">3</span>,{' '}
<span className="text-amber-500 dark:text-amber-400">2</span>,{' '}
<span className="text-amber-500 dark:text-amber-400">1</span>]
</>
}
/>

<CodeCard
fileName="utils.js"
className="right-4 md:right-4 lg:right-8 xl:right-12 -bottom-2 md:-bottom-6 lg:-bottom-8 xl:-bottom-12 rotate-[8deg]"
snippet={
<>
<span className="text-[var(--accent-primary)]">function</span> sum(
<span className="text-emerald-500 dark:text-emerald-400">
a
</span>,{' '}
<span className="text-emerald-500 dark:text-emerald-400">b</span>){' '}
{'{'}
{'\n'}
{' '}
<span className="text-[var(--accent-primary)]">return</span>{' '}
<span className="text-emerald-500 dark:text-emerald-400">a</span>{' '}
<span className="text-purple-500 dark:text-purple-400">+</span>{' '}
<span className="text-emerald-500 dark:text-emerald-400">b</span>;
{'\n'}
{'}'}
</>
}
/>
</>
);
}
Loading