initalizing

This commit is contained in:
sirir 2025-04-04 21:24:15 +02:00
commit 04ffbccb28
21 changed files with 2974 additions and 0 deletions

5
README.md Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

20
package.json Normal file
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
src/assets/duck.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

1
src/assets/vue.svg Normal file
View 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

View 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>

View 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
View 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
View 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
View 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>

View 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>

View File

5
src/main.js Normal file
View 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
View File

7
vite.config.js Normal file
View File

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
})