1
0
Fork 0

Add /legal-notice and /contact; Improve styling

This commit is contained in:
Moritz Ruth 2021-06-20 13:07:49 +02:00
parent fffd7e2025
commit 086eefcefa
No known key found for this signature in database
GPG key ID: AFD57E23E753841B
18 changed files with 576 additions and 80 deletions

View file

@ -2,6 +2,11 @@
"root": true,
"extends": "awzzm-vue/v3",
"rules": {
"vue/no-static-inline-styles": "off"
"vue/no-static-inline-styles": "off",
"unicorn/prevent-abbreviations": ["warn", {
"replacements": {
"i": false
}
}]
}
}

View file

@ -4,6 +4,8 @@
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Moritz Ruth</title>
<link rel="preload" as="font" href="/node_modules/@fontsource/plus-jakarta-sans/files/plus-jakarta-sans-latin-400-normal.woff2">
<link rel="preload" as="font" href="/node_modules/@fontsource/syne/files/syne-latin-800-normal.woff2">
<link rel="stylesheet" href="/node_modules/@fontsource/plus-jakarta-sans/400.css">
<link rel="stylesheet" href="/node_modules/@fontsource/plus-jakarta-sans/800.css">
<link rel="stylesheet" href="/node_modules/@fontsource/syne/800.css">
@ -11,7 +13,7 @@
<meta name="keywords" content="web, dev, development, coding, moritz, ruth, development, design">
<link rel="shortcut icon" type="image/png" href="/icon.png">
</head>
<body class="bg-[#fefefe]">
<body class="bg-[#fefefe] overflow-x-hidden">
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
<script

View file

@ -4,7 +4,7 @@
"scripts": {
"dev": "vite --host",
"build": "vite build",
"serve": "vite preview",
"start": "vite preview",
"lint": "eslint . --fix"
},
"dependencies": {
@ -14,9 +14,11 @@
"blobs": "^2.2.1-beta.1",
"kute.js": "^2.1.2",
"vue": "^3.1.1",
"vue-i18n": "^9.1.6",
"vue-router": "4"
},
"devDependencies": {
"@intlify/vite-plugin-vue-i18n": "^2.2.1",
"@vitejs/plugin-vue": "^1.2.3",
"@vue/compiler-sfc": "^3.1.1",
"eslint": "^7.28.0",

193
pnpm-lock.yaml generated
View file

@ -3,6 +3,7 @@ lockfileVersion: 5.3
specifiers:
'@fontsource/plus-jakarta-sans': ^4.4.5
'@fontsource/syne': ^4.4.5
'@intlify/vite-plugin-vue-i18n': ^2.2.1
'@vitejs/plugin-vue': ^1.2.3
'@vue/compiler-sfc': ^3.1.1
'@vueuse/core': ^5.0.3
@ -14,6 +15,7 @@ specifiers:
vite-plugin-pages: ^0.13.1
vite-plugin-windicss: ^1.0.4
vue: ^3.1.1
vue-i18n: ^9.1.6
vue-router: '4'
windicss: ^3.1.3
@ -24,9 +26,11 @@ dependencies:
blobs: 2.2.1-beta.1
kute.js: 2.1.2
vue: 3.1.1
vue-i18n: 9.1.6_vue@3.1.1
vue-router: 4.0.9_vue@3.1.1
devDependencies:
'@intlify/vite-plugin-vue-i18n': 2.2.1_vite@2.3.7+vue-i18n@9.1.6
'@vitejs/plugin-vue': 1.2.3_@vue+compiler-sfc@3.1.1
'@vue/compiler-sfc': 3.1.1_vue@3.1.1
eslint: 7.28.0
@ -295,6 +299,101 @@ packages:
resolution: {integrity: sha512-YK1dF3I+HVmqnLARwq1pptG420jjhtS6xzFLAO6/8ap8XDIfp2wiCpenkkVenXMGzKBto0S7+D3pzvVCQ/q77Q==}
dev: false
/@intlify/cli/0.5.0:
resolution: {integrity: sha512-FKPL2Avs+X7+Poer+XHbXBMNYVdFCYvTpuMr8OqnCkHlLtKgCrskeUzLyryelT4G8JG9ZZXOE9AZkiqo/AWYyg==}
engines: {node: '>= 10'}
hasBin: true
dependencies:
'@intlify/core': 9.1.6
'@intlify/core-base': 9.1.6
'@intlify/message-compiler': 9.1.6
'@intlify/shared': 9.1.6
chalk: 4.1.1
debug: 4.3.1
glob: 7.1.7
jsonc-eslint-parser: 1.0.1
source-map: 0.6.1
yaml-eslint-parser: 0.3.2
yargs: 16.2.0
transitivePeerDependencies:
- supports-color
dev: true
/@intlify/core-base/9.1.6:
resolution: {integrity: sha512-d5GDPpsQbqPkisSJA5b6nJFEkalY/IHAd7vOLNd/Sj4YaNRzXtInu2FoqKiOv8e/lQnXGTpurdCZg5Jxq1Gsxw==}
engines: {node: '>= 10'}
dependencies:
'@intlify/devtools-if': 9.1.6
'@intlify/message-compiler': 9.1.6
'@intlify/message-resolver': 9.1.6
'@intlify/runtime': 9.1.6
'@intlify/shared': 9.1.6
'@intlify/vue-devtools': 9.1.6
/@intlify/core/9.1.6:
resolution: {integrity: sha512-/B/2mRUqiNByKI+M13zRotiCecFHrlSg+V9HLSsIDW9xVWIn/NXucfCD4CUHzIPPGcCtzNjxixKTojGnWcsLSg==}
engines: {node: '>= 10'}
dependencies:
'@intlify/core-base': 9.1.6
dev: true
/@intlify/devtools-if/9.1.6:
resolution: {integrity: sha512-m8Api+kh+BtFa2FZ/JjIdr1ibsGGqBjdKCzWo5BZecEUxBquIeOQZwpokPh/0K5j+/PZleFXkVAMC5mNt+9WdA==}
engines: {node: '>= 10'}
dependencies:
'@intlify/shared': 9.1.6
/@intlify/message-compiler/9.1.6:
resolution: {integrity: sha512-DR8645VOrVK6x/8tkaCpHnckMAIcoOgeNS5j0wB12RfZoXYQp7vAXMaOP511KMll2mXCREgIB0ojpajiof7yzQ==}
engines: {node: '>= 10'}
dependencies:
'@intlify/message-resolver': 9.1.6
'@intlify/shared': 9.1.6
source-map: 0.6.1
/@intlify/message-resolver/9.1.6:
resolution: {integrity: sha512-UUnbawQa5U9sffd5wRIscqtyY1xWlwJbyfwCLPEWLvBhyAnCwPYlvaHGnnO0CSi0fzJTVwlV9DYzobh3agDeMA==}
engines: {node: '>= 10'}
/@intlify/runtime/9.1.6:
resolution: {integrity: sha512-U1QZ+TPf3kQQvWo4BA2mj3cHAxMRHXNTBhu2u+deh6ubTqXdZ19XGBTMSasrXG6RE+zSio9oM+ndoLja7JGtPg==}
engines: {node: '>= 10'}
dependencies:
'@intlify/message-compiler': 9.1.6
'@intlify/message-resolver': 9.1.6
'@intlify/shared': 9.1.6
/@intlify/shared/9.1.6:
resolution: {integrity: sha512-6MtsKulyfZxdD7OuxjaODjj8QWoHCnLFAk4wkWiHqBCa6UCTC0qXjtEeZ1MxpQihvFmmJZauBUu25EvtngW5qQ==}
engines: {node: '>= 10'}
/@intlify/vite-plugin-vue-i18n/2.2.1_vite@2.3.7+vue-i18n@9.1.6:
resolution: {integrity: sha512-TUmuXhj2VIyyRzGhY9OtiXXEqaibtfV61oj0RDHbvpURd7B3Fukw/X7kzorz/dtSzo3DWNTcUgBElJrzeuGJGA==}
engines: {node: '>= 12'}
peerDependencies:
vite: ^2.3.6
vue-i18n: ^9.1.6
dependencies:
'@intlify/cli': 0.5.0
'@intlify/shared': 9.1.6
'@rollup/pluginutils': 4.1.0
debug: 4.3.1
fast-glob: 3.2.5
vite: 2.3.7
vue-i18n: 9.1.6_vue@3.1.1
transitivePeerDependencies:
- rollup
- supports-color
dev: true
/@intlify/vue-devtools/9.1.6:
resolution: {integrity: sha512-UdNovg4OML9rIr1sOGZzTfNr1nUy4UQpDf5ni4dNC93T6FIkVJz0n1Np7Vp7e6gDjcmufRYcV99tEwjQSN9+5A==}
engines: {node: '>= 10'}
dependencies:
'@intlify/message-resolver': 9.1.6
'@intlify/runtime': 9.1.6
'@intlify/shared': 9.1.6
/@nodelib/fs.scandir/2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@ -346,6 +445,16 @@ packages:
picomatch: 2.3.0
dev: false
/@rollup/pluginutils/4.1.0:
resolution: {integrity: sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ==}
engines: {node: '>= 8.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0
dependencies:
estree-walker: 2.0.2
picomatch: 2.3.0
dev: true
/@types/buble/0.19.2:
resolution: {integrity: sha512-uUD8zIfXMKThmFkahTXDGI3CthFH1kMg2dOm3KLi4GlC5cbARA64bEcUMbbWdWdE73eoc/iBB9PiTMqH0dNS2Q==}
dependencies:
@ -695,6 +804,14 @@ packages:
escape-string-regexp: 1.0.5
dev: true
/cliui/7.0.4:
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
dependencies:
string-width: 4.2.2
strip-ansi: 6.0.0
wrap-ansi: 7.0.0
dev: true
/color-convert/1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
@ -1299,6 +1416,11 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
/get-caller-file/2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
dev: true
/get-intrinsic/1.1.1:
resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==}
dependencies:
@ -1616,6 +1738,14 @@ packages:
minimist: 1.2.5
dev: true
/jsonc-eslint-parser/1.0.1:
resolution: {integrity: sha512-mh5LY5byThmc692EqJS3Ss9sViNoNeCLNG5VQUgJLoAFFM3FzdIetd99qEiiQ+NXBVAIUgX5sWeK9leniS8RbQ==}
dependencies:
eslint-utils: 2.1.0
eslint-visitor-keys: 2.1.0
espree: 7.3.1
dev: true
/kute.js/2.1.2:
resolution: {integrity: sha512-UlQr6Rlu/vaXUacSrspkMO6hhr5p+VyX/9/r2+YFWooTOzb9hZGWoOC8FsP4UT+IwYMLunFx7sj8HwEl0LTTUg==}
dependencies:
@ -2163,6 +2293,11 @@ packages:
jsesc: 0.5.0
dev: false
/require-directory/2.1.1:
resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=}
engines: {node: '>=0.10.0'}
dev: true
/require-from-string/2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
@ -2619,6 +2754,19 @@ packages:
- supports-color
dev: true
/vue-i18n/9.1.6_vue@3.1.1:
resolution: {integrity: sha512-FEC4HZkTH6QRIu/A0wlo0VS/GH3w/fuCC6xfvoC8IyhhtbG9A+go9NfW+HZ1ZXdAcO4EWcVQi04M+iSwuxgixw==}
engines: {node: '>= 10'}
peerDependencies:
vue: ^3.0.0
dependencies:
'@intlify/core-base': 9.1.6
'@intlify/shared': 9.1.6
'@intlify/vue-devtools': 9.1.6
'@vue/devtools-api': 6.0.0-beta.14
vue: 3.1.1
dev: false
/vue-router/4.0.9_vue@3.1.1:
resolution: {integrity: sha512-i3IaZJ57YeMbRHQlqKyXdUMr5NzTCcJkn3f8u38TsZjYWtGcd3IX2zRd3389SCOwuRf11mgfHAyngR6FVDE9og==}
peerDependencies:
@ -2687,10 +2835,24 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/wrap-ansi/7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.2
strip-ansi: 6.0.0
dev: true
/wrappy/1.0.2:
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
dev: true
/y18n/5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
dev: true
/yallist/3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
dev: true
@ -2699,7 +2861,38 @@ packages:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
dev: true
/yaml-eslint-parser/0.3.2:
resolution: {integrity: sha512-32kYO6kJUuZzqte82t4M/gB6/+11WAuHiEnK7FreMo20xsCKPeFH5tDBU7iWxR7zeJpNnMXfJyXwne48D0hGrg==}
dependencies:
eslint-visitor-keys: 1.3.0
lodash: 4.17.21
yaml: 1.10.2
dev: true
/yaml/1.10.2:
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
engines: {node: '>= 6'}
dev: true
/yaml/2.0.0-6:
resolution: {integrity: sha512-YPUm0Z0sei53zauT7HWkkxyIBJhb9Gnf5jv4w4ahw5/v3PjFGhZOt4paXH6g9hzcMJqmNxZwoGfF1JzE2jvSgg==}
engines: {node: '>= 12'}
dev: true
/yargs-parser/20.2.7:
resolution: {integrity: sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==}
engines: {node: '>=10'}
dev: true
/yargs/16.2.0:
resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
engines: {node: '>=10'}
dependencies:
cliui: 7.0.4
escalade: 3.1.1
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.2
y18n: 5.0.8
yargs-parser: 20.2.7
dev: true

View file

@ -1 +1,3 @@
https://moritz-ruth.de/* https://moritzruth.de/:splat 301!
https://moritz-ruth.de/* https://moritzruth.de/:splat 308!
/agb /terms-and-conditions 308
/* /index.html 200

View file

@ -1,12 +0,0 @@
Betreffend moritzruth.de und alle Seiten unter Subdomains von moritzruth.de:
Angaben gemäß § 5 TMG
Moritz Ruth
Zum Galgenberg 19
66539 Neunkirchen
E-Mail: hey@m0.is
Haftung für Links:
Unser Angebot enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar. Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend entfernen.

View file

@ -1,9 +1,17 @@
<template>
<NavigationMenu/>
<div class="overflow-x-hidden">
<div class="bg-white text-black max-w-1200px mx-auto px-6 sm:px-10">
<router-view/>
</div>
<div class="bg-white text-black max-w-1200px w-full mx-auto px-6 sm:px-10" style="min-height: calc(100vh - 160px)">
<router-view/>
</div>
<div class="w-full h-20 space-x-10 flex items-center justify-center">
<router-link
v-for="item in items"
:key="item.to"
class="uppercase tracking-wide text-sm"
:to="item.to"
>
{{ t(item.labelKey) }}
</router-link>
</div>
</template>
@ -11,13 +19,60 @@
body {
font-size: 20px;
}
::selection {
@apply bg-blue-900 bg-opacity-80 text-white;
}
.asterisk-list > li {
&:not(:last-child) {
margin-bottom: 5px;
}
&::before {
@apply text-blue-900;
content: "*";
margin-right: 5px;
}
}
</style>
<i18n lang="yaml">
en:
footer:
tac: Terms & Conditions
legal: Legal Notice
de:
footer:
tac: AGB
legal: Impressum
</i18n>
<script>
import { useI18n } from "vue-i18n"
import NavigationMenu from "./components/NavigationMenu.vue"
const FOOTER_LINKS = [
// {
// labelKey: "footer.tac",
// to: "/terms-and-conditions"
// },
{
labelKey: "footer.legal",
to: "/legal-notice"
}
]
export default {
name: "App",
components: { NavigationMenu }
components: { NavigationMenu },
setup() {
const { t } = useI18n()
return {
t,
items: FOOTER_LINKS
}
}
}
</script>

View file

@ -1,5 +1,5 @@
<template>
<svg :style="{ width: size, height: size, filter: `blur(${blur}px)` }">
<svg :style="{ width: size + 'px', height: size + 'px', filter: `blur(${blur}px)` }">
<path
v-for="(blob, index) in blobs"
:key="index"
@ -14,7 +14,7 @@
import * as blobs2 from "blobs/v2"
import KUTE from "kute.js"
import { ref } from "vue"
import { getListOfIndexes } from "../getListOfIndexes.js"
import { getListOfIndexes } from "../utils/getListOfIndexes.js"
const BLOB_OPTIONS = {
extraPoints: 6,

View file

@ -1,24 +1,29 @@
<template>
<div class="sm:hidden fixed z-101 bottom-3 right-3 rounded-full backdrop-filter backdrop-blur-20 bg-white bg-opacity-20 w-20 h-20 flex justify-center items-center shadow-lg">
<div role="button" class="flex flex-col justify-evenly items-center h-10" @click="active = !active">
<div class="sm:hidden fixed z-101 bottom-2 right-2 rounded-full backdrop-filter bg-white w-18 h-18 flex justify-center items-center shadow-lg _blur-backdrop-or-hide">
<div
role="button"
aria-label="Toggle navigation menu"
class="flex flex-col justify-evenly items-center h-10"
@click="active = !active"
>
<div class="w-10 h-2px bg-black transition duration-200 transform" :style="`transform: ${active ? 'translateY(350%)' : ''} rotate(${active ? 45 : 0}deg)`"/>
<div class="w-10 h-2px bg-black transition duration-200 transform" :style="`transform: ${active ? 'translateY(-350%)' : ''} rotate(${active ? -45 : 0}deg)`"/>
</div>
</div>
<nav
class="fixed sm:sticky top-0 z-100 w-screen h-screen sm:h-20 backdrop-filter backdrop-blur-40 bg-white bg-opacity-40 transition duration-200"
class="fixed sm:sticky top-0 z-100 w-full h-screen sm:h-20 backdrop-filter bg-white transition duration-200 _blur-backdrop-or-hide"
:class="[scrolled && 'shadow-lg', active ? 'opacity-100' : '-sm:opacity-0 -sm:pointer-events-none']"
>
<div class="flex items-center justify-between h-full max-w-1200px mx-auto flex-grow -sm:flex-col px-6 sm:px-10">
<div class="fixed transition-all duration-500" :style="{ left: blobState.x + 'px', top: blobState.y + 'px', opacity: blobState.show ? 1 : 0 }">
<BlurredBlobCanvas
:colors="['#eb34cf', '#79faff']"
:colors="['#eb34cf', '#818cff']"
:opacity-variation="0"
:minimum-opacity="0.9"
:minimum-duration="1000"
:duration-variation="500"
:blur="10"
:size="100"
:size="80"
/>
</div>
<router-link class="uppercase font-special relative top-1 -sm:mt-20" to="/" @click="active = false">
@ -33,27 +38,51 @@
:to="item.to"
@click="active = false"
>
{{ item.label }}
{{ t(item.labelKey) }}
</router-link>
</div>
</div>
</nav>
</template>
<i18n lang="yaml">
en:
projects: Projects
contact: Contact
de:
projects: Projekte
contact: Kontakt
</i18n>
<style scoped>
._blur-backdrop-or-hide {
@apply bg-opacity-90;
backdrop-filter: blur(20px);
}
@supports (backdrop-filter: blur(20px)) {
._blur-backdrop-or-hide {
@apply bg-opacity-40;
}
}
</style>
<script>
import { useWindowScroll, useWindowSize } from "@vueuse/core"
import { computed, ref, watch, reactive } from "vue"
import { useRoute } from "vue-router"
import { useI18n } from "vue-i18n"
import { useWindowScrollLock } from "../utils/useWindowScrollLock.js"
import BlurredBlobCanvas from "./BlurredBlobCanvas.vue"
const ITEMS = [
// {
// labelKey: "projects",
// to: "/projects"
// },
{
label: "Projects",
to: "/projects"
},
{
label: "Contact",
labelKey: "contact",
to: "/contact"
}
]
@ -77,7 +106,7 @@
const activeItem = computed(() => items.find(item => item.to === route.path) ?? null)
const blobState = reactive({
x: windowWidth / 2,
y: 20,
y: 10,
show: false
})
@ -88,16 +117,19 @@
const { x, width, y } = activeItem.value.element.value.$el.getBoundingClientRect()
blobState.x = x + (width / 2) - 20
blobState.y = y - 20
blobState.y = y - 30
blobState.show = true
}
}, { immediate: true })
const { t } = useI18n()
return {
scrolled: computed(() => windowScroll.value > 0),
blobState,
items,
active
active,
t
}
}
}

27
src/main.css Normal file
View file

@ -0,0 +1,27 @@
@layer base {
.prose {
& > * {
@apply mt-6;
}
h1 {
@apply font-bold text-3xl sm:text-5xl text-gray-900;
}
h2 {
@apply font-bold text-2xl sm:text-4xl text-blue-700;
h1 ~ & {
@apply mt-4;
}
p ~ & {
@apply mt-10;
}
}
address, p {
@apply not-italic text-lg max-w-240;
}
}
}

View file

@ -1,14 +1,30 @@
import "./main.css"
import "virtual:windi.css"
import routes from "virtual:generated-pages"
import { createApp } from "vue"
import { createRouter, createWebHistory } from "vue-router"
import { createI18n } from "vue-i18n"
import App from "./App.vue"
const i18n = createI18n({
legacy: false,
fallbackLocale: "en",
locale: navigator.language
})
document.documentElement.lang = navigator.language.startsWith("de") ? "de" : "en"
const router = createRouter({
history: createWebHistory(),
routes
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) return savedPosition
if (to.hash) return { el: to.hash }
return { top: 0 }
}
})
createApp(App)
.use(router)
.use(i18n)
.mount("#app")

View file

@ -1,11 +1,87 @@
<template>
<div>
contact
</div>
<main class="text-2xl sm:text-3xl pt-26">
<h1 class="font-bold text-3xl sm:text-5xl text-gray-900 mb-10">{{ t("heading") }}</h1>
<p>
{{ t("you_want") }}
</p>
<ul class="asterisk-list mt-5">
<li>{{ t("develop_website") }}</li>
<li>{{ t("program_tool") }}</li>
<li>{{ t("design_print") }}</li>
<li>{{ t("help_server") }}</li>
</ul>
<p class="mt-10 mb-10">
{{ t("twitter") }}
<a
class="text-blue-900"
href="https://twitter.com/moritz_ruth"
target="_blank"
rel="noopener noreferrer"
>
@moritz_ruth
</a>
{{ t("or_email") }}
</p>
<div class="relative text-4xl group inline-block">
<BlurredBlobCanvas
class="absolute -top-10 left-22 pointer-events-none transition duration-400 opacity-0 group-hover:opacity-80"
:colors="['#818cff']"
:size="120"
:blur="20"
:minimum-duration="500"
:minimum-opacity="0.6"
:opacity-variation="0.4"
/>
<a class="text-blue-900 relative ml-2" href="mailto:hey@m0.is">
hey@m0.is
</a>
</div>
<p class="mt-10 mb-10">
{{ t("happy_to_hear") }}
</p>
<p class="mt-10 text-lg">
{{ t("disclaimer") }}
</p>
</main>
</template>
<i18n lang="yaml">
en:
heading: Contact
you_want: Want me to
develop_website: develop your website ( 1000),
program_tool: program a tool ( 500),
design_print: design a poster, brochure or business card ( 500),
help_server: help you with your server or domain ( 300)?
twitter: Hit me up on Twitter
or_email: "or send me an email (en/de):"
happy_to_hear: Im always happy to hear from you.
disclaimer: The prices listed above are not meant to be understood as a binding offer.
de:
heading: Kontakt
you_want: Willst du, dass ich
develop_website: deine Webseite entwickle ( 1000),
program_tool: ein Tool programmiere ( 500),
design_print: ein Poster, eine Broschüre oder eine Visitenkarte designe ( 500),
help_server: dir mit deinem Server oder deiner Domain helfe ( 300)?
twitter: Schreib mir auf Twitter
or_email: "oder sende mir eine Email:"
happy_to_hear: Ich freue mich auf deine Nachricht.
disclaimer: Die genannten Preise stellen kein verbindliches Angebot dar.
</i18n>
<script>
import { useI18n } from "vue-i18n"
import BlurredBlobCanvas from "../components/BlurredBlobCanvas.vue"
export default {
name: "ContactPage"
name: "ContactPage",
components: { BlurredBlobCanvas },
setup() {
const { t } = useI18n()
return { t }
}
}
</script>

View file

@ -1,8 +1,8 @@
<template>
<main class="flex -md:flex-col justify-between items-start md:items-center min-h-80vh">
<div class="font-bold relative -md:pt-20vh -md:pb-20 transform skew-x-7 -skew-y-7 rotate-7">
<div class="relative -md:pt-20vh -md:pb-20">
<div class="_fade-2">
<div class="_pattern transform rotate-179 absolute w-full h-40 -left-4 md:-left-10 top-20vh -md:-mt-10 md:-top-10 opacity-3 md:opacity-5"/>
<div class="_pattern transform rotate-179.5 absolute w-full h-40 -left-4 md:-left-10 top-20vh -md:-mt-10 md:-top-10 opacity-3 md:opacity-5"/>
</div>
<div class="_slide">
<ClickToBounce v-slot="{ setTrigger }" class="text-6xl md:text-7xl -md:-mt-2 mb-30 font-special" style="--delay: 0">
@ -10,49 +10,61 @@
</ClickToBounce>
</div>
<div class="font-special text-3xl sm:text-5xl mb-5 _slide" style="--delay: 200">
Im<br>
{{ t("i_am") }}<br>
<span class="text-1.1em text-transparent bg-clip-text bg-gradient-to-b from-blue-500 to-blue-900">Moritz Ruth</span>
</div>
<div class="text-gray-800 text-2xl sm:text-3xl">
<ul class="_list">
<ul class="asterisk-list">
<li class="_slide" style="--delay: 400">
software developer
{{ t("software_developer") }}
</li>
<li class="_slide" style="--delay: 600">
graphic designer
{{ t("designer") }}
</li>
<li class="_slide" style="--delay: 800">
typography enthusiast
{{ t("typography_enthusiast") }}
</li>
</ul>
</div>
<router-link class="mt-10 text-2xl sm:text-3xl block text-blue-900 _slide" style="--delay: 1000" to="/contact">
{{ t("contact_me") }}
</router-link>
</div>
<div class="self-center">
<BlurredBlobCanvas
class="_fade-1"
:colors="['#6577fc', '#eb34cf', '#6577fc', '#eb34cf']"
:size="300"
:blur="30"
:minimum-duration="2000"
:duration-variation="500"
:minimum-opacity="0.2"
:opacity-variation="0.4"
/>
</div>
<BlurredBlobCanvas
class="_fade-1 self-center"
:colors="['#6577fc', '#eb34cf', '#6577fc', '#eb34cf']"
:size="300"
:blur="30"
:minimum-duration="2000"
:duration-variation="500"
:minimum-opacity="0.2"
:opacity-variation="0.4"
/>
</main>
</template>
<i18n lang="yaml">
en:
i_am: Im
software_developer: software developer
designer: web and print designer
typography_enthusiast: typography enthusiast
contact_me: Drop me a message!
de:
i_am: ""
software_developer: Software-Entwickler
designer: Web und Print-Designer
typography_enthusiast: Typography-Enthusiast
contact_me: Schreib mir!
</i18n>
<style scoped>
._pattern {
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='72' viewBox='0 0 36 72'%3E%3Cg fill-rule='evenodd'%3E%3Cg \
fill='%23000000' fill-opacity='1'%3E%3Cpath d='M2 6h12L8 18 2 6zm18 36h12l-6 12-6-12z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
}
._list > li::before {
@apply text-blue-900;
content: "*";
}
/* created using emojicursor.app */
._clap {
cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='50' height='90' viewport='0 0 100 100' style='fill:black;font-size:50px;'><text y='50%'>\
@ -64,7 +76,7 @@
}
._fade-2 {
animation: fade 2s 2s ease-out both;
animation: fade 2s 1s ease-out both;
}
._slide {
@ -75,7 +87,7 @@
@screen md {
._slide {
animation-delay: calc(var(--delay) * 1.4ms + 1.5s);
animation-delay: calc(var(--delay) * 1.4ms + 0.4s);
}
}
@ -103,11 +115,16 @@
</style>
<script>
import { useI18n } from "vue-i18n"
import BlurredBlobCanvas from "../components/BlurredBlobCanvas.vue"
import ClickToBounce from "../components/ClickToBounce.vue"
export default {
name: "IndexPage",
components: { ClickToBounce, BlurredBlobCanvas }
components: { ClickToBounce, BlurredBlobCanvas },
setup() {
const { t } = useI18n()
return { t }
}
}
</script>

View file

@ -0,0 +1,61 @@
<template>
<main class="prose pt-20">
<h1>{{ t("heading") }}</h1>
<p v-if="!isGerman">
<i>As required by German law.</i>
</p>
<p>
Dieses Impressum gilt für diese Webseite (moritzruth.de) und alle Webseiten, welche unter Subdomains von moritzruth.de erreichbar sind.
</p>
<h2>Angaben gemäß § 5 TMG</h2>
<address>
Moritz Ruth<br>
Zum Galgenberg 19<br>
66539 Neunkirchen
</address>
<p>
E-Mail-Adresse:
<a class="text-blue-900" href="mailto:hey@m0.is">
hey@m0.is
</a>
</p>
<h2>Haftung für Links</h2>
<p>
Diese Webseite enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine
Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich.
</p>
<p>
Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft.
Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar.
</p>
<p>
Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar.
Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend entfernen.
</p>
</main>
</template>
<i18n lang="yaml">
en:
heading: Legal Notice
de:
heading: Impressum
</i18n>
<script>
import { useI18n } from "vue-i18n"
import { computed } from "vue"
export default {
name: "LegalNoticePage",
setup() {
const { locale, t } = useI18n()
return {
isGerman: computed(() => locale.value.startsWith("de")),
t
}
}
}
</script>

View file

@ -1,11 +0,0 @@
<template>
<div>
projects
</div>
</template>
<script>
export default {
name: "ProjectsPage"
}
</script>

View file

@ -0,0 +1,29 @@
<template>
<div class="prose pt-20">
<h1>{{ t("heading") }}</h1>
<p>{{ t("message") }}</p>
</div>
</template>
<i18n lang="yaml">
en:
message: Not yet available.
heading: Terms and Conditions
de:
message: Noch nicht verfügbar.
heading: Allgemeine Geschäftsbedingungen
</i18n>
<script>
import { useI18n } from "vue-i18n"
export default {
name: "TermsAndConditionsPage",
setup() {
const { t } = useI18n()
return { t }
}
}
</script>

View file

@ -1,6 +1,7 @@
import vuePlugin from "@vitejs/plugin-vue"
import windicssPlugin from "vite-plugin-windicss"
import pagesPlugin from "vite-plugin-pages"
import vueI18nPlugin from "@intlify/vite-plugin-vue-i18n"
/**
* https://vitejs.dev/config/
@ -9,6 +10,7 @@ import pagesPlugin from "vite-plugin-pages"
export default {
plugins: [
vuePlugin(),
vueI18nPlugin(),
pagesPlugin(),
windicssPlugin()
]