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
1 change: 1 addition & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MLBlock</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='8' fill='%23D97757'/><rect x='9' y='9' width='14' height='14' rx='3' fill='white'/></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@400;500;600&family=Nunito:wght@400;600;700;800&display=swap" rel="stylesheet">
Expand Down
Binary file added frontend/public/assets/poc-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/assets/team/ali.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/assets/team/chedli.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/assets/team/ilan.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/assets/team/sacha.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import useAppStore from './store/useAppStore'
import HomePage from './pages/HomePage'
import EditorPage from './pages/EditorPage'
import HowItWorksPage from './pages/HowItWorksPage'
import AboutPage from './pages/AboutPage'

export default function App() {
const screen = useAppStore(s => s.screen)
return screen === 'build' ? <EditorPage /> : <HomePage />
if (screen === 'build') return <EditorPage />
if (screen === 'how-it-works') return <HowItWorksPage />
if (screen === 'about') return <AboutPage />
return <HomePage />
}
207 changes: 158 additions & 49 deletions frontend/src/components/landing/FeaturesSection.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,165 @@
import React from 'react'
import React from 'react';

type Feature = { color: string; icon: React.ReactNode; title: string; desc: string }
type Feature = {
color: string;
icon: React.ReactNode;
title: string;
desc: string;
};
Comment on lines +1 to +8

const FEATURES: Feature[] = [
{
color: '#E59060',
icon: <div style={{ width: 18, height: 18, borderRadius: 6, background: 'rgba(255,255,255,.85)' }} />,
title: 'Comme un jeu de construction',
desc: "Attrape un bloc, dépose-le dans ton projet. Il s'emboîte tout seul à la bonne place. Aucune ligne à taper.",
},
{
color: '#B6A0E3',
icon: (
<div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
<div style={{ width: 18, height: 4, borderRadius: 2, background: 'rgba(255,255,255,.85)' }} />
<div style={{ width: 18, height: 4, borderRadius: 2, background: 'rgba(255,255,255,.85)' }} />
<div style={{ width: 11, height: 4, borderRadius: 2, background: 'rgba(255,255,255,.85)' }} />
</div>
),
title: 'Toutes les IA',
desc: 'Images, textes, tableaux de chiffres : reconnais, classe et prédis avec des modèles simples ou de vrais réseaux de neurones.',
},
{
color: '#7DAFEA',
icon: <span style={{ color: '#fff', fontSize: 12 }}>▶</span>,
title: 'Vois-le apprendre',
desc: "Appuie sur Lancer et regarde, tour après tour, ton modèle se tromper de moins en moins et devenir de plus en plus précis.",
},
]
{
color: '#E59060',
icon: (
<div
style={{
width: 18,
height: 18,
borderRadius: 6,
background: 'rgba(255,255,255,.85)',
}}
/>
),
title: 'Comme un jeu de construction',
desc: "Attrape un bloc, dépose-le dans ton projet. Il s'emboîte tout seul à la bonne place. Aucune ligne à taper.",
},
{
color: '#B6A0E3',
icon: (
<div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
<div
style={{
width: 18,
height: 4,
borderRadius: 2,
background: 'rgba(255,255,255,.85)',
}}
/>
<div
style={{
width: 18,
height: 4,
borderRadius: 2,
background: 'rgba(255,255,255,.85)',
}}
/>
<div
style={{
width: 11,
height: 4,
borderRadius: 2,
background: 'rgba(255,255,255,.85)',
}}
/>
</div>
),
title: 'Images, textes, tableaux',
desc: 'Images, textes, tableaux de chiffres : reconnais, classe et prédis avec des modèles simples ou de vrais réseaux de neurones.',
},
{
color: '#7DAFEA',
icon: <span style={{ color: '#fff', fontSize: 12 }}>▶</span>,
title: 'Vois-le apprendre',
desc: 'Appuie sur Lancer et regarde, tour après tour, ton modèle se tromper de moins en moins et devenir de plus en plus précis.',
},
];

export default function FeaturesSection() {
return (
<section style={{ background: '#1f1916', borderTop: '1px solid rgba(255,255,255,.05)' }}>
<div style={{ maxWidth: 1240, margin: '0 auto', padding: '72px 48px' }}>
<h2 style={{ fontFamily: "'Fredoka', sans-serif", fontWeight: 600, fontSize: 34, letterSpacing: '-.01em', margin: '0 0 8px' }}>
L'intelligence artificielle, en pièces à assembler
</h2>
<p style={{ color: '#b7ada3', fontSize: 17, fontWeight: 600, margin: '0 0 44px' }}>
Chaque étape pour faire apprendre une machine devient un bloc coloré.
</p>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 22 }}>
{FEATURES.map(({ color, icon, title, desc }) => (
<div key={title} style={{ background: '#251e1a', border: '1px solid rgba(255,255,255,.06)', borderRadius: 20, padding: 28 }}>
<div style={{ width: 46, height: 46, borderRadius: 14, background: color, boxShadow: '0 3px 0 rgba(0,0,0,.2)', display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 18 }}>
{icon}
</div>
<h3 style={{ fontFamily: "'Fredoka', sans-serif", fontWeight: 600, fontSize: 21, margin: '0 0 8px' }}>{title}</h3>
<p style={{ color: '#aba29a', fontSize: 15, lineHeight: 1.55, fontWeight: 600, margin: 0 }}>{desc}</p>
return (
<section
id="fonctionnalites"
style={{
background: '#1f1916',
borderTop: '1px solid rgba(255,255,255,.05)',
}}
>
<div
className="landing-section-pad"
style={{
maxWidth: 1240,
margin: '0 auto',
padding: '72px 48px',
}}
>
<h2
style={{
fontFamily: "'Fredoka', sans-serif",
fontWeight: 600,
fontSize: 34,
letterSpacing: '-.01em',
margin: '0 0 8px',
}}
>
L'intelligence artificielle, en pièces à assembler
</h2>
<p
style={{
color: '#b7ada3',
fontSize: 17,
fontWeight: 600,
margin: '0 0 44px',
}}
>
Chaque étape de l'apprentissage devient un bloc.
</p>
<div
className="landing-features-grid"
style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: 22,
}}
>
{FEATURES.map(({ color, icon, title, desc }) => (
<div
key={title}
style={{
background: '#251e1a',
border: '1px solid rgba(255,255,255,.06)',
borderRadius: 20,
padding: 28,
}}
>
<div
style={{
width: 46,
height: 46,
borderRadius: 14,
background: color,
boxShadow: '0 3px 0 rgba(0,0,0,.2)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginBottom: 18,
}}
>
{icon}
</div>
<h3
style={{
fontFamily: "'Fredoka', sans-serif",
fontWeight: 600,
fontSize: 21,
margin: '0 0 8px',
}}
>
{title}
</h3>
<p
style={{
color: '#aba29a',
fontSize: 15,
lineHeight: 1.55,
fontWeight: 600,
margin: 0,
}}
>
{desc}
</p>
</div>
))}
</div>
</div>
))}
</div>
</div>
</section>
)
</section>
);
}
36 changes: 10 additions & 26 deletions frontend/src/components/landing/HeroBlockStack.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useLayoutEffect } from 'react'
import { buildClusters, snapW, blockBorderRadius } from '../../utils/snapLogic'
import React from 'react'

type HeroBlock = {
key: number
Expand All @@ -18,29 +17,14 @@ const BLOCKS: HeroBlock[] = [
{ key: 4, bg: '#7DAFEA', color: '#2a211c', label: <>Apprendre <span style={{ background: 'rgba(255,255,255,.85)', padding: '2px 7px', borderRadius: 6 }}>10</span> tours</>, isLast: true },
]

export default function HeroBlockStack() {
useLayoutEffect(() => {
const timer = setTimeout(() => {
const els = [...document.querySelectorAll<HTMLElement>('[data-hero-block]')]
if (!els.length) return
const R = 12
const widths = els.map(el => el.offsetWidth)
const clusters = buildClusters(widths)
const bands = widths.map(w => snapW(w, clusters) ?? w)
const n = els.length
els.forEach((el, i) => {
el.style.minWidth = bands[i] + 'px'
if (i === 0) {
const br = bands[0] === bands[1] ? '0px' : R + 'px'
el.style.borderRadius = `14px ${br} ${br} 0px`
} else {
el.style.borderRadius = blockBorderRadius(i - 1, n - 1, bands.slice(1), bands[0])
}
})
}, 120)
return () => clearTimeout(timer)
}, [])
// Border radii are hardcoded to match the snap-aligned result (all 5 blocks at the same width).
function blockRadius(isHat?: boolean, isLast?: boolean) {
if (isHat) return '14px 0px 0px 0px'
if (isLast) return '0px 0px 12px 12px'
return '0px 0px 0px 0px'
}

export default function HeroBlockStack() {
return (
<div style={{ animation: 'mlbFloat 5s ease-in-out infinite' }}>
<div style={{ position: 'relative', padding: 26, background: '#211b18', border: '1px solid rgba(255,255,255,.07)', borderRadius: 24, boxShadow: '0 30px 60px rgba(0,0,0,.4)', maxWidth: 380, marginLeft: 'auto' }}>
Expand All @@ -53,7 +37,6 @@ export default function HeroBlockStack() {
{BLOCKS.map(({ key, bg, color, label, isHat, isLast }) => (
<div
key={key}
data-hero-block={key}
style={{
position: 'relative',
zIndex: BLOCKS.length - key,
Expand All @@ -62,11 +45,12 @@ export default function HeroBlockStack() {
fontWeight: 800,
fontSize: isHat ? 14 : 13.5,
padding: isHat ? '11px 16px 13px' : '13px 16px 11px',
borderRadius: isHat ? '14px 14px 0 0' : isLast ? '0 0 14px 14px' : 0,
borderRadius: blockRadius(isHat, isLast),
boxShadow: '0 2px 0 rgba(0,0,0,.18)',
display: 'inline-flex',
gap: 7,
alignItems: 'center',
minWidth: 270,
}}
>
{!isHat && <div style={{ position: 'absolute', top: 0, left: 20, width: 24, height: 11, background: '#211b18', borderRadius: '0 0 999px 999px' }} />}
Expand Down
17 changes: 11 additions & 6 deletions frontend/src/components/landing/HeroSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ import HeroBlockStack from './HeroBlockStack'
export default function HeroSection() {
const goBuild = useAppStore(s => s.goBuild)

const scrollToFeatures = () =>
document.getElementById('fonctionnalites')?.scrollIntoView({ behavior: 'smooth' })

return (
<section style={{ maxWidth: 1240, margin: '0 auto', padding: '48px 48px 90px', display: 'grid', gridTemplateColumns: '1.05fr .95fr', gap: 56, alignItems: 'center' }}>
<section className="landing-hero" style={{ maxWidth: 1240, margin: '0 auto', padding: '48px 48px 90px', display: 'grid', gridTemplateColumns: '1.05fr .95fr', gap: 56, alignItems: 'center' }}>
<div>
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 8, background: 'rgba(217,119,87,.14)', border: '1px solid rgba(217,119,87,.35)', color: '#E8915F', padding: '7px 14px', borderRadius: 999, fontWeight: 800, fontSize: 13, letterSpacing: '.02em' }}>
<span style={{ width: 7, height: 7, borderRadius: '50%', background: '#E8915F' }} />
SANS CODE · POUR APPRENDRE L'IA
Sans code, pour apprendre l'IA
</div>
<h1 style={{ fontFamily: "'Fredoka', sans-serif", fontWeight: 600, fontSize: 62, lineHeight: 1.04, letterSpacing: '-.02em', margin: '22px 0 0', textWrap: 'balance' }}>
Crée ton intelligence<br />artificielle, <span style={{ color: '#D97757' }}>bloc par bloc.</span>
</h1>
<p style={{ fontSize: 19, lineHeight: 1.55, color: '#b7ada3', maxWidth: 470, margin: '22px 0 0', fontWeight: 600 }}>
Empile des blocs colorés pour construire un modèle qui apprend tout seul : reconnaître des images, comprendre des phrases, deviner des réponses. Pas besoin de savoir programmer, il suffit d'assembler.
Empile des blocs pour construire un modèle qui apprend tout seul : reconnaître des images, comprendre des phrases, prédire des évènements. Pas besoin de savoir programmer, il suffit d'assembler.
</p>
<div style={{ display: 'flex', gap: 14, marginTop: 34 }}>
<button
Expand All @@ -25,14 +28,16 @@ export default function HeroSection() {
<span style={{ fontSize: 13 }}>▶</span> Commencer à construire
</button>
<button
onClick={goBuild}
onClick={scrollToFeatures}
style={{ background: 'rgba(255,255,255,.06)', color: '#f0e9e3', border: '1px solid rgba(255,255,255,.14)', padding: '15px 24px', borderRadius: 14, fontWeight: 800, fontSize: 16, cursor: 'pointer' }}
>
Voir un exemple
En savoir plus
</button>
</div>
</div>
<HeroBlockStack />
<div className="landing-hero-visual">
<HeroBlockStack />
</div>
</section>
)
}
23 changes: 17 additions & 6 deletions frontend/src/components/landing/HomeFooter.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
export default function HomeFooter() {
return (
<footer style={{ maxWidth: 1240, margin: '0 auto', padding: '34px 48px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', color: '#6f665e', fontSize: 14, fontWeight: 700 }}>
<span>MLBlock — le machine learning en blocs</span>
<span>Fait pour apprendre en s'amusant 🧱</span>
</footer>
)
return (
<footer
style={{
maxWidth: 1240,
margin: '0 auto',
padding: '34px 48px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
color: '#6f665e',
fontSize: 14,
fontWeight: 700,
}}
>
<span>© 2026 PoC Innovation</span>
</footer>
);
}
Comment on lines 1 to 19
Loading
Loading