changes
This commit is contained in:
parent
82447b6a92
commit
e55d5feced
28 changed files with 2006 additions and 1543 deletions
|
@ -1,15 +0,0 @@
|
|||
body {
|
||||
font-family: "Alata", sans-serif;
|
||||
}
|
||||
|
||||
.page-enter-active, .page-leave-active {
|
||||
transition: 500ms ease opacity;
|
||||
}
|
||||
|
||||
.page-enter, .page-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.page-enter-to, .page-leave {
|
||||
opacity: 1;
|
||||
}
|
10
assets/js/footerItems.js
Normal file
10
assets/js/footerItems.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
export const footerItems = [
|
||||
{
|
||||
label: "Legal Notice",
|
||||
to: "/legal-notice"
|
||||
},
|
||||
{
|
||||
label: "Privacy Policy",
|
||||
to: "/privacy-policy"
|
||||
}
|
||||
];
|
7
assets/js/toModifierClasses.js
Normal file
7
assets/js/toModifierClasses.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import _kebabCase from "lodash.kebabcase";
|
||||
|
||||
export function toModifierClasses(base, classes) {
|
||||
return Object.entries(classes)
|
||||
.filter(([, value]) => Boolean(value))
|
||||
.map(([key]) => `${base}--${_kebabCase(key)}`);
|
||||
}
|
6
assets/styles/_colors.scss
Normal file
6
assets/styles/_colors.scss
Normal file
|
@ -0,0 +1,6 @@
|
|||
$background: black;
|
||||
$background-c: white;
|
||||
$pink: #BB2081;
|
||||
$pink-c: white;
|
||||
$blue: #14AAD8;
|
||||
$blue-c: white;
|
8
assets/styles/_content.scss
Normal file
8
assets/styles/_content.scss
Normal file
|
@ -0,0 +1,8 @@
|
|||
@mixin content($padding: 20px) {
|
||||
$width: 800px;
|
||||
|
||||
max-width: calc(100vw - #{$padding} * 2);
|
||||
width: $width;
|
||||
padding: 0 $padding;
|
||||
margin: 0 auto;
|
||||
}
|
11
assets/styles/_screenSize.scss
Normal file
11
assets/styles/_screenSize.scss
Normal file
|
@ -0,0 +1,11 @@
|
|||
@mixin mobile() {
|
||||
@media (max-width: 800px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin notMobile() {
|
||||
@media (min-width: 801px) {
|
||||
@content;
|
||||
}
|
||||
}
|
1
assets/styles/_z-indexes.scss
Normal file
1
assets/styles/_z-indexes.scss
Normal file
|
@ -0,0 +1 @@
|
|||
$navigation-bar: 5;
|
48
assets/styles/formatted/_heading.scss
Normal file
48
assets/styles/formatted/_heading.scss
Normal file
|
@ -0,0 +1,48 @@
|
|||
@use "../screenSize";
|
||||
|
||||
%heading--1, %heading--2, %heading--3, %heading--4, %heading--5, %heading--6 {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
}
|
||||
|
||||
%heading--1 {
|
||||
font-size: 3.8rem;
|
||||
|
||||
@include screenSize.mobile {
|
||||
font-size: 2.7rem;
|
||||
}
|
||||
}
|
||||
|
||||
%heading--2 {
|
||||
font-size: 3rem;
|
||||
|
||||
@include screenSize.mobile {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
%heading--3 {
|
||||
font-size: 2.6rem;
|
||||
|
||||
@include screenSize.mobile {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
%heading--4 {
|
||||
font-size: 2.2rem;
|
||||
|
||||
@include screenSize.mobile {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
%heading--5 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
%heading--6 {
|
||||
font-size: 1.4rem;
|
||||
}
|
11
assets/styles/formatted/_link.scss
Normal file
11
assets/styles/formatted/_link.scss
Normal file
|
@ -0,0 +1,11 @@
|
|||
%link {
|
||||
color: var(--colors-link);
|
||||
text-decoration: none;
|
||||
|
||||
transition: 100ms linear opacity;
|
||||
opacity: 1;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
7
assets/styles/formatted/_paragraph.scss
Normal file
7
assets/styles/formatted/_paragraph.scss
Normal file
|
@ -0,0 +1,7 @@
|
|||
%paragraph {
|
||||
margin: 20px 0;
|
||||
|
||||
li {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
22
assets/styles/formatted/index.scss
Normal file
22
assets/styles/formatted/index.scss
Normal file
|
@ -0,0 +1,22 @@
|
|||
@use "link";
|
||||
@use "heading";
|
||||
@use "paragraph";
|
||||
|
||||
$_elements: (
|
||||
"paragraph": ("p", "ul"),
|
||||
"link": "a",
|
||||
"heading--1": "h1",
|
||||
"heading--2": "h2",
|
||||
"heading--3": "h3",
|
||||
"heading--4": "h4",
|
||||
"heading--5": "h5",
|
||||
"heading--6": "h6",
|
||||
);
|
||||
|
||||
@each $class, $tags in $_elements {
|
||||
@each $tag in $tags {
|
||||
#{$tag}.formatted, .formatted #{$tag}, .#{$class} {
|
||||
@extend %#{$class};
|
||||
}
|
||||
}
|
||||
}
|
67
assets/styles/global.scss
Normal file
67
assets/styles/global.scss
Normal file
|
@ -0,0 +1,67 @@
|
|||
@use "./colors";
|
||||
@use "./content";
|
||||
@use "./formatted";
|
||||
|
||||
// General
|
||||
|
||||
html, body {
|
||||
font-family: "Alata", sans-serif;
|
||||
font-size: 16px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
width: 100vw;
|
||||
overflow-x: hidden;
|
||||
min-height: 100vh;
|
||||
|
||||
background: colors.$background;
|
||||
color: colors.$background-c;
|
||||
}
|
||||
|
||||
// Transitions
|
||||
|
||||
.page-enter-active, .page-leave-active {
|
||||
transition: 500ms ease opacity;
|
||||
}
|
||||
|
||||
.page-enter, .page-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.page-enter-to, .page-leave {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
// Other
|
||||
|
||||
.content {
|
||||
@include content.content();
|
||||
}
|
||||
|
||||
.link {
|
||||
position: relative;
|
||||
|
||||
text-decoration: none;
|
||||
color: colors.$background-c;
|
||||
&:hover, &:focus {
|
||||
outline: none;
|
||||
|
||||
&::after {
|
||||
background-position: 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
|
||||
height: 2px;
|
||||
top: 102%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
background: linear-gradient(90deg, colors.$pink 0%, colors.$blue 100%) 100% 0;
|
||||
background-size: 260% 100%;
|
||||
transition: 500ms ease background-position;
|
||||
}
|
||||
}
|
|
@ -1,10 +1,30 @@
|
|||
<template>
|
||||
<div class="animated-logo">
|
||||
<svg class="animated-logo__svg" xmlns="http://www.w3.org/2000/svg" viewBox="76.75 182.661 358.5 146.679">
|
||||
<path class="animated-logo__m1" d=" M 121.75 182.749 L 166.75 329.339 L 76.75 329.339 L 121.75 182.749 Z " fill-rule="evenodd" fill="currentColor"/>
|
||||
<path class="animated-logo__m2" d=" M 206.75 182.749 L 251.75 329.339 L 161.75 329.339 L 206.75 182.749 Z " fill-rule="evenodd" fill="currentColor"/>
|
||||
<path class="animated-logo__r1" d=" M 327.25 182.705 L 372.25 329.295 L 282.25 329.295 L 327.25 182.705 Z " fill-rule="evenodd" fill="currentColor"/>
|
||||
<path class="animated-logo__r2" d=" M 390.537 329.339 L 345.25 182.837 L 435.25 182.661 L 390.537 329.339 Z " fill-rule="evenodd" fill="currentColor"/>
|
||||
<path
|
||||
class="animated-logo__m1"
|
||||
d=" M 121.75 182.749 L 166.75 329.339 L 76.75 329.339 L 121.75 182.749 Z "
|
||||
fill-rule="evenodd"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
class="animated-logo__m2"
|
||||
d=" M 206.75 182.749 L 251.75 329.339 L 161.75 329.339 L 206.75 182.749 Z "
|
||||
fill-rule="evenodd"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
class="animated-logo__r1"
|
||||
d=" M 327.25 182.705 L 372.25 329.295 L 282.25 329.295 L 327.25 182.705 Z "
|
||||
fill-rule="evenodd"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
class="animated-logo__r2"
|
||||
d=" M 390.537 329.339 L 345.25 182.837 L 435.25 182.661 L 390.537 329.339 Z "
|
||||
fill-rule="evenodd"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<div class="animated-logo__name">
|
||||
Moritz Ruth
|
||||
|
@ -21,7 +41,7 @@
|
|||
$slide-length: 37.5%;
|
||||
|
||||
.animated-logo {
|
||||
width: 250px;
|
||||
width: 300px;
|
||||
max-width: 60vw;
|
||||
|
||||
animation: scaleUp $scale-up-duration ease-out both;
|
||||
|
@ -34,7 +54,7 @@
|
|||
}
|
||||
|
||||
.animated-logo__name {
|
||||
font-size: 2rem;
|
||||
font-size: 2.3rem;
|
||||
animation: fadeIn $slide-duration $scale-up-duration ease both;
|
||||
text-align: center;
|
||||
}
|
||||
|
|
74
components/ExternalLink.vue
Normal file
74
components/ExternalLink.vue
Normal file
|
@ -0,0 +1,74 @@
|
|||
<template>
|
||||
<a
|
||||
class="external-link link"
|
||||
rel="noopener"
|
||||
target="_blank"
|
||||
:href="href"
|
||||
>
|
||||
<template v-if="this.$slots.default && this.$slots.default[0] && this.$slots.default[0].text">
|
||||
<slot/>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ label }}
|
||||
</template>
|
||||
<ExternalIcon class="external-link__icon"/>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.external-link {
|
||||
padding-right: 2px;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.external-link__icon {
|
||||
margin-left: 3px;
|
||||
|
||||
width: 15px;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import ExternalIcon from "@/assets/icons/external.svg";
|
||||
|
||||
export default {
|
||||
name: "ExternalLink",
|
||||
components: { ExternalIcon },
|
||||
props: {
|
||||
href: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
showProtocol: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showQuery: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
label() {
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const url = new (process.server ? require("url").URL : window.URL)(this.href);
|
||||
let label = "";
|
||||
|
||||
if (this.showProtocol) {
|
||||
label += url.protocol;
|
||||
label += "//";
|
||||
}
|
||||
|
||||
label += url.host + url.pathname;
|
||||
|
||||
if (this.showQuery) {
|
||||
label += url.search;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
47
components/MyFooter.vue
Normal file
47
components/MyFooter.vue
Normal file
|
@ -0,0 +1,47 @@
|
|||
<template>
|
||||
<footer class="my-footer">
|
||||
<nuxt-link
|
||||
v-for="item in $options.items"
|
||||
class="my-footer__link link"
|
||||
:key="item.label"
|
||||
:to="item.to"
|
||||
@click.native.passive="open = false"
|
||||
>
|
||||
{{ item.label }}
|
||||
</nuxt-link>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@use "~@/assets/styles/screenSize";
|
||||
@use "~@/assets/styles/colors";
|
||||
|
||||
.my-footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
width: 100vw;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.my-footer__link:not(:last-child) {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
@include screenSize.mobile {
|
||||
.my-footer__link:not(:last-child) {
|
||||
margin-right: 0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { footerItems } from "@/assets/js/footerItems";
|
||||
|
||||
export default {
|
||||
name: "MyFooter",
|
||||
items: footerItems
|
||||
};
|
||||
</script>
|
323
components/NavigationBar.vue
Normal file
323
components/NavigationBar.vue
Normal file
|
@ -0,0 +1,323 @@
|
|||
<template>
|
||||
<div class="navigation-bar" :class="classes">
|
||||
<div class="navigation-bar__placeholder"></div>
|
||||
<nav class="navigation-bar__content">
|
||||
<div class="navigation-bar__title-container">
|
||||
<span
|
||||
class="navigation-bar__title"
|
||||
:class="{ 'navigation-bar__title--show': showTitle }"
|
||||
>
|
||||
{{ title }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="navigation-bar__toggle" @click="open = !open">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="navigation-bar__links">
|
||||
<div
|
||||
v-for="item in $options.items"
|
||||
class="navigation-bar__link-container"
|
||||
:key="item.label"
|
||||
>
|
||||
<nuxt-link
|
||||
v-if="item.to"
|
||||
class="navigation-bar__link"
|
||||
:to="item.to"
|
||||
@click.native.passive="open = false"
|
||||
>
|
||||
{{ item.label }}
|
||||
</nuxt-link>
|
||||
<a
|
||||
v-else
|
||||
class="navigation-bar__link"
|
||||
:key="item.label"
|
||||
rel="noopener"
|
||||
:href="item.href"
|
||||
@click.passive="open = false"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@use "~@/assets/styles/screenSize";
|
||||
@use "~@/assets/styles/colors";
|
||||
@use "~@/assets/styles/content";
|
||||
@use "~@/assets/styles/z-indexes";
|
||||
|
||||
body {
|
||||
--navigation-bar-height: 80px;
|
||||
}
|
||||
|
||||
.navigation-bar__placeholder {
|
||||
height: var(--navigation-bar-height);
|
||||
}
|
||||
|
||||
.navigation-bar__content {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: z-indexes.$navigation-bar;
|
||||
|
||||
height: var(--navigation-bar-height);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 30px;
|
||||
|
||||
.navigation-bar--show-background & {
|
||||
background: colors.$background;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-bar__toggle {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
& > span {
|
||||
display: block;
|
||||
background-color: colors.$background-c;
|
||||
|
||||
width: 30px;
|
||||
height: 2px;
|
||||
|
||||
transition: 200ms linear;
|
||||
transition-property: opacity, transform;
|
||||
|
||||
&:nth-child(2) {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-bar__title-container {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.navigation-bar__title {
|
||||
font-size: 1.4rem;
|
||||
display: block;
|
||||
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
transition: 200ms ease;
|
||||
transition-property: opacity, transform;
|
||||
|
||||
&.navigation-bar__title--show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-bar__links {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
background: colors.$background;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: 200ms ease opacity;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.navigation-bar__link-container {
|
||||
position: relative;
|
||||
background: white;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
background: linear-gradient(135deg, colors.$blue 25%, colors.$pink, colors.$blue 75%);
|
||||
background-size: 400% 400%;
|
||||
animation: 1s infinite linear reverse navigation-bar__gradient;
|
||||
|
||||
pointer-events: none;
|
||||
mix-blend-mode: multiply;
|
||||
|
||||
opacity: 0;
|
||||
transition: 200ms opacity ease;
|
||||
}
|
||||
|
||||
&:hover, &:focus-within {
|
||||
&::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.navigation-bar__link {
|
||||
outline: none;
|
||||
transform: translate(-5px, -5px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-bar__link {
|
||||
display: block;
|
||||
color: colors.$background-c;
|
||||
text-decoration: none;
|
||||
font-size: 1.4rem;
|
||||
|
||||
background: black;
|
||||
padding: 10px;
|
||||
|
||||
transition: 200ms transform ease;
|
||||
}
|
||||
|
||||
@keyframes navigation-bar__gradient {
|
||||
from {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
to {
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-bar--open {
|
||||
.navigation-bar__toggle > span {
|
||||
&:nth-child(1) {
|
||||
transform: translateY(10px) rotate(45deg);
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
transform: translateY(-10px) rotate(-45deg);
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-bar__links {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@include screenSize.notMobile {
|
||||
body {
|
||||
--navigation-bar-height: 100px;
|
||||
}
|
||||
|
||||
.navigation-bar__content {
|
||||
@include content.content(40px);
|
||||
}
|
||||
|
||||
.navigation-bar__title {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.navigation-bar__toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navigation-bar__links {
|
||||
position: static;
|
||||
width: auto;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.navigation-bar__link-container:not(:last-child) {
|
||||
margin-bottom: 0;
|
||||
margin-right: 40px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { toModifierClasses } from "@/assets/js/toModifierClasses";
|
||||
|
||||
const ITEMS = [
|
||||
{
|
||||
label: "Start",
|
||||
to: "/"
|
||||
},
|
||||
{
|
||||
label: "Projects",
|
||||
to: "/projects"
|
||||
}
|
||||
];
|
||||
|
||||
export default {
|
||||
name: "NavigationBar",
|
||||
props: {
|
||||
backgroundAfterScroll: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
open: false,
|
||||
scrollPosition: 0
|
||||
}),
|
||||
computed: {
|
||||
showTitle: vm => vm.scrollPosition > 60,
|
||||
showBackground: vm => vm.backgroundAfterScroll ? vm.scrollPosition > 0 : true,
|
||||
classes() {
|
||||
const { open, showBackground } = this;
|
||||
|
||||
return toModifierClasses("navigation-bar", {
|
||||
open,
|
||||
showBackground
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const scrollListener = () => {
|
||||
this.scrollPosition = window.scrollY;
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", scrollListener, { passive: true });
|
||||
|
||||
this.$on("hook:beforeDestroy", () => {
|
||||
window.removeEventListener("scroll", scrollListener);
|
||||
});
|
||||
|
||||
scrollListener();
|
||||
},
|
||||
items: ITEMS
|
||||
};
|
||||
</script>
|
|
@ -1,96 +0,0 @@
|
|||
<template>
|
||||
<div class="project">
|
||||
<h2 class="heading--3">
|
||||
{{ title }}
|
||||
</h2>
|
||||
<span class="project__type">{{ type }}</span>
|
||||
<div class="project__description">
|
||||
<slot/>
|
||||
</div>
|
||||
<hr class="project__divider"/>
|
||||
<div class="project__buttons">
|
||||
<KButton v-if="link" :href="link">
|
||||
Open
|
||||
<template v-slot:suffix>
|
||||
<ArrowRightIcon/>
|
||||
</template>
|
||||
</KButton>
|
||||
<KButton v-if="github" :href="`https://github.com/${github}`">
|
||||
GitHub
|
||||
<template v-slot:prefix>
|
||||
<GitHubIcon/>
|
||||
</template>
|
||||
</KButton>
|
||||
<KButton v-if="npm" :href="`https://www.npmjs.com/package/${npm}`">
|
||||
NPM
|
||||
<template v-slot:prefix>
|
||||
<NPMIcon style="width: 30px; top: 4px"/>
|
||||
</template>
|
||||
</KButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.project__type {
|
||||
margin-top: -1rem;
|
||||
margin-bottom: 1rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.project__description {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.project__divider {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
width: 175px;
|
||||
height: 1px;
|
||||
background-color: black;
|
||||
|
||||
border: none;
|
||||
}
|
||||
|
||||
.project__buttons {
|
||||
display: flex;
|
||||
|
||||
& > *:not(:last-child) {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import KButton from "kiste/components/KButton.vue";
|
||||
import ArrowRightIcon from "@/assets/icons/arrow_right.svg";
|
||||
import GitHubIcon from "@/assets/icons/github.svg";
|
||||
import NPMIcon from "@/assets/icons/npm.svg";
|
||||
|
||||
export default {
|
||||
name: "GProject",
|
||||
components: { KButton, ArrowRightIcon, GitHubIcon, NPMIcon },
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
link: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
github: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
npm: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -1,22 +1,23 @@
|
|||
<template>
|
||||
<div class="default-layout fill-screen">
|
||||
<KApp>
|
||||
<div class="default-layout">
|
||||
<nuxt/>
|
||||
<KFooter/>
|
||||
</KApp>
|
||||
<MyFooter/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.default-layout {
|
||||
min-height: 100vh;
|
||||
width: 100vw;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import KApp from "kiste/components/KApp.vue";
|
||||
import KFooter from "kiste/components/KFooter.vue";
|
||||
import MyFooter from "@/components/MyFooter";
|
||||
|
||||
export default {
|
||||
name: "DefaultLayout",
|
||||
components: { KApp, KFooter }
|
||||
components: { MyFooter }
|
||||
};
|
||||
</script>
|
||||
|
|
13
layouts/empty.vue
Normal file
13
layouts/empty.vue
Normal file
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<nuxt/>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "EmptyLayout"
|
||||
};
|
||||
</script>
|
|
@ -1,18 +0,0 @@
|
|||
<template>
|
||||
<KApp>
|
||||
<nuxt/>
|
||||
</KApp>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import KApp from "kiste/components/KApp.vue";
|
||||
|
||||
export default {
|
||||
name: "WithoutFooterLayout",
|
||||
components: { KApp }
|
||||
};
|
||||
</script>
|
|
@ -18,7 +18,7 @@ export default {
|
|||
/*
|
||||
** Global CSS
|
||||
*/
|
||||
css: ["@/assets/global.scss"],
|
||||
css: ["@/assets/styles/global.scss"],
|
||||
|
||||
layoutTransition: "page",
|
||||
/*
|
||||
|
@ -30,8 +30,7 @@ export default {
|
|||
*/
|
||||
buildModules: [
|
||||
// Doc: https://github.com/nuxt-community/eslint-module
|
||||
"@nuxtjs/eslint-module",
|
||||
"kiste/nuxt"
|
||||
"@nuxtjs/eslint-module"
|
||||
],
|
||||
/*
|
||||
** Nuxt.js modules
|
||||
|
@ -41,32 +40,6 @@ export default {
|
|||
"@nuxtjs/pwa"
|
||||
],
|
||||
|
||||
kiste: {
|
||||
theme: {
|
||||
contentPadding: "10px"
|
||||
},
|
||||
navigationItems: [
|
||||
{
|
||||
label: "Home",
|
||||
to: "/"
|
||||
},
|
||||
{
|
||||
label: "Projects",
|
||||
to: "/projects"
|
||||
}
|
||||
],
|
||||
footerItems: [
|
||||
{
|
||||
label: "Legal Notice",
|
||||
to: "/legal-notice"
|
||||
},
|
||||
{
|
||||
label: "Privacy Policy",
|
||||
to: "/privacy-policy"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// https://pwa.nuxtjs.org/modules/workbox.html
|
||||
workbox: { workboxExtensions: ["@/assets/js/font-sw.js"] },
|
||||
|
||||
|
@ -77,17 +50,17 @@ export default {
|
|||
mobileApp: false,
|
||||
name: "Moritz Ruth",
|
||||
author: "Moritz Ruth",
|
||||
description: "The official website of Moritz Ruth",
|
||||
description: "Moritz Ruth is a web developer, photograph and digital creator.",
|
||||
lang: "en",
|
||||
themeColor: "#ffffff"
|
||||
themeColor: "#000000"
|
||||
},
|
||||
// https://developer.mozilla.org/en-US/docs/Web/Manifest
|
||||
manifest: {
|
||||
name: "Moritz Ruth",
|
||||
short_name: "Moritz Ruth",
|
||||
background_color: "#ffffff",
|
||||
background_color: "#000000",
|
||||
display: "browser",
|
||||
description: "The official website of Moritz Ruth"
|
||||
description: "Moritz Ruth is a web developer, photograph and digital creator."
|
||||
}
|
||||
},
|
||||
|
||||
|
|
20
package.json
20
package.json
|
@ -5,30 +5,30 @@
|
|||
"author": "Moritz Ruth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "nuxt",
|
||||
"dev": "HOST=0.0.0.0 nuxt",
|
||||
"generate": "nuxt generate",
|
||||
"start": "serve dist",
|
||||
"lint": "eslint --ext .js,.vue --ignore-path .gitignore ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxtjs/pwa": "^3.0.0-beta.19",
|
||||
"kiste": "^1.2.7",
|
||||
"nuxt": "^2.11.0",
|
||||
"@nuxtjs/pwa": "^3.0.0-beta.20",
|
||||
"lodash.kebabcase": "^4.1.1",
|
||||
"nuxt": "^2.12.2",
|
||||
"shaped.js": "^1.0.3",
|
||||
"vue": "^2.6.11",
|
||||
"vue-ripple-directive": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@moritzruth/eslint-config": "^1.0.6",
|
||||
"@moritzruth/eslint-config": "^1.0.9",
|
||||
"@nuxtjs/eslint-module": "^1.1.0",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-import-resolver-webpack": "^0.12.1",
|
||||
"eslint-plugin-nuxt": "^0.5.0",
|
||||
"eslint-plugin-nuxt": "^0.5.2",
|
||||
"fibers": "^4.0.2",
|
||||
"sass": "^1.24.4",
|
||||
"sass-loader": "^8.0.1",
|
||||
"sass": "^1.26.3",
|
||||
"sass-loader": "^8.0.2",
|
||||
"serve": "^11.3.0",
|
||||
"shaped.js": "^1.0.2",
|
||||
"svg-to-vue-component": "^0.3.8"
|
||||
}
|
||||
}
|
||||
|
|
198
pages/index.vue
198
pages/index.vue
|
@ -1,173 +1,109 @@
|
|||
<template>
|
||||
<div class="index-page">
|
||||
<KNavigationBar background-after-scroll/>
|
||||
<AnimatedLogo/>
|
||||
<NavigationBar background-after-scroll title="Start"/>
|
||||
<canvas class="index-page__background" ref="canvas"/>
|
||||
<main class="index-page__content">
|
||||
<div class="index-page__socials">
|
||||
<a class="index-page__social-link" href="https://github.com/moritzruth" title="GitHub">
|
||||
<GitHubIcon class="index-page__social-icon"/>
|
||||
</a>
|
||||
<a class="index-page__social-link" href="https://twitter.com/moritzruth_dev" title="Twitter">
|
||||
<TwitterIcon class="index-page__social-icon"/>
|
||||
</a>
|
||||
<a class="index-page__social-link" href="https://instagram.com/moritzruth_dev" title="Instagram">
|
||||
<InstagramIcon class="index-page__social-icon"/>
|
||||
</a>
|
||||
<a class="index-page__social-link" href="mailto:dev@moritz-ruth.de" title="Email">
|
||||
<EmailIcon class="index-page__social-icon"/>
|
||||
</a>
|
||||
<div class="index-page__content">
|
||||
<AnimatedLogo/>
|
||||
</div>
|
||||
</main>
|
||||
<KFooter class="index-page__footer"/>
|
||||
<footer class="index-page__footer">
|
||||
<nuxt-link
|
||||
v-for="item in $options.footerItems"
|
||||
class="index-page__footer-link"
|
||||
:key="item.label"
|
||||
:to="item.to"
|
||||
@click.native.passive="open = false"
|
||||
>
|
||||
{{ item.label }}
|
||||
</nuxt-link>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "~kiste/css/mixins/screenSize";
|
||||
<style lang="scss">
|
||||
@use "~@/assets/styles/screenSize";
|
||||
@use "~@/assets/styles/colors";
|
||||
|
||||
.index-page {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
height: calc(100vh - var(--x-navbar-height));
|
||||
background: black;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.index-page__background {
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
z-index: -1;
|
||||
|
||||
animation: appear 2s 1.2s linear both;
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.index-page__socials {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
.index-page__content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.index-page__social-link {
|
||||
color: black;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.index-page__social-icon {
|
||||
width: 40px;
|
||||
|
||||
transition: 200ms linear opacity;
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
height: calc(100vh - var(--navigation-bar-height));
|
||||
}
|
||||
|
||||
.index-page__footer {
|
||||
position: absolute;
|
||||
padding: 20px 0;
|
||||
|
||||
margin-bottom: 10px;
|
||||
|
||||
@include screenSize.notMobile {
|
||||
top: unset;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
width: fit-content;
|
||||
padding: 20px;
|
||||
margin: 0;
|
||||
|
||||
flex-direction: row;
|
||||
opacity: 0.5;
|
||||
transition: 200ms linear opacity;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.index-page__footer-link {
|
||||
text-decoration: none;
|
||||
color: colors.$background-c;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include screenSize.mobile {
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import KNavigationBar from "kiste/components/KNavigationBar.vue";
|
||||
import KFooter from "kiste/components/KFooter.vue";
|
||||
import { Canvas } from "shaped.js";
|
||||
import AnimatedLogo from "@/components/AnimatedLogo.vue";
|
||||
import GitHubIcon from "@/assets/icons/github.svg";
|
||||
import TwitterIcon from "@/assets/icons/twitter.svg";
|
||||
import InstagramIcon from "@/assets/icons/instagram.svg";
|
||||
import EmailIcon from "@/assets/icons/email.svg";
|
||||
import AnimatedLogo from "@/components/AnimatedLogo";
|
||||
import NavigationBar from "@/components/NavigationBar";
|
||||
import { footerItems } from "@/assets/js/footerItems";
|
||||
|
||||
const COLORS = [
|
||||
"rgba(0, 0, 0, 0.8)",
|
||||
"rgba(0, 255, 150, 0.8)",
|
||||
"rgba(0, 255, 150, 0.2)",
|
||||
"rgba(0, 150, 255, 0.8)",
|
||||
"rgba(0, 150, 255, 0.2)"
|
||||
"#BB2081",
|
||||
"#14AAD8",
|
||||
"#ffffff"
|
||||
];
|
||||
|
||||
const LINES = [
|
||||
{
|
||||
minCount: 8,
|
||||
probability: 1 / 10000,
|
||||
height: 2,
|
||||
probability: 1 / 20000,
|
||||
height: 1,
|
||||
length: 100,
|
||||
speed: [-0.2, 0.2],
|
||||
speed: [-0.1, 0.4],
|
||||
colors: COLORS
|
||||
},
|
||||
{
|
||||
minCount: 8,
|
||||
probability: 1 / 50000,
|
||||
probability: 1 / 10000,
|
||||
height: 5,
|
||||
length: [20, 200],
|
||||
speed: [0.2, 0.3],
|
||||
colors: COLORS,
|
||||
randomizeYAfterLeave: true
|
||||
},
|
||||
{
|
||||
probability: 1 / 50000,
|
||||
height: 50,
|
||||
length: 50,
|
||||
speed: [0.2, 0.5],
|
||||
length: 5,
|
||||
speed: [-0.4, 0.4],
|
||||
colors: COLORS
|
||||
},
|
||||
{
|
||||
probability: 1 / 5000,
|
||||
height: 3,
|
||||
length: 3,
|
||||
speed: [-1, 1],
|
||||
colors: COLORS
|
||||
},
|
||||
{
|
||||
minCount: 8,
|
||||
probability: 1 / 50000,
|
||||
height: [20, 200],
|
||||
length: [20, 200],
|
||||
speed: [0.2, 0.3],
|
||||
colors: COLORS,
|
||||
randomizeYAfterLeave: true
|
||||
},
|
||||
{
|
||||
probability: 1 / 5000,
|
||||
probability: 1 / 10000,
|
||||
height: [20, 200],
|
||||
length: 2,
|
||||
speed: [-0.2, 0.2],
|
||||
|
@ -177,31 +113,32 @@
|
|||
|
||||
export default {
|
||||
name: "IndexPage",
|
||||
layout: "none",
|
||||
components: { AnimatedLogo, GitHubIcon, TwitterIcon, InstagramIcon, EmailIcon, KNavigationBar, KFooter },
|
||||
layout: "empty",
|
||||
components: { NavigationBar, AnimatedLogo },
|
||||
mounted() {
|
||||
let nextConfig = 0;
|
||||
let configIndex = 0;
|
||||
|
||||
if (localStorage !== undefined) {
|
||||
const rawValue = localStorage.getItem("nextBackground");
|
||||
|
||||
if (rawValue) {
|
||||
try {
|
||||
nextConfig = JSON.parse(rawValue);
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch {}
|
||||
configIndex = JSON.parse(rawValue);
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nextConfig > LINES.length - 1) {
|
||||
nextConfig = 0;
|
||||
if (configIndex > LINES.length - 1) {
|
||||
configIndex = 0;
|
||||
}
|
||||
|
||||
if (localStorage !== undefined) {
|
||||
localStorage.setItem("nextBackground", nextConfig + 1);
|
||||
localStorage.setItem("nextBackground", JSON.stringify(configIndex + 1));
|
||||
}
|
||||
|
||||
const config = LINES[nextConfig];
|
||||
const config = LINES[configIndex];
|
||||
const backgroundCanvas = new Canvas(this.$refs.canvas, {
|
||||
lines: config,
|
||||
fillWindowSize: true
|
||||
|
@ -210,6 +147,7 @@
|
|||
this.$once("hook:beforeDestroy", () => {
|
||||
backgroundCanvas.destroy();
|
||||
});
|
||||
}
|
||||
},
|
||||
footerItems
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<div class="legal-notice-page">
|
||||
<KNavigationBar title="Legal Notice"/>
|
||||
<div class="content">
|
||||
<h1 class="heading--1">
|
||||
Legal Notice
|
||||
|
@ -59,7 +58,6 @@
|
|||
class="link"
|
||||
href="https://datenschutz-generator.de/?l=de"
|
||||
rel="noopener"
|
||||
title="Rechtstext von Dr. Schwenke - für weitere Informationen bitte anklicken."
|
||||
target="_blank"
|
||||
>Erstellt mit kostenlosem Datenschutz-Generator.de von Dr. Thomas Schwenke</a>
|
||||
</div>
|
||||
|
@ -67,11 +65,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import KNavigationBar from "kiste/components/KNavigationBar.vue";
|
||||
|
||||
export default {
|
||||
name: "LegalNoticePage",
|
||||
components: { KNavigationBar },
|
||||
head: { htmlAttrs: { lang: "de" } }
|
||||
head: () => ({ htmlAttrs: { lang: "de" } })
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
<template>
|
||||
<div class="privacy-policy-page">
|
||||
<KNavigationBar title="Datenschutzerklärung"/>
|
||||
<div class="content">
|
||||
<h1 class="heading--1">
|
||||
<NavigationBar title="Datenschutz"/>
|
||||
<div class="content formatted">
|
||||
<h1>
|
||||
Datenschutzerklärung
|
||||
</h1>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Verantwortlicher im Sinne der Datenschutzgesetze, insbesondere der EU-Datenschutzgrundverordnung (DSGVO), ist:
|
||||
</p>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Moritz Ruth<br>
|
||||
Zum Galgenberg 19<br>
|
||||
66539 Neunkirchen<br>
|
||||
Deutschland
|
||||
</p>
|
||||
<h2 class="heading--2">
|
||||
<h2>
|
||||
Ihre Betroffenenrechte
|
||||
</h2>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Unter den angegebenen Kontaktdaten unseres Datenschutzbeauftragten können Sie jederzeit folgende Rechte ausüben:
|
||||
</p>
|
||||
<ul class="paragraph">
|
||||
<ul>
|
||||
<li>Auskunft über Ihre bei uns gespeicherten Daten und deren Verarbeitung (Art. 15 DSGVO),</li>
|
||||
<li>Berichtigung unrichtiger personenbezogener Daten (Art. 16 DSGVO),</li>
|
||||
<li>Löschung Ihrer bei uns gespeicherten Daten (Art. 17 DSGVO),</li>
|
||||
|
@ -34,260 +34,262 @@
|
|||
abgeschlossen haben (Art. 20 DSGVO).
|
||||
</li>
|
||||
</ul>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Sofern Sie uns eine Einwilligung erteilt haben, können Sie diese jederzeit mit Wirkung für die Zukunft
|
||||
widerrufen.
|
||||
</p>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Sie können sich jederzeit mit einer Beschwerde an eine Aufsichtsbehörde wenden, z. B. an die zuständige
|
||||
Aufsichtsbehörde des Bundeslands Ihres Wohnsitzes oder an die für uns als verantwortliche Stelle zuständige
|
||||
Behörde.
|
||||
</p>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Eine Liste der Aufsichtsbehörden (für den nichtöffentlichen Bereich) mit Anschrift finden Sie unter:
|
||||
<KExternalLink href="https://www.bfdi.bund.de/DE/Infothek/Anschriften_Links/anschriften_links-node.html"/>.
|
||||
<ExternalLink href="https://www.bfdi.bund.de/DE/Infothek/Anschriften_Links/anschriften_links-node.html"/>.
|
||||
</p>
|
||||
<h2 class="heading--2">
|
||||
<h2>
|
||||
Erfassung allgemeiner Informationen beim Besuch unserer Website
|
||||
</h2>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Art und Zweck der Verarbeitung
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Wenn Sie auf unsere Website zugreifen, d.h., wenn Sie sich nicht registrieren oder anderweitig Informationen
|
||||
übermitteln, werden automatisch Informationen allgemeiner Natur erfasst. Diese Informationen (Server-Logfiles)
|
||||
beinhalten etwa die Art des Webbrowsers, das verwendete Betriebssystem, den Domainnamen Ihres
|
||||
Internet-Service-Providers, Ihre IP-Adresse und ähnliches.
|
||||
</p>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Sie werden insbesondere zu folgenden Zwecken verarbeitet
|
||||
</p>
|
||||
<ul class="paragraph">
|
||||
<ul>
|
||||
<li>Sicherstellung eines problemlosen Verbindungsaufbaus der Website,</li>
|
||||
<li>Sicherstellung einer reibungslosen Nutzung unserer Website,</li>
|
||||
<li>Auswertung der Systemsicherheit und -stabilität sowie</li>
|
||||
<li>zu weiteren administrativen Zwecken.</li>
|
||||
</ul>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Wir verwenden Ihre Daten nicht, um Rückschlüsse auf Ihre Person zu ziehen. Informationen dieser Art werden von
|
||||
uns ggfs. statistisch ausgewertet, um unseren Internetauftritt und die dahinterstehende Technik zu
|
||||
optimieren.
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Rechtsgrundlage
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Die Verarbeitung erfolgt gemäß Art. 6 Abs. 1 lit. f DSGVO auf Basis unseres berechtigten Interesses an der
|
||||
Verbesserung der Stabilität und Funktionalität unserer Website.
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Empfänger
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Empfänger der Daten sind ggf. technische Dienstleister, die für den Betrieb und die Wartung unserer Webseite als
|
||||
Auftragsverarbeiter tätig werden.
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Speicherdauer
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Die Daten werden gelöscht, sobald diese für den Zweck der Erhebung nicht mehr erforderlich sind. Dies ist für
|
||||
die Daten, die der Bereitstellung der Webseite dienen, grundsätzlich der Fall, wenn die jeweilige Sitzung
|
||||
beendet ist.
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Bereitstellung vorgeschrieben oder erforderlich
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Die Bereitstellung der vorgenannten personenbezogenen Daten ist weder gesetzlich noch vertraglich
|
||||
vorgeschrieben. Ohne die IP-Adresse ist jedoch der Dienst und die Funktionsfähigkeit unserer Website nicht
|
||||
gewährleistet. Zudem können einzelne Dienste und Services nicht verfügbar oder eingeschränkt sein. Aus diesem
|
||||
Grund ist ein Widerspruch ausgeschlossen.
|
||||
</p>
|
||||
<h2 class="heading--2">
|
||||
<h2>
|
||||
Verwendung von Scriptbibliotheken (Google Webfonts)
|
||||
</h2>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Art und Zweck der Verarbeitung
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Um unsere Inhalte browserübergreifend korrekt und grafisch ansprechend darzustellen, verwenden wir auf dieser
|
||||
Website „Google Web Fonts“ der Google LLC (1600 Amphitheatre Parkway, Mountain View, CA 94043, USA; nachfolgend
|
||||
„Google“) zur Darstellung von Schriften.
|
||||
</p>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Die Datenschutzrichtlinie des Bibliothekbetreibers Google finden Sie hier:
|
||||
<KExternalLink href="https://www.google.com/policies/privacy/"/>
|
||||
<ExternalLink href="https://www.google.com/policies/privacy/"/>
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Rechtsgrundlage
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Rechtsgrundlage für die Einbindung von Google Webfonts und dem damit verbundenen Datentransfer zu Google ist
|
||||
Ihre Einwilligung (Art. 6 Abs. 1 lit. a DSGVO).
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Empfänger
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Der Aufruf von Scriptbibliotheken oder Schriftbibliotheken löst automatisch eine Verbindung zum Betreiber der
|
||||
Bibliothek aus. Dabei ist es theoretisch möglich – aktuell allerdings auch unklar ob und ggf. zu welchen Zwecken
|
||||
– dass der Betreiber in diesem Fall Google Daten erhebt.
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Speicherdauer
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Wir erheben keine personenbezogenen Daten, durch die Einbindung von Google Webfonts.
|
||||
</p>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Weitere Informationen zu Google Web Fonts finden Sie unter
|
||||
<KExternalLink href="https://developers.google.com/fonts/faq"/> und in der Datenschutzerklärung von Google:
|
||||
<KExternalLink href="https://www.google.com/policies/privacy/"/>.
|
||||
<ExternalLink href="https://developers.google.com/fonts/faq"/> und in der Datenschutzerklärung von Google:
|
||||
<ExternalLink href="https://www.google.com/policies/privacy/"/>.
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Drittlandtransfer
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Google verarbeitet Ihre Daten in den USA und hat sich dem EU_US Privacy Shield unterworfen
|
||||
<KExternalLink href="https://www.privacyshield.gov/EU-US-Framework"/>.
|
||||
<ExternalLink href="https://www.privacyshield.gov/EU-US-Framework"/>.
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Bereitstellung vorgeschrieben oder erforderlich
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Die Bereitstellung der personenbezogenen Daten ist weder gesetzlich, noch vertraglich vorgeschrieben. Allerdings
|
||||
kann ggfs. die korrekte Darstellung der Inhalte durch Standardschriften nicht möglich sein.
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Widerruf der Einwilligung
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Zur Darstellung der Inhalte wird regelmäßig die Programmiersprache JavaScript verwendet. Sie können der
|
||||
Datenverarbeitung daher widersprechen, indem Sie die Ausführung von JavaScript in Ihrem Browser deaktivieren
|
||||
oder einen JavaScript-Blocker installieren. Bitte beachten Sie, dass es hierdurch zu Funktionseinschränkungen
|
||||
auf der Website kommen kann.
|
||||
</p>
|
||||
<h2 class="heading--2">
|
||||
<h2>
|
||||
Eingebettete YouTube-Videos
|
||||
</h2>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Art und Zweck der Verarbeitung
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Auf einigen unserer Webseiten betten wir YouTube-Videos ein. Betreiber der entsprechenden Plugins ist die
|
||||
YouTube, LLC, 901 Cherry Ave., San Bruno, CA 94066, USA (nachfolgend „YouTube“). Wenn Sie eine Seite mit dem
|
||||
YouTube-Plugin besuchen, wird eine Verbindung zu Servern von YouTube hergestellt. Dabei wird YouTube mitgeteilt,
|
||||
welche Seiten Sie besuchen. Wenn Sie in Ihrem YouTube-Account eingeloggt sind, kann YouTube Ihr Surfverhalten
|
||||
Ihnen persönlich zuzuordnen. Dies verhindern Sie, indem Sie sich vorher aus Ihrem YouTube-Account ausloggen.
|
||||
</p>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Wird ein YouTube-Video gestartet, setzt der Anbieter Cookies ein, die Hinweise über das Nutzerverhalten sammeln.
|
||||
</p>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Weitere Informationen zu Zweck und Umfang der Datenerhebung und ihrer Verarbeitung durch YouTube erhalten Sie in
|
||||
den Datenschutzerklärungen des Anbieters, Dort erhalten Sie auch weitere Informationen zu Ihren diesbezüglichen
|
||||
Rechten und Einstellungsmöglichkeiten zum Schutze Ihrer Privatsphäre
|
||||
(<KExternalLink href="https://policies.google.com/privacy"/>). Google verarbeitet Ihre Daten in den USA und hat
|
||||
sich dem EU-US Privacy Shield unterworfen <KExternalLink href="https://www.privacyshield.gov/EU-US-Framework"/>.
|
||||
(<ExternalLink href="https://policies.google.com/privacy"/>). Google verarbeitet Ihre Daten in den USA und hat
|
||||
sich dem EU-US Privacy Shield unterworfen <ExternalLink href="https://www.privacyshield.gov/EU-US-Framework"/>.
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Rechtsgrundlage
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Rechtsgrundlage für die Einbindung von YouTube und dem damit verbundenen Datentransfer zu Google ist Ihre
|
||||
Einwilligung (Art. 6 Abs. 1 lit. a DSGVO).
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Empfänger
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Der Aufruf von YouTube löst automatisch eine Verbindung zu Google aus.
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Speicherdauer und Widerruf der Einwilligung:
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Wer das Speichern von Cookies für das Google-Ad-Programm deaktiviert hat, wird auch beim Anschauen von
|
||||
YouTube-Videos mit keinen solchen Cookies rechnen müssen. YouTube legt aber auch in anderen Cookies
|
||||
nicht-personenbezogene Nutzungsinformationen ab. Möchten Sie dies verhindern, so müssen Sie das Speichern von
|
||||
Cookies im Browser blockieren.
|
||||
</p>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Weitere Informationen zum Datenschutz bei „YouTube“ finden Sie in der Datenschutzerklärung des Anbieters unter:
|
||||
<KExternalLink href="https://www.google.de/intl/de/policies/privacy/"/>
|
||||
<ExternalLink href="https://www.google.de/intl/de/policies/privacy/"/>
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Drittlandtransfer
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Google verarbeitet Ihre Daten in den USA und hat sich dem EU_US Privacy Shield unterworfen
|
||||
<KExternalLink href="https://www.privacyshield.gov/EU-US-Framework"/>.
|
||||
<ExternalLink href="https://www.privacyshield.gov/EU-US-Framework"/>.
|
||||
</p>
|
||||
<h3 class="heading--3">
|
||||
<h3>
|
||||
Bereitstellung vorgeschrieben oder erforderlich
|
||||
</h3>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Die Bereitstellung Ihrer personenbezogenen Daten erfolgt freiwillig, allein auf Basis Ihrer Einwilligung. Sofern
|
||||
Sie den Zugriff unterbinden, kann es hierdurch zu Funktionseinschränkungen auf der Website kommen.
|
||||
</p>
|
||||
<h2 class="heading--2">
|
||||
<h2>
|
||||
SSL-Verschlüsselung
|
||||
</h2>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Um die Sicherheit Ihrer Daten bei der Übertragung zu schützen, verwenden wir dem aktuellen Stand der Technik
|
||||
entsprechende Verschlüsselungsverfahren (z. B. SSL) über HTTPS.
|
||||
</p>
|
||||
<h2 class="heading--2">
|
||||
<h2>
|
||||
Änderung unserer Datenschutzbestimmungen
|
||||
</h2>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Wir behalten uns vor, diese Datenschutzerklärung anzupassen, damit sie stets den aktuellen rechtlichen
|
||||
Anforderungen entspricht oder um Änderungen unserer Leistungen in der Datenschutzerklärung umzusetzen, z.B. bei
|
||||
der Einführung neuer Services. Für Ihren erneuten Besuch gilt dann die neue Datenschutzerklärung.
|
||||
</p>
|
||||
<h2 class="heading--2">
|
||||
<h2>
|
||||
Fragen an den Datenschutzbeauftragten
|
||||
</h2>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Wenn Sie Fragen zum Datenschutz haben, schreiben Sie uns bitte eine E-Mail oder wenden Sie sich direkt an die
|
||||
für den Datenschutz verantwortliche Person:
|
||||
</p>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Moritz Ruth<br>
|
||||
Zum Galgenberg 19<br>
|
||||
66539 Neunkirchen<br>
|
||||
Deutschland
|
||||
</p>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
<b>Telefon</b>: +49 176 46146329
|
||||
</p>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
<b>E-Mail</b>: <a class="link" href="mailto:legal@moritz-ruth.de">legal@moritz-ruth.de</a>
|
||||
</p>
|
||||
<p class="paragraph">
|
||||
<p>
|
||||
Die Datenschutzerklärung wurde mithilfe der activeMind AG erstellt, den Experten für
|
||||
<KExternalLink
|
||||
<ExternalLink
|
||||
href="https://www.activemind.de/datenschutz/datenschutzhinweis-generator/"
|
||||
>
|
||||
externe Datenschutzbeauftragte
|
||||
</KExternalLink> (Version #2019-04-10).
|
||||
</ExternalLink>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.privacy-policy-page {
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import KNavigationBar from "kiste/components/KNavigationBar.vue";
|
||||
import KExternalLink from "kiste/components/KExternalLink.vue";
|
||||
import ExternalLink from "@/components/ExternalLink";
|
||||
import NavigationBar from "@/components/NavigationBar";
|
||||
|
||||
export default {
|
||||
name: "PrivacyPolicyPage",
|
||||
components: { KNavigationBar, KExternalLink },
|
||||
head: { htmlAttrs: { lang: "de" } }
|
||||
components: { NavigationBar, ExternalLink },
|
||||
head: () => ({ htmlAttrs: { lang: "de" } })
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
<template>
|
||||
<div class="projects-page">
|
||||
<KNavigationBar title="Projects" background-after-scroll/>
|
||||
<div class="content">
|
||||
<h1 class="heading--1">
|
||||
Projects
|
||||
</h1>
|
||||
<GProject
|
||||
type="JavaScript library"
|
||||
title="shaped.js"
|
||||
github="moritzruth/shaped.js"
|
||||
npm="shaped.js"
|
||||
>
|
||||
Generate beautiful moving shapes using a canvas element which can for example be used for backgrounds.
|
||||
See it in action <nuxt-link class="link" to="/">on the home page</nuxt-link>.
|
||||
Every time you reload the page, it shows another variation.
|
||||
</GProject>
|
||||
<GProject
|
||||
type="App (english)"
|
||||
title="RelaxYourEyes"
|
||||
link="https://relaxyoureyes.moritz-ruth.de"
|
||||
github="moritzruth/relaxyoureyes"
|
||||
>
|
||||
A timer which reminds you to relax your eyes every 20 minutes.
|
||||
</GProject>
|
||||
<GProject
|
||||
type="App (german)"
|
||||
title="Schweredruck-Simulation"
|
||||
link="https://app.moritz-ruth.de/schweredruck-simulation"
|
||||
github="moritzruth/schweredruck-simulation"
|
||||
>
|
||||
Simulate and calculate the hydrostatic pressure in different liquids.
|
||||
</GProject>
|
||||
<GProject
|
||||
type="Website"
|
||||
title="cryptic-game.net"
|
||||
link="https://test.cryptic-game.net"
|
||||
github="cryptic-game/website/tree/vueWebsite"
|
||||
>
|
||||
I am responsible for the new (not yet released) website of the
|
||||
<a class="link" href="https://github.com/cryptic-game">Cryptic game</a>.
|
||||
(Just the website, not the game itself.)
|
||||
</GProject>
|
||||
<GProject
|
||||
type="Vue.js/Nuxt.js plugin"
|
||||
title="Kiste"
|
||||
github="moritzruth/kiste"
|
||||
npm="kiste"
|
||||
>
|
||||
A collection of UI components and styles used for example by this website.
|
||||
</GProject>
|
||||
<GProject
|
||||
type="Node.js Library"
|
||||
title="log-groups"
|
||||
github="moritzruth/log-groups"
|
||||
npm="log-groups"
|
||||
>
|
||||
A pretty console logging library for printing grouped messages with Node.js.
|
||||
</GProject>
|
||||
<GProject
|
||||
type="Node.js script"
|
||||
title="Zimmerlampe"
|
||||
github="moritzruth/zimmerlampe"
|
||||
>
|
||||
A simple script I use for controlling my Stairville CLB4 as room lamp.
|
||||
</GProject>
|
||||
<GProject
|
||||
type="Node.js library"
|
||||
title="node-enttec-open-dmx-usb"
|
||||
github="moritzruth/node-enttec-open-dmx-usb"
|
||||
>
|
||||
A Node.js library for sending DMX data through the
|
||||
<a class="link" href="https://www.enttec.co.uk/en/product/controls/dmx-usb-interfaces/open-dmx-usb/">
|
||||
Enttec Open DMX USB Interface</a>.
|
||||
</GProject>
|
||||
<GProject
|
||||
type="Configuration"
|
||||
title="eslint-config"
|
||||
github="moritzruth/eslint-config"
|
||||
npm="@moritzruth/eslint-config"
|
||||
>
|
||||
My personal ESLint configuration I use in all of my projects.
|
||||
</GProject>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import KNavigationBar from "kiste/components/KNavigationBar.vue";
|
||||
import GProject from "@/components/pages/projects/GProject.vue";
|
||||
|
||||
export default {
|
||||
name: "ProjectsPage",
|
||||
components: { GProject, KNavigationBar }
|
||||
};
|
||||
</script>
|
|
@ -8,6 +8,7 @@ module.exports = {
|
|||
alias: {
|
||||
"@": path.resolve(__dirname),
|
||||
"~": path.resolve(__dirname)
|
||||
}
|
||||
},
|
||||
extensions: [".vue", ".js"]
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue