feat: Add SCSS structure with variables, mixins, and component styles

- Created _variables.scss for color, spacing, typography, and breakpoints.
- Added _mixins.scss for reusable styles including responsive breakpoints, transitions, and box shadows.
- Implemented styles for various components: header, hero, about, contact, education, projects, and skills.
- Established a main.scss file to import component styles and an index.scss for global styles.
This commit is contained in:
Dayron
2025-11-11 18:13:01 +01:00
parent bd3161e31b
commit cdff0c8c5c
18 changed files with 2329 additions and 1633 deletions

392
package-lock.json generated
View File

@@ -23,6 +23,7 @@
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"sass": "^1.94.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.46.3",
"vite": "^7.2.2"
@@ -1050,6 +1051,316 @@
"node": ">= 8"
}
},
"node_modules/@parcel/watcher": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.5.1",
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-freebsd-x64": "2.5.1",
"@parcel/watcher-linux-arm-glibc": "2.5.1",
"@parcel/watcher-linux-arm-musl": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
"@parcel/watcher-linux-arm64-musl": "2.5.1",
"@parcel/watcher-linux-x64-glibc": "2.5.1",
"@parcel/watcher-linux-x64-musl": "2.5.1",
"@parcel/watcher-win32-arm64": "2.5.1",
"@parcel/watcher-win32-ia32": "2.5.1",
"@parcel/watcher-win32-x64": "2.5.1"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
"integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
"integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
"integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
"integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
"integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
"integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
"integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
"integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
"integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
"integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
"integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.43",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.43.tgz",
@@ -1937,6 +2248,22 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -2018,6 +2345,20 @@
"dev": true,
"license": "MIT"
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"dev": true,
"license": "Apache-2.0",
"optional": true,
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.250",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.250.tgz",
@@ -2498,6 +2839,13 @@
"node": ">= 4"
}
},
"node_modules/immutable": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz",
"integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==",
"dev": true,
"license": "MIT"
},
"node_modules/import-fresh": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@@ -2783,6 +3131,14 @@
"dev": true,
"license": "MIT"
},
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/node-releases": {
"version": "2.0.27",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
@@ -2996,6 +3352,20 @@
"node": ">=0.10.0"
}
},
"node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -3083,6 +3453,28 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/sass": {
"version": "1.94.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.94.0.tgz",
"integrity": "sha512-Dqh7SiYcaFtdv5Wvku6QgS5IGPm281L+ZtVD1U2FJa7Q0EFRlq8Z3sjYtz6gYObsYThUOz9ArwFqPZx+1azILQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.0.2",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
},
"optionalDependencies": {
"@parcel/watcher": "^2.4.1"
}
},
"node_modules/scheduler": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",

View File

@@ -25,6 +25,7 @@
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"sass": "^1.94.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.46.3",
"vite": "^7.2.2"

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
import React, { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import { useState, useEffect } from 'react';
import Header from './components/Header';
import Hero from './components/Hero';
import About from './components/About';
@@ -7,7 +6,7 @@ import Skills from './components/Skills';
import Projects from './components/Projects';
import Education from './components/Education';
import Contact from './components/Contact';
import './App.css';
import './styles/main.scss';
function App() {
const [darkMode, setDarkMode] = useState(false);

View File

@@ -1,6 +1,6 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import './styles/index.scss'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(

137
src/styles/README.md Normal file
View File

@@ -0,0 +1,137 @@
# 🎨 Structure SCSS du Portfolio
Ce projet utilise une architecture SCSS moderne et organisée pour maintenir un code CSS propre et facilement maintenable.
## 📁 Structure des fichiers
```
src/styles/
├── _variables.scss # Variables globales (couleurs, espacements, breakpoints)
├── _mixins.scss # Mixins réutilisables
├── _global.scss # Styles globaux et reset CSS
├── index.scss # Point d'entrée principal
├── main.scss # Import des composants
└── components/ # Styles spécifiques aux composants
├── _header.scss
├── _hero.scss
├── _about.scss
├── _skills.scss
├── _projects.scss
├── _education.scss
└── _contact.scss
```
## 🎯 Architecture
### Variables (`_variables.scss`)
- **Couleurs** : Système de couleurs pour les thèmes clair/sombre
- **Espacements** : Variables responsive pour padding/margin
- **Breakpoints** : Points de rupture pour le responsive design
- **Typography** : Tailles de police fluides
- **Z-index** : Système organisé pour la gestion des couches
### Mixins (`_mixins.scss`)
- **Responsive** : `@include respond-to(md)`, `@include respond-above(lg)`
- **Composants** : `@include card()`, `@include button-primary()`
- **Animations** : `@include transition()`, `@include fade-in-animation()`
- **Layout** : `@include flex-center()`, `@include grid-responsive()`
- **Accessibilité** : `@include focus-style()`, `@include visually-hidden()`
### Styles globaux (`_global.scss`)
- Reset CSS moderne
- Styles de base pour les éléments HTML
- Classes utilitaires
- Animations keyframes
- Gestion des thèmes (clair/sombre)
## 🛠️ Utilisation
### Responsive Design
```scss
.component {
padding: 20px;
@include respond-to(md) {
padding: 10px;
}
@include respond-above(xl) {
padding: 40px;
}
}
```
### Composants réutilisables
```scss
.card {
@include card(32px, $border-radius-large, medium);
}
.button {
@include button-primary();
}
```
### Système de couleurs
```scss
.element {
background: var(--bg-primary);
color: var(--text-primary);
border: 1px solid var(--border-color);
}
```
### Grilles responsive
```scss
.grid {
@include grid-responsive(300px, 24px);
}
```
## 🎨 Thèmes
Le système de thèmes utilise les variables CSS pour permettre le basculement dynamique :
```scss
// Variables définies dans _variables.scss
$colors: (
light: (
bg-primary: #ffffff,
text-primary: #1e293b,
// ...
),
dark: (
bg-primary: #0f172a,
text-primary: #f1f5f9,
// ...
)
);
```
## 🚀 Avantages de cette architecture
1. **Modularité** : Chaque composant a ses styles séparés
2. **Réutilisabilité** : Mixins pour éviter la duplication
3. **Maintenabilité** : Variables centralisées
4. **Performance** : Compilation optimisée avec Sass
5. **Responsive** : Système de breakpoints cohérent
6. **Accessibilité** : Mixins dédiés pour l'a11y
7. **Thèmes** : Support natif clair/sombre
## 📝 Bonnes pratiques
- Utiliser les variables pour toutes les valeurs récurrentes
- Préférer les mixins aux classes utilitaires
- Organiser les styles par composant
- Utiliser les imports avec `@use` plutôt que `@import`
- Suivre la convention de nommage BEM
- Documenter les mixins complexes
## 🔧 Compilation
Les fichiers SCSS sont automatiquement compilés par Vite lors du développement et de la construction.
```bash
npm run dev # Compilation en mode développement
npm run build # Compilation pour la production
```

View File

@@ -1,80 +1,65 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
// ========================
// GLOBAL STYLES SCSS
// ========================
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
@use 'variables' as *;
@use 'mixins' as *;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
/* Importation des polices Google Fonts */
// Importation des polices Google Fonts
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
/* Reset CSS moderne */
*, *::before, *::after {
// Variables CSS pour les thèmes
:root {
// Couleurs principales
--primary-color: #{$primary-color};
--primary-hover: #{$primary-hover};
--secondary-color: #{$secondary-color};
--accent-color: #{$accent-color};
// Variables des thèmes - Light par défaut
@each $property, $value in map-get($colors, light) {
--#{$property}: #{$value};
}
// Transitions
--transition-fast: #{$transition-fast};
--transition-normal: #{$transition-normal};
--transition-slow: #{$transition-slow};
// Espacements
--section-padding: #{$section-padding};
--container-padding: #{$container-padding};
--border-radius: #{$border-radius};
--border-radius-large: #{$border-radius-large};
}
// Thème sombre
[data-theme="dark"] {
@each $property, $value in map-get($colors, dark) {
--#{$property}: #{$value};
}
}
// Reset et base
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* Variables CSS globales */
:root {
/* Polices */
--font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
/* Tailles de police responsive */
--fs-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
--fs-sm: clamp(0.875rem, 0.8rem + 0.375vw, 1rem);
--fs-base: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
--fs-lg: clamp(1.125rem, 1.05rem + 0.375vw, 1.25rem);
--fs-xl: clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem);
--fs-2xl: clamp(1.5rem, 1.35rem + 0.75vw, 2rem);
--fs-3xl: clamp(2rem, 1.75rem + 1.25vw, 2.5rem);
--fs-4xl: clamp(2.5rem, 2rem + 2.5vw, 4rem);
/* Espacements */
--space-xs: clamp(0.25rem, 0.2rem + 0.25vw, 0.5rem);
--space-sm: clamp(0.5rem, 0.4rem + 0.5vw, 1rem);
--space-md: clamp(1rem, 0.8rem + 1vw, 2rem);
--space-lg: clamp(2rem, 1.5rem + 2.5vw, 4rem);
--space-xl: clamp(4rem, 3rem + 5vw, 8rem);
/* Z-index */
--z-dropdown: 1000;
--z-sticky: 1020;
--z-fixed: 1030;
--z-modal-backdrop: 1040;
--z-modal: 1050;
--z-popover: 1060;
--z-tooltip: 1070;
}
/* Configuration du document */
html {
scroll-behavior: smooth;
scroll-padding-top: 80px;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
@include custom-scrollbar();
}
body {
font-family: var(--font-family);
font-size: var(--fs-base);
font-family: $font-family;
font-size: map-get($font-sizes, base);
line-height: 1.6;
color: var(--text-primary);
background-color: var(--bg-primary);
@@ -82,10 +67,20 @@ body {
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
overflow-x: hidden;
@include transition(color background-color);
}
/* Amélioration des médias */
img, picture, video, canvas, svg {
.app {
min-height: 100vh;
overflow-x: hidden;
}
// Amélioration des médias
img,
picture,
video,
canvas,
svg {
display: block;
max-width: 100%;
height: auto;
@@ -95,8 +90,11 @@ img {
border-style: none;
}
/* Amélioration des formulaires */
input, button, textarea, select {
// Amélioration des formulaires
input,
button,
textarea,
select {
font: inherit;
}
@@ -106,152 +104,74 @@ button {
cursor: pointer;
}
/* Amélioration des liens */
// Amélioration des liens
a {
color: inherit;
text-decoration: none;
}
/* Amélioration des listes */
ul, ol {
// Amélioration des listes
ul,
ol {
list-style: none;
}
/* Amélioration des titres */
h1, h2, h3, h4, h5, h6 {
// Amélioration des titres
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: 600;
line-height: 1.2;
color: var(--text-primary);
}
/* Amélioration des paragraphes */
// Amélioration des paragraphes
p {
color: var(--text-secondary);
max-width: 65ch;
}
/* Amélioration de l'accessibilité */
/* Masquer visuellement mais garder accessible aux lecteurs d'écran */
.visually-hidden {
position: absolute !important;
width: 1px !important;
height: 1px !important;
padding: 0 !important;
margin: -1px !important;
overflow: hidden !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
border: 0 !important;
}
/* Focus visible amélioré */
.focus-outline {
outline: 2px solid var(--primary-color);
outline-offset: 2px;
border-radius: 4px;
}
/* Amélioration de la sélection de texte */
::selection {
background-color: var(--primary-color);
color: white;
text-shadow: none;
}
::-moz-selection {
background-color: var(--primary-color);
color: white;
text-shadow: none;
}
/* Amélioration des scrollbars */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-secondary);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: var(--text-muted);
border-radius: 4px;
transition: background-color 0.2s ease;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-secondary);
}
/* Support des scrollbars Firefox */
* {
scrollbar-width: thin;
scrollbar-color: var(--text-muted) var(--bg-secondary);
}
/* Amélioration pour les utilisateurs préférant les animations réduites */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* Mode haut contraste */
@media (prefers-contrast: high) {
:root {
--text-primary: #000000;
--text-secondary: #333333;
--bg-primary: #ffffff;
--bg-secondary: #f0f0f0;
--border-color: #666666;
}
[data-theme="dark"] {
--text-primary: #ffffff;
--text-secondary: #cccccc;
--bg-primary: #000000;
--bg-secondary: #1a1a1a;
--border-color: #999999;
}
}
/* Support pour les écrans très larges */
@media (min-width: 1440px) {
// Classes utilitaires
.container {
max-width: map-get($breakpoints, xl);
margin: 0 auto;
padding: var(--container-padding);
@include respond-above(xxl) {
max-width: 1400px;
}
}
/* Support pour les très petits écrans */
@media (max-width: 320px) {
:root {
--container-padding: 0 12px;
.section-header {
text-align: center;
margin-bottom: 60px;
.section-title {
font-size: map-get($font-sizes, 3xl);
font-weight: 700;
margin-bottom: 16px;
@include gradient-text();
@include respond-to(sm) {
font-size: map-get($font-sizes, 2xl);
}
}
/* Amélioration des performances */
.gpu-accelerated {
transform: translateZ(0);
-webkit-transform: translateZ(0);
will-change: transform;
.section-subtitle {
font-size: map-get($font-sizes, lg);
color: var(--text-secondary);
max-width: 600px;
margin: 0 auto;
}
}
/* Classes utilitaires */
// Classes d'accessibilité
.visually-hidden,
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
@include visually-hidden();
}
.not-sr-only {
@@ -265,61 +185,28 @@ p {
white-space: normal;
}
/* Amélioration de l'impression */
@media print {
* {
background: transparent !important;
color: black !important;
box-shadow: none !important;
text-shadow: none !important;
// Focus pour l'accessibilité
button:focus-visible,
a:focus-visible,
input:focus-visible,
textarea:focus-visible {
@include focus-style();
}
a, a:visited {
text-decoration: underline;
// Amélioration de la sélection de texte
::selection {
background-color: $primary-color;
color: white;
text-shadow: none;
}
a[href]:after {
content: " (" attr(href) ")";
::-moz-selection {
background-color: $primary-color;
color: white;
text-shadow: none;
}
abbr[title]:after {
content: " (" attr(title) ")";
}
pre, blockquote {
border: 1px solid #999;
page-break-inside: avoid;
}
thead {
display: table-header-group;
}
tr, img {
page-break-inside: avoid;
}
img {
max-width: 100% !important;
}
p, h2, h3 {
orphans: 3;
widows: 3;
}
h2, h3 {
page-break-after: avoid;
}
}
/* Amélioration du loading */
.loading {
pointer-events: none;
opacity: 0.6;
}
/* Animations globales */
// Classes d'animation
@keyframes fadeIn {
from {
opacity: 0;
@@ -384,7 +271,13 @@ p {
}
}
/* Classes d'animation */
@keyframes spin {
to {
transform: rotate(360deg);
}
}
// Classes d'animation
.animate-fade-in {
animation: fadeIn 0.6s ease-out;
}
@@ -408,3 +301,106 @@ p {
.animate-bounce {
animation: bounce 1s infinite;
}
.loading {
pointer-events: none;
opacity: 0.6;
}
.gpu-accelerated {
transform: translateZ(0);
-webkit-transform: translateZ(0);
will-change: transform;
}
// Amélioration pour les utilisateurs préférant les animations réduites
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
// Mode haut contraste
@media (prefers-contrast: high) {
:root {
--text-primary: #000000;
--text-secondary: #333333;
--bg-primary: #ffffff;
--bg-secondary: #f0f0f0;
--border-color: #666666;
}
[data-theme="dark"] {
--text-primary: #ffffff;
--text-secondary: #cccccc;
--bg-primary: #000000;
--bg-secondary: #1a1a1a;
--border-color: #999999;
}
}
// Support pour les très petits écrans
@include respond-to(xs) {
:root {
--container-padding: 0 12px;
}
}
// Amélioration de l'impression
@media print {
* {
background: transparent !important;
color: black !important;
box-shadow: none !important;
text-shadow: none !important;
}
a,
a:visited {
text-decoration: underline;
}
a[href]:after {
content: " (" attr(href) ")";
}
abbr[title]:after {
content: " (" attr(title) ")";
}
pre,
blockquote {
border: 1px solid #999;
page-break-inside: avoid;
}
thead {
display: table-header-group;
}
tr,
img {
page-break-inside: avoid;
}
img {
max-width: 100% !important;
}
p,
h2,
h3 {
orphans: 3;
widows: 3;
}
h2,
h3 {
page-break-after: avoid;
}
}

233
src/styles/_mixins.scss Normal file
View File

@@ -0,0 +1,233 @@
// ========================
// MIXINS SCSS
// ========================
@use 'variables' as *;
// Mixin pour les breakpoints responsive
@mixin respond-to($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
@media (max-width: map-get($breakpoints, $breakpoint)) {
@content;
}
} @else {
@warn "Breakpoint #{$breakpoint} not found in $breakpoints.";
}
}
@mixin respond-above($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
@media (min-width: map-get($breakpoints, $breakpoint)) {
@content;
}
} @else {
@warn "Breakpoint #{$breakpoint} not found in $breakpoints.";
}
}
// Mixin pour les transitions
@mixin transition($properties: all, $duration: $transition-normal, $timing: ease) {
transition: $properties $duration $timing;
}
// Mixin pour les ombres
@mixin box-shadow($level: medium) {
@if $level == light {
box-shadow: var(--shadow-light);
} @else if $level == medium {
box-shadow: var(--shadow-medium);
} @else if $level == large {
box-shadow: var(--shadow-large);
}
}
// Mixin pour les cartes
@mixin card($padding: 32px, $radius: $border-radius-large, $shadow: light) {
background: var(--bg-primary);
padding: $padding;
border-radius: $radius;
@include box-shadow($shadow);
@include transition(transform, $transition-normal);
&:hover {
transform: translateY(-4px);
@include box-shadow(medium);
}
}
// Mixin pour les boutons
@mixin button-base {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 14px 28px;
border: none;
border-radius: $border-radius;
font-weight: 600;
text-decoration: none;
cursor: pointer;
@include transition();
font-size: map-get($font-sizes, base);
}
@mixin button-primary {
@include button-base;
background: $primary-color;
color: white;
@include box-shadow(medium);
&:hover {
background: $primary-hover;
transform: translateY(-2px);
@include box-shadow(large);
}
}
@mixin button-secondary {
@include button-base;
background: transparent;
color: $primary-color;
border: 2px solid $primary-color;
&:hover {
background: $primary-color;
color: white;
transform: translateY(-2px);
}
}
// Mixin pour les grilles responsive
@mixin grid-responsive($min-width: 300px, $gap: 32px) {
display: grid;
grid-template-columns: repeat(auto-fit, minmax($min-width, 1fr));
gap: $gap;
}
// Mixin pour le texte dégradé
@mixin gradient-text($color1: $primary-color, $color2: $accent-color, $direction: 135deg) {
background: linear-gradient($direction, $color1, $color2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
// Mixin pour centrer flex
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
// Mixin pour masquer visuellement (accessibilité)
@mixin visually-hidden {
position: absolute !important;
width: 1px !important;
height: 1px !important;
padding: 0 !important;
margin: -1px !important;
overflow: hidden !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
border: 0 !important;
}
// Mixin pour les focus accessibles
@mixin focus-style {
outline: 2px solid $primary-color;
outline-offset: 2px;
border-radius: 4px;
}
// Mixin pour l'animation d'apparition
@mixin fade-in-animation($duration: 0.6s, $delay: 0s) {
opacity: 0;
animation: fadeIn $duration ease-out $delay forwards;
}
// Mixin pour les barres de progression
@mixin progress-bar($height: 8px, $bg-color: var(--bg-secondary)) {
height: $height;
background: $bg-color;
border-radius: $height / 2;
overflow: hidden;
.progress-fill {
height: 100%;
border-radius: $height / 2;
@include transition(width, $transition-slow);
}
}
// Mixin pour les icônes avec background
@mixin icon-container($size: 60px, $bg-color: $primary-color, $radius: $border-radius) {
width: $size;
height: $size;
border-radius: $radius;
@include flex-center;
background-color: #{$bg-color}20;
color: $bg-color;
}
// Mixin pour les tags/badges
@mixin tag($padding: 6px 12px, $bg-color: var(--bg-secondary), $text-color: var(--text-secondary)) {
padding: $padding;
background: $bg-color;
border-radius: 20px;
font-size: map-get($font-sizes, xs);
font-weight: 500;
color: $text-color;
@include transition();
}
// Mixin pour les liens avec effet hover
@mixin link-hover($color: $primary-color) {
position: relative;
@include transition(color);
&:hover {
color: $color;
}
&::after {
content: '';
position: absolute;
width: 0;
height: 2px;
bottom: -4px;
left: 50%;
transform: translateX(-50%);
background: $color;
@include transition(width);
}
&:hover::after {
width: 100%;
}
}
// Mixin pour les scrollbars personnalisées
@mixin custom-scrollbar($width: 8px, $track-color: var(--bg-secondary), $thumb-color: var(--text-muted)) {
&::-webkit-scrollbar {
width: $width;
height: $width;
}
&::-webkit-scrollbar-track {
background: $track-color;
border-radius: 4px;
}
&::-webkit-scrollbar-thumb {
background: $thumb-color;
border-radius: 4px;
@include transition(background-color);
&:hover {
background: var(--text-secondary);
}
}
// Support Firefox
scrollbar-width: thin;
scrollbar-color: $thumb-color $track-color;
}

View File

@@ -0,0 +1,95 @@
// ========================
// VARIABLES SCSS
// ========================
// Couleurs principales
$primary-color: #3b82f6;
$primary-hover: #2563eb;
$secondary-color: #64748b;
$accent-color: #10b981;
// Système de couleurs pour les thèmes
$colors: (
light: (
bg-primary: #ffffff,
bg-secondary: #f8fafc,
bg-tertiary: #f1f5f9,
text-primary: #1e293b,
text-secondary: #64748b,
text-muted: #94a3b8,
border-color: #e2e8f0,
shadow-light: 0 1px 3px 0 rgba(0, 0, 0, 0.1),
shadow-medium: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
shadow-large: 0 10px 15px -3px rgba(0, 0, 0, 0.1)
),
dark: (
bg-primary: #0f172a,
bg-secondary: #1e293b,
bg-tertiary: #334155,
text-primary: #f1f5f9,
text-secondary: #cbd5e1,
text-muted: #64748b,
border-color: #334155,
shadow-light: 0 1px 3px 0 rgba(0, 0, 0, 0.3),
shadow-medium: 0 4px 6px -1px rgba(0, 0, 0, 0.3),
shadow-large: 0 10px 15px -3px rgba(0, 0, 0, 0.3)
)
);
// Transitions
$transition-fast: 0.2s ease;
$transition-normal: 0.3s ease;
$transition-slow: 0.5s ease;
// Espacements
$section-padding: 80px 0;
$container-padding: 0 20px;
// Border radius
$border-radius: 12px;
$border-radius-large: 20px;
// Breakpoints
$breakpoints: (
xs: 320px,
sm: 480px,
md: 768px,
lg: 1024px,
xl: 1200px,
xxl: 1440px
);
// Z-index
$z-index: (
dropdown: 1000,
sticky: 1020,
fixed: 1030,
modal-backdrop: 1040,
modal: 1050,
popover: 1060,
tooltip: 1070
);
// Polices
$font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
// Tailles de police responsive
$font-sizes: (
xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem),
sm: clamp(0.875rem, 0.8rem + 0.375vw, 1rem),
base: clamp(1rem, 0.95rem + 0.25vw, 1.125rem),
lg: clamp(1.125rem, 1.05rem + 0.375vw, 1.25rem),
xl: clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem),
2xl: clamp(1.5rem, 1.35rem + 0.75vw, 2rem),
3xl: clamp(2rem, 1.75rem + 1.25vw, 2.5rem),
4xl: clamp(2.5rem, 2rem + 2.5vw, 4rem)
);
// Espacements responsive
$spaces: (
xs: clamp(0.25rem, 0.2rem + 0.25vw, 0.5rem),
sm: clamp(0.5rem, 0.4rem + 0.5vw, 1rem),
md: clamp(1rem, 0.8rem + 1vw, 2rem),
lg: clamp(2rem, 1.5rem + 2.5vw, 4rem),
xl: clamp(4rem, 3rem + 5vw, 8rem)
);

View File

@@ -0,0 +1,112 @@
// ========================
// ABOUT SECTION SCSS
// ========================
@use '../variables' as *;
@use '../mixins' as *;
.about {
padding: var(--section-padding);
background: var(--bg-secondary);
&-content {
display: grid;
grid-template-columns: 1fr 300px;
gap: 60px;
margin-bottom: 60px;
@include respond-to(md) {
grid-template-columns: 1fr;
gap: 40px;
}
}
&-text {
display: flex;
flex-direction: column;
gap: 32px;
}
&-card {
@include card(32px, $border-radius-large, light);
h3 {
font-size: map-get($font-sizes, xl);
font-weight: 700;
margin-bottom: 16px;
color: $primary-color;
}
p {
color: var(--text-secondary);
line-height: 1.7;
strong {
color: $primary-color;
font-weight: 600;
}
}
}
&-stats {
display: flex;
flex-direction: column;
gap: 20px;
@include respond-to(md) {
flex-direction: row;
flex-wrap: wrap;
}
.stat-card {
@include card(24px, $border-radius, light);
text-align: center;
flex: 1;
min-width: 140px;
.stat-icon {
color: $primary-color;
margin-bottom: 12px;
}
.stat-value {
font-size: map-get($font-sizes, 2xl);
font-weight: 800;
color: var(--text-primary);
margin-bottom: 4px;
}
.stat-label {
font-size: map-get($font-sizes, sm);
color: var(--text-secondary);
font-weight: 500;
}
}
}
&-highlight {
background: linear-gradient(135deg, $primary-color, $accent-color);
padding: 40px;
border-radius: $border-radius-large;
text-align: center;
color: white;
h3 {
font-size: map-get($font-sizes, xl);
margin-bottom: 16px;
}
p {
font-style: italic;
margin-bottom: 16px;
line-height: 1.7;
max-width: none;
color: rgba(255, 255, 255, 0.9);
}
cite {
font-weight: 600;
color: rgba(255, 255, 255, 0.8);
}
}
}

View File

@@ -0,0 +1,231 @@
// ========================
// CONTACT SECTION SCSS
// ========================
@use '../variables' as *;
@use '../mixins' as *;
.contact {
padding: var(--section-padding);
background: var(--bg-secondary);
&-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 60px;
@include respond-to(md) {
grid-template-columns: 1fr;
gap: 40px;
}
}
&-intro {
h3 {
display: flex;
align-items: center;
gap: 12px;
font-size: map-get($font-sizes, xl);
font-weight: 700;
margin-bottom: 16px;
color: var(--text-primary);
}
p {
color: var(--text-secondary);
line-height: 1.7;
margin-bottom: 32px;
}
}
&-methods {
display: flex;
flex-direction: column;
gap: 16px;
margin-bottom: 40px;
.contact-method {
display: flex;
align-items: center;
gap: 16px;
padding: 20px;
background: var(--bg-primary);
border-radius: $border-radius;
@include box-shadow(light);
@include transition();
&:hover {
transform: translateX(8px);
@include box-shadow(medium);
}
.contact-icon {
@include icon-container(50px, $primary-color, $border-radius);
flex-shrink: 0;
}
.contact-details {
h4 {
font-weight: 600;
margin-bottom: 4px;
color: var(--text-primary);
}
span {
color: var(--text-secondary);
}
}
}
}
.social-links {
h4 {
margin-bottom: 16px;
color: var(--text-primary);
}
.social-grid {
display: flex;
gap: 16px;
@include respond-to(md) {
flex-direction: column;
}
.social-link {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
background: var(--bg-primary);
border-radius: $border-radius;
@include box-shadow(light);
@include transition();
&:hover {
@include box-shadow(medium);
}
.social-icon {
@include icon-container(32px, $primary-color, 6px);
}
span {
font-weight: 500;
color: var(--text-secondary);
}
}
}
}
&-form-container {
h3 {
font-size: map-get($font-sizes, xl);
font-weight: 700;
margin-bottom: 24px;
color: var(--text-primary);
}
.success-message {
display: flex;
align-items: center;
gap: 12px;
background: $accent-color;
color: white;
padding: 16px;
border-radius: $border-radius;
margin-bottom: 24px;
font-weight: 500;
}
.contact-form {
display: flex;
flex-direction: column;
gap: 20px;
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
@include respond-to(md) {
grid-template-columns: 1fr;
}
}
.form-group {
display: flex;
flex-direction: column;
gap: 8px;
label {
font-weight: 500;
color: var(--text-primary);
}
input,
textarea {
padding: 12px 16px;
border: 2px solid var(--border-color);
border-radius: $border-radius;
background: var(--bg-primary);
color: var(--text-primary);
font-family: inherit;
@include transition(border-color);
&:focus {
outline: none;
border-color: $primary-color;
}
&::placeholder {
color: var(--text-muted);
}
}
textarea {
resize: vertical;
min-height: 120px;
}
}
.submit-btn {
@include button-primary();
justify-content: center;
gap: 12px;
padding: 16px 32px;
font-size: map-get($font-sizes, base);
&:disabled {
opacity: 0.7;
cursor: not-allowed;
&:hover {
transform: none;
}
}
.loading-spinner {
width: 16px;
height: 16px;
border: 2px solid transparent;
border-top: 2px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
}
}
}
}
&-footer {
text-align: center;
margin-top: 60px;
padding-top: 40px;
border-top: 1px solid var(--border-color);
p {
color: var(--text-muted);
margin: 0 auto;
}
}
}

View File

@@ -0,0 +1,292 @@
// ========================
// EDUCATION SECTION SCSS
// ========================
@use '../variables' as *;
@use '../mixins' as *;
.education {
padding: var(--section-padding);
&-content {
display: flex;
flex-direction: column;
gap: 60px;
}
&-card {
display: flex;
gap: 32px;
@include card(32px, $border-radius-large, light);
@include respond-to(md) {
flex-direction: column;
text-align: center;
}
&.main-education {
.education-timeline {
display: flex;
flex-direction: column;
align-items: center;
flex-shrink: 0;
@include respond-to(md) {
flex-direction: row;
justify-content: center;
margin-bottom: 20px;
}
.timeline-dot {
width: 60px;
height: 60px;
border-radius: 50%;
@include flex-center();
color: white;
margin-bottom: 16px;
@include respond-to(md) {
margin-bottom: 0;
margin-right: 16px;
}
}
.timeline-line {
width: 2px;
height: 100px;
background: var(--border-color);
border-radius: 1px;
@include respond-to(md) {
display: none;
}
}
}
.education-content-card {
flex: 1;
}
.education-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 20px;
@include respond-to(md) {
flex-direction: column;
align-items: center;
text-align: center;
}
.education-title {
h3 {
font-size: map-get($font-sizes, xl);
font-weight: 700;
margin-bottom: 12px;
color: var(--text-primary);
}
.education-meta {
display: flex;
flex-direction: column;
gap: 8px;
span {
display: flex;
align-items: center;
gap: 8px;
font-size: map-get($font-sizes, sm);
color: var(--text-secondary);
@include respond-to(md) {
justify-content: center;
}
}
.school-name {
font-weight: 600;
}
}
}
.education-status {
text-align: right;
@include respond-to(md) {
text-align: center;
margin-top: 16px;
}
.status-badge {
@include tag(6px 12px);
font-size: map-get($font-sizes, xs);
font-weight: 600;
border-radius: 20px;
}
.current-year {
display: block;
font-size: map-get($font-sizes, sm);
color: var(--text-muted);
margin-top: 8px;
}
}
}
.education-description {
color: var(--text-secondary);
line-height: 1.7;
margin-bottom: 24px;
}
.education-highlights {
h4 {
display: flex;
align-items: center;
gap: 8px;
font-size: map-get($font-sizes, base);
font-weight: 600;
margin-bottom: 16px;
color: var(--text-primary);
@include respond-to(md) {
justify-content: center;
}
}
.highlights-grid {
@include grid-responsive(200px, 12px);
.highlight-tag {
@include tag(8px 12px, var(--bg-secondary), var(--text-secondary));
font-size: map-get($font-sizes, sm);
text-align: center;
border-radius: $border-radius;
&:hover {
background: $primary-color;
color: white;
transform: scale(1.05);
}
}
}
}
}
}
.certifications {
text-align: center;
&-title {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
font-size: map-get($font-sizes, xl);
font-weight: 700;
margin-bottom: 32px;
color: var(--text-primary);
}
&-grid {
@include grid-responsive(300px, 24px);
margin-bottom: 60px;
@include respond-to(md) {
grid-template-columns: 1fr;
}
.certification-card {
@include card(24px, $border-radius, light);
.cert-header {
h4 {
font-size: map-get($font-sizes, lg);
font-weight: 600;
margin-bottom: 8px;
color: var(--text-primary);
}
.cert-provider {
display: block;
font-size: map-get($font-sizes, sm);
color: var(--text-secondary);
margin-bottom: 4px;
}
.cert-date {
display: block;
font-size: map-get($font-sizes, xs);
color: var(--text-muted);
margin-bottom: 16px;
}
}
.cert-skills {
display: flex;
flex-wrap: wrap;
gap: 8px;
.cert-skill-tag {
@include tag(4px 8px);
font-size: map-get($font-sizes, xs);
font-weight: 500;
border-radius: 12px;
}
}
}
}
}
.learning-goals {
h3 {
font-size: map-get($font-sizes, xl);
font-weight: 700;
margin-bottom: 32px;
color: var(--text-primary);
text-align: center;
}
.goals-grid {
@include grid-responsive(300px, 20px);
@include respond-to(md) {
grid-template-columns: 1fr;
}
.goal-item {
@include card(20px, $border-radius, light);
.goal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
.goal-text {
font-weight: 500;
color: var(--text-primary);
}
.goal-percentage {
font-size: map-get($font-sizes, sm);
color: $primary-color;
font-weight: 600;
}
}
.goal-progress {
@include progress-bar(6px);
.goal-progress-bar {
height: 100%;
background: $primary-color;
border-radius: 3px;
@include transition(width, $transition-slow);
}
}
}
}
}
}

View File

@@ -0,0 +1,142 @@
// ========================
// HEADER / NAVIGATION SCSS
// ========================
@use '../variables' as *;
@use '../mixins' as *;
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: map-get($z-index, fixed);
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--border-color);
@include transition();
[data-theme="dark"] & {
background: rgba(15, 23, 42, 0.95);
}
.nav {
@include flex-center();
justify-content: space-between;
padding: 16px 20px;
max-width: map-get($breakpoints, xl);
margin: 0 auto;
}
.nav-brand {
a {
font-size: map-get($font-sizes, xl);
font-weight: 700;
color: var(--text-primary);
@include gradient-text();
@include transition();
&:hover {
transform: scale(1.05);
}
}
}
.nav-menu {
display: flex;
gap: 32px;
a {
color: var(--text-secondary);
font-weight: 500;
@include link-hover();
}
&.mobile-menu {
display: none;
position: fixed;
top: 100%;
right: 0;
width: 100%;
height: calc(100vh - 80px);
background: var(--bg-primary);
flex-direction: column;
padding: 40px 20px;
gap: 24px;
border-top: 1px solid var(--border-color);
@include box-shadow(large);
@include transition(transform opacity);
&.open {
top: 80px;
transform: translateX(0);
opacity: 1;
}
&:not(.open) {
transform: translateX(100%);
opacity: 0;
}
a {
font-size: map-get($font-sizes, lg);
padding: 16px 0;
border-bottom: 1px solid var(--border-color);
}
}
}
.nav-controls {
display: flex;
align-items: center;
gap: 16px;
}
.theme-toggle,
.menu-toggle {
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
padding: 8px;
border-radius: 8px;
@include transition();
&:hover {
color: $primary-color;
background: var(--bg-secondary);
transform: scale(1.1);
}
&:focus-visible {
@include focus-style();
}
}
.menu-toggle {
display: none;
@include respond-to(md) {
display: block;
}
}
.desktop-menu {
@include respond-to(md) {
display: none;
}
}
}
// Responsive
@include respond-to(md) {
.header {
.nav {
padding: 12px 16px;
}
.mobile-menu {
display: flex !important;
}
}
}

View File

@@ -0,0 +1,176 @@
// ========================
// HERO SECTION SCSS
// ========================
@use '../variables' as *;
@use '../mixins' as *;
.hero {
position: relative;
min-height: 100vh;
display: flex;
align-items: center;
padding: var(--section-padding);
background: linear-gradient(135deg, var(--bg-primary) 0%, var(--bg-secondary) 100%);
overflow: hidden;
&-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 60px;
align-items: center;
max-width: map-get($breakpoints, xl);
margin: 0 auto;
padding: var(--container-padding);
@include respond-to(md) {
grid-template-columns: 1fr;
text-align: center;
gap: 40px;
}
}
&-text {
z-index: 2;
}
&-title {
font-size: map-get($font-sizes, 4xl);
font-weight: 800;
margin-bottom: 16px;
@include gradient-text();
@include respond-to(sm) {
font-size: map-get($font-sizes, 3xl);
}
}
&-subtitle {
font-size: map-get($font-sizes, xl);
font-weight: 600;
color: var(--text-secondary);
margin-bottom: 24px;
@include respond-to(sm) {
font-size: map-get($font-sizes, lg);
}
}
&-description {
font-size: map-get($font-sizes, lg);
color: var(--text-secondary);
margin-bottom: 40px;
line-height: 1.7;
}
&-buttons {
display: flex;
gap: 20px;
margin-bottom: 40px;
@include respond-to(md) {
flex-direction: column;
align-items: center;
}
@include respond-to(sm) {
gap: 12px;
}
.btn {
@include button-base();
@include respond-to(sm) {
padding: 12px 20px;
font-size: 0.9rem;
}
&-primary {
@include button-primary();
}
&-secondary {
@include button-secondary();
}
}
}
&-social {
display: flex;
gap: 16px;
.social-link {
@include flex-center();
width: 48px;
height: 48px;
background: var(--bg-secondary);
color: var(--text-secondary);
border-radius: 50%;
@include box-shadow(light);
@include transition();
&:hover {
color: $primary-color;
background: white;
transform: translateY(-2px);
@include box-shadow(medium);
}
}
}
&-image {
@include flex-center();
}
&-avatar {
position: relative;
&:hover {
transform: scale(1.05) rotate(5deg);
@include transition(transform, 0.5s, spring);
}
}
.avatar-placeholder {
width: 300px;
height: 300px;
border-radius: 50%;
background: linear-gradient(135deg, $primary-color, $accent-color);
@include flex-center();
font-size: map-get($font-sizes, 4xl);
font-weight: 800;
color: white;
@include box-shadow(large);
@include respond-to(md) {
width: 250px;
height: 250px;
font-size: map-get($font-sizes, 3xl);
}
@include respond-to(sm) {
width: 200px;
height: 200px;
font-size: map-get($font-sizes, 2xl);
}
}
&-particles {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
z-index: 1;
.particle {
position: absolute;
width: 4px;
height: 4px;
background: $primary-color;
border-radius: 50%;
opacity: 0.3;
}
}
}

View File

@@ -0,0 +1,154 @@
// ========================
// PROJECTS SECTION SCSS
// ========================
@use '../variables' as *;
@use '../mixins' as *;
.projects {
padding: var(--section-padding);
background: var(--bg-secondary);
&-grid {
@include grid-responsive(400px, 40px);
margin-bottom: 60px;
@include respond-to(md) {
grid-template-columns: 1fr;
}
}
.project-card {
background: var(--bg-primary);
border-radius: $border-radius-large;
overflow: hidden;
@include box-shadow(light);
@include transition();
&:hover {
transform: translateY(-10px) scale(1.02);
@include box-shadow(large);
}
.project-header {
padding: 24px 24px 0;
display: flex;
justify-content: space-between;
align-items: flex-start;
.project-icon {
@include icon-container(60px, $primary-color, $border-radius);
}
.project-status {
display: flex;
align-items: center;
.status-badge {
@include tag(6px 12px);
font-size: map-get($font-sizes, xs);
font-weight: 600;
border-radius: 20px;
}
}
}
.project-content {
padding: 24px;
.project-title {
font-size: map-get($font-sizes, xl);
font-weight: 700;
margin-bottom: 12px;
color: var(--text-primary);
}
.project-description {
color: var(--text-secondary);
line-height: 1.6;
margin-bottom: 20px;
}
.project-technologies {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 20px;
.tech-tag {
@include tag(6px 12px, var(--bg-secondary), var(--text-secondary));
font-size: map-get($font-sizes, xs);
font-weight: 500;
border-radius: 20px;
}
}
.project-features {
h4 {
font-size: map-get($font-sizes, base);
font-weight: 600;
margin-bottom: 12px;
color: var(--text-primary);
}
ul {
display: flex;
flex-direction: column;
gap: 8px;
li {
color: var(--text-secondary);
position: relative;
padding-left: 20px;
&::before {
content: '';
color: $primary-color;
position: absolute;
left: 0;
top: 0;
}
}
}
}
}
.project-footer {
padding: 0 24px 24px;
.project-links {
display: flex;
gap: 12px;
.project-link {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 16px;
border-radius: $border-radius;
font-weight: 500;
border: 2px solid var(--border-color);
color: var(--text-secondary);
@include transition();
&:hover {
border-color: $primary-color;
color: $primary-color;
transform: translateY(-2px);
}
&.primary {
background: $primary-color;
border-color: $primary-color;
color: white;
&:hover {
background: $primary-hover;
border-color: $primary-hover;
}
}
}
}
}
}
}

View File

@@ -0,0 +1,109 @@
// ========================
// SKILLS SECTION SCSS
// ========================
@use '../variables' as *;
@use '../mixins' as *;
.skills {
padding: var(--section-padding);
&-grid {
@include grid-responsive(300px, 32px);
margin-bottom: 60px;
}
.skill-category {
@include card(32px, $border-radius-large, light);
.category-header {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 24px;
.category-icon {
@include icon-container(60px, $primary-color, $border-radius);
}
.category-title {
font-size: map-get($font-sizes, lg);
font-weight: 700;
color: var(--text-primary);
}
}
.skills-list {
display: flex;
flex-direction: column;
gap: 20px;
.skill-item {
display: flex;
flex-direction: column;
gap: 8px;
.skill-header {
display: flex;
justify-content: space-between;
align-items: center;
.skill-name {
font-weight: 600;
color: var(--text-primary);
}
.skill-percentage {
font-size: map-get($font-sizes, sm);
color: var(--text-secondary);
}
}
.skill-bar {
@include progress-bar(8px);
.skill-progress {
height: 100%;
border-radius: 4px;
@include transition(width, $transition-slow);
}
}
}
}
}
.soft-skills {
text-align: center;
&-title {
font-size: map-get($font-sizes, xl);
font-weight: 700;
margin-bottom: 32px;
color: var(--text-primary);
}
&-grid {
display: flex;
flex-wrap: wrap;
gap: 16px;
justify-content: center;
.soft-skill-tag {
@include tag(12px 20px, var(--bg-primary), var(--text-secondary));
display: flex;
align-items: center;
gap: 8px;
border: 2px solid var(--border-color);
border-radius: 50px;
font-weight: 500;
cursor: default;
&:hover {
border-color: $primary-color;
color: $primary-color;
transform: translateY(-2px);
}
}
}
}
}

6
src/styles/index.scss Normal file
View File

@@ -0,0 +1,6 @@
// ========================
// INDEX SCSS - Point d'entrée principal
// ========================
// Global styles uniquement - les composants sont dans main.scss
@use 'global';

15
src/styles/main.scss Normal file
View File

@@ -0,0 +1,15 @@
// ========================
// MAIN SCSS FILE
// ========================
// Base styles
@use 'global';
// Components
@use 'components/header';
@use 'components/hero';
@use 'components/about';
@use 'components/skills';
@use 'components/projects';
@use 'components/education';
@use 'components/contact';