initalizing
This commit is contained in:
commit
04ffbccb28
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Vue 3 + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).
|
13
index.html
Normal file
13
index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + Vue</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
1197
package-lock.json
generated
Normal file
1197
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
package.json
Normal file
20
package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "my-vue-app",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"format": "prettier --write \"src/**/*.{js,vue,css,html,json}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"prettier": "^3.5.3",
|
||||
"vite": "^6.2.0"
|
||||
}
|
||||
}
|
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
133
src/App.vue
Normal file
133
src/App.vue
Normal file
@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div class="app">
|
||||
<Header />
|
||||
<main>
|
||||
<Landing />
|
||||
<div class="content">
|
||||
<Experience />
|
||||
</div>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Header from "./components/Header.vue";
|
||||
import Landing from "./components/Landing.vue";
|
||||
import Experience from "./components/Experience.vue";
|
||||
import Footer from "./components/Footer.vue";
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
components: {
|
||||
Header,
|
||||
Landing,
|
||||
Experience,
|
||||
Footer,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Global Styles */
|
||||
:root {
|
||||
--duck-yellow: #ffdb58;
|
||||
--duck-orange: #f97316;
|
||||
--duck-blue: #4682b4;
|
||||
--duck-dark-blue: #1e3a8a;
|
||||
--duck-green: #228b22;
|
||||
--bg-color: #fffdf7;
|
||||
--text-color: #333;
|
||||
--gray-600: #4b5563;
|
||||
--gray-700: #374151;
|
||||
--gray-500: #6b7280;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
|
||||
Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.app {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--bg-color);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 2rem;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.content {
|
||||
padding: 0 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animation Classes */
|
||||
@keyframes float {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wave {
|
||||
0%,
|
||||
100% {
|
||||
transform: rotate(-5deg);
|
||||
}
|
||||
50% {
|
||||
transform: rotate(5deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes splash {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-float {
|
||||
animation: float 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.animate-wave {
|
||||
animation: wave 3s ease-in-out infinite;
|
||||
transform-origin: bottom center;
|
||||
}
|
||||
|
||||
.animate-splash {
|
||||
animation: splash 0.5s ease-out forwards;
|
||||
}
|
||||
</style>
|
BIN
src/assets/duck-dance.gif
Normal file
BIN
src/assets/duck-dance.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 MiB |
BIN
src/assets/duck-dance2.gif
Normal file
BIN
src/assets/duck-dance2.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 MiB |
BIN
src/assets/duck-dance3.gif
Normal file
BIN
src/assets/duck-dance3.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 MiB |
BIN
src/assets/duck.gif
Normal file
BIN
src/assets/duck.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 130 KiB |
1
src/assets/vue.svg
Normal file
1
src/assets/vue.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
After Width: | Height: | Size: 496 B |
311
src/components/DuckTerminal.vue
Normal file
311
src/components/DuckTerminal.vue
Normal file
@ -0,0 +1,311 @@
|
||||
<template>
|
||||
<div class="duck-terminal">
|
||||
<div class="terminal-header">
|
||||
<div class="terminal-buttons">
|
||||
<span class="terminal-button close"></span>
|
||||
<span class="terminal-button minimize"></span>
|
||||
<span class="terminal-button maximize"></span>
|
||||
</div>
|
||||
<div class="terminal-title">portfolio@duck:~</div>
|
||||
</div>
|
||||
<div class="terminal-body">
|
||||
<div class="terminal-content" ref="terminalContent">
|
||||
<div
|
||||
class="terminal-line"
|
||||
v-for="(line, index) in displayedLines"
|
||||
:key="index"
|
||||
>
|
||||
<template v-if="line.type === 'command'">
|
||||
<span class="terminal-prompt">portfolio@duck:~$</span>
|
||||
<span class="terminal-text">{{ line.text }}</span>
|
||||
</template>
|
||||
<template v-else-if="line.type === 'skills'">
|
||||
<div class="terminal-output skills-grid">
|
||||
<div class="skill-item" v-for="(skill, skillIndex) in line.skills" :key="skillIndex">
|
||||
{{ skill }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="terminal-output">{{ line.text }}</span>
|
||||
</template>
|
||||
</div>
|
||||
<div
|
||||
class="terminal-line typing-line"
|
||||
v-if="currentLineIndex < terminalLines.length && isTypingCommand"
|
||||
>
|
||||
<span class="terminal-prompt">portfolio@duck:~$</span>
|
||||
<span class="terminal-text typing">{{ currentText }}</span>
|
||||
<span class="terminal-cursor"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "DuckTerminal",
|
||||
data() {
|
||||
return {
|
||||
terminalLines: [
|
||||
{ type: "command", text: "cat profil.txt" },
|
||||
{
|
||||
type: "output",
|
||||
text: "Développeur PHP et Admin Sys/Réseau avec passion pour le web",
|
||||
},
|
||||
{ type: "command", text: "cat experience.txt" },
|
||||
{
|
||||
type: "output",
|
||||
text: "Plus de 4 ans d'expérience dans différents contextes professionnels",
|
||||
},
|
||||
{ type: "command", text: "ls -la skills/" },
|
||||
{
|
||||
type: "skills",
|
||||
skills: [
|
||||
"PHP", "Laravel", "MySQL", "Linux",
|
||||
"Symfony", "Docker", "Git", "Networking",
|
||||
"JavaScript", "REST API", "DevOps", "Cloud"
|
||||
]
|
||||
},
|
||||
{ type: "command", text: "whoami" },
|
||||
{
|
||||
type: "output",
|
||||
text: "Expert en développement PHP et en administration système/réseau",
|
||||
},
|
||||
],
|
||||
displayedLines: [],
|
||||
currentLineIndex: 0,
|
||||
currentText: "",
|
||||
currentCharIndex: 0,
|
||||
typingSpeed: 40,
|
||||
commandDelay: 300,
|
||||
outputDelay: 300,
|
||||
isTypingCommand: false,
|
||||
isProcessing: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.startTyping();
|
||||
},
|
||||
updated() {
|
||||
this.scrollToBottom();
|
||||
},
|
||||
methods: {
|
||||
scrollToBottom() {
|
||||
if (this.$refs.terminalContent) {
|
||||
this.$refs.terminalContent.scrollTop = this.$refs.terminalContent.scrollHeight;
|
||||
}
|
||||
},
|
||||
startTyping() {
|
||||
this.processNextLine();
|
||||
},
|
||||
processNextLine() {
|
||||
if (this.currentLineIndex >= this.terminalLines.length || this.isProcessing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentLine = this.terminalLines[this.currentLineIndex];
|
||||
this.isProcessing = true;
|
||||
|
||||
if (currentLine.type === "command") {
|
||||
// For command lines, animate typing
|
||||
this.isTypingCommand = true;
|
||||
this.currentText = "";
|
||||
this.currentCharIndex = 0;
|
||||
this.typeCurrentLine();
|
||||
} else {
|
||||
// For output and skills lines, just add them after a delay
|
||||
this.isTypingCommand = false;
|
||||
setTimeout(() => {
|
||||
this.displayedLines.push(currentLine);
|
||||
this.currentLineIndex++;
|
||||
this.isProcessing = false;
|
||||
this.processNextLine();
|
||||
}, this.outputDelay);
|
||||
}
|
||||
},
|
||||
typeCurrentLine() {
|
||||
if (!this.isTypingCommand || this.currentLineIndex >= this.terminalLines.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentLine = this.terminalLines[this.currentLineIndex];
|
||||
|
||||
if (this.currentCharIndex < currentLine.text.length) {
|
||||
// Still typing the current character
|
||||
this.currentText = currentLine.text.substring(
|
||||
0,
|
||||
this.currentCharIndex + 1
|
||||
);
|
||||
this.currentCharIndex++;
|
||||
setTimeout(this.typeCurrentLine, this.typingSpeed);
|
||||
} else {
|
||||
// Finished typing this command
|
||||
setTimeout(() => {
|
||||
// Add the completed command to displayed lines
|
||||
this.displayedLines.push({ ...currentLine });
|
||||
this.currentLineIndex++;
|
||||
this.isTypingCommand = false;
|
||||
this.isProcessing = false;
|
||||
|
||||
// Wait a bit before processing the next line
|
||||
setTimeout(this.processNextLine, this.commandDelay);
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.duck-terminal {
|
||||
width: 620px; /* Fixed width as requested */
|
||||
max-width: 620px;
|
||||
min-width: 620px;
|
||||
background-color: #1a1b26;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
font-family: "Courier New", monospace;
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
|
||||
border: 1px solid #30374b;
|
||||
margin-bottom: 2rem;
|
||||
height: 320px;
|
||||
}
|
||||
|
||||
.terminal-header {
|
||||
height: 36px;
|
||||
background-color: #24283b;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
border-bottom: 1px solid #30374b;
|
||||
}
|
||||
|
||||
.terminal-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.terminal-button {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.terminal-button.close {
|
||||
background-color: #ff5f57;
|
||||
}
|
||||
|
||||
.terminal-button.minimize {
|
||||
background-color: #febc2e;
|
||||
}
|
||||
|
||||
.terminal-button.maximize {
|
||||
background-color: #28c840;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
color: #a9b1d6;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.terminal-body {
|
||||
height: calc(100% - 36px);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.terminal-content {
|
||||
padding: 1.25rem;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
color: #a9b1d6;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #30374b #1a1b26;
|
||||
}
|
||||
|
||||
.terminal-content::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.terminal-content::-webkit-scrollbar-track {
|
||||
background: #1a1b26;
|
||||
}
|
||||
|
||||
.terminal-content::-webkit-scrollbar-thumb {
|
||||
background-color: #30374b;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.terminal-line {
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.terminal-prompt {
|
||||
color: var(--duck-yellow, #ffdb58);
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.terminal-text {
|
||||
color: #a9b1d6;
|
||||
}
|
||||
|
||||
.terminal-output {
|
||||
color: #7aa2f7;
|
||||
}
|
||||
|
||||
.skills-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-gap: 0.5rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.skill-item {
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.terminal-cursor {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 16px;
|
||||
background-color: #7aa2f7;
|
||||
animation: blink 1s step-start infinite;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%,
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
51%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.typing-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.typing {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
/* Make the terminal responsive while maintaining aspect ratio */
|
||||
@media (max-width: 768px) {
|
||||
.duck-terminal {
|
||||
width: 100%;
|
||||
min-width: auto;
|
||||
max-width: 620px;
|
||||
}
|
||||
}
|
||||
</style>
|
501
src/components/Experience.vue
Normal file
501
src/components/Experience.vue
Normal file
@ -0,0 +1,501 @@
|
||||
<template>
|
||||
<section id="experience" class="experience">
|
||||
<div class="experience-container">
|
||||
<h2 class="section-title">Compétences</h2>
|
||||
<div class="skills-grid">
|
||||
<div class="skill-card animate-splash">
|
||||
<h3 class="skill-title">Langages & Frameworks</h3>
|
||||
<div class="skill-tags">
|
||||
<span class="skill-tag">PHP</span>
|
||||
<span class="skill-tag">Drupal</span>
|
||||
<span class="skill-tag">Symfony</span>
|
||||
<span class="skill-tag">Laravel</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-card animate-splash">
|
||||
<h3 class="skill-title">Systèmes & DevOps</h3>
|
||||
<div class="skill-tags">
|
||||
<span class="skill-tag">Docker</span>
|
||||
<span class="skill-tag">Linux</span>
|
||||
<span class="skill-tag">Git</span>
|
||||
<span class="skill-tag">CI/CD</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-card animate-splash">
|
||||
<h3 class="skill-title">Administration & Sécurité</h3>
|
||||
<div class="skill-tags">
|
||||
<span class="skill-tag">Administration Systèmes</span>
|
||||
<span class="skill-tag">Sécurité Web</span>
|
||||
<span class="skill-tag">Monitoring</span>
|
||||
<span class="skill-tag">SQL</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="experience-section">
|
||||
<h2 class="section-title">Mon Parcours</h2>
|
||||
<div class="timeline">
|
||||
<div class="timeline-title">
|
||||
<h3 class="timeline-heading">Expérience Professionnelle</h3>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="timeline-icon timeline-icon-work"
|
||||
>
|
||||
<path
|
||||
d="M8 3c.132 0 .263 0 .393 0 7.107.007 10.372 6.167 11.917 11.917.078.287.156.575.234.863"
|
||||
></path>
|
||||
<path
|
||||
d="M20.039 17.39c-.221-.89-.943-1.396-1.871-1.394a1.99 1.99 0 0 0-1.988 1.994v.012a1.998 1.998 0 0 0 2.004 1.998c.464 0 .91-.196 1.244-.548"
|
||||
></path>
|
||||
<path
|
||||
d="M7.05 4.095c-1.815-.407-3.246-.089-4.028 1.007C1.082 7.547 1.603 12.19 4 16c2.492 0 4.623-1.33 6.234-3.219"
|
||||
></path>
|
||||
<path d="M11.167 12c1.174-1.525 2.272-3.747 2.272-6.8V3"></path>
|
||||
<circle cx="16" cy="10" r="1"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="timeline-items">
|
||||
<div
|
||||
class="timeline-card animate-splash"
|
||||
style="animation-delay: 0s"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">
|
||||
Administrateur Systèmes et Réseaux Web | STUDIO BESTIO
|
||||
</h3>
|
||||
<p class="card-date">Février 2025 - Aujourd'hui</p>
|
||||
<div class="card-location">
|
||||
<span>Nancy, France</span>
|
||||
<span>· Indépendant</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-tags">
|
||||
<span class="card-tag">Docker</span>
|
||||
<span class="card-tag">Linux</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
Gestion de l'hébergement, de sauvegardes, de la maintenance et
|
||||
de la sécurité des sites web pour Studiobestio. Administration
|
||||
des serveurs, supervision des performances, gestion de la
|
||||
journalisation, mise en place de solutions de sécurité pour
|
||||
assurer une protection contre les cybermenaces. Garantir le
|
||||
bon fonctionnement, la sécurité et la fiabilité des sites web
|
||||
des clients.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="timeline-card animate-splash"
|
||||
style="animation-delay: 0.1s"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">Développeur PHP | GIE SIMA</h3>
|
||||
<p class="card-date">Mai 2023 - Aujourd'hui</p>
|
||||
<div class="card-location">
|
||||
<span>Nancy, France</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-tags">
|
||||
<span class="card-tag">PHP</span>
|
||||
<span class="card-tag">Drupal</span>
|
||||
<span class="card-tag">Docker</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
Réecriture et maintenance d'une application pour la vente de
|
||||
contrats de santé ou prévoyance déstinée à des
|
||||
courtiers/conseillers.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="timeline-card animate-splash"
|
||||
style="animation-delay: 0.2s"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">Consultant | DAVIDSON CONSULTING</h3>
|
||||
<p class="card-date">Février 2023 - Mai 2023</p>
|
||||
<div class="card-location">
|
||||
<span>Nancy, France</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-tags">
|
||||
<span class="card-tag">PHP</span>
|
||||
<span class="card-tag">Drupal</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>Développement d'applications clients</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="timeline-card animate-splash"
|
||||
style="animation-delay: 0.3s"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">
|
||||
Développeur web | GANTOIS INDUSTRIES
|
||||
</h3>
|
||||
<p class="card-date">Sept. 2021 - Avr. 2022</p>
|
||||
<div class="card-location">
|
||||
<span>Saint-Dié, France</span>
|
||||
<span>· Intérimaire</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-tags">
|
||||
<span class="card-tag">PHP</span>
|
||||
<span class="card-tag">Symfony</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>Développement d'applications métier</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="timeline-card animate-splash"
|
||||
style="animation-delay: 0.4s"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">
|
||||
Développeur web | GANTOIS INDUSTRIES
|
||||
</h3>
|
||||
<p class="card-date">Nov. 2020 - Sept. 2021</p>
|
||||
<div class="card-location">
|
||||
<span>Saint-Dié, France</span>
|
||||
<span>· Alternance</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-tags">
|
||||
<span class="card-tag">PHP</span>
|
||||
<span class="card-tag">Symfony</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
Développement et maintenance d'une application de contrôle
|
||||
qualité pour les pièces industrielles.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="timeline-card animate-splash"
|
||||
style="animation-delay: 0.5s"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">
|
||||
Développeur Full Stack | 2110 FINANCE
|
||||
</h3>
|
||||
<p class="card-date">Juillet 2020</p>
|
||||
<div class="card-location">
|
||||
<span>Saint-Dié, France</span>
|
||||
<span>· Stage</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-tags">
|
||||
<span class="card-tag">PHP</span>
|
||||
<span class="card-tag">Laravel</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>Application de messagerie interne</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-divider"></div>
|
||||
|
||||
<div>
|
||||
<div class="timeline-title">
|
||||
<h3 class="timeline-heading">Formation</h3>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="timeline-icon timeline-icon-education"
|
||||
>
|
||||
<path
|
||||
d="M22 12A10 10 0 1 1 12 2a1 1 0 0 1 1 1v1a1 1 0 1 1-2 0V3.07A8 8 0 1 0 19 12h-3a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h1"
|
||||
></path>
|
||||
<path d="M11.8 22h.2a3 3 0 0 0 3-3v-1"></path>
|
||||
<path
|
||||
d="M20 19a2 2 0 0 0 2-2v-1a3 3 0 0 0-3-3h-1a2 2 0 0 0-2 2"
|
||||
></path>
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="timeline-items">
|
||||
<div
|
||||
class="timeline-card education-card animate-splash"
|
||||
style="animation-delay: 0s"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">
|
||||
Licence en application web et mobile | Université de
|
||||
Lorraine
|
||||
</h3>
|
||||
<p class="card-date">2020 - 2021</p>
|
||||
<div class="card-location">
|
||||
<span>Nancy, France</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
Formation en développement d'applications web et mobiles
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="timeline-card education-card animate-splash"
|
||||
style="animation-delay: 0.1s"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">
|
||||
DUT informatique | Université de Lorraine
|
||||
</h3>
|
||||
<p class="card-date">2018 - 2020</p>
|
||||
<div class="card-location">
|
||||
<span>Nancy, France</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
Formation en informatique générale avec spécialisation en
|
||||
développement
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="timeline-card education-card animate-splash"
|
||||
style="animation-delay: 0.2s"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">
|
||||
Baccalauréat Scientifique | Lycée Gaston Bachelard
|
||||
</h3>
|
||||
<p class="card-date">2015 - 2018</p>
|
||||
<div class="card-location">
|
||||
<span>Bar-sur-Aube, France</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>Baccalauréat avec option mathématiques</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Experience",
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.experience {
|
||||
padding: 2rem 0;
|
||||
scroll-margin-top: 4rem;
|
||||
}
|
||||
|
||||
.experience-container {
|
||||
max-width: 64rem;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.875rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.skills-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.skill-card {
|
||||
background-color: white;
|
||||
padding: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.skill-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--duck-dark-blue);
|
||||
}
|
||||
|
||||
.skill-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.skill-tag {
|
||||
background-color: var(--duck-yellow);
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.experience-section {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.timeline-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.timeline-heading {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.timeline-icon {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.timeline-icon-work {
|
||||
color: var(--duck-blue);
|
||||
}
|
||||
|
||||
.timeline-icon-education {
|
||||
color: var(--duck-orange);
|
||||
}
|
||||
|
||||
.timeline-items {
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.timeline-card {
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid #e5e7eb;
|
||||
background-color: white;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
border-left: 4px solid var(--duck-blue);
|
||||
}
|
||||
|
||||
.education-card {
|
||||
border-left: 4px solid var(--duck-orange);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
padding: 1.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.card-date {
|
||||
font-size: 0.875rem;
|
||||
color: var(--gray-500);
|
||||
}
|
||||
|
||||
.card-location {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
color: var(--gray-600);
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.card-tags {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.card-tag {
|
||||
background-color: rgba(34, 139, 34, 0.2);
|
||||
color: var(--text-color);
|
||||
font-size: 0.75rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 1.5rem;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.card-content p {
|
||||
color: var(--gray-700);
|
||||
}
|
||||
|
||||
.section-divider {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background-color: #e5e7eb;
|
||||
margin: 2.5rem 0;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.skills-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
</style>
|
174
src/components/Footer.vue
Normal file
174
src/components/Footer.vue
Normal file
@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<footer class="footer">
|
||||
<div class="footer-container">
|
||||
<div class="footer-left">
|
||||
<DuckSvg svgClass="footer-duck" />
|
||||
<div>
|
||||
<h3 class="footer-title">Romaric SIRI</h3>
|
||||
<p class="footer-subtitle">Développeur PHP | Admin Sys</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
<p class="footer-text">
|
||||
Nancy, Grand Est —
|
||||
<a href="tel:0632717245" class="footer-link">06 32 71 72 45</a>
|
||||
</p>
|
||||
<p class="footer-text">
|
||||
<a href="mailto:contact@maric.ro" class="footer-link"
|
||||
>contact@maric.ro</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-duck-right animate-float">
|
||||
<DuckSvg />
|
||||
<div class="duck-shadow"></div>
|
||||
</div>
|
||||
<div class="footer-duck-left animate-float">
|
||||
<DuckSvg />
|
||||
<div class="duck-shadow"></div>
|
||||
</div>
|
||||
<p class="copyright">© 2025 Romaric SIRI. All rights reserved. 🦆</p>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DuckSvg from "./ducks/Duck1.vue";
|
||||
|
||||
export default {
|
||||
name: "Footer",
|
||||
components: {
|
||||
DuckSvg,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.footer {
|
||||
padding: 2rem 1rem;
|
||||
background-color: rgba(255, 219, 88, 0.2);
|
||||
margin-top: 2rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.footer-container {
|
||||
max-width: 72rem;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.footer-duck {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.footer-title {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.footer-subtitle {
|
||||
font-size: 0.875rem;
|
||||
color: var(--gray-600);
|
||||
}
|
||||
|
||||
.footer-right {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 0.875rem;
|
||||
color: var(--gray-600);
|
||||
}
|
||||
|
||||
.footer-link {
|
||||
color: var(--gray-600);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.footer-duck-right {
|
||||
position: absolute;
|
||||
bottom: -1.25rem;
|
||||
right: 1rem;
|
||||
transform: translateY(0);
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.footer-duck-right svg {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
.footer-duck-left {
|
||||
position: absolute;
|
||||
bottom: -1.25rem;
|
||||
left: 1rem;
|
||||
transform: translateY(0);
|
||||
opacity: 0.3;
|
||||
animation-delay: 2s;
|
||||
}
|
||||
|
||||
.footer-duck-left svg {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
.duck-shadow {
|
||||
position: absolute;
|
||||
width: 6rem;
|
||||
height: 2rem;
|
||||
background-color: rgba(70, 130, 180, 0.3);
|
||||
border-radius: 9999px;
|
||||
bottom: -1rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
text-align: center;
|
||||
font-size: 0.75rem;
|
||||
color: var(--gray-500);
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.footer {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.footer-container {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.footer-left {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.footer-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.footer-duck-right {
|
||||
right: 2.5rem;
|
||||
}
|
||||
|
||||
.footer-duck-left {
|
||||
left: 5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
240
src/components/Header.vue
Normal file
240
src/components/Header.vue
Normal file
@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<header class="header">
|
||||
<div class="header-container">
|
||||
<div class="header-left">
|
||||
<div class="duck-container">
|
||||
<DuckSvg svgClass="duck-svg animate-float" />
|
||||
<div class="bubble animate-wave">
|
||||
<span class="bubble-text">Quack!</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-info">
|
||||
<h1 class="header-title">Romaric SIRI</h1>
|
||||
<p class="header-subtitle">
|
||||
Développeur PHP | Administrateur Systèmes & Réseaux Web
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="social-links">
|
||||
<a href="mailto:contact@maric.ro" title="Email" class="social-link">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="icon"
|
||||
>
|
||||
<rect width="20" height="16" x="2" y="4" rx="2"></rect>
|
||||
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"></path>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="tel:0632717245" title="Téléphone" class="social-link">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="icon"
|
||||
>
|
||||
<path
|
||||
d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"
|
||||
></path>
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href="https://linkedin.com/in/romaric-siri-a25949181"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="LinkedIn"
|
||||
class="social-link"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="icon"
|
||||
>
|
||||
<path
|
||||
d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"
|
||||
></path>
|
||||
<rect width="4" height="12" x="2" y="9"></rect>
|
||||
<circle cx="4" cy="4" r="2"></circle>
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="GitHub"
|
||||
class="social-link"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="icon"
|
||||
>
|
||||
<path
|
||||
d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"
|
||||
></path>
|
||||
<path d="M9 18c-4.51 2-5-2-7-2"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DuckSvg from "./ducks/Duck1.vue";
|
||||
|
||||
export default {
|
||||
name: "Header",
|
||||
components: {
|
||||
DuckSvg,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
position: relative;
|
||||
padding: 2.5rem 1rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.duck-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.duck-svg {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
}
|
||||
|
||||
.bubble {
|
||||
padding: 0.5rem 1rem;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
transform: translateX(80%) translateY(-30%);
|
||||
background-color: white;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.bubble:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 8px solid transparent;
|
||||
border-right-color: white;
|
||||
border-left: 0;
|
||||
margin-top: -8px;
|
||||
margin-left: -8px;
|
||||
}
|
||||
|
||||
.bubble-text {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.header-info {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 1.875rem;
|
||||
font-weight: 700;
|
||||
color: var(--duck-dark-blue);
|
||||
}
|
||||
|
||||
.header-subtitle {
|
||||
font-size: 1.125rem;
|
||||
color: var(--gray-600);
|
||||
}
|
||||
|
||||
.social-links {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.social-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
border-radius: 0.375rem;
|
||||
border: 1px solid #e5e7eb;
|
||||
background-color: white;
|
||||
color: var(--text-color);
|
||||
transition:
|
||||
background-color 0.2s,
|
||||
color 0.2s;
|
||||
}
|
||||
|
||||
.social-link:hover {
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.header {
|
||||
padding: 2.5rem 2rem 1rem;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
332
src/components/Landing.vue
Normal file
332
src/components/Landing.vue
Normal file
@ -0,0 +1,332 @@
|
||||
<template>
|
||||
<section class="landing">
|
||||
<div class="landing-container">
|
||||
<div class="landing-content animate-splash">
|
||||
<div class="terminal-duck-container">
|
||||
<div class="terminal-wrapper">
|
||||
<DuckTerminal />
|
||||
</div>
|
||||
<div class="duck-container">
|
||||
<div class="duck-placeholder">
|
||||
<img
|
||||
src="../assets/duck.gif"
|
||||
alt="Dancing Duck"
|
||||
class="dancing-duck"
|
||||
@load="duckLoaded = true"
|
||||
:class="{ 'duck-loaded': duckLoaded }"
|
||||
/>
|
||||
<div class="duck-loading" v-if="!duckLoaded">
|
||||
<span class="loading-text">Chargement du canard...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="landing-buttons">
|
||||
<button class="btn btn-primary">
|
||||
Voir mon parcours
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="btn-icon"
|
||||
>
|
||||
<path d="M12 5v14"></path>
|
||||
<path d="m19 12-7 7-7-7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<a href="mailto:contact@maric.ro" class="btn btn-secondary"
|
||||
>Me contacter</a
|
||||
>
|
||||
</div>
|
||||
<div class="duck-container-small">
|
||||
<DuckSvg svgClass="duck-svg animate-float" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Background Duck Elements -->
|
||||
<div class="bg-duck bg-duck-1">
|
||||
<DuckSvg />
|
||||
</div>
|
||||
<div class="bg-duck bg-duck-2">
|
||||
<DuckSvg />
|
||||
</div>
|
||||
<div class="bg-duck bg-duck-3">
|
||||
<DuckSvg />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DuckSvg from "./ducks/Duck1.vue";
|
||||
import DuckTerminal from "./DuckTerminal.vue";
|
||||
|
||||
export default {
|
||||
name: "Landing",
|
||||
components: {
|
||||
DuckSvg,
|
||||
DuckTerminal,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
duckLoaded: false,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.landing {
|
||||
padding: 4rem 1rem;
|
||||
min-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.landing-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
max-width: 90%;
|
||||
margin: 0 auto;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.landing-content {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.terminal-duck-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.terminal-wrapper {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.duck-container {
|
||||
margin-top: 1.5rem;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.landing-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
height: 2.75rem;
|
||||
border-radius: 0.375rem;
|
||||
padding: 0 2rem;
|
||||
transition: all 0.2s;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--duck-yellow);
|
||||
color: black;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: rgba(255, 219, 88, 0.9);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--duck-blue);
|
||||
color: var(--duck-blue);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: rgba(70, 130, 180, 0.1);
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.duck-container-small {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.duck-svg {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
}
|
||||
|
||||
.dancing-duck {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
width: auto;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.duck-placeholder {
|
||||
width: 320px;
|
||||
height: 320px;
|
||||
position: relative;
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.duck-loaded {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.duck-loading {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: var(--duck-yellow, #ffdb58);
|
||||
font-family: "Courier New", monospace;
|
||||
font-size: 1rem;
|
||||
animation: duck-bounce 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes duck-bounce {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Background Duck Elements */
|
||||
.bg-duck {
|
||||
position: absolute;
|
||||
opacity: 0.1;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.bg-duck-1 {
|
||||
top: 8rem;
|
||||
right: 2.5rem;
|
||||
transform: rotate(12deg);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bg-duck-1 svg {
|
||||
width: 16rem;
|
||||
height: 16rem;
|
||||
}
|
||||
|
||||
.bg-duck-2 {
|
||||
bottom: 40%;
|
||||
left: 0;
|
||||
transform: rotate(-12deg);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bg-duck-2 svg {
|
||||
width: 12rem;
|
||||
height: 12rem;
|
||||
}
|
||||
|
||||
.bg-duck-3 {
|
||||
bottom: 33.333%;
|
||||
right: 33.333%;
|
||||
opacity: 0.05;
|
||||
transform: rotate(45deg);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bg-duck-3 svg {
|
||||
width: 24rem;
|
||||
height: 24rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.landing-content {
|
||||
text-align: left;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.terminal-duck-container {
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.terminal-wrapper {
|
||||
width: 60%;
|
||||
margin-right: 2rem;
|
||||
}
|
||||
|
||||
.duck-container {
|
||||
width: 40%;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.landing-buttons {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.duck-placeholder {
|
||||
width: 100%;
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
.bg-duck-1,
|
||||
.bg-duck-2 {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.bg-duck-3 {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
34
src/components/ducks/Duck1.vue
Normal file
34
src/components/ducks/Duck1.vue
Normal file
@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
:class="svgClass"
|
||||
>
|
||||
<ellipse cx="256" cy="256" rx="180" ry="170" fill="#FFDB58" />
|
||||
<circle cx="360" cy="180" r="90" fill="#FFDB58" />
|
||||
<circle cx="390" cy="160" r="15" fill="#333" />
|
||||
<path d="M430 190 L480 180 L430 210 Z" fill="#F97316" />
|
||||
<path d="M180 370 L150 410 L210 410 Z" fill="#F97316" />
|
||||
<path d="M300 370 L270 410 L330 410 Z" fill="#F97316" />
|
||||
<ellipse
|
||||
cx="200"
|
||||
cy="250"
|
||||
rx="60"
|
||||
ry="40"
|
||||
fill="#FFE082"
|
||||
transform="rotate(-10, 200, 250)"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "DuckSvg",
|
||||
props: {
|
||||
svgClass: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
0
src/components/ducks/Duck2.vue
Normal file
0
src/components/ducks/Duck2.vue
Normal file
5
src/main.js
Normal file
5
src/main.js
Normal file
@ -0,0 +1,5 @@
|
||||
import { createApp } from "vue";
|
||||
import "./style.css";
|
||||
import App from "./App.vue";
|
||||
|
||||
createApp(App).mount("#app");
|
0
src/style.css
Normal file
0
src/style.css
Normal file
7
vite.config.js
Normal file
7
vite.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
})
|
Loading…
x
Reference in New Issue
Block a user