Compare commits

...

3 Commits

Author SHA1 Message Date
0340ce23e7 added favicon
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-02-09 20:22:08 +01:00
372984e55b revert 2023-02-09 20:21:20 +01:00
248868b1bd added favicon 2023-02-09 20:18:46 +01:00
11 changed files with 2536 additions and 2601 deletions

1
.prettierigonore Normal file
View File

@ -0,0 +1 @@
.nuxt

1
.prettierrc.json Normal file
View File

@ -0,0 +1 @@
{}

View File

@ -7,7 +7,7 @@ pipeline:
username: xeovalyte
password:
from_secret: docker_password
repo: gitea.xeovalyte.dev/xeovalyte/portfolio
repo: gitea.xeovalyte.dev/xeovalyte/website
tags:
- latest
registry: gitea.xeovalyte.dev

41
components/About.vue Normal file
View File

@ -0,0 +1,41 @@
<template>
<section class="flex h-screen snap-center items-center justify-center">
<div
class="my-40 flex w-full max-w-xl flex-wrap items-center justify-center gap-5 whitespace-pre-wrap text-lg text-primary-500"
>
<h2
v-animate="{ preset: 'slide-right', duration: 500 }"
class="mb-10 text-center text-3xl font-bold"
>
{{ t("title") }}
</h2>
<div v-animate="{ preset: 'slide-right', delay: 400, duration: 500 }">
{{ t("message") }}
</div>
</div>
</section>
</template>
<script setup>
const { t } = useI18n({ useScope: "local" });
</script>
<i18n lang="yaml">
en:
message: |
Hi,
My name is Timo Boomers (16) also known as Xeovalyte. I live in the Netherlands and I have always found electronics and computers interesting.
When I was 12 I started programming. I now program Websites, Discord bots and Arduino.
title: "About"
nl:
message: |
Hallo,
Mijn naam is Timo Boomers (16) ook wel bekend als Xeovalyte. Ik heb electronica en computers altijd interresant gevonden en ben toen gaan programmeren.
Toen ik 12 was begon ik met programmeren. Ondertussen programmeer ik Websites, Discord bots en Arduino.
title: "Over mij"
</i18n>

40
components/Heading.vue Normal file
View File

@ -0,0 +1,40 @@
<template>
<section
class="relative flex h-screen snap-center items-center justify-center"
>
<div
class="flex flex-wrap items-center justify-center gap-5 text-center text-primary-500"
>
<img
v-animate="{ preset: 'slide-left', delay: 300 }"
src="../assets/icons/logo.svg"
alt="Logo Xeovalyte"
width="150"
/>
<div class="space-y-2">
<h1
v-animate="{ preset: 'slide-up', delay: 450 }"
class="text-7xl font-bold"
>
Xeovalyte
</h1>
<div
v-animate="{ preset: 'slide-right', delay: 600 }"
class="h-1 w-full rounded-full bg-primary-500"
/>
<h3
v-animate="{ preset: 'slide-down', delay: 750 }"
class="text-4xl font-bold"
>
Timo Boomers
</h3>
</div>
<Icon
size="2em"
name="ph:arrow-fat-line-down"
class="absolute bottom-20 basis-full animate-bounce hover:cursor-pointer"
/>
</div>
</section>
</template>

85
components/Skills.vue Normal file
View File

@ -0,0 +1,85 @@
<template>
<section class="flex h-screen snap-center items-center justify-center">
<div
class="my-40 mx-5 flex w-full max-w-3xl flex-col text-primary-500"
v-animate="{ stagger: true, preset: 'slide-left', duration: 500 }"
>
<h2 class="mb-10 text-center text-3xl font-bold">
{{ t("skills.skills") }}
</h2>
<h2 class="text-lg font-bold">Arduino</h2>
<div class="mb-5 grid grid-cols-5 items-center gap-x-4">
<div
class="col-span-5 h-4 w-full rounded-full bg-dark-600 sm:col-span-4"
>
<div class="h-4 w-3/5 rounded-full bg-primary-100" />
</div>
<h2 class="hidden text-lg font-bold sm:inline-block">
{{ t("skills.intermediate") }}
</h2>
</div>
<h2 class="text-lg font-bold">Docker</h2>
<div class="mb-5 grid grid-cols-5 items-center gap-x-4">
<div
class="col-span-5 h-4 w-full rounded-full bg-dark-600 sm:col-span-4"
>
<div class="h-4 w-4/5 rounded-full bg-primary-100" />
</div>
<h2 class="hidden text-lg font-bold sm:inline-block">
{{ t("skills.advanced") }}
</h2>
</div>
<h2 class="text-lg font-bold">Javascript</h2>
<div class="mb-5 grid grid-cols-5 items-center gap-x-4">
<div
class="col-span-5 h-4 w-full rounded-full bg-dark-600 sm:col-span-4"
>
<div class="h-4 w-2/3 rounded-full bg-primary-100" />
</div>
<h2 class="hidden text-lg font-bold sm:inline-block">
{{ t("skills.intermediate") }}
</h2>
</div>
<h2 class="text-lg font-bold">Linux</h2>
<div class="mb-5 grid grid-cols-5 items-center gap-x-4">
<div
class="col-span-5 h-4 w-full rounded-full bg-dark-600 sm:col-span-4"
>
<div class="h-4 w-4/5 rounded-full bg-primary-100" />
</div>
<h2 class="hidden text-lg font-bold sm:inline-block">
{{ t("skills.advanced") }}
</h2>
</div>
<h2 class="text-lg font-bold">Nuxt</h2>
<div class="mb-5 grid grid-cols-5 items-center gap-x-4">
<div
class="col-span-5 h-4 w-full rounded-full bg-dark-600 sm:col-span-4"
>
<div class="h-4 w-3/5 rounded-full bg-primary-100" />
</div>
<h2 class="hidden text-lg font-bold sm:inline-block">
{{ t("skills.intermediate") }}
</h2>
</div>
</div>
</section>
</template>
<script setup>
const { t } = useI18n({ useScope: "local" });
</script>
<i18n lang="yaml">
en:
skills:
skills: "Skills"
intermediate: "Intermediate"
advanced: "Advanced"
nl:
skills:
skills: "Vaardigheden"
intermediate: "Gemiddeld"
advanced: "Geavanceerd"
</i18n>

View File

@ -7,27 +7,7 @@ export default defineNuxtConfig({
"nuxt-icons",
"@nuxtjs/robots",
"@nuxtjs/i18n",
"@nuxtjs/plausible",
],
plausible: {
domain: 'xeovalyte.com',
apiHost: 'https://plausible.xeovalyte.dev'
},
app: {
head: {
title: 'Xeovalyte | Timo Boomers Portfolio',
link: [
{
"rel": "icon",
"href": "/favicon.ico",
"type": "image/png"
}
],
meta: [
{ name: 'description', content: 'This is the official portfolio of Xeovalyte or Timo Boomers'}
]
}
},
i18n: {
baseUrl: 'https://xeovalyte.com',
locales: [

4833
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,11 +8,10 @@
"postinstall": "nuxt prepare"
},
"devDependencies": {
"@nuxtjs/plausible": "^0.2.0",
"@nuxtjs/tailwindcss": "^6.2.0",
"@vueuse/core": "^9.8.2",
"@vueuse/nuxt": "^9.8.2",
"nuxt": "3.1.2",
"nuxt": "3.0.0",
"nuxt-icon": "^0.1.8",
"prettier": "^2.8.3",
"prettier-plugin-tailwindcss": "^0.2.2"
@ -21,6 +20,7 @@
"@nuxtjs/i18n": "^8.0.0-beta.9",
"@nuxtjs/robots": "^3.0.0",
"locomotive-scroll": "^4.1.4",
"motion": "^10.15.5",
"nuxt-icons": "^3.1.0"
}
}

9
pages/index.vue Normal file
View File

@ -0,0 +1,9 @@
<template>
<div class="h-screen snap-y snap-mandatory overflow-y-auto bg-dark-200">
<Heading />
<About />
<Skills />
</div>
</template>
<script setup></script>

101
plugins/motion.js Normal file
View File

@ -0,0 +1,101 @@
export default defineNuxtPlugin((NuxtApp) => {
NuxtApp.vueApp.directive('animate', (el, binding) => {
el.classList.add('animate')
el.setAttribute('data-animate-duration', binding.value.duration || 250 )
el.setAttribute('data-animate-delay', binding.value.delay || 200 )
el.setAttribute('data-animate-preset', binding.value.preset || 'fade' )
el.setAttribute('data-animate-stagger', binding.value.stagger || false )
el.setAttribute('data-animate-stagger-delay', binding.value.staggerDelay || 100 )
})
NuxtApp.hook('page:finish', () => {
const applyClasses = (el, type) => {
const initialClasses = JSON.parse(el.getAttribute('data-animate-initial'))
const animateClasses = JSON.parse(el.getAttribute('data-animate-animate'))
if (type === 'initial') {
Object.keys(initialClasses).forEach(x => {
el.style[x] = initialClasses[x]
})
el.style['transition-property'] = 'none'
}
if (type === 'animate') {
Object.keys(animateClasses).forEach(x => {
el.style[x] = animateClasses[x]
})
el.style['transition-property'] = 'all'
}
}
const callback = (entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
applyClasses(entry.target, 'animate')
} else {
applyClasses(entry.target, 'initial')
}
})
}
// Configure staggering animations
let targets = document.querySelectorAll('.animate')
targets.forEach((target) => {
if (target.getAttribute('data-animate-stagger') === 'true') {
target.classList.remove('animate')
const collection = target.children
Object.values(collection).forEach((child, index) => {
child.setAttribute('data-animate-preset', target.getAttribute('data-animate-preset') )
child.setAttribute('data-animate-duration', target.getAttribute('data-animate-duration') )
child.style.transitionDelay = Number(target.getAttribute('data-animate-delay')) + index * Number(target.getAttribute('data-animate-stagger-delay')) + 'ms'
child.classList.add('animate')
})
} else {
target.style.transitionDelay = target.getAttribute('data-animate-delay') + 'ms'
}
})
// Configure presets
const observer = new IntersectionObserver(callback)
targets = document.querySelectorAll('.animate')
targets.forEach(target => {
const preset = target.getAttribute('data-animate-preset')
if (preset === 'fade') {
target.setAttribute('data-animate-initial', JSON.stringify({ opacity: 0 }))
target.setAttribute('data-animate-animate', JSON.stringify({ opacity: 100 }))
} else if (preset === 'slide-left') {
target.setAttribute('data-animate-initial', JSON.stringify({ opacity: 0, transform: 'translateX(-6em)', filter: 'blur(5px)' }))
target.setAttribute('data-animate-animate', JSON.stringify({ opacity: 100, transform: 'translateX(0)', filter: 'blur(0)' }))
} else if (preset === 'slide-right') {
target.setAttribute('data-animate-initial', JSON.stringify({ opacity: 0, transform: 'translateX(6em)', filter: 'blur(5px)' }))
target.setAttribute('data-animate-animate', JSON.stringify({ opacity: 100, transform: 'translateX(0)', filter: 'blur(0px)' }))
} else if (preset === 'slide-up') {
target.setAttribute('data-animate-initial', JSON.stringify({ opacity: 0, transform: 'translateY(-2em)', filter: 'blur(5px)' }))
target.setAttribute('data-animate-animate', JSON.stringify({ opacity: 100, transform: 'translateY(0)', filter: 'blur(0px)' }))
} else if (preset === 'slide-down') {
target.setAttribute('data-animate-initial', JSON.stringify({ opacity: 0, transform: 'translateY(2em)', filter: 'blur(5px)' }))
target.setAttribute('data-animate-animate', JSON.stringify({ opacity: 100, transform: 'translateY(0)', filter: 'blur(0px)' }))
}
applyClasses(target, 'initial')
target.classList.add('transition-all')
target.style['transition-duration'] = target.getAttribute('data-animate-duration') + 'ms'
observer.observe(target)
})
})
})