diff --git a/package-lock.json b/package-lock.json index 5cb6cd1..0efa389 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 631087c..4742b2f 100644 --- a/package.json +++ b/package.json @@ -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" diff --git a/src/App.css b/src/App.css deleted file mode 100644 index cd5bfd6..0000000 --- a/src/App.css +++ /dev/null @@ -1,1394 +0,0 @@ -/* Variables CSS pour les thèmes */ -:root { - /* Couleurs principales */ - --primary-color: #3b82f6; - --primary-hover: #2563eb; - --secondary-color: #64748b; - --accent-color: #10b981; - - /* Couleurs des thèmes */ - --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); - - /* 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: 12px; - --border-radius-large: 20px; -} - -/* Thème sombre */ -[data-theme="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); -} - -/* Reset et base */ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - line-height: 1.6; - color: var(--text-primary); - background-color: var(--bg-primary); - transition: color var(--transition-normal), background-color var(--transition-normal); -} - -.app { - min-height: 100vh; - overflow-x: hidden; -} - -/* Conteneur global */ -.container { - max-width: 1200px; - margin: 0 auto; - padding: var(--container-padding); -} - -/* En-tête de section */ -.section-header { - text-align: center; - margin-bottom: 60px; -} - -.section-title { - font-size: clamp(2rem, 4vw, 3rem); - font-weight: 700; - margin-bottom: 16px; - background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.section-subtitle { - font-size: 1.125rem; - color: var(--text-secondary); - max-width: 600px; - margin: 0 auto; -} - -/* Header / Navigation */ -.header { - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 1000; - background: rgba(255, 255, 255, 0.95); - backdrop-filter: blur(10px); - border-bottom: 1px solid var(--border-color); - transition: all var(--transition-normal); -} - -[data-theme="dark"] .header { - background: rgba(15, 23, 42, 0.95); -} - -.nav { - display: flex; - justify-content: space-between; - align-items: center; - padding: 16px 20px; - max-width: 1200px; - margin: 0 auto; -} - -.nav-brand a { - font-size: 1.5rem; - font-weight: 700; - color: var(--text-primary); - text-decoration: none; - background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.nav-menu { - display: flex; - list-style: none; - gap: 32px; -} - -.nav-menu a { - color: var(--text-secondary); - text-decoration: none; - font-weight: 500; - position: relative; - transition: color var(--transition-normal); -} - -.nav-menu a:hover { - color: var(--primary-color); -} - -.nav-menu a::after { - content: ''; - position: absolute; - width: 0; - height: 2px; - bottom: -4px; - left: 50%; - transform: translateX(-50%); - background: var(--primary-color); - transition: width var(--transition-normal); -} - -.nav-menu a:hover::after { - width: 100%; -} - -.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; - transition: all var(--transition-normal); -} - -.theme-toggle:hover, -.menu-toggle:hover { - color: var(--primary-color); - background: var(--bg-secondary); -} - -.menu-toggle { - display: none; -} - -.mobile-menu { - display: none; -} - -/* Hero Section */ -.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; -} - -.hero-content { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 60px; - align-items: center; - max-width: 1200px; - margin: 0 auto; - padding: var(--container-padding); -} - -.hero-text { - z-index: 2; -} - -.hero-title { - font-size: clamp(2.5rem, 5vw, 4rem); - font-weight: 800; - margin-bottom: 16px; - background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.hero-subtitle { - font-size: clamp(1.25rem, 2.5vw, 1.5rem); - font-weight: 600; - color: var(--text-secondary); - margin-bottom: 24px; -} - -.hero-description { - font-size: 1.125rem; - color: var(--text-secondary); - margin-bottom: 40px; - line-height: 1.7; -} - -.hero-buttons { - display: flex; - gap: 20px; - margin-bottom: 40px; -} - -.btn { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 14px 28px; - border: none; - border-radius: var(--border-radius); - font-weight: 600; - text-decoration: none; - cursor: pointer; - transition: all var(--transition-normal); - font-size: 1rem; -} - -.btn-primary { - background: var(--primary-color); - color: white; - box-shadow: var(--shadow-medium); -} - -.btn-primary:hover { - background: var(--primary-hover); - transform: translateY(-2px); - box-shadow: var(--shadow-large); -} - -.btn-secondary { - background: transparent; - color: var(--primary-color); - border: 2px solid var(--primary-color); -} - -.btn-secondary:hover { - background: var(--primary-color); - color: white; - transform: translateY(-2px); -} - -.hero-social { - display: flex; - gap: 16px; -} - -.social-link { - display: flex; - align-items: center; - justify-content: center; - width: 48px; - height: 48px; - background: var(--bg-secondary); - color: var(--text-secondary); - border-radius: 50%; - text-decoration: none; - transition: all var(--transition-normal); - box-shadow: var(--shadow-light); -} - -.social-link:hover { - color: var(--primary-color); - background: white; - transform: translateY(-2px); - box-shadow: var(--shadow-medium); -} - -.hero-image { - display: flex; - justify-content: center; - align-items: center; -} - -.hero-avatar { - position: relative; -} - -.avatar-placeholder { - width: 300px; - height: 300px; - border-radius: 50%; - background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); - display: flex; - align-items: center; - justify-content: center; - font-size: 4rem; - font-weight: 800; - color: white; - box-shadow: var(--shadow-large); -} - -.hero-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: var(--primary-color); - border-radius: 50%; - opacity: 0.3; -} - -/* About Section */ -.about { - padding: var(--section-padding); - background: var(--bg-secondary); -} - -.about-content { - display: grid; - grid-template-columns: 1fr 300px; - gap: 60px; - margin-bottom: 60px; -} - -.about-text { - display: flex; - flex-direction: column; - gap: 32px; -} - -.about-card { - background: var(--bg-primary); - padding: 32px; - border-radius: var(--border-radius-large); - box-shadow: var(--shadow-light); - transition: transform var(--transition-normal), box-shadow var(--transition-normal); -} - -.about-card:hover { - transform: translateY(-4px); - box-shadow: var(--shadow-medium); -} - -.about-card h3 { - font-size: 1.5rem; - font-weight: 700; - margin-bottom: 16px; - color: var(--primary-color); -} - -.about-card p { - color: var(--text-secondary); - line-height: 1.7; -} - -.about-stats { - display: flex; - flex-direction: column; - gap: 20px; -} - -.stat-card { - background: var(--bg-primary); - padding: 24px; - border-radius: var(--border-radius); - text-align: center; - box-shadow: var(--shadow-light); - transition: all var(--transition-normal); -} - -.stat-card:hover { - transform: translateY(-4px); - box-shadow: var(--shadow-medium); -} - -.stat-icon { - color: var(--primary-color); - margin-bottom: 12px; -} - -.stat-value { - font-size: 2rem; - font-weight: 800; - color: var(--text-primary); - margin-bottom: 4px; -} - -.stat-label { - font-size: 0.875rem; - color: var(--text-secondary); - font-weight: 500; -} - -.about-highlight { - background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); - padding: 40px; - border-radius: var(--border-radius-large); - text-align: center; - color: white; -} - -.about-highlight h3 { - font-size: 1.5rem; - margin-bottom: 16px; -} - -.about-highlight p { - font-style: italic; - margin-bottom: 16px; - line-height: 1.7; -} - -.about-highlight cite { - font-weight: 600; -} - -/* Skills Section */ -.skills { - padding: var(--section-padding); -} - -.skills-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: 32px; - margin-bottom: 60px; -} - -.skill-category { - background: var(--bg-primary); - padding: 32px; - border-radius: var(--border-radius-large); - box-shadow: var(--shadow-light); - transition: all var(--transition-normal); -} - -.skill-category:hover { - transform: translateY(-4px); - box-shadow: var(--shadow-medium); -} - -.category-header { - display: flex; - align-items: center; - gap: 16px; - margin-bottom: 24px; -} - -.category-icon { - width: 60px; - height: 60px; - border-radius: var(--border-radius); - display: flex; - align-items: center; - justify-content: center; -} - -.category-title { - font-size: 1.25rem; - 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: 0.875rem; - color: var(--text-secondary); -} - -.skill-bar { - height: 8px; - background: var(--bg-secondary); - border-radius: 4px; - overflow: hidden; -} - -.skill-progress { - height: 100%; - border-radius: 4px; - transition: width var(--transition-slow); -} - -.soft-skills { - text-align: center; -} - -.soft-skills-title { - font-size: 1.5rem; - font-weight: 700; - margin-bottom: 32px; - color: var(--text-primary); -} - -.soft-skills-grid { - display: flex; - flex-wrap: wrap; - gap: 16px; - justify-content: center; -} - -.soft-skill-tag { - display: flex; - align-items: center; - gap: 8px; - padding: 12px 20px; - background: var(--bg-primary); - border: 2px solid var(--border-color); - border-radius: 50px; - font-weight: 500; - color: var(--text-secondary); - transition: all var(--transition-normal); - cursor: default; -} - -.soft-skill-tag:hover { - border-color: var(--primary-color); - color: var(--primary-color); - transform: translateY(-2px); -} - -/* Projects Section */ -.projects { - padding: var(--section-padding); - background: var(--bg-secondary); -} - -.projects-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); - gap: 40px; - margin-bottom: 60px; -} - -.project-card { - background: var(--bg-primary); - border-radius: var(--border-radius-large); - overflow: hidden; - box-shadow: var(--shadow-light); - transition: all var(--transition-normal); -} - -.project-card:hover { - box-shadow: var(--shadow-large); -} - -.project-header { - padding: 24px 24px 0; - display: flex; - justify-content: space-between; - align-items: flex-start; -} - -.project-icon { - width: 60px; - height: 60px; - border-radius: var(--border-radius); - display: flex; - align-items: center; - justify-content: center; -} - -.project-status { - display: flex; - align-items: center; -} - -.status-badge { - padding: 6px 12px; - border-radius: 20px; - font-size: 0.75rem; - font-weight: 600; -} - -.project-content { - padding: 24px; -} - -.project-title { - font-size: 1.5rem; - 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 { - padding: 6px 12px; - background: var(--bg-secondary); - border-radius: 20px; - font-size: 0.75rem; - font-weight: 500; - color: var(--text-secondary); -} - -.project-features h4 { - font-size: 1rem; - font-weight: 600; - margin-bottom: 12px; - color: var(--text-primary); -} - -.project-features ul { - list-style: none; - display: flex; - flex-direction: column; - gap: 8px; -} - -.project-features li { - color: var(--text-secondary); - position: relative; - padding-left: 20px; -} - -.project-features li::before { - content: '•'; - color: var(--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: var(--border-radius); - text-decoration: none; - font-weight: 500; - border: 2px solid var(--border-color); - color: var(--text-secondary); - transition: all var(--transition-normal); -} - -.project-link:hover { - border-color: var(--primary-color); - color: var(--primary-color); - transform: translateY(-2px); -} - -.project-link.primary { - background: var(--primary-color); - border-color: var(--primary-color); - color: white; -} - -.project-link.primary:hover { - background: var(--primary-hover); - border-color: var(--primary-hover); -} - -/* Education Section */ -.education { - padding: var(--section-padding); -} - -.education-content { - display: flex; - flex-direction: column; - gap: 60px; -} - -.education-card { - display: flex; - gap: 32px; - background: var(--bg-primary); - border-radius: var(--border-radius-large); - padding: 32px; - box-shadow: var(--shadow-light); - transition: all var(--transition-normal); -} - -.education-card:hover { - transform: translateY(-4px); - box-shadow: var(--shadow-medium); -} - -.education-timeline { - display: flex; - flex-direction: column; - align-items: center; - flex-shrink: 0; -} - -.timeline-dot { - width: 60px; - height: 60px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: white; - margin-bottom: 16px; -} - -.timeline-line { - width: 2px; - height: 100px; - background: var(--border-color); - border-radius: 1px; -} - -.education-content-card { - flex: 1; -} - -.education-header { - display: flex; - justify-content: space-between; - align-items: flex-start; - margin-bottom: 20px; -} - -.education-title h3 { - font-size: 1.5rem; - font-weight: 700; - margin-bottom: 12px; - color: var(--text-primary); -} - -.education-meta { - display: flex; - flex-direction: column; - gap: 8px; -} - -.education-meta span { - display: flex; - align-items: center; - gap: 8px; - font-size: 0.875rem; - color: var(--text-secondary); -} - -.education-status { - text-align: right; -} - -.current-year { - display: block; - font-size: 0.875rem; - 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: 1rem; - font-weight: 600; - margin-bottom: 16px; - color: var(--text-primary); -} - -.highlights-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 12px; -} - -.highlight-tag { - padding: 8px 12px; - background: var(--bg-secondary); - border-radius: var(--border-radius); - font-size: 0.875rem; - color: var(--text-secondary); - text-align: center; - transition: all var(--transition-normal); -} - -.highlight-tag:hover { - background: var(--primary-color); - color: white; - transform: scale(1.05); -} - -.certifications { - text-align: center; -} - -.certifications-title { - display: flex; - align-items: center; - justify-content: center; - gap: 12px; - font-size: 1.5rem; - font-weight: 700; - margin-bottom: 32px; - color: var(--text-primary); -} - -.certifications-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: 24px; - margin-bottom: 60px; -} - -.certification-card { - background: var(--bg-primary); - padding: 24px; - border-radius: var(--border-radius); - box-shadow: var(--shadow-light); - transition: all var(--transition-normal); -} - -.certification-card:hover { - box-shadow: var(--shadow-medium); -} - -.cert-header h4 { - font-size: 1.125rem; - font-weight: 600; - margin-bottom: 8px; - color: var(--text-primary); -} - -.cert-provider { - display: block; - font-size: 0.875rem; - color: var(--text-secondary); - margin-bottom: 4px; -} - -.cert-date { - display: block; - font-size: 0.75rem; - color: var(--text-muted); - margin-bottom: 16px; -} - -.cert-skills { - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -.cert-skill-tag { - padding: 4px 8px; - border-radius: 12px; - font-size: 0.75rem; - font-weight: 500; -} - -.learning-goals h3 { - font-size: 1.5rem; - font-weight: 700; - margin-bottom: 32px; - color: var(--text-primary); - text-align: center; -} - -.goals-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: 20px; -} - -.goal-item { - background: var(--bg-primary); - padding: 20px; - border-radius: var(--border-radius); - box-shadow: var(--shadow-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: 0.875rem; - color: var(--primary-color); - font-weight: 600; -} - -.goal-progress { - height: 6px; - background: var(--bg-secondary); - border-radius: 3px; - overflow: hidden; -} - -.goal-progress-bar { - height: 100%; - background: var(--primary-color); - border-radius: 3px; - transition: width var(--transition-slow); -} - -/* Contact Section */ -.contact { - padding: var(--section-padding); - background: var(--bg-secondary); -} - -.contact-content { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 60px; -} - -.contact-intro h3 { - display: flex; - align-items: center; - gap: 12px; - font-size: 1.5rem; - font-weight: 700; - margin-bottom: 16px; - color: var(--text-primary); -} - -.contact-intro p { - color: var(--text-secondary); - line-height: 1.7; - margin-bottom: 32px; -} - -.contact-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: var(--border-radius); - text-decoration: none; - transition: all var(--transition-normal); - box-shadow: var(--shadow-light); -} - -.contact-method:hover { - transform: translateX(8px); - box-shadow: var(--shadow-medium); -} - -.contact-icon { - width: 50px; - height: 50px; - border-radius: var(--border-radius); - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; -} - -.contact-details h4 { - font-weight: 600; - margin-bottom: 4px; - color: var(--text-primary); -} - -.contact-details span { - color: var(--text-secondary); -} - -.social-links h4 { - margin-bottom: 16px; - color: var(--text-primary); -} - -.social-grid { - display: flex; - gap: 16px; -} - -.social-link { - display: flex; - align-items: center; - gap: 8px; - padding: 12px 16px; - background: var(--bg-primary); - border-radius: var(--border-radius); - text-decoration: none; - transition: all var(--transition-normal); - box-shadow: var(--shadow-light); -} - -.social-link:hover { - box-shadow: var(--shadow-medium); -} - -.social-icon { - width: 32px; - height: 32px; - border-radius: 6px; - display: flex; - align-items: center; - justify-content: center; -} - -.social-link span { - font-weight: 500; - color: var(--text-secondary); -} - -.contact-form-container h3 { - font-size: 1.5rem; - font-weight: 700; - margin-bottom: 24px; - color: var(--text-primary); -} - -.success-message { - display: flex; - align-items: center; - gap: 12px; - background: #10b981; - color: white; - padding: 16px; - border-radius: var(--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; -} - -.form-group { - display: flex; - flex-direction: column; - gap: 8px; -} - -.form-group label { - font-weight: 500; - color: var(--text-primary); -} - -.form-group input, -.form-group textarea { - padding: 12px 16px; - border: 2px solid var(--border-color); - border-radius: var(--border-radius); - background: var(--bg-primary); - color: var(--text-primary); - font-family: inherit; - transition: border-color var(--transition-normal); -} - -.form-group input:focus, -.form-group textarea:focus { - outline: none; - border-color: var(--primary-color); -} - -.form-group textarea { - resize: vertical; - min-height: 120px; -} - -.submit-btn { - display: flex; - align-items: center; - justify-content: center; - gap: 12px; - padding: 16px 32px; - background: var(--primary-color); - color: white; - border: none; - border-radius: var(--border-radius); - font-weight: 600; - cursor: pointer; - transition: all var(--transition-normal); - font-size: 1rem; -} - -.submit-btn:hover:not(:disabled) { - background: var(--primary-hover); - transform: translateY(-2px); -} - -.submit-btn:disabled { - opacity: 0.7; - cursor: not-allowed; -} - -.loading-spinner { - width: 16px; - height: 16px; - border: 2px solid transparent; - border-top: 2px solid white; - border-radius: 50%; - animation: spin 1s linear infinite; -} - -@keyframes spin { - to { - transform: rotate(360deg); - } -} - -.contact-footer { - text-align: center; - margin-top: 60px; - padding-top: 40px; - border-top: 1px solid var(--border-color); -} - -.contact-footer p { - color: var(--text-muted); -} - -/* Responsive Design */ -@media (max-width: 768px) { - .desktop-menu { - display: none; - } - - .menu-toggle { - display: block; - } - - .mobile-menu { - display: flex; - 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); - box-shadow: var(--shadow-large); - } - - .mobile-menu.open { - top: 80px; - } - - .mobile-menu a { - font-size: 1.125rem; - padding: 16px 0; - border-bottom: 1px solid var(--border-color); - } - - .hero-content { - grid-template-columns: 1fr; - text-align: center; - gap: 40px; - } - - .hero-buttons { - flex-direction: column; - align-items: center; - } - - .about-content { - grid-template-columns: 1fr; - } - - .about-stats { - flex-direction: row; - flex-wrap: wrap; - } - - .stat-card { - flex: 1; - min-width: 140px; - } - - .skills-grid { - grid-template-columns: 1fr; - } - - .projects-grid { - grid-template-columns: 1fr; - } - - .education-card { - flex-direction: column; - text-align: center; - } - - .timeline-line { - display: none; - } - - .education-header { - flex-direction: column; - align-items: center; - text-align: center; - } - - .contact-content { - grid-template-columns: 1fr; - gap: 40px; - } - - .form-row { - grid-template-columns: 1fr; - } - - .social-grid { - flex-direction: column; - } - - .goals-grid, - .certifications-grid { - grid-template-columns: 1fr; - } -} - -@media (max-width: 480px) { - :root { - --section-padding: 60px 0; - --container-padding: 0 16px; - } - - .nav { - padding: 12px 16px; - } - - .hero-title { - font-size: 2.5rem; - } - - .hero-subtitle { - font-size: 1.25rem; - } - - .section-title { - font-size: 2rem; - } - - .about-card, - .skill-category, - .project-card, - .education-card { - padding: 20px; - } - - .hero-buttons { - gap: 12px; - } - - .btn { - padding: 12px 20px; - font-size: 0.9rem; - } -} - -/* Améliorations d'accessibilité */ -@media (prefers-reduced-motion: reduce) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} - -/* Amélioration du focus pour l'accessibilité */ -button:focus-visible, -a:focus-visible, -input:focus-visible, -textarea:focus-visible { - outline: 2px solid var(--primary-color); - outline-offset: 2px; -} - -/* Scroll smooth */ -html { - scroll-behavior: smooth; -} - -/* Amélioration de la sélection de texte */ -::selection { - background: var(--primary-color); - color: white; -} diff --git a/src/App.tsx b/src/App.tsx index 91f7e71..21d14a1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -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); diff --git a/src/main.tsx b/src/main.tsx index bef5202..42d5996 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -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( diff --git a/src/styles/README.md b/src/styles/README.md new file mode 100644 index 0000000..c2973bd --- /dev/null +++ b/src/styles/README.md @@ -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 +``` \ No newline at end of file diff --git a/src/index.css b/src/styles/_global.scss similarity index 50% rename from src/index.css rename to src/styles/_global.scss index 526a971..a1f48b3 100644 --- a/src/index.css +++ b/src/styles/_global.scss @@ -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; -} +// Classes utilitaires +.container { + max-width: map-get($breakpoints, xl); + margin: 0 auto; + padding: var(--container-padding); -/* 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) { - .container { + @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); + } + } + + .section-subtitle { + font-size: map-get($font-sizes, lg); + color: var(--text-secondary); + max-width: 600px; + margin: 0 auto; } } -/* Amélioration des performances */ -.gpu-accelerated { - transform: translateZ(0); - -webkit-transform: translateZ(0); - will-change: transform; -} - -/* 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; - } - - 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; - } +// Focus pour l'accessibilité +button:focus-visible, +a:focus-visible, +input:focus-visible, +textarea:focus-visible { + @include focus-style(); } -/* Amélioration du loading */ -.loading { - pointer-events: none; - opacity: 0.6; +// Amélioration de la sélection de texte +::selection { + background-color: $primary-color; + color: white; + text-shadow: none; } -/* Animations globales */ +::-moz-selection { + background-color: $primary-color; + color: white; + text-shadow: none; +} + +// 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; + } +} \ No newline at end of file diff --git a/src/styles/_mixins.scss b/src/styles/_mixins.scss new file mode 100644 index 0000000..2918a54 --- /dev/null +++ b/src/styles/_mixins.scss @@ -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; +} \ No newline at end of file diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss new file mode 100644 index 0000000..40d0235 --- /dev/null +++ b/src/styles/_variables.scss @@ -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) +); \ No newline at end of file diff --git a/src/styles/components/_about.scss b/src/styles/components/_about.scss new file mode 100644 index 0000000..a9ae442 --- /dev/null +++ b/src/styles/components/_about.scss @@ -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); + } + } +} \ No newline at end of file diff --git a/src/styles/components/_contact.scss b/src/styles/components/_contact.scss new file mode 100644 index 0000000..42c8219 --- /dev/null +++ b/src/styles/components/_contact.scss @@ -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; + } + } +} \ No newline at end of file diff --git a/src/styles/components/_education.scss b/src/styles/components/_education.scss new file mode 100644 index 0000000..8190302 --- /dev/null +++ b/src/styles/components/_education.scss @@ -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); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/styles/components/_header.scss b/src/styles/components/_header.scss new file mode 100644 index 0000000..bc3d62d --- /dev/null +++ b/src/styles/components/_header.scss @@ -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; + } + } +} \ No newline at end of file diff --git a/src/styles/components/_hero.scss b/src/styles/components/_hero.scss new file mode 100644 index 0000000..1174145 --- /dev/null +++ b/src/styles/components/_hero.scss @@ -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; + } + } +} \ No newline at end of file diff --git a/src/styles/components/_projects.scss b/src/styles/components/_projects.scss new file mode 100644 index 0000000..1ef5813 --- /dev/null +++ b/src/styles/components/_projects.scss @@ -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; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/styles/components/_skills.scss b/src/styles/components/_skills.scss new file mode 100644 index 0000000..3be9007 --- /dev/null +++ b/src/styles/components/_skills.scss @@ -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); + } + } + } + } +} \ No newline at end of file diff --git a/src/styles/index.scss b/src/styles/index.scss new file mode 100644 index 0000000..4384374 --- /dev/null +++ b/src/styles/index.scss @@ -0,0 +1,6 @@ +// ======================== +// INDEX SCSS - Point d'entrée principal +// ======================== + +// Global styles uniquement - les composants sont dans main.scss +@use 'global'; \ No newline at end of file diff --git a/src/styles/main.scss b/src/styles/main.scss new file mode 100644 index 0000000..e69c8ae --- /dev/null +++ b/src/styles/main.scss @@ -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'; \ No newline at end of file