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:
392
package-lock.json
generated
392
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
1394
src/App.css
1394
src/App.css
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
@@ -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
137
src/styles/README.md
Normal 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
|
||||
```
|
||||
@@ -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
233
src/styles/_mixins.scss
Normal 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;
|
||||
}
|
||||
95
src/styles/_variables.scss
Normal file
95
src/styles/_variables.scss
Normal 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)
|
||||
);
|
||||
112
src/styles/components/_about.scss
Normal file
112
src/styles/components/_about.scss
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
231
src/styles/components/_contact.scss
Normal file
231
src/styles/components/_contact.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
292
src/styles/components/_education.scss
Normal file
292
src/styles/components/_education.scss
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
142
src/styles/components/_header.scss
Normal file
142
src/styles/components/_header.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
176
src/styles/components/_hero.scss
Normal file
176
src/styles/components/_hero.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
154
src/styles/components/_projects.scss
Normal file
154
src/styles/components/_projects.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
109
src/styles/components/_skills.scss
Normal file
109
src/styles/components/_skills.scss
Normal 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
6
src/styles/index.scss
Normal 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
15
src/styles/main.scss
Normal 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';
|
||||
Reference in New Issue
Block a user