Add form section

This commit is contained in:
Paul Nicoué 2023-01-20 18:08:51 +01:00
parent c6705914e0
commit 2a4cf52465
33 changed files with 1031 additions and 311 deletions

107
app.vue
View file

@ -1,7 +1,6 @@
<template>
<div class="app">
<AppHeader class="app__header" />
<NuxtPage class="app__main" />
<AppFooter class="app__footer" />
</div>
@ -26,101 +25,6 @@
<style lang="scss">
// --------------------------------------------------
// FONTS AND COLORS
// --------------------------------------------------
body {
font-family: var(--text-font-family);
font-size: var(--text-font-size);
font-weight: var(--regular-font-weight);
line-height: var(--line-height);
color: var(--secondary-color);
background-color: var(--primary-color);
}
h1 {
font-family: var(--title-font-family);
font-size: var(--h1-font-size);
font-weight: var(--medium-font-weight);
}
h2 {
font-family: var(--title-font-family);
font-size: var(--h2-font-size);
margin: 0 0 4rem 0;
}
h3 {
font-family: var(--text-font-family);
font-size: var(--h3-font-size);
margin: 2rem 0 1rem 0;
}
strong {
font-weight: var(--bold-font-weight);
}
em {
font-style: italic;
}
// --------------------------------------------------
// LINK STYLE
// --------------------------------------------------
a {
color: var(--secondary-color);
text-decoration: underline var(--accent-color);
border-radius: 2px;
transition: color 200ms ease-in-out;
&:hover,
&:focus,
&:active {
color: var(--accent-color);
}
&:focus-visible {
outline: 1px dashed var(--accent-color);
outline-offset: 2px;
animation: expand-outline 200ms ease-in-out;
}
}
// --------------------------------------------------
// BUTTON STYLE
// --------------------------------------------------
button {
padding: 1rem 1.5rem;
color: var(--primary-color);
background-image: var(--button-gradient);
background-size: 100%;
background-position: right center;
border: none;
border-radius: 40px;
cursor: pointer;
font-family: var(--text-font-family);
font-size: var(--text-font-size);
font-weight: var(--semi-bold-font-weight);
line-height: var(--line-height);
text-align: center;
transition: background-size 200ms ease-in-out;
&:hover,
&:focus,
&:active {
background-size: 300%;
}
&:focus-visible {
outline: 1px dashed var(--accent-color);
outline-offset: 4px;
animation: expand-outline 200ms ease-in-out;
}
}
// --------------------------------------------------
// LAYOUT
// --------------------------------------------------
@ -130,15 +34,12 @@
min-height: 100svh;
display: grid;
grid:
'app-header' auto
'app-main' 1fr
'app-footer' auto
/ 1fr;
place-items: center;
&__header {
grid-area: app-header;
}
/ 100%;
place-content: start center;
place-items: center center;
row-gap: 4rem;
&__main {
grid-area: app-main;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -20,6 +20,15 @@
}
}
@keyframes expand-height {
0% {
height: 0;
}
100% {
height: calc(100% - var(--regular-icon-size));
}
}
@keyframes fade-in-from-bottom {
0% {
opacity: 0;

View file

@ -59,3 +59,102 @@
font-weight: 900;
font-style: normal;
}
// Karla
@font-face {
font-family: 'Karla';
src: url(~/assets/fonts/karla/Karla-ExtraLight.ttf) format('truetype');
font-weight: 200;
font-style: normal;
}
@font-face {
font-family: 'Karla';
src: url(~/assets/fonts/karla/Karla-ExtraLightItalic.ttf) format('truetype');
font-weight: 200;
font-style: italic;
}
@font-face {
font-family: 'Karla';
src: url(~/assets/fonts/karla/Karla-Light.ttf) format('truetype');
font-weight: 300;
font-style: normal;
}
@font-face {
font-family: 'Karla';
src: url(~/assets/fonts/karla/Karla-LightItalic.ttf) format('truetype');
font-weight: 300;
font-style: italic;
}
@font-face {
font-family: 'Karla';
src: url(~/assets/fonts/karla/Karla-Regular.ttf) format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'Karla';
src: url(~/assets/fonts/karla/Karla-Italic.ttf) format('truetype');
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: 'Karla';
src: url(~/assets/fonts/karla/Karla-Medium.ttf) format('truetype');
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: 'Karla';
src: url(~/assets/fonts/karla/Karla-MediumItalic.ttf) format('truetype');
font-weight: 500;
font-style: italic;
}
@font-face {
font-family: 'Karla';
src: url(~/assets/fonts/karla/Karla-SemiBold.ttf) format('truetype');
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: 'Karla';
src: url(~/assets/fonts/karla/Karla-SemiBoldItalic.ttf) format('truetype');
font-weight: 600;
font-style: italic;
}
@font-face {
font-family: 'Karla';
src: url(~/assets/fonts/karla/Karla-Bold.ttf) format('truetype');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: 'Karla';
src: url(~/assets/fonts/karla/Karla-BoldItalic.ttf) format('truetype');
font-weight: 700;
font-style: italic;
}
@font-face {
font-family: 'Karla';
src: url(~/assets/fonts/karla/Karla-ExtraBold.ttf) format('truetype');
font-weight: 800;
font-style: normal;
}
@font-face {
font-family: 'Karla';
src: url(~/assets/fonts/karla/Karla-ExtraBoldItalic.ttf) format('truetype');
font-weight: 800;
font-style: italic;
}

View file

@ -1,69 +0,0 @@
// --------------------------------------------------
// VARIABLES
// --------------------------------------------------
:root {
// Fonts
--text-font-family: 'Cairo', sans-serif;
--title-font-family: 'Cairo', sans-serif;
--extra-light-font-weight: 200;
--light-font-weight: 300;
--regular-font-weight: 400;
--medium-font-weight: 500;
--semi-bold-font-weight: 600;
--bold-font-weight: 700;
--extra-bold-font-weight: 800;
--black-font-weight: 900;
--h1-font-size: 2rem;
--h2-font-size: 1.8rem;
--h3-font-size: 1.6rem;
--button-font-size: 1.4rem;
--text-font-size: 1.2rem;
--caption-font-size: 1.1rem;
--footnote-font-size: 1rem;
--line-height: 1.2;
// Dimensions
--regular-icon-size: 2.5rem;
--small-icon-size: 1.2rem;
// Colors
--eerie-black: #212121;
--jet: #333333;
--silver: #C2C2C2;
--emerald: #72C080;
--granny-smith-apple: #A3F3B0;
--middle-green: #428F53;
--primary-color: var(--eerie-black);
--primary-color-light: var(--jet);
--primary-color-dark: black;
--secondary-color: white;
--secondary-color-dark: var(--silver);
--accent-color: var(--emerald);
--accent-color-light: var(--granny-smith-apple);
--accent-color-dark: var(--middle-green);
--button-gradient: linear-gradient(
30deg,
hsl(131deg 38% 60%) 1%,
hsl(131deg 40% 62%) 34%,
hsl(130deg 43% 64%) 46%,
hsl(130deg 46% 66%) 54%,
hsl(130deg 49% 69%) 60%,
hsl(130deg 53% 71%) 66%,
hsl(130deg 58% 73%) 71%,
hsl(130deg 63% 75%) 76%,
hsl(130deg 69% 77%) 83%,
hsl(130deg 77% 80%) 100%
);
}
// Media queries
$tablet-media-query: 48rem;
$desktop-media-query: 62rem;

251
assets/styles/main.scss Normal file
View file

@ -0,0 +1,251 @@
// --------------------------------------------------
// VARIABLES
// --------------------------------------------------
:root {
// Fonts
--text-font-family: 'Karla', sans-serif;
--title-font-family: 'Cairo', sans-serif;
--extra-light-font-weight: 200;
--light-font-weight: 300;
--regular-font-weight: 400;
--medium-font-weight: 500;
--semi-bold-font-weight: 600;
--bold-font-weight: 700;
--extra-bold-font-weight: 800;
--black-font-weight: 900;
--h1-font-size: 2rem;
--h2-font-size: 2rem;
--h3-font-size: 1.8rem;
--button-font-size: 1.4rem;
--text-font-size: 1.2rem;
--caption-font-size: 1.1rem;
--footnote-font-size: 1rem;
--line-height: 1.2;
// Dimensions
--content-width: Min(100%, 120rem);
--text-width: Min(100%, 60rem);
--regular-icon-size: 2rem;
--small-icon-size: calc(var(--text-font-size) * var(--line-height));
--button-gap: 0.5rem;
// Colors
--eerie-black: #212121;
--sonic-silver: #7a7a7a;
--silver: #C2C2C2;
--emerald: #72C080;
--granny-smith-apple: #A3F3B0;
--middle-green: #428F53;
--primary-color: var(--eerie-black);
--primary-color-light: var(--sonic-silver);
--primary-color-dark: black;
--secondary-color: white;
--secondary-color-dark: var(--silver);
--accent-color: var(--emerald);
--accent-color-light: var(--granny-smith-apple);
--accent-color-dark: var(--middle-green);
--button-gradient: linear-gradient(
45deg,
hsl(131deg 38% 60%) 1%,
hsl(131deg 40% 62%) 34%,
hsl(130deg 43% 64%) 46%,
hsl(130deg 46% 66%) 54%,
hsl(130deg 49% 69%) 60%,
hsl(130deg 53% 71%) 66%,
hsl(130deg 58% 73%) 71%,
hsl(130deg 63% 75%) 76%,
hsl(130deg 69% 77%) 83%,
hsl(130deg 77% 80%) 100%
);
}
// Media queries
$tablet-media-query: 48rem;
$desktop-media-query: 62rem;
// --------------------------------------------------
// FONTS AND COLORS
// --------------------------------------------------
body {
font-family: var(--text-font-family);
font-size: var(--text-font-size);
font-weight: var(--regular-font-weight);
line-height: var(--line-height);
color: var(--secondary-color);
background-color: var(--primary-color);
}
h1 {
font-family: var(--title-font-family);
font-size: var(--h1-font-size);
font-weight: var(--medium-font-weight);
}
h2 {
font-family: var(--title-font-family);
font-size: var(--h2-font-size);
}
h3 {
font-family: var(--text-font-family);
font-size: var(--h3-font-size);
}
p {
font-weight: var(--light-font-weight);
}
strong {
font-weight: var(--bold-font-weight);
}
em {
font-style: italic;
}
// --------------------------------------------------
// LINK STYLE
// --------------------------------------------------
a {
color: var(--secondary-color);
text-decoration: underline var(--accent-color);
border-radius: 2px;
transition: color 200ms ease-in-out;
&:hover,
&:focus,
&:active {
color: var(--accent-color);
}
&:focus-visible {
outline: 1px dashed var(--accent-color);
outline-offset: 2px;
animation: expand-outline 200ms ease-in-out;
}
}
// --------------------------------------------------
// BUTTON STYLE
// --------------------------------------------------
button {
padding: 1rem 1.5rem;
color: var(--primary-color);
background-image: var(--button-gradient);
background-size: 100%;
background-position: right center;
border: none;
border-radius: 40px;
cursor: pointer;
font-family: var(--title-font-family);
font-size: var(--text-font-size);
font-weight: var(--semi-bold-font-weight);
line-height: var(--line-height);
text-align: center;
transition: background-size 200ms ease-in-out;
&:hover,
&:focus,
&:active {
background-size: 300%;
}
&:focus-visible {
outline: 1px dashed var(--accent-color);
outline-offset: 4px;
animation: expand-outline 200ms ease-in-out;
}
}
@mixin button-with-icon {
display: flex;
justify-content: center;
align-items: center;
&-text {
transform: translateX(calc((var(--button-gap) + var(--small-icon-size)) / 2));
transition: transform 200ms ease-in-out;
}
&-icon {
display: inherit;
flex-shrink: 0;
opacity: 0;
width: var(--small-icon-size);
height: var(--small-icon-size);
margin: 0 0 0 var(--button-gap);
transform: translateX(calc((var(--button-gap) + var(--small-icon-size)) / 2));
transition:
opacity 200ms ease-in-out,
transform 200ms ease-in-out;
}
&:hover &-text,
&:focus &-text,
&:active &-text {
transform: translateX(0);
}
&:hover &-icon,
&:focus &-icon,
&:active &-icon {
opacity: 1;
transform: translateX(0);
}
}
// --------------------------------------------------
// FORM STYLE
// --------------------------------------------------
div:has(label) { // Not yet supported in Firefox
position: relative;
}
label {
position: absolute;
top: calc(-1 * (var(--text-font-size) / 2));
left: 1rem;
display: inline-block;
padding: 0 0.5rem;
font-family: var(--title-font-family);
background-color: var(--primary-color);
}
input,
textarea {
font-family: var(--text-font-family);
font-size: var(--text-font-size);
font-weight: var(--light-font-weight);
line-height: var(--line-height);
width: 100%;
padding: 1rem;
color: var(--secondary-color);
background-color: var(--primary-color);
border: 1px solid var( --primary-color-light);
border-radius: 10px;
transition: border 200ms ease-in-out;
&:hover,
&:focus,
&:active {
border: 1px solid var(--accent-color);
outline: none;
}
}
textarea {
resize: vertical;
min-height: 15rem;
}

View file

@ -7,7 +7,7 @@
<div class="error__emoticon" aria-hidden="true">¯\(°_o)/¯</div>
<button class="error__button" @click="$emit('handleError')">
<span class="error__button-text">Retourner à la page d'accueil</span>
<svg class="error__button-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
<svg class="error__button-icon" aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
<path d="M 2 8.7 L 12 1 L 22 8.7 L 22 20.8 C 22 22.016 21.006 23 19.778 23 L 4.222 23 C 2.995 23 2 22.016 2 20.8 L 2 8.7 Z"/>
<polyline points="8.667 23 8.667 12 15.333 12 15.333 23"/>
</svg>
@ -35,11 +35,11 @@
<style lang="scss" scoped>
// --------------------------------------------------
// ERROR STYLE
// STYLE
// --------------------------------------------------
main {
padding: 4rem 2rem;
padding: 2rem;
}
.error {
@ -67,6 +67,7 @@
&__emoticon {
opacity: 0;
margin: 0 2rem;
font-family: var(--title-font-family);
font-size: var(--h1-font-size);
font-weight: var(--medium-font-weight);
animation: fade-in-from-top 400ms ease-in-out 600ms forwards;
@ -87,41 +88,7 @@
.error {
&__button {
display: flex;
justify-content: center;
align-items: center;
&-text {
transform: translateX(calc((0.8rem + var(--small-icon-size)) / 2));
transition: transform 200ms ease-in-out;
}
&-icon {
display: inherit;
flex-shrink: 0;
opacity: 0;
width: var(--small-icon-size);
height: var(--small-icon-size);
margin: 0 0 0 0.8rem;
transform: translateX(calc((0.8rem + var(--small-icon-size)) / 2));
transition:
opacity 200ms ease-in-out,
transform 200ms ease-in-out;
}
&:hover,
&:focus,
&:active {
.error__button-text {
transform: translateX(0);
}
.error__button-icon {
opacity: 1;
transform: translateX(0);
}
}
@include button-with-icon;
}
}
}

View file

@ -52,7 +52,7 @@
<style lang="scss" scoped>
// --------------------------------------------------
// FOOTER STYLE
// STYLE
// --------------------------------------------------
footer {

View file

@ -1,24 +0,0 @@
<template>
<header>
<div class="header-bar">
</div>
</header>
</template>
<script setup>
</script>
<style lang="scss" scoped>
// --------------------------------------------------
// HEADER STYLE
// --------------------------------------------------
header {
width: 100%;
height: 0;
}
</style>

View file

@ -0,0 +1,57 @@
<template>
<div class="contact-decoration" aria-hidden="true">
<div class="contact-decoration__icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
<path d="M 3.2 3.2 L 20.8 3.2 C 22.01 3.2 23 4.19 23 5.4 L 23 18.6 C 23 19.81 22.01 20.8 20.8 20.8 L 3.2 20.8 C 1.99 20.8 1 19.81 1 18.6 L 1 5.4 C 1 4.19 1.99 3.2 3.2 3.2 Z"/>
<polyline points="23 5.4 12 13.1 1 5.4"/>
</svg>
</div>
<div class="contact-decoration__separator"></div>
</div>
</template>
<script setup>
</script>
<style lang="scss" scoped>
// --------------------------------------------------
// LAYOUT & STYLE
// --------------------------------------------------
.contact-decoration {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
height: 100%;
margin: 0 -1rem;
&__icon {
width: calc(var(--regular-icon-size) + 1rem);
height: calc(var(--regular-icon-size) + 1rem);
display: flex;
justify-content: center;
align-items: center;
background-color: var(--primary-color);
border: 1px solid var(--accent-color);
border-radius: 50%;
svg {
stroke: var(--secondary-color);
width: var(--regular-icon-size);
height: var(--regular-icon-size);
}
}
&__separator {
width: 1px;
height: 0;
background-color: var(--accent-color);
animation: expand-height 600ms ease-in-out 600ms forwards;
}
}
</style>

132
components/ContactForm.vue Normal file
View file

@ -0,0 +1,132 @@
<template>
<form class="contact-form">
<div class="contact-form__name">
<label for="name">Nom</label>
<input type="text" id="name" name="name" required />
</div>
<div class="contact-form__email">
<label for="email">Adresse e-mail</label>
<input type="email" id="email" name="email" required />
</div>
<div class="contact-form__subject">
<label for="subject">Sujet</label>
<input type="text" id="subject" name="subject" required />
</div>
<div class="contact-form__message">
<label for="message">Message</label>
<textarea id="message" name="message" required></textarea>
</div>
<button class="contact-form__button" @click.prevent="sendEmail()">
<span class="contact-form__button-text">Envoyer</span>
<svg class="contact-form__button-icon" aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
<line x1="23" y1="1" x2="10.9" y2="13.1"/>
<polygon points="23 1 15.3 23 10.9 13.1 1 8.7 23 1"/>
</svg>
</button>
</form>
</template>
<script setup>
// --------------------------------------------------
// DATA
// --------------------------------------------------
// const contactName = ref('');
// const contactEmailAddress = ref('');
// const contactSubject = ref('');
// const contactMessage = ref('');
const contactName = 'Saint Victeur';
const contactEmailAddress = 'saint.victeur@email.com';
const contactSubject = 'Test - API Mailjet';
const contactMessage = `Saint Victeur vous bénit !`;
// --------------------------------------------------
// LOGIC
// --------------------------------------------------
const sendEmail = async () => {
try {
$fetch('/api/mailjet', {
method: 'post',
body: {
name: contactName,
emailAddress: contactEmailAddress,
subject: contactSubject,
message: contactMessage
}
});
console.log('Success!');
} catch(err) {
console.log('Failure!');
}
}
</script>
<style lang="scss" scoped>
// --------------------------------------------------
// LAYOUT & STYLE
// --------------------------------------------------
.contact-form {
width: var(--text-width);
display: grid;
grid:
'name name' auto
'email email' auto
'subject subject' auto
'message message' auto
'button .' auto
/ 1fr 1fr;
place-content: start;
place-items: start;
gap: 2rem;
&__name {
grid-area: name;
}
&__email {
grid-area: email;
}
&__subject {
grid-area: subject;
}
&__message {
grid-area: message;
}
&__name,
&__email,
&__subject,
&__message {
position: relative;
width: 100%;
}
&__button {
grid-area: button;
@include button-with-icon;
}
@media screen and (min-width: $tablet-media-query) {
grid:
'name .' auto
'email .' auto
'subject subject' auto
'message message' auto
'button .' auto
/ 1fr 1fr;
}
}
</style>

View file

@ -0,0 +1,87 @@
<template>
<section class="contact-header">
<h2 class="contact-header__title">Contact</h2>
<p class="contact-header__text">Vous avez un projet web à réaliser ? Contactez-moi à l'adresse e-mail <a href="mailto:contact@paulnicoue.com" target="_blank">contact@paulnicoue.com</a> ou via le formulaire ci-après. Vous pouvez également me retrouver sur les plateformes <a href="https://www.malt.fr/profile/paulnicoue" target="_blank">Malt</a> (mise en relation des entreprises et travailleurs indépendants) et <a href="https://gitlab.com/paulnicoue" target="_blank">GitLab</a> (hébergement et gestion de code source).</p>
<div class="contact-header__links" aria-hidden="true">
<a tabindex="-1" href="https://www.malt.fr/profile/paulnicoue" target="_blank" title="Consulter le profil Malt de Paul Nicoué">
<svg viewBox="0 0 24 24" fill="url(#icon-gradient)" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="icon-gradient" gradientUnits="userSpaceOnUse" x1="12" y1="0" x2="12" y2="24" gradientTransform="matrix(0.707651, 0.706562, -1, 1, 15.508194, -8.478749)">
<stop offset="0" style="stop-color: rgb(163, 243, 176);"/>
<stop offset="1" style="stop-color: rgb(114, 192, 128);"/>
</linearGradient>
</defs>
<path d="M 20.542 3.464 C 18.726 1.654 16.793 2.827 15.581 4.034 L 4.14 15.475 C 2.927 16.687 1.659 18.531 3.57 20.436 C 5.475 22.341 7.313 21.073 8.525 19.86 L 19.967 8.419 C 21.179 7.212 22.352 5.274 20.542 3.464 Z M 9.609 2.994 L 12.034 5.419 L 14.497 2.95 C 14.665 2.782 14.832 2.626 15.006 2.48 C 14.749 1.179 14.006 0 12.028 0 C 10.05 0 9.307 1.184 9.056 2.486 C 9.24 2.648 9.425 2.81 9.609 2.994 Z M 14.497 20.989 L 12.034 18.525 L 9.609 20.944 C 9.425 21.128 9.246 21.302 9.061 21.458 C 9.341 22.788 10.123 24 12.028 24 C 13.939 24 14.726 22.777 15 21.447 C 14.832 21.302 14.665 21.156 14.497 20.989 Z M 8.581 8.866 L 3.911 8.866 C 2.196 8.866 0 9.408 0 11.966 C 0 13.883 1.223 14.665 2.559 14.939 C 2.715 14.76 8.581 8.866 8.581 8.866 Z M 21.514 8.994 C 21.369 9.162 15.486 15.073 15.486 15.073 L 20.089 15.073 C 21.804 15.073 24 14.665 24 11.966 C 24 9.994 22.821 9.251 21.514 8.994 Z M 10.112 7.335 L 10.944 6.503 L 8.525 4.078 C 7.313 2.866 5.475 1.598 3.564 3.508 C 2.168 4.905 2.48 6.268 3.218 7.369 C 3.447 7.352 10.112 7.335 10.112 7.335 Z M 13.95 16.603 L 13.112 17.441 L 15.581 19.905 C 16.793 21.117 18.726 22.291 20.536 20.48 C 21.888 19.128 21.581 17.704 20.832 16.57 C 20.592 16.587 13.95 16.603 13.95 16.603 Z"/>
</svg>
</a>
<a tabindex="-1" href="https://gitlab.com/paulnicoue" target="_blank" title="Consulter le profil GitLab de Paul Nicoué">
<svg viewBox="0 0 24 24" fill="url(#icon-gradient)" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="icon-gradient" gradientUnits="userSpaceOnUse" x1="12" y1="0" x2="12" y2="24" gradientTransform="matrix(0.705192, 0.709016, -1, 1, 15.5377, -8.508194)">
<stop offset="0" style="stop-color: rgb(163, 243, 176);"/>
<stop offset="1" style="stop-color: rgb(114, 192, 128);"/>
</linearGradient>
</defs>
<path d="M 23.601 9.5 L 23.568 9.411 L 20.301 0.558 C 20.235 0.385 20.117 0.237 19.965 0.137 C 19.653 -0.063 19.254 -0.041 18.965 0.193 C 18.826 0.311 18.724 0.471 18.675 0.65 L 16.47 7.658 L 7.539 7.658 L 5.333 0.65 C 5.285 0.47 5.183 0.309 5.043 0.192 C 4.754 -0.042 4.355 -0.064 4.043 0.136 C 3.891 0.237 3.774 0.384 3.707 0.557 L 0.434 9.406 L 0.402 9.495 C -0.564 12.116 0.256 15.085 2.414 16.775 L 2.425 16.784 L 2.455 16.806 L 7.431 20.676 L 9.893 22.611 L 11.392 23.787 C 11.753 24.071 12.252 24.071 12.612 23.787 L 14.112 22.611 L 16.573 20.676 L 21.58 16.783 L 21.592 16.773 C 23.745 15.082 24.563 12.119 23.601 9.5 Z"/>
</svg>
</a>
</div>
</section>
</template>
<script setup>
</script>
<style lang="scss" scoped>
// --------------------------------------------------
// LAYOUT & STYLE
// --------------------------------------------------
.contact-header {
width: var(--text-width);
display: grid;
place-content: start;
place-items: start;
gap: 2rem;
&__links {
display: flex;
justify-content: center;
align-items: center;
a {
width: var(--regular-icon-size);
height: var(--regular-icon-size);
display: flex;
justify-content: center;
align-items: center;
+ a {
margin: 0 0 0 2rem;
}
svg {
width: 100%;
height: 100%;
transition:
fill 200ms ease-in-out,
transform 200ms ease-in-out;
}
&:hover,
&:focus,
&:active {
svg {
fill: var(--accent-color-light);
transform: scale(1.1) rotate(22.5deg);
}
}
}
}
}
</style>

View file

@ -0,0 +1,57 @@
<template>
<section class="contact">
<ContactDecoration class="contact__decoration" />
<ContactHeader class="contact__header" />
<ContactForm class="contact__form" />
</section>
</template>
<script setup>
</script>
<style lang="scss" scoped>
// --------------------------------------------------
// LAYOUT & STYLE
// --------------------------------------------------
.contact {
width: var(--content-width);
display: grid;
grid:
'decoration header' auto
'decoration form' auto
/ auto 1fr;
place-content: start stretch;
place-items: start stretch;
row-gap: 4rem;
column-gap: 2rem;
&__decoration {
grid-area: decoration;
}
&__header {
grid-area: header;
}
&__form {
grid-area: form;
}
@media screen and (min-width: $tablet-media-query) {
column-gap: 4rem;
}
@media screen and (min-width: $desktop-media-query) {
grid:
'decoration header form' auto
/ auto 1fr 2fr;
column-gap: 6rem;
}
}
</style>

53
components/HeroTitle.vue Normal file
View file

@ -0,0 +1,53 @@
<template>
<div class="hero-title">
<h1 class="hero-title__name">Paul Nicoué</h1>
<div class="hero-title__separator" aria-hidden="true"></div>
<h2 class="hero-title__job">Intégrateur web & développeur full stack</h2>
</div>
</template>
<script setup>
</script>
<style lang="scss" scoped>
// --------------------------------------------------
// STYLE
// --------------------------------------------------
.hero-title {
min-width: 50vw;
height: 100vh;
height: 100svh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
&__name {
opacity: 0;
margin: 0 2rem;
animation: fade-in-from-bottom 400ms ease-in-out 600ms forwards;
}
&__separator {
width: 0;
height: 1px;
margin: 0.5rem auto;
background-color: var(--accent-color);
animation: expand-width 400ms ease-in-out 200ms forwards;
}
&__job {
opacity: 0;
font-size: var(--h2-font-size);
font-weight: var(--light-font-weight);
margin: 0 2rem;
animation: fade-in-from-top 400ms ease-in-out 600ms forwards;
}
}
</style>

View file

@ -59,19 +59,21 @@ export const useAppHead = (pageTitleChunk) => {
],
script: [
/* Matomo tracking code */
{children: `
var _paq = window._paq = window._paq || [];
_paq.push(["disableCookies"]);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://matomo.paulnicoue.com/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '4']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
`}
{
children:
`var _paq = window._paq = window._paq || [];
_paq.push(["disableCookies"]);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://matomo.paulnicoue.com/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '4']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();`,
async: true
}
]
})
}

View file

@ -12,6 +12,10 @@ export default defineNuxtConfig({
'autoprefixer': {}
}
},
runtimeConfig: {
mailjetApiPrivate: process.env.MJ_APIKEY_PRIVATE,
mailjetApiPublic: process.env.MJ_APIKEY_PUBLIC
},
sitemap: {
hostname: 'https://paulnicoue.com'
},
@ -19,7 +23,7 @@ export default defineNuxtConfig({
css: {
preprocessorOptions: {
scss: {
additionalData: '@use "@/assets/styles/_variables.scss" as *;' // Global SASS variables import
additionalData: '@use "@/assets/styles/main.scss" as *;' // Mandatory for global SASS variables and mixins import
}
}
}

191
package-lock.json generated
View file

@ -7,12 +7,13 @@
"name": "Paul Nicoué",
"hasInstallScript": true,
"dependencies": {
"@funken-studio/sitemap-nuxt-3": "^4.0.4"
"@funken-studio/sitemap-nuxt-3": "^4.0.4",
"node-mailjet": "^6.0.1"
},
"devDependencies": {
"minireset.css": "^0.0.7",
"nuxt": "^3.0.0-rc.11",
"sass": "^1.56.1",
"nuxt": "^3.0.0",
"sass": "^1.57.1",
"sass-loader": "^13.2.0"
}
},
@ -2116,6 +2117,11 @@
"integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==",
"dev": true
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/autoprefixer": {
"version": "10.4.13",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz",
@ -2149,6 +2155,15 @@
"postcss": "^8.1.0"
}
},
"node_modules/axios": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"dependencies": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -2174,6 +2189,14 @@
}
]
},
"node_modules/bignumber.js": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz",
"integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==",
"engines": {
"node": "*"
}
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@ -2650,6 +2673,17 @@
"integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==",
"dev": true
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
@ -2990,6 +3024,14 @@
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.1.tgz",
"integrity": "sha512-aA964RUCsBt0FGoNIlA3uFgo2hO+WWC0fiC6DBps/0SFzkKcYoM/3CzVLIa5xSsrFjdioMdYgAIbwo80qp2MoA=="
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@ -3838,7 +3880,6 @@
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"dev": true,
"funding": [
{
"type": "individual",
@ -3854,6 +3895,19 @@
}
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
@ -4666,6 +4720,14 @@
"node": ">=4"
}
},
"node_modules/json-bigint": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
"dependencies": {
"bignumber.js": "^9.0.0"
}
},
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@ -4681,9 +4743,9 @@
"peer": true
},
"node_modules/json5": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"bin": {
"json5": "lib/cli.js"
},
@ -5052,8 +5114,6 @@
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"peer": true,
"engines": {
"node": ">= 0.6"
}
@ -5062,8 +5122,6 @@
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"peer": true,
"dependencies": {
"mime-db": "1.52.0"
},
@ -5333,6 +5391,20 @@
"node-gyp-build-test": "build-test.js"
}
},
"node_modules/node-mailjet": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/node-mailjet/-/node-mailjet-6.0.1.tgz",
"integrity": "sha512-hxwyX/UivcJbhfzDrFH8UVcNF0iZxOQnEBO70xLbQefrysLaKD34peKeaLeyyVQXlAV9mWrare+sd924P0TZFw==",
"dependencies": {
"axios": "^0.27.2",
"json-bigint": "^1.0.0",
"url-join": "^4.0.0"
},
"engines": {
"node": ">= 12.0.0",
"npm": ">= 6.9.0"
}
},
"node_modules/node-releases": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
@ -6723,9 +6795,9 @@
"dev": true
},
"node_modules/sass": {
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.56.1.tgz",
"integrity": "sha512-VpEyKpyBPCxE7qGDtOcdJ6fFbcpOM+Emu7uZLxVrkX8KVU/Dp5UF7WLvzqRuUhB6mqqQt1xffLoG+AndxTZrCQ==",
"version": "1.57.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.57.1.tgz",
"integrity": "sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==",
"dev": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
@ -7612,6 +7684,11 @@
"punycode": "^2.1.0"
}
},
"node_modules/url-join": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
"integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA=="
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -9997,6 +10074,11 @@
"integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==",
"dev": true
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"autoprefixer": {
"version": "10.4.13",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz",
@ -10011,6 +10093,15 @@
"postcss-value-parser": "^4.2.0"
}
},
"axios": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"requires": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
}
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -10022,6 +10113,11 @@
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"dev": true
},
"bignumber.js": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz",
"integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig=="
},
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@ -10356,6 +10452,14 @@
"integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==",
"dev": true
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"commander": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
@ -10610,6 +10714,11 @@
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.1.tgz",
"integrity": "sha512-aA964RUCsBt0FGoNIlA3uFgo2hO+WWC0fiC6DBps/0SFzkKcYoM/3CzVLIa5xSsrFjdioMdYgAIbwo80qp2MoA=="
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@ -11149,8 +11258,17 @@
"follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"dev": true
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
},
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"formdata-polyfill": {
"version": "4.0.10",
@ -11749,6 +11867,14 @@
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
},
"json-bigint": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
"requires": {
"bignumber.js": "^9.0.0"
}
},
"json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@ -11764,9 +11890,9 @@
"peer": true
},
"json5": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA=="
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
},
"jsonc-parser": {
"version": "3.2.0",
@ -12078,16 +12204,12 @@
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"peer": true
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
},
"mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"peer": true,
"requires": {
"mime-db": "1.52.0"
}
@ -12297,6 +12419,16 @@
"integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==",
"dev": true
},
"node-mailjet": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/node-mailjet/-/node-mailjet-6.0.1.tgz",
"integrity": "sha512-hxwyX/UivcJbhfzDrFH8UVcNF0iZxOQnEBO70xLbQefrysLaKD34peKeaLeyyVQXlAV9mWrare+sd924P0TZFw==",
"requires": {
"axios": "^0.27.2",
"json-bigint": "^1.0.0",
"url-join": "^4.0.0"
}
},
"node-releases": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
@ -13269,9 +13401,9 @@
"dev": true
},
"sass": {
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.56.1.tgz",
"integrity": "sha512-VpEyKpyBPCxE7qGDtOcdJ6fFbcpOM+Emu7uZLxVrkX8KVU/Dp5UF7WLvzqRuUhB6mqqQt1xffLoG+AndxTZrCQ==",
"version": "1.57.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.57.1.tgz",
"integrity": "sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==",
"dev": true,
"requires": {
"chokidar": ">=3.0.0 <4.0.0",
@ -13938,6 +14070,11 @@
"punycode": "^2.1.0"
}
},
"url-join": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
"integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA=="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View file

@ -9,8 +9,8 @@
],
"devDependencies": {
"minireset.css": "^0.0.7",
"nuxt": "^3.0.0-rc.11",
"sass": "^1.56.1",
"nuxt": "^3.0.0",
"sass": "^1.57.1",
"sass-loader": "^13.2.0"
},
"scripts": {
@ -22,6 +22,7 @@
"start": "nuxt start"
},
"dependencies": {
"@funken-studio/sitemap-nuxt-3": "^4.0.4"
"@funken-studio/sitemap-nuxt-3": "^4.0.4",
"node-mailjet": "^6.0.1"
}
}

View file

@ -1,11 +1,8 @@
<template>
<main>
<div class="title">
<h1 class="title__name">Paul Nicoué</h1>
<div class="title__separator" aria-hidden="true"></div>
<h2 class="title__job">Intégrateur web & développeur full stack</h2>
</div>
<HeroTitle class="hero-title" />
<ContactSection class="contact-section" />
</main>
</template>
@ -25,41 +22,35 @@
<style lang="scss" scoped>
// --------------------------------------------------
// INDEX STYLE
// LAYOUT
// --------------------------------------------------
main {
padding: 4rem 2rem;
}
width: 100%;
padding: 2rem;
display: grid;
grid:
'hero-title' auto
'contact-section' auto
/ 1fr;
place-content: start center;
place-items: start center;
row-gap: 4rem;
.title {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-width: 50vw;
text-align: center;
&__name {
opacity: 0;
margin: 0 2rem;
animation: fade-in-from-bottom 400ms ease-in-out 600ms forwards;
.hero-title {
grid-area: hero-title;
}
&__separator {
width: 0;
height: 1px;
margin: 0.5rem auto;
background-color: var(--accent-color);
animation: expand-width 400ms ease-in-out 200ms forwards;
.contact-section {
grid-area: contact-section;
}
&__job {
opacity: 0;
font-size: var(--h2-font-size);
font-weight: var(--light-font-weight);
margin: 0 2rem;
animation: fade-in-from-top 400ms ease-in-out 600ms forwards;
@media screen and (min-width: $tablet-media-query) {
padding: 2rem 4rem;
}
@media screen and (min-width: $desktop-media-query) {
padding: 2rem 6rem;
}
}

View file

@ -0,0 +1,65 @@
import Mailjet from 'node-mailjet';
export default defineEventHandler(async (event) => {
const body = await readBody(event);
// const mailjet = new Mailjet({
// apiKey: useRuntimeConfig().mailjetApiPublic,
// apiSecret: useRuntimeConfig().mailjetApiPrivate
// });
// const request = mailjet
// .post('send', { version: 'v3.1' })
// .request({
// Messages: [
// {
// From: {
// Email: 'contact@paulnicoue.com',
// Name: body.name,
// },
// To: [
// {
// Email: 'contact@paulnicoue.com',
// Name: 'Paul Nicoué',
// },
// ],
// Subject: body.subject,
// TextPart: body.message
// }
// ]
// });
// request
// .then(result => {console.log(result.body)})
// .catch(err => {console.log(err.statusCode)});
Mailjet
.apiConnect(
useRuntimeConfig().mailjetApiPublic,
useRuntimeConfig().mailjetApiPrivate
)
.post('send', { version: 'v3.1' })
.request({
Messages: [
{
From: {
Email: 'contact@paulnicoue.com',
Name: body.name,
},
To: [
{
Email: 'contact@paulnicoue.com',
Name: 'Paul Nicoué',
},
],
Subject: body.subject,
TextPart: body.message
}
]
})
.then((result) => console.log(result.body))
.catch((err) => console.log(err.statusCode));
return event.node.res.end();
});