Compare commits
66 commits
Author | SHA1 | Date | |
---|---|---|---|
|
2830fd9350 | ||
|
986126a2bb | ||
|
3d13c256e3 | ||
|
f67ba1e655 | ||
|
1553825719 | ||
|
f1961f5cef | ||
|
a67cddc74b | ||
|
68a43b6b9c | ||
|
f2b408fce5 | ||
|
35f5343149 | ||
|
b5eae39f89 | ||
|
39993112b7 | ||
|
d4dc47b1e1 | ||
|
25ee71b615 | ||
|
23a24c5842 | ||
|
29f549ddd9 | ||
|
223a32580c | ||
|
3723e4b036 | ||
|
b57c5a9be8 | ||
|
bb724f581b | ||
|
44d974a870 | ||
|
3c83d66bf5 | ||
|
79f1191e36 | ||
|
8f4c1fc7d9 | ||
|
cf4155784c | ||
|
0b952dd789 | ||
|
da0d919267 | ||
|
d7e3ae07b5 | ||
|
7fc87ea224 | ||
|
f5117882f8 | ||
|
7da03fc635 | ||
|
2382405651 | ||
|
20c7a512ed | ||
|
b3a288e281 | ||
|
21ed580988 | ||
|
dbb317e19a | ||
|
87dd1649b9 | ||
|
53c2987dcf | ||
|
d826d278a5 | ||
|
b87e84e5ce | ||
|
d72c3efeeb | ||
|
9757a3d076 | ||
|
eefc4cc8d4 | ||
|
7fe9cdeef3 | ||
|
9536a6fa77 | ||
|
8ea083c6d1 | ||
|
c258de84a2 | ||
|
8e04a3430a | ||
|
fb9cfd0783 | ||
|
65bd7bde60 | ||
|
69182778c9 | ||
|
1707b6f4af | ||
|
df3014e2a5 | ||
|
f7ae855221 | ||
|
35d5232575 | ||
|
065dc62cfb | ||
|
8f4a5f5c98 | ||
|
72cc7afe71 | ||
|
f881c1aac1 | ||
|
538d09f7be | ||
|
e4be1debf9 | ||
|
c646988dcf | ||
|
f7ceadfb83 | ||
|
7f89b30e63 | ||
|
2e12833d4b | ||
|
afe81f2bbb |
50 changed files with 3169 additions and 4617 deletions
2
.browserslistrc
Normal file
2
.browserslistrc
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
last 2 versions
|
||||||
|
not dead
|
|
@ -1,15 +1,15 @@
|
||||||
/* eslint-env node */
|
/* eslint-env node */
|
||||||
require("@rushstack/eslint-patch/modern-module-resolution");
|
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
extends: [
|
'extends': [
|
||||||
"plugin:vue/vue3-essential",
|
'plugin:vue/vue3-essential',
|
||||||
"eslint:recommended",
|
'eslint:recommended',
|
||||||
"@vue/eslint-config-typescript",
|
'@vue/eslint-config-typescript',
|
||||||
"@vue/eslint-config-prettier",
|
'@vue/eslint-config-prettier/skip-formatting'
|
||||||
],
|
],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: "latest",
|
ecmaVersion: 'latest'
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1 +1,8 @@
|
||||||
{}
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/prettierrc",
|
||||||
|
"semi": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 100,
|
||||||
|
"trailingComma": "none"
|
||||||
|
}
|
||||||
|
|
22
card/index.html
Normal file
22
card/index.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Document</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="flex">
|
||||||
|
<div class="image">
|
||||||
|
<img src="../src/assets/viktor-avatar.png" alt="Sebin Smug Icon" />
|
||||||
|
</div>
|
||||||
|
<div class="headings">
|
||||||
|
<h1>Viktor Kraastav</h1>
|
||||||
|
<h2>Character Reference Page</h2>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<!-- Open in browser and set mobile view to 2048x1072 with DPR: 2 and take screenshot -->
|
||||||
|
</body>
|
||||||
|
</html>
|
105
card/style.css
Normal file
105
card/style.css
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
@import "../src/assets/fonts/secular-one/secular-one.css";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #f8ebdd;
|
||||||
|
|
||||||
|
--theme-c-walnut-brown: #422322;
|
||||||
|
--theme-c-desert-sand-translucent: #e7c7b1bf;
|
||||||
|
|
||||||
|
--container-box-shadow: 1vw 1vw 4vw rgba(0, 0, 0, 0.7);
|
||||||
|
--welcome-header-mainline-font-size: 7vw;
|
||||||
|
--welcome-header-subline-font-size: 3.625vw;
|
||||||
|
|
||||||
|
--color-background: #2d4c5a;
|
||||||
|
--page-background: url(../src/assets/layered-waves-dark.svg);
|
||||||
|
--page-background-image-height: 100vw;
|
||||||
|
--page-background-image-max-height: 80vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--color-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
body::after {
|
||||||
|
background: var(--page-background);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center bottom;
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
content: "";
|
||||||
|
position: sticky;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
height: var(--page-background-image-height);
|
||||||
|
max-height: var(--page-background-image-max-height);
|
||||||
|
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex > * {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
flex: 0 0 60vh;
|
||||||
|
max-height: 100vh;
|
||||||
|
|
||||||
|
background-color: var(--theme-c-desert-sand-translucent);
|
||||||
|
|
||||||
|
border: 1vw solid var(--theme-c-walnut-brown);
|
||||||
|
border-radius: 4.5vw;
|
||||||
|
|
||||||
|
transform: scale(0.84) translateX(15%) rotateZ(calc(-1 * (11 * 1deg)));
|
||||||
|
transform-origin: 0 100%;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image img {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
transform: scale(1.2) rotateZ(calc(11 * 1deg));
|
||||||
|
}
|
||||||
|
|
||||||
|
.headings {
|
||||||
|
flex: 0 0 55vw;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headings :where(h1, h2) {
|
||||||
|
font-family: "Secular One", sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headings h1 {
|
||||||
|
font-size: var(--welcome-header-mainline-font-size);
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headings h2 {
|
||||||
|
font-size: var(--welcome-header-subline-font-size);
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
16
index.html
16
index.html
|
@ -2,8 +2,8 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="favicon" href="favicon.png" type="image/png" />
|
<link rel="favicon" href="/favicon.png" type="image/png" />
|
||||||
<link rel="icon" href="favicon.png" type="image/png" />
|
<link rel="icon" href="/favicon.png" type="image/png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||||
<title>Viktor Kraastav – Reference Page</title>
|
<title>Viktor Kraastav – Reference Page</title>
|
||||||
|
|
||||||
|
@ -17,11 +17,13 @@
|
||||||
<meta name="twitter:description" content="The official reference page for Viktor Kraastav with picture references and in-depth character descriptions" />
|
<meta name="twitter:description" content="The official reference page for Viktor Kraastav with picture references and in-depth character descriptions" />
|
||||||
<meta name="twitter:image" content="https://ref.sebin-nyshkim.net/viktor/preview.png" />
|
<meta name="twitter:image" content="https://ref.sebin-nyshkim.net/viktor/preview.png" />
|
||||||
|
|
||||||
<meta name="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta name="og:title" content="Viktor Kraastav - Reference Page" />
|
<meta property="og:title" content="Viktor Kraastav - Reference Page" />
|
||||||
<meta name="og:url" content="https://ref.sebin-nyshkim.net/viktor/" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta name="og:image" content="https://ref.sebin-nyshkim.net/viktor/preview.png" />
|
<meta property="og:locale:alternate" content="de_DE" />
|
||||||
<meta name="og:description" content="The official reference page for Viktor Kraastav with picture references and in-depth character descriptions" />
|
<meta property="og:url" content="https://ref.sebin-nyshkim.net/viktor/" />
|
||||||
|
<meta property="og:image" content="https://ref.sebin-nyshkim.net/viktor/preview.png" />
|
||||||
|
<meta property="og:description" content="The official reference page for Viktor Kraastav with picture references and in-depth character descriptions" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
|
5426
package-lock.json
generated
5426
package-lock.json
generated
File diff suppressed because it is too large
Load diff
45
package.json
45
package.json
|
@ -1,35 +1,38 @@
|
||||||
{
|
{
|
||||||
"name": "viktor-reference",
|
"name": "viktor-reference",
|
||||||
"version": "0.3.1a",
|
"version": "0.4.3",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host",
|
"dev": "vite --host",
|
||||||
"build": "run-p type-check build-only",
|
"build": "run-p type-check build-only",
|
||||||
"preview": "vite preview --port 4173",
|
"preview": "vite preview",
|
||||||
"build-only": "vite build",
|
"build-only": "vite build",
|
||||||
"type-check": "vue-tsc --noEmit",
|
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
|
||||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||||
|
"format": "prettier --write src/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.3.4",
|
||||||
"vue-router": "^4.1.6"
|
"vue-i18n": "^9.2.2",
|
||||||
|
"vue-router": "^4.2.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rushstack/eslint-patch": "^1.2.0",
|
"@rushstack/eslint-patch": "^1.3.3",
|
||||||
"@types/node": "^18.11.18",
|
"@tsconfig/node18": "^18.2.1",
|
||||||
"@types/vue-markdown": "^2.2.1",
|
"@types/node": "^20.5.9",
|
||||||
"@vitejs/plugin-vue": "^4.0.0",
|
"@vitejs/plugin-vue": "^4.3.4",
|
||||||
"@vue/eslint-config-prettier": "^7.0.0",
|
"@vue/eslint-config-prettier": "^8.0.0",
|
||||||
"@vue/eslint-config-typescript": "^11.0.2",
|
"@vue/eslint-config-typescript": "^11.0.3",
|
||||||
"@vue/tsconfig": "^0.1.3",
|
"@vue/tsconfig": "^0.4.0",
|
||||||
"eslint": "^8.32.0",
|
"autoprefixer": "^10.4.15",
|
||||||
"eslint-plugin-vue": "^9.9.0",
|
"eslint": "^8.48.0",
|
||||||
|
"eslint-plugin-vue": "^9.17.0",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^2.8.3",
|
"prettier": "^3.0.3",
|
||||||
"sass": "^1.57.1",
|
"sass": "^1.66.1",
|
||||||
"typescript": "^4.9.4",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^4.0.4",
|
"vite": "^4.4.9",
|
||||||
"vite-imagetools": "^4.0.15",
|
"vite-imagetools": "^5.0.8",
|
||||||
"vue-tsc": "^1.0.24"
|
"vue-tsc": "^1.8.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
public/preview.png
Normal file
BIN
public/preview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 654 KiB |
69
src/App.vue
69
src/App.vue
|
@ -1,18 +1,69 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { RouterView } from "vue-router";
|
import { ref } from 'vue'
|
||||||
import SiteNavigation from "@/components/SiteNavigation.vue";
|
import { RouterView } from 'vue-router'
|
||||||
import SiteFooter from "@/components/SiteFooter.vue";
|
import { version } from '../package.json'
|
||||||
import { version } from "../package.json";
|
|
||||||
|
import ModalDialog from '@/components/ModalDialog.vue'
|
||||||
|
import LocaleSwitcher from '@/components/LocaleSwitcher.vue'
|
||||||
|
import LinkButton from '@/components/LinkButton.vue'
|
||||||
|
import SiteNavigation from '@/components/SiteNavigation.vue'
|
||||||
|
import NavigationItem from '@/components/NavigationItem.vue'
|
||||||
|
import LanguageButton from '@/components/LanguageButton.vue'
|
||||||
|
import SiteFooter from '@/components/SiteFooter.vue'
|
||||||
|
|
||||||
|
import LanguageIcon from '@/assets/icons/LanguageIcon.vue'
|
||||||
|
|
||||||
|
const locales = [
|
||||||
|
{ code: 'en', name: 'English', flag: '🇬🇧' },
|
||||||
|
{ code: 'de', name: 'Deutsch', flag: '🇩🇪' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const langswitcher = ref<InstanceType<typeof ModalDialog>>()
|
||||||
|
|
||||||
|
const showModal = () => {
|
||||||
|
langswitcher.value?.showModal()
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
langswitcher.value?.close()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<SiteNavigation />
|
<ModalDialog id="lang-select" ref="langswitcher">
|
||||||
|
<template #heading>{{ $t('langswitcher.title') }}</template>
|
||||||
|
<template #message>
|
||||||
|
<p>{{ $t('langswitcher.prompt') }}</p>
|
||||||
|
<LocaleSwitcher id="locale-switch" v-model="$i18n.locale" :locales="locales" />
|
||||||
|
</template>
|
||||||
|
<template #buttons>
|
||||||
|
<LinkButton @click.prevent="close()">
|
||||||
|
{{ $t('langswitcher.buttonClose') }}
|
||||||
|
</LinkButton>
|
||||||
|
</template>
|
||||||
|
</ModalDialog>
|
||||||
|
|
||||||
|
<SiteNavigation>
|
||||||
|
<NavigationItem
|
||||||
|
v-for="(route, idx) in $router.getRoutes()"
|
||||||
|
:key="idx"
|
||||||
|
:icon="route.meta?.icon"
|
||||||
|
:href="route.path"
|
||||||
|
>
|
||||||
|
{{ $t(`${route.meta?.title}`) }}
|
||||||
|
</NavigationItem>
|
||||||
|
</SiteNavigation>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<RouterView />
|
<LanguageButton>
|
||||||
|
<LanguageIcon @click.prevent="showModal()" />
|
||||||
|
</LanguageButton>
|
||||||
|
<RouterView v-slot="{ Component }">
|
||||||
|
<Transition name="fade" mode="out-in">
|
||||||
|
<component :is="Component" :key="$route.path" />
|
||||||
|
</Transition>
|
||||||
|
</RouterView>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<SiteFooter>
|
<SiteFooter>v{{ version }} © {{ new Date().getFullYear() }} Sebin Nyshkim</SiteFooter>
|
||||||
v{{ version }} © {{ new Date().getFullYear() }} Sebin Nyshkim
|
|
||||||
</SiteFooter>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
<template>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg>
|
|
||||||
</template>
|
|
3
src/assets/icons/CircleCheckIcon.vue
Normal file
3
src/assets/icons/CircleCheckIcon.vue
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209c9.4-9.4 9.4-24.6 0-33.9s-24.6-9.4-33.9 0l-111 111-47-47c-9.4-9.4-24.6-9.4-33.9 0s-9.4 24.6 0 33.9l64 64c9.4 9.4 24.6 9.4 33.9 0L369 209z"/></svg>
|
||||||
|
</template>
|
3
src/assets/icons/CircleIcon.vue
Normal file
3
src/assets/icons/CircleIcon.vue
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M464 256A208 208 0 1 0 48 256a208 208 0 1 0 416 0zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256z"/></svg>
|
||||||
|
</template>
|
3
src/assets/icons/FurAffinityIcon.vue
Normal file
3
src/assets/icons/FurAffinityIcon.vue
Normal file
File diff suppressed because one or more lines are too long
3
src/assets/icons/IndustryIcon.vue
Normal file
3
src/assets/icons/IndustryIcon.vue
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M32 32C14.3 32 0 46.3 0 64V304v48 80c0 26.5 21.5 48 48 48H464c26.5 0 48-21.5 48-48V304 152.2c0-18.2-19.4-29.7-35.4-21.1L320 215.4V152.2c0-18.2-19.4-29.7-35.4-21.1L128 215.4V64c0-17.7-14.3-32-32-32H32z"/></svg>
|
||||||
|
</template>
|
3
src/assets/icons/LanguageIcon.vue
Normal file
3
src/assets/icons/LanguageIcon.vue
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M0 128C0 92.7 28.7 64 64 64H256h48 16H576c35.3 0 64 28.7 64 64V384c0 35.3-28.7 64-64 64H320 304 256 64c-35.3 0-64-28.7-64-64V128zm320 0V384H576V128H320zM178.3 175.9c-3.2-7.2-10.4-11.9-18.3-11.9s-15.1 4.7-18.3 11.9l-64 144c-4.5 10.1 .1 21.9 10.2 26.4s21.9-.1 26.4-10.2l8.9-20.1h73.6l8.9 20.1c4.5 10.1 16.3 14.6 26.4 10.2s14.6-16.3 10.2-26.4l-64-144zM160 233.2L179 276H141l19-42.8zM448 164c11 0 20 9 20 20v4h44 16c11 0 20 9 20 20s-9 20-20 20h-2l-1.6 4.5c-8.9 24.4-22.4 46.6-39.6 65.4c.9 .6 1.8 1.1 2.7 1.6l18.9 11.3c9.5 5.7 12.5 18 6.9 27.4s-18 12.5-27.4 6.9l-18.9-11.3c-4.5-2.7-8.8-5.5-13.1-8.5c-10.6 7.5-21.9 14-34 19.4l-3.6 1.6c-10.1 4.5-21.9-.1-26.4-10.2s.1-21.9 10.2-26.4l3.6-1.6c6.4-2.9 12.6-6.1 18.5-9.8l-12.2-12.2c-7.8-7.8-7.8-20.5 0-28.3s20.5-7.8 28.3 0l14.6 14.6 .5 .5c12.4-13.1 22.5-28.3 29.8-45H448 376c-11 0-20-9-20-20s9-20 20-20h52v-4c0-11 9-20 20-20z"/></svg>
|
||||||
|
</template>
|
|
@ -1,3 +0,0 @@
|
||||||
<template>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M226.5 92.9c14.3 42.9-.3 86.2-32.6 96.8s-70.1-15.6-84.4-58.5s.3-86.2 32.6-96.8s70.1 15.6 84.4 58.5zM100.4 198.6c18.9 32.4 14.3 70.1-10.2 84.1s-59.7-.9-78.5-33.3S-2.7 179.3 21.8 165.3s59.7 .9 78.5 33.3zM69.2 401.2C121.6 259.9 214.7 224 256 224s134.4 35.9 186.8 177.2c3.6 9.7 5.2 20.1 5.2 30.5v1.6c0 25.8-20.9 46.7-46.7 46.7c-11.5 0-22.9-1.4-34-4.2l-88-22c-15.3-3.8-31.3-3.8-46.6 0l-88 22c-11.1 2.8-22.5 4.2-34 4.2C84.9 480 64 459.1 64 433.3v-1.6c0-10.4 1.6-20.8 5.2-30.5zM421.8 282.7c-24.5-14-29.1-51.7-10.2-84.1s54-47.3 78.5-33.3s29.1 51.7 10.2 84.1s-54 47.3-78.5 33.3zM310.1 189.7c-32.3-10.6-46.9-53.9-32.6-96.8s52.1-69.1 84.4-58.5s46.9 53.9 32.6 96.8s-52.1 69.1-84.4 58.5z"/></svg>
|
|
||||||
</template>
|
|
3
src/assets/icons/TruckIcon.vue
Normal file
3
src/assets/icons/TruckIcon.vue
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M48 0C21.5 0 0 21.5 0 48V368c0 26.5 21.5 48 48 48H64c0 53 43 96 96 96s96-43 96-96H384c0 53 43 96 96 96s96-43 96-96h32c17.7 0 32-14.3 32-32s-14.3-32-32-32V288 256 237.3c0-17-6.7-33.3-18.7-45.3L512 114.7c-12-12-28.3-18.7-45.3-18.7H416V48c0-26.5-21.5-48-48-48H48zM416 160h50.7L544 237.3V256H416V160zM112 416a48 48 0 1 1 96 0 48 48 0 1 1 -96 0zm368-48a48 48 0 1 1 0 96 48 48 0 1 1 0-96z"/></svg>
|
||||||
|
</template>
|
|
@ -1,39 +1,39 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import PawIcon from "@/assets/icons/PawIcon.vue";
|
import FAIcon from '@/assets/icons/FurAffinityIcon.vue'
|
||||||
import TwitterIcon from "@/assets/icons/TwitterIcon.vue";
|
import TwitterIcon from '@/assets/icons/TwitterIcon.vue'
|
||||||
|
|
||||||
interface ArtistLink {
|
interface ArtistLink {
|
||||||
furaffinity?: string;
|
furaffinity?: string
|
||||||
twitter?: string;
|
twitter?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Attribution {
|
interface Attribution {
|
||||||
artwork: string;
|
artwork: string
|
||||||
artist: string;
|
artist: string
|
||||||
links: ArtistLink;
|
links: ArtistLink
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
attributions: Attribution[];
|
attributions: Attribution[]
|
||||||
}
|
}
|
||||||
|
|
||||||
defineProps<Props>();
|
defineProps<Props>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<table class="attribution-table">
|
<table class="attribution-table">
|
||||||
<thead class="attribution-table__head">
|
<thead class="attribution-table__head">
|
||||||
<tr class="attribution-table__row">
|
<tr class="attribution-table__row">
|
||||||
<th class="attribution-table__heading artwork">Artwork</th>
|
<th class="attribution-table__heading artwork">
|
||||||
<th class="attribution-table__heading artist">Artist</th>
|
{{ $t('attributions.artwork.headings[0]') }}
|
||||||
|
</th>
|
||||||
|
<th class="attribution-table__heading artist">
|
||||||
|
{{ $t('attributions.artwork.headings[1]') }}
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="attribution-table__body">
|
<tbody class="attribution-table__body">
|
||||||
<tr
|
<tr class="attribution-table__row" v-for="(attrib, idx) in attributions" :key="idx">
|
||||||
class="attribution-table__row"
|
|
||||||
v-for="(attrib, idx) in attributions"
|
|
||||||
:key="idx"
|
|
||||||
>
|
|
||||||
<td class="attribution-table__cell artwork">
|
<td class="attribution-table__cell artwork">
|
||||||
<img :src="attrib.artwork" alt="Image attribution" />
|
<img :src="attrib.artwork" alt="Image attribution" />
|
||||||
</td>
|
</td>
|
||||||
|
@ -44,7 +44,7 @@ defineProps<Props>();
|
||||||
<ul>
|
<ul>
|
||||||
<li v-if="attrib.links.furaffinity">
|
<li v-if="attrib.links.furaffinity">
|
||||||
<a :href="attrib.links.furaffinity" title="Fur Affinity">
|
<a :href="attrib.links.furaffinity" title="Fur Affinity">
|
||||||
<PawIcon />
|
<FAIcon />
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="attrib.links.twitter">
|
<li v-if="attrib.links.twitter">
|
||||||
|
@ -72,6 +72,7 @@ defineProps<Props>();
|
||||||
|
|
||||||
&.artist {
|
&.artist {
|
||||||
p {
|
p {
|
||||||
|
margin: 0.5rem 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +80,7 @@ defineProps<Props>();
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -87,12 +89,43 @@ defineProps<Props>();
|
||||||
|
|
||||||
li {
|
li {
|
||||||
flex: 0 0 2rem;
|
flex: 0 0 2rem;
|
||||||
max-width: 2rem;
|
display: flex;
|
||||||
padding: 0 0.5rem;
|
flex-flow: row wrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
background: var(--color-artist-link-background);
|
||||||
|
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
margin: 0 0.25rem;
|
||||||
|
border-radius: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding: 0.375rem;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: var(--color-artist-link-icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__row {
|
||||||
|
&:hover {
|
||||||
|
li {
|
||||||
|
background: var(--color-artist-link-background-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: var(--color-artist-link-icon-hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,30 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface ColorDict {
|
interface ColorDict {
|
||||||
name: string;
|
name: string
|
||||||
value: string;
|
value: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
colors: ColorDict[];
|
colors: ColorDict[]
|
||||||
}
|
}
|
||||||
|
|
||||||
defineProps<Props>();
|
defineProps<Props>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<table class="color-table">
|
<table class="color-table">
|
||||||
<thead class="color-table__head">
|
<thead class="color-table__head">
|
||||||
<tr class="color-table__row">
|
<tr class="color-table__row">
|
||||||
<th class="color-table__heading name">Body part</th>
|
<th class="color-table__heading name">{{ $t('data.colors.headings[0]') }}</th>
|
||||||
<th class="color-table__heading value">Value</th>
|
<th class="color-table__heading value">{{ $t('data.colors.headings[1]') }}</th>
|
||||||
<th class="color-table__heading color">Color</th>
|
<th class="color-table__heading color">{{ $t('data.colors.headings[2]') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="color-table__body">
|
<tbody class="color-table__body">
|
||||||
<tr class="color-table__row" v-for="(color, idx) in colors" :key="idx">
|
<tr class="color-table__row" v-for="(color, idx) in colors" :key="idx">
|
||||||
<td class="color-table__cell name">{{ color.name }}</td>
|
<td class="color-table__cell name">{{ $t(color.name) }}</td>
|
||||||
<td class="color-table__cell value">{{ color.value }}</td>
|
<td class="color-table__cell value">{{ color.value }}</td>
|
||||||
<td
|
<td class="color-table__cell color" :style="{ 'background-color': color.value }"></td>
|
||||||
class="color-table__cell color"
|
|
||||||
:style="{ 'background-color': color.value }"
|
|
||||||
></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -47,8 +44,16 @@ defineProps<Props>();
|
||||||
}
|
}
|
||||||
|
|
||||||
&.value {
|
&.value {
|
||||||
font-family: Menlo, JetBrains Mono, Source Code Pro, Monaco, Ubuntu Mono,
|
font-family:
|
||||||
Roboto Mono, Cascadia Code, Consolas, monospace;
|
Menlo,
|
||||||
|
JetBrains Mono,
|
||||||
|
Source Code Pro,
|
||||||
|
Monaco,
|
||||||
|
Ubuntu Mono,
|
||||||
|
Roboto Mono,
|
||||||
|
Cascadia Code,
|
||||||
|
Consolas,
|
||||||
|
monospace;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,25 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
headings: string[];
|
headings: string[]
|
||||||
data: string[][];
|
data: string[][]
|
||||||
}
|
}
|
||||||
|
|
||||||
defineProps<Props>();
|
defineProps<Props>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<table class="data-table">
|
<table class="data-table">
|
||||||
<thead class="data-table__head">
|
<thead class="data-table__head">
|
||||||
<tr class="data-table__row">
|
<tr class="data-table__row">
|
||||||
<th
|
<th class="data-table__heading" v-for="(heading, idx) in headings" :key="idx">
|
||||||
class="data-table__heading"
|
{{ $t(heading) }}
|
||||||
v-for="(heading, idx) in headings"
|
|
||||||
:key="idx"
|
|
||||||
>
|
|
||||||
{{ heading }}
|
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="data-table__body">
|
<tbody class="data-table__body">
|
||||||
<tr class="data-table__row" v-for="(row, idx) in data" :key="idx">
|
<tr class="data-table__row" v-for="(row, idx) in data" :key="idx">
|
||||||
<td class="data-table__cell" v-for="(cell, idx) in row" :key="idx">
|
<td class="data-table__cell" v-for="(cell, idx) in row" :key="idx">
|
||||||
{{ cell }}
|
{{ $t(cell) }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
27
src/components/LanguageButton.vue
Normal file
27
src/components/LanguageButton.vue
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<template>
|
||||||
|
<div class="lang-button">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.lang-button {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0.25rem 0.5rem auto auto;
|
||||||
|
min-width: 3rem;
|
||||||
|
z-index: 9001;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0.325rem 0 0.7rem 0;
|
||||||
|
content: '';
|
||||||
|
background-color: var(--color-background);
|
||||||
|
border-radius: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: var(--color-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
49
src/components/LinkButton.vue
Normal file
49
src/components/LinkButton.vue
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
interface Props {
|
||||||
|
href?: string
|
||||||
|
download?: boolean | any
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a class="link-button" :href="href" :[download]="download">
|
||||||
|
<slot></slot>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.link-button {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
background-color: var(--color-button);
|
||||||
|
color: var(--color-button-text);
|
||||||
|
|
||||||
|
font-weight: 700;
|
||||||
|
text-decoration: none;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
|
||||||
|
box-shadow: 0 0.5rem 0 0 var(--color-button-box-shadow);
|
||||||
|
|
||||||
|
transition: all 0.1s ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
top: -0.25rem;
|
||||||
|
box-shadow: 0 0.75rem 0 0 var(--color-button-box-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
top: 0.25rem;
|
||||||
|
box-shadow: 0 0.25rem 0 0 var(--color-button-box-shadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
93
src/components/LocaleSwitcher.vue
Normal file
93
src/components/LocaleSwitcher.vue
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import CircleCheckIcon from '@/assets/icons/CircleCheckIcon.vue'
|
||||||
|
import CircleIcon from '@/assets/icons/CircleIcon.vue'
|
||||||
|
|
||||||
|
interface LocaleOption {
|
||||||
|
code: string
|
||||||
|
name: string
|
||||||
|
flag: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
modelValue: string
|
||||||
|
id: string
|
||||||
|
locales: LocaleOption[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:modelValue', value: string): void
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
|
const selectModel = computed({
|
||||||
|
get() {
|
||||||
|
return props.modelValue
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="localeselect">
|
||||||
|
<div v-for="locale in locales" class="localeselect__locale" :key="`locale-${locale.code}`">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="lang"
|
||||||
|
class="localeselect__input"
|
||||||
|
:id="`lang-${locale.code}`"
|
||||||
|
:value="locale.code"
|
||||||
|
v-model="selectModel"
|
||||||
|
/>
|
||||||
|
<label class="localeselect__label" :for="`lang-${locale.code}`">
|
||||||
|
<CircleCheckIcon v-if="$i18n.locale === locale.code" />
|
||||||
|
<CircleIcon v-else />
|
||||||
|
<span>{{ locale.name }} {{ locale.flag }}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.localeselect {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
|
margin: var(--paragraph-margin) 0;
|
||||||
|
|
||||||
|
&__locale {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
flex: 1 0 1.25rem;
|
||||||
|
fill: var(--color-text);
|
||||||
|
transition: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
margin: 0 0 0 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
95
src/components/ModalDialog.vue
Normal file
95
src/components/ModalDialog.vue
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const modal = ref<HTMLDialogElement>()
|
||||||
|
|
||||||
|
const showModal = () => {
|
||||||
|
modal.value?.showModal()
|
||||||
|
document.body.inert = true
|
||||||
|
document.body.classList.add('scroll-lock')
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
modal.value?.close()
|
||||||
|
document.body.inert = false
|
||||||
|
document.body.classList.remove('scroll-lock')
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ showModal, close })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<dialog :id="id" class="modal" ref="modal">
|
||||||
|
<form method="dialog" class="modal__content">
|
||||||
|
<h2 class="modal__heading">
|
||||||
|
<slot name="heading"></slot>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="modal__message">
|
||||||
|
<slot name="message"></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal__buttons">
|
||||||
|
<slot name="buttons"></slot>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.modal {
|
||||||
|
position: fixed;
|
||||||
|
|
||||||
|
background: var(--color-modal-background);
|
||||||
|
|
||||||
|
color: var(--color-text);
|
||||||
|
|
||||||
|
width: var(--modal-width);
|
||||||
|
|
||||||
|
margin: auto;
|
||||||
|
border: var(--modal-border);
|
||||||
|
border-radius: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
animation: fade-in 1s;
|
||||||
|
|
||||||
|
&::backdrop {
|
||||||
|
backdrop-filter: blur(1rem);
|
||||||
|
animation: fade-in 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__message {
|
||||||
|
flex: 1 1 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__buttons .link-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
86
src/components/NavigationItem.vue
Normal file
86
src/components/NavigationItem.vue
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { RouterLink } from 'vue-router'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
icon: unknown | object
|
||||||
|
href: string
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<RouterLink class="navigation__link" :to="href">
|
||||||
|
<component class="navigation__link-icon" :is="icon" />
|
||||||
|
<span class="navigation__link-text">
|
||||||
|
<slot></slot>
|
||||||
|
</span>
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.navigation {
|
||||||
|
&__link {
|
||||||
|
flex: var(--navigation-link-flex);
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: var(--navigation-link-justify);
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
height: var(--navigation-link-height);
|
||||||
|
|
||||||
|
padding: var(--navigation-link-padding);
|
||||||
|
|
||||||
|
color: var(--color-router-link);
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: var(--navigation-link-box-shadow);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: var(--color-router-link-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover &-text {
|
||||||
|
color: var(--color-router-link-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin: var(--navigation-link-last-child-margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 1 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.router-link-exact-active {
|
||||||
|
color: var(--color-router-link-hover);
|
||||||
|
background-color: var(--color-router-link);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-router-link-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: var(--color-router-link-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link-icon {
|
||||||
|
width: var(--navigation-link-icon-size);
|
||||||
|
min-width: var(--navigation-link-icon-size);
|
||||||
|
max-width: var(--navigation-link-icon-size);
|
||||||
|
margin: 0 var(--navigation-link-icon-spacing);
|
||||||
|
fill: var(--color-router-link);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link-text {
|
||||||
|
display: var(--navigation-link-display);
|
||||||
|
font-size: var(--navigation-link-text-font-size);
|
||||||
|
margin: 0 0 0 1rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,17 +1,14 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
dropshadow?: boolean;
|
dropshadow?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
defineProps<Props>();
|
defineProps<Props>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<figure class="figure">
|
<figure class="figure">
|
||||||
<picture
|
<picture class="figure__image" :class="{ 'figure__image--dropshadow': dropshadow }">
|
||||||
class="figure__image"
|
|
||||||
:class="{ 'figure__image--dropshadow': dropshadow }"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</picture>
|
</picture>
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,7 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import router from "@/router";
|
|
||||||
import { RouterLink } from "vue-router";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nav class="navigation">
|
<nav class="navigation">
|
||||||
<div class="navigation__list">
|
<div class="navigation__list">
|
||||||
<RouterLink
|
<slot></slot>
|
||||||
class="navigation__link"
|
|
||||||
v-for="(route, idx) in router.options.routes"
|
|
||||||
:key="idx"
|
|
||||||
:to="route.path"
|
|
||||||
>
|
|
||||||
<component
|
|
||||||
class="navigation__link-icon"
|
|
||||||
:is="route.meta?.icon"
|
|
||||||
></component>
|
|
||||||
<span class="navigation__link-text">
|
|
||||||
{{ route.meta?.title }}
|
|
||||||
</span>
|
|
||||||
</RouterLink>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
@ -27,10 +9,7 @@ import { RouterLink } from "vue-router";
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.navigation {
|
.navigation {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: var(--navigation-position-top);
|
inset: var(--navigation-position);
|
||||||
right: var(--navigation-position-right);
|
|
||||||
bottom: var(--navigation-position-bottom);
|
|
||||||
left: var(--navigation-position-left);
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
width: var(--navigation-width);
|
width: var(--navigation-width);
|
||||||
|
@ -54,68 +33,5 @@ import { RouterLink } from "vue-router";
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__link {
|
|
||||||
flex: var(--navigation-link-flex);
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row nowrap;
|
|
||||||
justify-content: var(--navigation-link-justify);
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
height: var(--navigation-link-height);
|
|
||||||
|
|
||||||
padding: var(--navigation-link-padding);
|
|
||||||
|
|
||||||
color: var(--color-router-link);
|
|
||||||
box-shadow: none;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
box-shadow: var(--navigation-link-box-shadow);
|
|
||||||
|
|
||||||
svg {
|
|
||||||
fill: var(--color-router-link-hover);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover &-text {
|
|
||||||
color: var(--color-router-link-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin: var(--navigation-link-last-child-margin);
|
|
||||||
}
|
|
||||||
|
|
||||||
> * {
|
|
||||||
flex: 1 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.router-link-exact-active {
|
|
||||||
color: var(--color-router-link-hover);
|
|
||||||
background-color: var(--color-router-link);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--color-router-link-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
fill: var(--color-router-link-hover);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__link-icon {
|
|
||||||
width: var(--navigation-link-icon-size);
|
|
||||||
min-width: var(--navigation-link-icon-size);
|
|
||||||
max-width: var(--navigation-link-icon-size);
|
|
||||||
margin: 0 var(--navigation-link-icon-spacing);
|
|
||||||
fill: var(--color-router-link);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__link-text {
|
|
||||||
display: var(--navigation-link-display);
|
|
||||||
font-size: var(--navigation-link-text-font-size);
|
|
||||||
margin: 0 0 0 1rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -26,36 +26,22 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:not(:last-child):before {
|
&:not(:last-child):before {
|
||||||
content: "";
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: var(--timeline-stroke-position-top);
|
inset: var(--timeline-stroke-position-odd);
|
||||||
left: var(--timeline-stroke-position-horizontal);
|
|
||||||
height: var(--timeline-stroke-length);
|
height: var(--timeline-stroke-length);
|
||||||
border-left: var(--timeline-stroke-thickness) solid
|
border-left: var(--timeline-stroke-thickness) solid var(--timeline-stroke-color);
|
||||||
var(--timeline-stroke-color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 64em) {
|
&:nth-child(odd) {
|
||||||
&:nth-child(odd) {
|
margin: var(--timeline-item-margin-odd);
|
||||||
margin: var(--timeline-item-margin-odd);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(even) {
|
&:nth-child(even) {
|
||||||
margin: var(--timeline-item-margin-even);
|
margin: var(--timeline-item-margin-even);
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
left: auto;
|
inset: var(--timeline-stroke-position-even);
|
||||||
right: var(--timeline-stroke-position-horizontal);
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item__icon {
|
|
||||||
order: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item__content {
|
|
||||||
text-align: right;
|
|
||||||
margin: 0 1rem 0 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,8 +57,7 @@
|
||||||
background-color: var(--timeline-circle-background);
|
background-color: var(--timeline-circle-background);
|
||||||
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: var(--timeline-stroke-thickness) solid
|
border: var(--timeline-stroke-thickness) solid var(--timeline-stroke-color);
|
||||||
var(--timeline-stroke-color);
|
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
padding: var(--timeline-circle-padding);
|
padding: var(--timeline-circle-padding);
|
||||||
|
|
||||||
|
@ -82,9 +67,19 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:nth-child(even) &__icon {
|
||||||
|
order: var(--timeline-item-flex-order);
|
||||||
|
}
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
flex: 1 0 0;
|
flex: 1 0 0;
|
||||||
margin: 0 0 0 1rem;
|
margin: 0;
|
||||||
|
padding: var(--timeline-item-content-padding-odd);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(even) &__content {
|
||||||
|
text-align: var(--timeline-item-text-align-even);
|
||||||
|
padding: var(--timeline-item-content-padding-even);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__headline {
|
&__headline {
|
||||||
|
|
|
@ -6,12 +6,11 @@
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.timeline {
|
.timeline {
|
||||||
max-width: calc(var(--timeline-circle-size) * 15);
|
max-width: var(--timeline-max-width);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0 var(--container-spacing-right-safe) 0
|
padding: 0 var(--container-spacing-right-safe) 0 var(--container-spacing-left-safe);
|
||||||
var(--container-spacing-left-safe);
|
|
||||||
|
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,9 @@
|
||||||
<header class="welcome">
|
<header class="welcome">
|
||||||
<div class="welcome__image">
|
<div class="welcome__image">
|
||||||
<picture>
|
<picture>
|
||||||
<source
|
<source srcset="@/assets/viktor-avatar.png?w=400;800&format=avif&quality=75&as=srcset" />
|
||||||
srcset="@/assets/viktor-avatar.png?w=400;800&avif&quality=75&srcset"
|
<source srcset="@/assets/viktor-avatar.png?w=400;800&format=webp&quality=100&as=srcset" />
|
||||||
/>
|
<img src="@/assets/viktor-avatar.png?w=400&format=png" alt="Viktor Avatar" />
|
||||||
<source
|
|
||||||
srcset="@/assets/viktor-avatar.png?w=400;800&webp&quality=100&srcset"
|
|
||||||
/>
|
|
||||||
<img src="@/assets/viktor-avatar.png?w=400&png" alt="Viktor Avatar" />
|
|
||||||
</picture>
|
</picture>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
7
src/lang/index.ts
Normal file
7
src/lang/index.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import en from './translations/en.json'
|
||||||
|
import de from './translations/de.json'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
en,
|
||||||
|
de
|
||||||
|
}
|
138
src/lang/translations/de.json
Normal file
138
src/lang/translations/de.json
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
{
|
||||||
|
"welcomeHeader": {
|
||||||
|
"mainTitle": "Viktor Kraastav",
|
||||||
|
"subTitle": "Charakter-Referenzseite"
|
||||||
|
},
|
||||||
|
"nav": {
|
||||||
|
"home": "Startseite",
|
||||||
|
"general": "Allgemein",
|
||||||
|
"anatomy": "Anatomie",
|
||||||
|
"careerPath": "Lebenslauf",
|
||||||
|
"attributions": "Zuteilung"
|
||||||
|
},
|
||||||
|
"langswitcher": {
|
||||||
|
"title": "Sprache",
|
||||||
|
"prompt": "Diese Seite anzeigen in:",
|
||||||
|
"buttonClose": "Schließen"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"general": {
|
||||||
|
"heading": ["Schlüssel", "Wert"],
|
||||||
|
"fullName": ["Vollständiger Name", "Viktor Kraastav"],
|
||||||
|
"dob": ["Geboren am"],
|
||||||
|
"gender": ["Geschlecht", "männlich ♂️"],
|
||||||
|
"height": ["Größe"],
|
||||||
|
"weight": ["Gewicht"]
|
||||||
|
},
|
||||||
|
"sexuality": {
|
||||||
|
"heading": ["Schlüssel", "Wert"],
|
||||||
|
"identifiesAs": ["Identifiziert sich als", "Schwul"],
|
||||||
|
"preferredRole": ["Bevorzugte Rolle", "Passiv"]
|
||||||
|
},
|
||||||
|
"colors": {
|
||||||
|
"headings": ["Körperteil", "Wert", "Farbe"],
|
||||||
|
"front": "Front",
|
||||||
|
"limbs": "Arme, Beine",
|
||||||
|
"back": "Rücken",
|
||||||
|
"spine": "Wirbelsäule, Schweif",
|
||||||
|
"tissue": "Frecklinge, Gewebe",
|
||||||
|
"spikes": "Stacheln, Keule",
|
||||||
|
"eyesPrimary": "Augen (Hauptfarbe)",
|
||||||
|
"eyesSecondary": "Augen (Highlights)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"heading": "Willkommen auf Viktors Referenzseite",
|
||||||
|
"paragraphs": [
|
||||||
|
"Hier erfährst du alles über den Ankylosaurus namens Viktor.",
|
||||||
|
"Wähle einen Punkt aus der Navigation, um einzusteigen!"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"personality": {
|
||||||
|
"heading": "Persönlichkeit",
|
||||||
|
"paragraphs": [
|
||||||
|
"Viktor ist kein Mann vieler Worte, seine Ausdrucksweise ist einfach und direkt und manchmal auch etwas unverblümt. Diese forsche Art kommt nicht bei jedem gut an und bringt ihn regelmäßig in Schwierigkeiten.",
|
||||||
|
"Gleichzeitig ist das aber auch Ausdruck seines überschwänglichen Selbstbewusstseins. Er lässt sich von niemandem etwas gefallen und zögert nicht, jemandem die Meinung zu geigen. Wenn das zu Handgreiflichkeiten führt, nimmt er diese in Kauf.",
|
||||||
|
"All das lässt ihn vielleicht wie einen wirklich unangenehmen Zeitgenossen erscheinen, aber er schätzt die Gesellschaft von Leuten denen er vertraut sehr. Allerdings ist er sehr wählerisch, wen er zu diesem Personenkreis zählt. Er schätzt den persönlichen Kontakt, am liebsten bei ein paar Bier in seiner Lieblingsbar.",
|
||||||
|
"Er mag es einfach, deshalb ist er auch kein großer Fan von Hightech. Er hat zwar ein Smartphone, aber er hasst es genauso sehr wie die Tatsache, dass er darauf angewiesen ist, eines zu besitzen."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sexuality": {
|
||||||
|
"heading": "Sexualität",
|
||||||
|
"paragraphs": [
|
||||||
|
"Als Teenager bemerkte Viktor, dass er über die Jungs in seiner Klasse ein bisschen anders dachte. Vor allem im Sportunterricht neigte er dazu, seine Klassenkameraden länger zu beobachten, wenn sie sich in der Umkleidekabine rauften, wie es pubertierende Jungs eben tun. Er konnte sich jedoch nie ganz mit dem Gedanken anfreunden, dass er vielleicht ein bisschen anders ist als seine Freunde, die sich alle für Mädchen zu interessieren begannen, im Gegensatz zu ihm, der sich mehr für seine Kumpels interessierte.",
|
||||||
|
"Als er älter wurde, lernte er langsam, sich mit der Tatsache abzufinden, dass er in seinen Kumpels mehr sah als nur Freunde. Trotzdem hielt er sich weiterhin bedeckt.",
|
||||||
|
"Bis zu einem Abend auf einer Party, als sich einer seiner Freunde darüber beschwerte, dass ihn seine Freundin nicht mehr ranlassen würde. Die beiden waren schon gut angetrunken und Viktor machte ihm lallend den Vorschlag, dass er ihm als sein Kumpel aushelfen würde. Zuerst war sein Freund von dem Angebot irritiert, stimmte aber schließlich zu. Die beiden schlichen sich in ein Schlafzimmer in der Wohnung des Gastgebers, wo er seinen Freund den ganzen aufgestauten Druck in ihm ablassen ließ. Er erinnerte sich nicht mehr an viel von der Party, aber er vergaß nicht, wie gut es sich anfühlte, sich einem anderen Mann hinzugeben."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"anatomy": {
|
||||||
|
"images": {
|
||||||
|
"back": { "caption": "Viktor Referenz" },
|
||||||
|
"front": { "caption": "Viktor Vorderseite" }
|
||||||
|
},
|
||||||
|
"paragraphs": [
|
||||||
|
[
|
||||||
|
"Viktor ist ein zweibeiniger, plantigrader Ankylosaurus. Seine Haut ist größtenteils zweifarbig, mit verschiedenen Brauntönen.",
|
||||||
|
"Seine Stirn, Nase, Brust, sein Bauch und sein Schritt haben eine helle Wüstensandfarbe, die sich entlang der Unterseite seines Schwanzes fortsetzt.",
|
||||||
|
"Seine Wangen, Schultern, Oberschenkel und sein Rücken sind dagegen in einem satten Walnussbraun gehalten, das sich auch an den Seiten seines Schwanzes wiederfindet. Vom Hinterkopf über die Wirbelsäule bis zur Schwanzspitze verläuft ein durchgehender Streifen in tiefdunklem Zedernbraun. Arme und Beine zeichnen sich durch ein helles, erdiges Braun aus."
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Sein ganzer Körper ist mit aquamarinfarbenen Flecken gesprenkelt, die sich paarweise aus einem großen und einem kleinen Fleck zusammensetzen. Einzige Ausnahmen sind die Flecken auf den Wangen und hinter den unteren Wangenhörnern, die in Dreiergruppen auftreten, und die Oberseite seiner Schnauze, die einen großen einzelnen Fleck aufweist. Auch Maul, Zunge, Nasenlöcher und alle anderen Gewebe seines Körpers weisen diese Farbe auf.",
|
||||||
|
"Seine Augen leuchten in einer Mischung aus hellem Meeresgrün und elektrisierendem Blau.",
|
||||||
|
"Die Hörner und Krallen sind von einem typischen Knochenweiß. Angefangen mit dem Doppelhornpaar auf seinem Kopf zieht sich eine parallele Linie von Hörnern über seinen Rücken, mit zusätzlichen Hörnern an den Schultern und Oberschenkeln. Auch sein Schwanz ist auf beiden Seiten über die gesamte Länge mit Hörnern versehen. An der Spitze des Schwanzes befindet sich eine keulenartige Verknöcherung, mit der er Angreifer abwehren kann.",
|
||||||
|
"Seinen starken, stämmigen Körperbau verdankt er jahrelanger harter körperlicher Arbeit."
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"career": {
|
||||||
|
"paragraphs": [
|
||||||
|
"Viktor hatte in der Vergangenheit viele verschiedene Jobs, von denen einige sehr prägend waren, während andere nur die Rechnungen bezahlten."
|
||||||
|
],
|
||||||
|
"jobs": [
|
||||||
|
{
|
||||||
|
"title": "Kneipenwirt",
|
||||||
|
"desc": "Viktors berufliche Laufbahn begann als Barkeeper in einer Kneipe in seiner Heimatstadt. Dort fungierte er oft auch als Türsteher, wenn ein paar Gäste einen über den Durst getrunken hatten und einen Aufstand machten. Ein entscheidender Moment in diesem Job war, als einer von ihnen über den Tresen kletterte und ihn mit einer zerbrochenen Flasche bedrohte. Als er zu Boden gerungen wurde und eine zerbrochene Flasche vor seinem Gesicht hatte, musste er in Sekundenbruchteilen eine Entscheidung treffen. Mit einem kräftigen Schwung seines Schwanzknochens schlug er den Angreifer nieder. Diese Erfahrung lehrte ihn, wie wichtig es ist, sich effektiv gegen unangenehme Gesellen zu verteidigen und Bedrohungen auszuräumen, bevor sie entstehen."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Holzfäller",
|
||||||
|
"desc": "Nachdem er seine Heimatstadt verlassen hatte, begann Viktor sich als Holzfäller zu verdienen. Die Keule am Ende seines Schwanzes erwies sich dabei als sehr nützlich, denn sie diente ihm als Gegengewicht und ermöglichte es ihm, effizient und kraftvoll mit seiner Axt zuzuschlagen und selbst die größten Bäume mit Leichtigkeit zu fällen. Seine von Natur aus zähen Schuppen schützten ihn vor Splittern aus dem gefällten Holz. Die jahrelange harte körperliche Arbeit hat seinen Körper im Laufe der Jahre gestählt. Bevor er die gefällten Stämme zum Sägewerk brachte, machte Viktor immer eine Pause, um sich in der Stille des Waldes auszuruhen. Obwohl er die Abgeschiedenheit des Landlebens eine Zeit lang genoss, sehnte er sich wieder nach der Geselligkeit des Stadtlebens."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Automachaniker",
|
||||||
|
"desc": "Nach seinem Umzug in eine Vorstadt bewarb sich Viktor in einer Autowerkstatt, wo er lernte, wie man Autos repariert. Er wurde richtig gut darin und hatte Spaß daran, kaputten Fahrzeugen neues mechanisches Leben einzuhauchen. Mit der Zeit geriet die Werkstatt jedoch in finanzielle Schwierigkeiten, da es immer schwieriger wurde, an Ersatzteile zu kommen, da die Autohersteller diese nur an zertifizierte Reparaturpartner abgaben und die Zertifizierungen dafür unbezahlbar waren. Viktor musste zusehen, wie sich das Geschäft langsam verschlechterte, da immer mehr qualifizierte Mitarbeiter entlassen wurden, bis die Werkstatt schließlich für immer geschlossen wurde."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Bauarbeiter",
|
||||||
|
"desc": "Da er Gefallen an körperlich anstrengender Arbeit gefunden hatte, nahm Viktor einen Job als Bauarbeiter an. Mit einem Schutzhelm auf dem Kopf und einem Werkzeuggürtel um die Hüfte war er immer bereit, mit anzupacken (oder zuzuschlagen, wenn Mauern oder Felsbrocken abgetragen werden mussten). Da er durch seine früheren Jobs mehr als genug Krafttraining hatte, wurde er oft mit Tragearbeiten beauftragt, um die Baustelle zu räumen und Baumaterial zu transportieren. Nach getaner Arbeit genoss Viktor die Gesellschaft seiner Kollegen bei einem Feierabendbier bis spät in die Nacht. An einem dieser Abende wurde er im Schutze der Nacht mit einem Kollegen etwas leichtsinnig und beide wurden in flagranti erwischt, als sie auf der Baustelle miteinander rummachten. In den folgenden Wochen hielten die Kollegen Abstand zu Viktor und auch die gemeinsamen Abende lösten sich sehr schnell auf, als er sich zu ihnen gesellen wollte. Die anhaltende Ausgrenzung veranlasste ihn schließlich, sich etwas Neues zu suchen."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Schweißer",
|
||||||
|
"desc": "In seinem Beruf als Schweißer verbrachte Viktor seine Tage im Schichtbetrieb in einer Werkstatt. Neben Spezialwerkzeugen und Plasmaschweißgeräten benutzte er auch seinen kräftigen Schwanzknüppel, um Metallteile in Form zu hämmern. Er eignete sich eine Vielzahl von Schweißtechniken an, um Metallstrukturen zu verbinden oder zu reparieren. Er arbeitete mit einer Vielzahl von Legierungen wie Stahl, Aluminium und Titan. Viktor zeigte bei seiner Arbeit extreme Geschicklichkeit, große Präzision und Liebe zum Detail. Diese Hingabe war jedoch einem eifersüchtigen Kollegen ein Dorn im Auge, der Viktor als Konkurrenten ansah und gegen ihn intrigierte, indem er seine Arbeit sabotierte, was schließlich in seiner Kündigung endete, nachdem ein Kunde die Schweißerei wegen Pfusch verklagt hatte. Viktor hat nie herausgefunden, wer der Schuldige war, und der Verlust dieses Jobs lastet immer noch auf ihm."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lieferant",
|
||||||
|
"desc": "Als Viktor knapp bei Kasse war, nahm er einen Job in der Gig-Economy als Fahrer an, der Pakete für einen großen Online-Lieferdienst ausfuhr. Durch seine früheren körperlich anstrengenden Jobs konnte er selbst sperrige Lieferungen relativ leicht an ihr Ziel bringen. Hätten ihm die Manager des Versandzentrums nicht ständig im Nacken gesessen, hätte er diesen Job vielleicht sogar noch länger behalten können. Aber als einer der Manager versuchte, ihn vor dem ganzen Team bloßzustellen, platzte ihm der Kragen und er brach ihm mit seiner Keule das Bein. Am nächsten Tag musste er sich natürlich nicht mehr zum Dienst melden."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Hafenarbeiter",
|
||||||
|
"desc": "Derzeit arbeitet Viktor an den Docks in der Hafenstadt, in die er gezogen ist. Zu seinen Hauptaufgaben gehören das Be- und Entladen von Schiffen und der Transport der Ladung zu und von den Lagerhäusern. Das geschäftige Treiben im Hafen geht ihm manchmal auf die Nerven. Vor allem, wenn die Besatzungen der anlegenden Schiffe nach langer Zeit wieder an Land kommen und deutlich machen, dass sie mit \"Landratten\" nicht viel Umgang haben. Viktor lässt sich davon allerdings nicht sonderlich beeindrucken und mault zurück, wenn er den Eindruck bekommt, dass sie auf Ärger aus sind."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"attributions": {
|
||||||
|
"artwork": {
|
||||||
|
"heading": "Kunstwerke",
|
||||||
|
"headings": ["Kunstwerk", "Künstler"]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"heading": "Sonstiges",
|
||||||
|
"headings": ["Typ", "Quelle"],
|
||||||
|
"icons": ["Symbole"],
|
||||||
|
"headingFont": ["Überschriften", "von"],
|
||||||
|
"copyFont": ["Fließtext", "von"],
|
||||||
|
"background": ["Hintergrund", ["Layered Waves aus", "von"]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
138
src/lang/translations/en.json
Normal file
138
src/lang/translations/en.json
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
{
|
||||||
|
"welcomeHeader": {
|
||||||
|
"mainTitle": "Viktor Kraastav",
|
||||||
|
"subTitle": "Character Reference Page"
|
||||||
|
},
|
||||||
|
"nav": {
|
||||||
|
"home": "Home",
|
||||||
|
"general": "General",
|
||||||
|
"anatomy": "Anatomy",
|
||||||
|
"careerPath": "Career Path",
|
||||||
|
"attributions": "Attributions"
|
||||||
|
},
|
||||||
|
"langswitcher": {
|
||||||
|
"title": "Language",
|
||||||
|
"prompt": "View this page in:",
|
||||||
|
"buttonClose": "Close"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"general": {
|
||||||
|
"heading": ["Key", "Value"],
|
||||||
|
"fullName": ["Full Name", "Viktor Kraastav"],
|
||||||
|
"dob": ["Date of Birth"],
|
||||||
|
"gender": ["Sex/Gender", "male ♂️"],
|
||||||
|
"height": ["Height"],
|
||||||
|
"weight": ["Weight"]
|
||||||
|
},
|
||||||
|
"sexuality": {
|
||||||
|
"heading": ["Key", "Value"],
|
||||||
|
"identifiesAs": ["Identifies as", "Gay"],
|
||||||
|
"preferredRole": ["Preferred role", "Bottom"]
|
||||||
|
},
|
||||||
|
"colors": {
|
||||||
|
"headings": ["Body part", "Value", "Color"],
|
||||||
|
"front": "Front",
|
||||||
|
"limbs": "Arms, legs",
|
||||||
|
"back": "Back Main",
|
||||||
|
"spine": "Back Spine",
|
||||||
|
"tissue": "Highlight scales, tissue",
|
||||||
|
"spikes": "Spikes, tail club",
|
||||||
|
"eyesPrimary": "Eyes primary",
|
||||||
|
"eyesSecondary": "Eyes secondary"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"heading": "Welcome to Viktor's Ref Page",
|
||||||
|
"paragraphs": [
|
||||||
|
"Here you can learn all about the ankylosaurus named Viktor.",
|
||||||
|
"Pick an item from the navigation to dive in!"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"personality": {
|
||||||
|
"heading": "Personality",
|
||||||
|
"paragraphs": [
|
||||||
|
"Viktor is not a man of many words, his manner of expression is simple and direct, and sometimes a little blunt. This brash manner doesn't go down well with everyone and regularly gets him into trouble.",
|
||||||
|
"At the same time, this is also an expression of his exuberant self-confidence. He won't take any crap from anyone and doesn't hesitate to give someone a piece of his mind. If that results in fisticuffs, he'll deal with them.",
|
||||||
|
"All this may make him look like a really unpleasant fellow, but he greatly appreciates the company of people he trusts. He is very selective about whom he counts among this group of people, though. He appreciates personal contact, preferably over a few beers at his favorite bar.",
|
||||||
|
"He likes things to be simple, which is why he is not a big fan of high-tech. He does have a smartphone, but he hates using it as much as he dislikes the fact he's dependent on owning one."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sexuality": {
|
||||||
|
"heading": "Sexuality",
|
||||||
|
"paragraphs": [
|
||||||
|
"As a teenager, Viktor noticed that he felt a bit differently about the boys in his class. Especially in gym class, his eyes tended to linger longer on his classmates as they rough-housed in the locker room, as pubescent boys do. However, he could never quite come to terms with the idea that he might be a little different from his friends, who were all beginning to take an interest in girls, unlike him, who was more interested in his buddies.",
|
||||||
|
"As he got older, he slowly learned to come to terms with the fact that he saw more in his buddies than just friends. However, he still kept a low profile.",
|
||||||
|
"Until one evening at a party, when one of his friends complained that he wouldn't get any from his girlfriend. Both of them were already well intoxicated and Viktor made him the slurred proposal that he would help him out as his bro. At first his buddy was irritated by the offer, but eventually agreed. The two snuck into a bedroom at the host's place, where Viktor would let his buddy unload all the pent-up pressure inside him. He wouldn't remember much of the party, but what he didn't forget how good it felt to give himself to another man."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"anatomy": {
|
||||||
|
"images": {
|
||||||
|
"back": { "caption": "Viktor Ref" },
|
||||||
|
"front": { "caption": "Viktor Frontal Shot" }
|
||||||
|
},
|
||||||
|
"paragraphs": [
|
||||||
|
[
|
||||||
|
"Viktor is a bipedal plantigrade Ankylosaurus. His skin is mostly bicolored, with several shades of brown.",
|
||||||
|
"His forehead, nose, chest, belly and crotch are of light desert sand colors that run through the underside of his tail.",
|
||||||
|
"His cheeks, shoulders, back and thighs, in contrast, stand out with a rich walnut brown, which is also found on the sides of his tail. From the back of his head, down his spine and across the top of his tail is a continuous strip of deep dark cedar brown. Arms and legs are distinguished by a light earthy brown."
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"His entire body is speckled with scattered aquamarine spots, which come in pairs of one large and one small spot. The only exceptions are the spots on his cheeks and behind his lower cheek horns, which come in threes and his the top side of his snout, which sports a big single spot. Mouth, tongue, nostrils and any other tissue of his body also feature this color.",
|
||||||
|
"His eyes shine with a mixture of light sea green and electric blue.",
|
||||||
|
"Horns and claws are of a typical bone white. Starting with the double pair of horns on his head, a parallel line of horns continues down his back, with additional horns adorning his shoulders and thighs. His tail is also armed with horns on both sides along its entire length. At the tip of the tail is a club-like ossification that he can use for fending off foes.",
|
||||||
|
"He owes his strong, stocky physique to years of hard physical labor."
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"career": {
|
||||||
|
"paragraphs": [
|
||||||
|
"Viktor's had many different jobs in the past, some of which where very formative, other's just paid the bills."
|
||||||
|
],
|
||||||
|
"jobs": [
|
||||||
|
{
|
||||||
|
"title": "Bartender",
|
||||||
|
"desc": "Viktor's professional career began as a bartender at a pub in his hometown. There he often doubled as a bouncer when a few guests got too drunk and started making a fuss. A defining moment of that job was when someone climbed over the counter and threatened him with a broken bottle. Wrestled to the ground and with a broken bottle in front of his face, he had to make a split-second decision. With a powerful swing of his tail bone, he knocked the attacker down. This experience taught him the importance of effectively defending himself against unpleasant fellows and to eliminate threats before they get close to him ever again."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Lumberjack",
|
||||||
|
"desc": "After leaving his hometown, Viktor started working as a lumberjack. The club at the end of his tail came very much in handy as a counterweight for each swing of his axe, allowing him to strike efficiently and powerfully, felling even the largest trees with relative ease. His naturally tough scales protected him from splinters from the felled wood. The long-lasting hard physical work toughened his body over the years. Before taking the cut logs to the sawmill, Viktor always took a break to rest in the peace and quiet of the forest. Although he enjoyed the seclusion of the country life for a while, he was longing for the sociability of city life again."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Car Mechanic",
|
||||||
|
"desc": "Moving into a suburban town, Viktor applied at an auto repair shop, where he learned the ins and outs of fixing cars. He became really good at it and enjoyed breathing new mechanical life into broken down vehicles. However, as time went on, the repair shop faced financial troubles, as it became increasingly difficult to come by spare parts as auto makers would only deal them to certified repair partners and certifications were prohibitively expensive. Viktor had to watch business slowly deteriorate, as skilled coworkers kept getting laid off, until the repair shop closed down for good."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Construction Worker",
|
||||||
|
"desc": "Having taken a liking in physically demanding work, Viktor took on a job as a construction worker. Hard hat perched atop his head and belt tool slung around his waist, he was always ready to lend a hand (or tail if walls or boulders needed a good teardown). Since he had more than enough strength training from his previous jobs, he was often assigned carrying jobs to clear the site and haul building materials. After work was done, Viktor enjoyed the company of his colleagues over an after-work beer until late in the evenings. On one of these evenings, he got a little reckless with a colleague under the veil of the night and both were caught in the act of fooling around with each other on the construction site. In the following weeks, the colleagues kept their distance from Viktor and the evenings together also dissolved very quickly when he joined them. The continued ostracization ultimately prompted him to look for something new."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Welder",
|
||||||
|
"desc": "In his job as a welder, Viktor spent his days working shifts in a workshop. In addition to special tools and plasma welders, he also used his powerful tail club to hammer metal parts into shape. He acquired a wide variety of welding techniques to join or repair metal structures. He worked with a wide variety of alloys such as steel, aluminum and titanium. Viktor showed extreme skill in his work with great precision and attention to detail. However, this dedication was a thorn in the side of a jealous colleague who saw Viktor as a rival and schemed against him, sabotaging his work, which ultimately ended in his termination after a customer sued the welding shop for botching the job. Viktor never found out who the culprit was, and the loss of that job still hangs over him."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Delivery Driver",
|
||||||
|
"desc": "When Viktor was strapped for cash, he took on a job in the gig economy as a driver delivering packages for a large online delivery service. His previous physically demanding jobs allowed him to haul even bulky deliveries to their destination with relative ease. If the shipping center managers hadn't been breathing down his neck constantly, he might have held this job even longer. But after one of the managers tried to show him up in front of the whole team, he snapped and broke their leg with his tail club. Of course, he didn't have to report for duty the next day."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Docks Werehouse Worker",
|
||||||
|
"desc": "Currently Viktor works at the docks in the port town he moved to. His main responsibilities include loading and unloading cargo from ships and transporting it to and from warehousing. The hustle and bustle of the port sometimes gets on his nerves. Especially when the crew of docking ships come ashore again after a long time and make it clear that they don't have much contact with \"landlubbers\". Viktor doesn't get particularly impressed by this and foul mouths them right back if he gets the impression they're looking for trouble."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"attributions": {
|
||||||
|
"artwork": {
|
||||||
|
"heading": "Artworks",
|
||||||
|
"headings": ["Artwork", "Artist"]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"heading": "Other",
|
||||||
|
"headings": ["Type", "Source"],
|
||||||
|
"icons": ["Icons"],
|
||||||
|
"headingFont": ["Heading Font", "by"],
|
||||||
|
"copyFont": ["Copy Font", "by"],
|
||||||
|
"background": ["Background", ["Layered Waves aus", "von"]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
src/main.ts
28
src/main.ts
|
@ -1,12 +1,24 @@
|
||||||
import { createApp } from "vue";
|
import { createApp } from 'vue'
|
||||||
import App from "./App.vue";
|
import App from './App.vue'
|
||||||
import router from "./router";
|
import router from './router'
|
||||||
|
import { createI18n } from 'vue-i18n'
|
||||||
|
import messages from './lang'
|
||||||
|
|
||||||
import "normalize.css";
|
import 'normalize.css'
|
||||||
import "@/scss/main.scss";
|
import '@/scss/main.scss'
|
||||||
|
|
||||||
const app = createApp(App);
|
type MessageSchema = typeof messages.en
|
||||||
|
|
||||||
app.use(router);
|
const i18n = createI18n<[MessageSchema], 'en' | 'de'>({
|
||||||
|
legacy: false,
|
||||||
|
locale: 'en',
|
||||||
|
fallbackLocale: 'en',
|
||||||
|
messages
|
||||||
|
})
|
||||||
|
|
||||||
app.mount("body");
|
const app = createApp(App)
|
||||||
|
|
||||||
|
app.use(router)
|
||||||
|
app.use(i18n)
|
||||||
|
|
||||||
|
app.mount('body')
|
||||||
|
|
|
@ -1,64 +1,68 @@
|
||||||
import { createRouter, createWebHistory } from "vue-router";
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import HomeView from "../views/HomeView.vue";
|
import HomeView from '@/views/HomeView.vue'
|
||||||
|
|
||||||
import HomeIcon from "@/assets/icons/HomeIcon.vue";
|
import HomeIcon from '@/assets/icons/HomeIcon.vue'
|
||||||
import IdCardIcon from "@/assets/icons/IdCardIcon.vue";
|
import IdCardIcon from '@/assets/icons/IdCardIcon.vue'
|
||||||
import PaletteIcon from "@/assets/icons/PaletteIcon.vue";
|
import PaletteIcon from '@/assets/icons/PaletteIcon.vue'
|
||||||
import BriefcaseIcon from "@/assets/icons/BriefcaseIcon.vue";
|
import BriefcaseIcon from '@/assets/icons/BriefcaseIcon.vue'
|
||||||
import CircleInfoIcon from "@/assets/icons/CircleInfoIcon.vue";
|
import CircleInfoIcon from '@/assets/icons/CircleInfoIcon.vue'
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
scrollBehavior: () => {
|
scrollBehavior: () => {
|
||||||
return { top: 0 };
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve({ top: 0 })
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: '/',
|
||||||
name: "home",
|
name: 'home',
|
||||||
component: HomeView,
|
component: HomeView,
|
||||||
meta: {
|
meta: {
|
||||||
title: "Home",
|
title: 'nav.home',
|
||||||
icon: HomeIcon,
|
icon: HomeIcon
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/general",
|
path: '/general',
|
||||||
name: "general",
|
name: 'general',
|
||||||
component: () => import("@/views/GeneralView.vue"),
|
component: () => import('@/views/GeneralView.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: "General",
|
title: 'nav.general',
|
||||||
icon: IdCardIcon,
|
icon: IdCardIcon
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/anatomy",
|
path: '/anatomy',
|
||||||
name: "anatomy",
|
name: 'anatomy',
|
||||||
component: () => import("@/views/AnatomyView.vue"),
|
component: () => import('@/views/AnatomyView.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: "Anatomy",
|
title: 'nav.anatomy',
|
||||||
icon: PaletteIcon,
|
icon: PaletteIcon
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/career-path",
|
path: '/career-path',
|
||||||
name: "career-path",
|
name: 'career-path',
|
||||||
component: () => import("@/views/CareerPathView.vue"),
|
component: () => import('@/views/CareerPathView.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: "Career Path",
|
title: 'nav.careerPath',
|
||||||
icon: BriefcaseIcon,
|
icon: BriefcaseIcon
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/attributions",
|
path: '/attributions',
|
||||||
name: "attributions",
|
name: 'attributions',
|
||||||
component: () => import("@/views/AttributionsView.vue"),
|
component: () => import('@/views/AttributionsView.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: "Attributions",
|
title: 'nav.attributions',
|
||||||
icon: CircleInfoIcon,
|
icon: CircleInfoIcon
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
});
|
})
|
||||||
|
|
||||||
export default router;
|
export default router
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@import "fontfaces";
|
@import 'fontfaces';
|
||||||
|
|
||||||
/* theme colors */
|
/* theme colors */
|
||||||
:root {
|
:root {
|
||||||
|
@ -33,8 +33,8 @@
|
||||||
|
|
||||||
/* general purpose variables */
|
/* general purpose variables */
|
||||||
:root {
|
:root {
|
||||||
--font-family-copy: "Arvo", sans-serif;
|
--font-family-copy: 'Arvo', sans-serif;
|
||||||
--font-family-headings: "Secular One", serif;
|
--font-family-headings: 'Secular One', serif;
|
||||||
|
|
||||||
--font-size: 18px;
|
--font-size: 18px;
|
||||||
--text-line-height: 1.5;
|
--text-line-height: 1.5;
|
||||||
|
@ -43,6 +43,8 @@
|
||||||
--page-background-image: url(@/assets/layered-waves-light.svg);
|
--page-background-image: url(@/assets/layered-waves-light.svg);
|
||||||
--page-background-image-height: 100vw;
|
--page-background-image-height: 100vw;
|
||||||
--page-background-image-max-height: 50vh;
|
--page-background-image-max-height: 50vh;
|
||||||
|
--page-transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
||||||
|
--page-transform: translateY(5em);
|
||||||
|
|
||||||
--paragraph-margin: 1rem;
|
--paragraph-margin: 1rem;
|
||||||
|
|
||||||
|
@ -54,20 +56,19 @@
|
||||||
--container-spacing-bottom-safe: max(1rem, env(safe-area-inset-bottom));
|
--container-spacing-bottom-safe: max(1rem, env(safe-area-inset-bottom));
|
||||||
--container-spacing-left-safe: max(1rem, env(safe-area-inset-left));
|
--container-spacing-left-safe: max(1rem, env(safe-area-inset-left));
|
||||||
|
|
||||||
--textblock-padding: 0 var(--container-spacing-right-safe) 0
|
--textblock-padding: 0 var(--container-spacing-right-safe) 0 var(--container-spacing-left-safe);
|
||||||
var(--container-spacing-left-safe);
|
|
||||||
|
|
||||||
--welcome-padding: 2rem var(--container-spacing-right-safe) 2rem
|
--welcome-padding: 2rem var(--container-spacing-right-safe) 2rem
|
||||||
var(--container-spacing-left-safe);
|
var(--container-spacing-left-safe);
|
||||||
--welcome-headings-text-align: center;
|
--welcome-headings-text-align: center;
|
||||||
|
|
||||||
|
--modal-width: 100%;
|
||||||
|
--modal-border: 0.25rem solid var(--color-modal-border);
|
||||||
|
|
||||||
--navigation-flow: row nowrap;
|
--navigation-flow: row nowrap;
|
||||||
--navigation-justify: space-evenly;
|
--navigation-justify: space-evenly;
|
||||||
--navigation-align: center;
|
--navigation-align: center;
|
||||||
--navigation-position-top: auto;
|
--navigation-position: auto 0 0 0;
|
||||||
--navigation-position-right: 0;
|
|
||||||
--navigation-position-bottom: 0;
|
|
||||||
--navigation-position-left: 0;
|
|
||||||
|
|
||||||
--navigation-size: 4rem;
|
--navigation-size: 4rem;
|
||||||
--navigation-width: 100%;
|
--navigation-width: 100%;
|
||||||
|
@ -75,19 +76,14 @@
|
||||||
--navigation-height: auto;
|
--navigation-height: auto;
|
||||||
--navigation-padding: 0 0 0 env(safe-area-inset-left);
|
--navigation-padding: 0 0 0 env(safe-area-inset-left);
|
||||||
|
|
||||||
--navigation-cutout: 0 0
|
--navigation-cutout: 0 0 calc(var(--navigation-size) + env(safe-area-inset-bottom)) 0;
|
||||||
calc(var(--navigation-size) + env(safe-area-inset-bottom)) 0;
|
--navigation-cutout-page-background: calc(var(--navigation-size) + env(safe-area-inset-bottom));
|
||||||
--navigation-cutout-page-background: calc(
|
|
||||||
var(--navigation-size) + env(safe-area-inset-bottom)
|
|
||||||
);
|
|
||||||
--navigation-cutout-main: 0;
|
--navigation-cutout-main: 0;
|
||||||
|
|
||||||
--navigation-link-display: none;
|
--navigation-link-display: none;
|
||||||
--navigation-link-flex: 1 0 var(--navigation-size);
|
--navigation-link-flex: 1 0 var(--navigation-size);
|
||||||
--navigation-link-justify: center;
|
--navigation-link-justify: center;
|
||||||
--navigation-link-height: calc(
|
--navigation-link-height: calc(var(--navigation-size) + env(safe-area-inset-bottom));
|
||||||
var(--navigation-size) + env(safe-area-inset-bottom)
|
|
||||||
);
|
|
||||||
--navigation-link-padding: 0 0 env(safe-area-inset-bottom) 0;
|
--navigation-link-padding: 0 0 env(safe-area-inset-bottom) 0;
|
||||||
--navigation-link-icon-size: calc(var(--navigation-size) / 2);
|
--navigation-link-icon-size: calc(var(--navigation-size) / 2);
|
||||||
--navigation-link-icon-spacing: calc(var(--navigation-size) * 0.25);
|
--navigation-link-icon-spacing: calc(var(--navigation-size) * 0.25);
|
||||||
|
@ -96,8 +92,8 @@
|
||||||
var(--color-router-link);
|
var(--color-router-link);
|
||||||
--navigation-link-last-child-margin: 0;
|
--navigation-link-last-child-margin: 0;
|
||||||
|
|
||||||
--timeline-max-width: calc(var(--timeline-circle-size) * 15);
|
--timeline-max-width: calc(var(--timeline-circle-size) * 10);
|
||||||
--timeline-circle-size: 4rem;
|
--timeline-circle-size: 3rem;
|
||||||
--timeline-circle-font-size: calc(var(--timeline-circle-size) * 0.2);
|
--timeline-circle-font-size: calc(var(--timeline-circle-size) * 0.2);
|
||||||
--timeline-circle-background-color: var(--theme-c-woody-brown);
|
--timeline-circle-background-color: var(--theme-c-woody-brown);
|
||||||
--timeline-circle-padding: calc(var(--timeline-circle-font-size));
|
--timeline-circle-padding: calc(var(--timeline-circle-font-size));
|
||||||
|
@ -109,22 +105,33 @@
|
||||||
var(--timeline-circle-size) / 2 - var(--timeline-stroke-thickness) / 2
|
var(--timeline-circle-size) / 2 - var(--timeline-stroke-thickness) / 2
|
||||||
);
|
);
|
||||||
|
|
||||||
|
--timeline-stroke-position-odd: var(--timeline-stroke-position-top) auto auto
|
||||||
|
var(--timeline-stroke-position-horizontal);
|
||||||
|
--timeline-stroke-position-even: var(--timeline-stroke-position-odd);
|
||||||
|
|
||||||
--timeline-item-margin: 1.5rem 0;
|
--timeline-item-margin: 1.5rem 0;
|
||||||
--timeline-item-margin-odd: 0 0 0 calc(50% - var(--timeline-circle-size) / 2);
|
--timeline-item-margin-odd: var(--timeline-item-margin);
|
||||||
--timeline-item-margin-even: 0 calc(50% - var(--timeline-circle-size) / 2) 0 0;
|
--timeline-item-margin-even: var(--timeline-item-margin);
|
||||||
|
--timeline-item-text-align-even: left;
|
||||||
|
--timeline-item-content-padding-odd: 0 0 0 var(--container-spacing-left-safe);
|
||||||
|
--timeline-item-content-padding-even: var(--timeline-item-content-padding-odd);
|
||||||
|
|
||||||
--table-border-radius: 1rem;
|
--table-border-radius: 1rem;
|
||||||
--table-outer-spacing: 0 var(--container-spacing-right-safe) 0
|
--table-outer-spacing: 0 var(--container-spacing-right-safe) 0 var(--container-spacing-left-safe);
|
||||||
var(--container-spacing-left-safe);
|
|
||||||
--table-cell-padding: 0.25rem 0.5rem;
|
--table-cell-padding: 0.25rem 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* semantic color variables for this project */
|
/* semantic color variables for this project */
|
||||||
:root {
|
:root {
|
||||||
--color-background: var(--theme-c-antique-white);
|
--color-background: var(--theme-c-antique-white);
|
||||||
--color-background-body: var(--theme-c-walnut-brown);
|
--color-background-body: var(--theme-c-desert-sand);
|
||||||
|
|
||||||
--color-border: var(--theme-c-crystal-blue);
|
--color-modal-background: var(--theme-c-desert-sand);
|
||||||
|
--color-modal-border: var(--theme-c-walnut-brown);
|
||||||
|
|
||||||
|
--color-button: var(--theme-c-walnut-brown);
|
||||||
|
--color-button-box-shadow: var(--theme-c-deep-oak);
|
||||||
|
--color-button-text: var(--theme-c-text-dark);
|
||||||
|
|
||||||
--color-heading: var(--theme-c-text-light);
|
--color-heading: var(--theme-c-text-light);
|
||||||
--color-text: var(--theme-c-text-light);
|
--color-text: var(--theme-c-text-light);
|
||||||
|
@ -147,6 +154,11 @@
|
||||||
--table-cell-text-color: var(--theme-c-text-light);
|
--table-cell-text-color: var(--theme-c-text-light);
|
||||||
--table-cell-text-color-hover: var(--theme-c-text-dark);
|
--table-cell-text-color-hover: var(--theme-c-text-dark);
|
||||||
|
|
||||||
|
--color-artist-link-background: var(--theme-c-text-light);
|
||||||
|
--color-artist-link-background-hover: var(--theme-c-text-dark);
|
||||||
|
--color-artist-link-icon: var(--theme-c-text-dark);
|
||||||
|
--color-artist-link-icon-hover: var(--theme-c-text-light);
|
||||||
|
|
||||||
--timeline-stroke-color: var(--theme-c-dark-grey-blue);
|
--timeline-stroke-color: var(--theme-c-dark-grey-blue);
|
||||||
--timeline-circle-background: var(--theme-c-dark-grey-blue-translucent);
|
--timeline-circle-background: var(--theme-c-dark-grey-blue-translucent);
|
||||||
--timeline-icon-color: var(--theme-c-antique-white);
|
--timeline-icon-color: var(--theme-c-antique-white);
|
||||||
|
@ -156,8 +168,14 @@
|
||||||
:root {
|
:root {
|
||||||
--page-background-image: url(@/assets/layered-waves-dark.svg);
|
--page-background-image: url(@/assets/layered-waves-dark.svg);
|
||||||
--color-background: var(--theme-c-dark-grey-blue);
|
--color-background: var(--theme-c-dark-grey-blue);
|
||||||
|
--color-background-body: var(--theme-c-deep-oak);
|
||||||
|
|
||||||
--color-border: var(--theme-c-desert-sand);
|
--color-modal-background: var(--theme-c-deep-oak);
|
||||||
|
--color-modal-border: var(--theme-c-antique-white);
|
||||||
|
|
||||||
|
--color-button: var(--theme-c-antique-white);
|
||||||
|
--color-button-box-shadow: var(--theme-c-desert-sand);
|
||||||
|
--color-button-text: var(--theme-c-text-light);
|
||||||
|
|
||||||
--color-heading: var(--theme-c-text-dark);
|
--color-heading: var(--theme-c-text-dark);
|
||||||
--color-text: var(--theme-c-text-dark);
|
--color-text: var(--theme-c-text-dark);
|
||||||
|
@ -174,6 +192,11 @@
|
||||||
--table-cell-text-color: var(--theme-c-text-dark);
|
--table-cell-text-color: var(--theme-c-text-dark);
|
||||||
--table-cell-text-color-hover: var(--theme-c-text-dark);
|
--table-cell-text-color-hover: var(--theme-c-text-dark);
|
||||||
|
|
||||||
|
--color-artist-link-background: var(--theme-c-text-dark);
|
||||||
|
--color-artist-link-background-hover: var(--theme-c-text-dark);
|
||||||
|
--color-artist-link-icon: var(--theme-c-text-light);
|
||||||
|
--color-artist-link-icon-hover: var(--theme-c-text-light);
|
||||||
|
|
||||||
--timeline-stroke-color: var(--theme-c-crystal-blue);
|
--timeline-stroke-color: var(--theme-c-crystal-blue);
|
||||||
--timeline-circle-background: var(--theme-c-crystal-blue-translucent);
|
--timeline-circle-background: var(--theme-c-crystal-blue-translucent);
|
||||||
--timeline-icon-color: var(--theme-c-antique-white);
|
--timeline-icon-color: var(--theme-c-antique-white);
|
||||||
|
@ -184,6 +207,8 @@
|
||||||
:root {
|
:root {
|
||||||
--table-cell-padding: 0.5rem 1rem;
|
--table-cell-padding: 0.5rem 1rem;
|
||||||
--font-size: 20px;
|
--font-size: 20px;
|
||||||
|
--timeline-circle-size: 4rem;
|
||||||
|
--modal-width: 30rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,28 +218,19 @@
|
||||||
--page-background-image-max-height: 100vh;
|
--page-background-image-max-height: 100vh;
|
||||||
|
|
||||||
--color-table-color-cell-width: 10rem;
|
--color-table-color-cell-width: 10rem;
|
||||||
--textblock-padding: 0 var(--container-spacing-right-safe) 0
|
--textblock-padding: 0 var(--container-spacing-right-safe) 0 var(--container-spacing-left-safe);
|
||||||
var(--container-spacing-left-safe);
|
|
||||||
|
|
||||||
--navigation-cutout: 0 0 0
|
--navigation-cutout: 0 0 0 calc(var(--navigation-size) + env(safe-area-inset-left));
|
||||||
calc(var(--navigation-size) + env(safe-area-inset-left));
|
|
||||||
--navigation-cutout-page-background: 0;
|
--navigation-cutout-page-background: 0;
|
||||||
--navigation-cutout-main: var(--navigation-cutout);
|
--navigation-cutout-main: var(--navigation-cutout);
|
||||||
|
|
||||||
--navigation-flow: column nowrap;
|
--navigation-flow: column nowrap;
|
||||||
--navigation-justify: flex-start;
|
--navigation-justify: flex-start;
|
||||||
--navigation-align: stretch;
|
--navigation-align: stretch;
|
||||||
--navigation-position-top: 0;
|
--navigation-position: 0 auto 0 0;
|
||||||
--navigation-position-right: auto;
|
|
||||||
--navigation-position-bottom: 0;
|
|
||||||
--navigation-position-left: 0;
|
|
||||||
|
|
||||||
--navigation-width: calc(
|
--navigation-width: calc(var(--navigation-size) + env(safe-area-inset-left));
|
||||||
var(--navigation-size) + env(safe-area-inset-left)
|
--navigation-width-expanded: var(--navigation-width);
|
||||||
);
|
|
||||||
--navigation-width-expanded: calc(
|
|
||||||
var(--navigation-size) * 3.75 + env(safe-area-inset-left)
|
|
||||||
);
|
|
||||||
|
|
||||||
--navigation-link-display: block;
|
--navigation-link-display: block;
|
||||||
--navigation-link-flex: 0 0 --navigation-size;
|
--navigation-link-flex: 0 0 --navigation-size;
|
||||||
|
@ -240,7 +256,18 @@
|
||||||
|
|
||||||
@media (min-width: 64em) {
|
@media (min-width: 64em) {
|
||||||
:root {
|
:root {
|
||||||
|
--timeline-max-width: calc(var(--timeline-circle-size) * 15);
|
||||||
--timeline-stroke-length: calc(100% - var(--timeline-circle-size));
|
--timeline-stroke-length: calc(100% - var(--timeline-circle-size));
|
||||||
|
--timeline-stroke-position-even: var(--timeline-stroke-position-top)
|
||||||
|
var(--timeline-stroke-position-horizontal) auto auto;
|
||||||
|
--timeline-item-margin-odd: 0 0 0 calc(50% - var(--timeline-circle-size) / 2);
|
||||||
|
--timeline-item-margin-even: 0 calc(50% - var(--timeline-circle-size) / 2) 0 0;
|
||||||
|
--timeline-item-flex-order: 1;
|
||||||
|
--timeline-item-content-margin-odd: 0 0 0 1rem;
|
||||||
|
--timeline-item-content-margin-even: 0 1rem 0 0;
|
||||||
|
--timeline-item-text-align-even: right;
|
||||||
|
--timeline-item-content-padding-odd: var(--textblock-padding);
|
||||||
|
--timeline-item-content-padding-even: var(--textblock-padding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,3 +282,16 @@
|
||||||
--font-size: 28px;
|
--font-size: 28px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) and (min-width: 50em) {
|
||||||
|
:root {
|
||||||
|
--navigation-width-expanded: calc(var(--navigation-size) * 3.75 + env(safe-area-inset-left));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion) {
|
||||||
|
:root {
|
||||||
|
--page-transition: opacity 0.5s ease;
|
||||||
|
--page-transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
@import "@/assets/fonts/arvo/arvo";
|
@import '@/assets/fonts/arvo/arvo';
|
||||||
@import "@/assets/fonts/secular-one/secular-one";
|
@import '@/assets/fonts/secular-one/secular-one';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@import "@/scss/base";
|
@import '@/scss/base';
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
font-family: var(--font-family-copy);
|
font-family: var(--font-family-copy);
|
||||||
|
@ -19,7 +19,9 @@ body {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
background: var(--color-background-body);
|
background: var(--color-background-body);
|
||||||
transition: color 0.5s, background-color 0.5s;
|
transition:
|
||||||
|
color 0.5s,
|
||||||
|
background-color 0.5s;
|
||||||
line-height: var(--text-line-height);
|
line-height: var(--text-line-height);
|
||||||
|
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
|
@ -41,11 +43,9 @@ main {
|
||||||
&:after {
|
&:after {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
content: "";
|
content: '';
|
||||||
position: sticky;
|
position: sticky;
|
||||||
right: 0;
|
inset: auto 0 var(--navigation-cutout-page-background) 0;
|
||||||
bottom: var(--navigation-cutout-page-background);
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
height: var(--page-background-image-height);
|
height: var(--page-background-image-height);
|
||||||
max-height: var(--page-background-image-max-height);
|
max-height: var(--page-background-image-max-height);
|
||||||
|
@ -84,6 +84,7 @@ h1 {
|
||||||
p {
|
p {
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
margin: var(--paragraph-margin) 0;
|
margin: var(--paragraph-margin) 0;
|
||||||
|
hyphens: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@ -96,8 +97,7 @@ a {
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--color-link-text-hover);
|
color: var(--color-link-text-hover);
|
||||||
box-shadow: inset 0 calc(var(--link-inset-box-shadow) * -1) 0 0
|
box-shadow: inset 0 calc(var(--link-inset-box-shadow) * -1) 0 0 var(--color-link-text-underline);
|
||||||
var(--color-link-text-underline);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +144,18 @@ table {
|
||||||
background-color: var(--table-row-background-hover);
|
background-color: var(--table-row-background-hover);
|
||||||
color: var(--table-cell-text-color-hover);
|
color: var(--table-cell-text-color-hover);
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--theme-c-text-dark);
|
||||||
|
box-shadow: inset 0 calc(var(--link-inset-box-shadow) * -0.125) 0 0
|
||||||
|
var(--table-cell-text-color-hover);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--theme-c-text-light);
|
||||||
|
box-shadow: inset 0 calc(var(--link-inset-box-shadow) * -1) 0 0
|
||||||
|
var(--table-cell-text-color-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
fill: var(--table-cell-text-color-hover);
|
fill: var(--table-cell-text-color-hover);
|
||||||
}
|
}
|
||||||
|
@ -159,3 +171,14 @@ table {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
transition: var(--page-transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-from,
|
||||||
|
.fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: var(--page-transform);
|
||||||
|
}
|
||||||
|
|
|
@ -1,196 +1,165 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import RefImage from "@/components/RefImage.vue";
|
import RefImage from '@/components/RefImage.vue'
|
||||||
import ColorTable from "@/components/ColorTable.vue";
|
import ColorTable from '@/components/ColorTable.vue'
|
||||||
|
|
||||||
const colors = [
|
const colors = [
|
||||||
{ name: "Front", value: "#e7c7b1" },
|
{ name: 'data.colors.front', value: '#e7c7b1' },
|
||||||
{ name: "Arms, legs", value: "#493428" },
|
{ name: 'data.colors.limbs', value: '#493428' },
|
||||||
{ name: "Back Main", value: "#422322" },
|
{ name: 'data.colors.back', value: '#422322' },
|
||||||
{ name: "Back Spine", value: "#341c1c" },
|
{ name: 'data.colors.spine', value: '#341c1c' },
|
||||||
{ name: "Highlight scales, tissue", value: "#6bb9db" },
|
{ name: 'data.colors.tissue', value: '#6bb9db' },
|
||||||
{ name: "Spikes, tail club", value: "#f8ebdd" },
|
{ name: 'data.colors.spikes', value: '#f8ebdd' },
|
||||||
{ name: "Eyes primary", value: "#a7eef1" },
|
{ name: 'data.colors.eyesPrimary', value: '#a7eef1' },
|
||||||
{ name: "Eyes secondary", value: "#6dabd1" },
|
{ name: 'data.colors.eyesSecondary', value: '#6dabd1' }
|
||||||
];
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<article>
|
||||||
<h1>{{ $route.meta.title }}</h1>
|
<section>
|
||||||
</section>
|
<h1>{{ $t(`${$route.meta.title}`) }}</h1>
|
||||||
|
</section>
|
||||||
|
|
||||||
<RefImage dropshadow>
|
<RefImage dropshadow>
|
||||||
<template v-if="$route.query.nsfw">
|
<template v-if="$route.query.nsfw">
|
||||||
<source
|
<source
|
||||||
srcset="
|
srcset="
|
||||||
@/assets/viktor-ref-NSFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&avif&quality=75&withoutEnlargement&srcset
|
@/assets/viktor-ref-NSFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||||
"
|
"
|
||||||
sizes="(min-width: 64em) 500px, (min-width: 50em) 420px, 375px"
|
sizes="(min-width: 64em) 500px, (min-width: 50em) 420px, 375px"
|
||||||
type="image/avif"
|
type="image/avif"
|
||||||
/>
|
/>
|
||||||
<source
|
<source
|
||||||
srcset="
|
srcset="
|
||||||
@/assets/viktor-ref-NSFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&webp&quality=100&withoutEnlargement&srcset
|
@/assets/viktor-ref-NSFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||||
"
|
"
|
||||||
sizes="(min-width: 64em) 500px, (min-width: 50em) 420px, 375px"
|
sizes="(min-width: 64em) 500px, (min-width: 50em) 420px, 375px"
|
||||||
type="image/webp"
|
type="image/webp"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
srcset="
|
srcset="
|
||||||
@/assets/viktor-ref-NSFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&png&withoutEnlargement&srcset
|
@/assets/viktor-ref-NSFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&format=png&withoutEnlargement&as=srcset
|
||||||
"
|
"
|
||||||
sizes="(min-width: 64em) 500px, (min-width: 50em) 420px, 375px"
|
sizes="(min-width: 64em) 500px, (min-width: 50em) 420px, 375px"
|
||||||
alt="Viktor Ref by sabertoofs"
|
alt="Viktor Ref by sabertoofs"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<source
|
<source
|
||||||
srcset="
|
srcset="
|
||||||
@/assets/viktor-ref-SFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&avif&quality=75&withoutEnlargement&srcset
|
@/assets/viktor-ref-SFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||||
"
|
"
|
||||||
sizes="(min-width: 64em) 500px, (min-width: 50em) 420px, 375px"
|
sizes="(min-width: 64em) 500px, (min-width: 50em) 420px, 375px"
|
||||||
type="image/avif"
|
type="image/avif"
|
||||||
/>
|
/>
|
||||||
<source
|
<source
|
||||||
srcset="
|
srcset="
|
||||||
@/assets/viktor-ref-SFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&webp&quality=100&withoutEnlargement&srcset
|
@/assets/viktor-ref-SFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||||
"
|
"
|
||||||
sizes="(min-width: 64em) 500px, (min-width: 50em) 420px, 375px"
|
sizes="(min-width: 64em) 500px, (min-width: 50em) 420px, 375px"
|
||||||
type="image/webp"
|
type="image/webp"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
srcset="
|
srcset="
|
||||||
@/assets/viktor-ref-SFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&png&withoutEnlargement&srcset
|
@/assets/viktor-ref-SFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&format=png&withoutEnlargement&as=srcset
|
||||||
"
|
"
|
||||||
sizes="(min-width: 64em) 500px, (min-width: 50em) 420px, 375px"
|
sizes="(min-width: 64em) 500px, (min-width: 50em) 420px, 375px"
|
||||||
alt="Viktor Ref by sabertoofs"
|
:alt="`${$t('anatomy.images.back.caption')} by sabertoofs`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #caption>
|
<template #caption>
|
||||||
Viktor Ref © <a href="http://twitter.com/sabertoofs">sabertoofs</a>
|
{{ $t('anatomy.images.back.caption') }} ©
|
||||||
</template>
|
<a href="http://twitter.com/sabertoofs">sabertoofs</a>
|
||||||
</RefImage>
|
</template>
|
||||||
|
</RefImage>
|
||||||
|
|
||||||
<ColorTable :colors="colors"></ColorTable>
|
<ColorTable :colors="colors"></ColorTable>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<p>
|
<p v-for="(p, i) in $tm('anatomy.paragraphs[0]')" :key="i">{{ p }}</p>
|
||||||
Viktor is a bipedal plantigrade Ankylosaurus. His skin is mostly
|
</section>
|
||||||
bicolored, with several shades of brown.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
<RefImage dropshadow>
|
||||||
His forehead, nose, chest, belly and crotch are of light desert sand
|
<template v-if="$route.query.nsfw">
|
||||||
colors that run through the underside of his tail.
|
<source
|
||||||
</p>
|
srcset="
|
||||||
|
@/assets/viktor-front-NSFW-alpha.png?w=430;530;590;710;860;1060;1180;1420;1290;1590;1770;2130&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||||
<p>
|
"
|
||||||
His cheeks, shoulders, back and thighs, in contrast, stand out with a rich
|
sizes="(min-width: 64em) 710px,
|
||||||
walnut brown, which is also found on the sides of his tail. From the back
|
|
||||||
of his head, down his spine and across the top of his tail is a continuous
|
|
||||||
strip of deep dark cedar brown. Arms and legs are distinguished by a light
|
|
||||||
earthy brown.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<RefImage dropshadow>
|
|
||||||
<template v-if="$route.query.nsfw">
|
|
||||||
<source
|
|
||||||
srcset="
|
|
||||||
@/assets/viktor-front-NSFW-alpha.png?w=430;530;590;710;860;1060;1180;1420;1290;1590;1770;2130&avif&quality=75&withoutEnlargement&srcset
|
|
||||||
"
|
|
||||||
sizes="(min-width: 64em) 710px,
|
|
||||||
(min-width: 50em) 590px,
|
(min-width: 50em) 590px,
|
||||||
(min-width: 27em) 530px,
|
(min-width: 27em) 530px,
|
||||||
430px"
|
430px"
|
||||||
type="image/avif"
|
type="image/avif"
|
||||||
/>
|
/>
|
||||||
<source
|
<source
|
||||||
srcset="
|
srcset="
|
||||||
@/assets/viktor-front-NSFW-alpha.png?w=430;530;590;710;860;1060;1180;1420;1290;1590;1770;2130&webp&quality=100&withoutEnlargement&srcset
|
@/assets/viktor-front-NSFW-alpha.png?w=430;530;590;710;860;1060;1180;1420;1290;1590;1770;2130&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||||
"
|
"
|
||||||
sizes="(min-width: 64em) 710px,
|
sizes="(min-width: 64em) 710px,
|
||||||
(min-width: 50em) 590px,
|
(min-width: 50em) 590px,
|
||||||
(min-width: 27em) 530px,
|
(min-width: 27em) 530px,
|
||||||
430px"
|
430px"
|
||||||
type="image/webp"
|
type="image/webp"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
srcset="
|
srcset="
|
||||||
@/assets/viktor-front-NSFW-alpha.png?w=430;530;590;710;860;1060;1180;1420;1290;1590;1770;2130&png&withoutEnlargement&srcset
|
@/assets/viktor-front-NSFW-alpha.png?w=430;530;590;710;860;1060;1180;1420;1290;1590;1770;2130&format=png&withoutEnlargement&as=srcset
|
||||||
"
|
"
|
||||||
sizes="(min-width: 64em) 710px,
|
sizes="(min-width: 64em) 710px,
|
||||||
(min-width: 50em) 590px,
|
(min-width: 50em) 590px,
|
||||||
(min-width: 27em) 530px,
|
(min-width: 27em) 530px,
|
||||||
430px"
|
430px"
|
||||||
alt="Viktor frontal shot by sabertoofs"
|
alt="Viktor frontal shot by sabertoofs"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<source
|
<source
|
||||||
srcset="
|
srcset="
|
||||||
@/assets/viktor-front-SFW-alpha.png?w=430;530;590;710;860;1060;1180;1420;1290;1590;1770;2130&avif&quality=75&withoutEnlargement&srcset
|
@/assets/viktor-front-SFW-alpha.png?w=430;530;590;710;860;1060;1180;1420;1290;1590;1770;2130&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||||
"
|
"
|
||||||
sizes="(min-width: 64em) 710px,
|
sizes="(min-width: 64em) 710px,
|
||||||
(min-width: 50em) 590px,
|
(min-width: 50em) 590px,
|
||||||
(min-width: 27em) 530px,
|
(min-width: 27em) 530px,
|
||||||
430px"
|
430px"
|
||||||
type="image/avif"
|
type="image/avif"
|
||||||
/>
|
/>
|
||||||
<source
|
<source
|
||||||
srcset="
|
srcset="
|
||||||
@/assets/viktor-front-SFW-alpha.png?w=430;530;590;710;860;1060;1180;1420;1290;1590;1770;2130&webp&quality=100&withoutEnlargement&srcset
|
@/assets/viktor-front-SFW-alpha.png?w=430;530;590;710;860;1060;1180;1420;1290;1590;1770;2130&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||||
"
|
"
|
||||||
sizes="(min-width: 64em) 710px,
|
sizes="(min-width: 64em) 710px,
|
||||||
(min-width: 50em) 590px,
|
(min-width: 50em) 590px,
|
||||||
(min-width: 27em) 530px,
|
(min-width: 27em) 530px,
|
||||||
430px"
|
430px"
|
||||||
type="image/webp"
|
type="image/webp"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
srcset="
|
srcset="
|
||||||
@/assets/viktor-front-SFW-alpha.png?w=430;530;590;710;860;1060;1180;1420;1290;1590;1770;2130&png&withoutEnlargement&srcset
|
@/assets/viktor-front-SFW-alpha.png?w=430;530;590;710;860;1060;1180;1420;1290;1590;1770;2130&format=png&withoutEnlargement&as=srcset
|
||||||
"
|
"
|
||||||
sizes="(min-width: 64em) 710px,
|
sizes="(min-width: 64em) 710px,
|
||||||
(min-width: 50em) 590px,
|
(min-width: 50em) 590px,
|
||||||
(min-width: 27em) 530px,
|
(min-width: 27em) 530px,
|
||||||
430px"
|
430px"
|
||||||
alt="Viktor frontal shot by sabertoofs"
|
:alt="`${$t('anatomy.images.front.caption')} by sabertoofs`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #caption>
|
<template #caption>
|
||||||
Viktor frontal shot ©
|
{{ $t('anatomy.images.front.caption') }} ©
|
||||||
<a href="http://twitter.com/sabertoofs">sabertoofs</a>
|
<a href="http://twitter.com/sabertoofs">sabertoofs</a>
|
||||||
</template>
|
</template>
|
||||||
</RefImage>
|
</RefImage>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<p>
|
<p v-for="(p, i) in $tm('anatomy.paragraphs[1]')" :key="i">{{ p }}</p>
|
||||||
His entire body is speckled with scattered aquamarine spots, which come in
|
</section>
|
||||||
pairs of one large and one small spot. The only exceptions are the spots
|
</article>
|
||||||
on his cheeks and behind his lower cheek horns, which come in threes and
|
|
||||||
his the top side of his snout, which sports a big single spot. Mouth,
|
|
||||||
tongue, nostrils and any other tissue of his body also feature this color.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>His eyes shine with a mixture of light sea green and electric blue.</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Horns and claws are of a typical bone white. Starting with the double pair
|
|
||||||
of horns on his head, a parallel line of horns continues down his back,
|
|
||||||
with additional horns adorning his shoulders and thighs. His tail is also
|
|
||||||
armed with horns on both sides along its entire length. At the tip of the
|
|
||||||
tail is a club-like ossification that he can use for fending off foes.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>He owes his strong, stocky physique to years of hard physical labor.</p>
|
|
||||||
</section>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,121 +1,103 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import AttributionTable from "@/components/AttributionTable.vue";
|
import AttributionTable from '@/components/AttributionTable.vue'
|
||||||
|
|
||||||
import ViktorRefAlpha from "@/assets/viktor-ref-SFW-alpha.png?w=400&webp&quality=100&imagetools";
|
import ViktorRefAlpha from '@/assets/viktor-ref-SFW-alpha.png?w=400&format=webp&quality=100&imagetools'
|
||||||
import ViktorFront from "@/assets/viktor-front-SFW-alpha.png?w=400&webp&quality=100&imagetools";
|
import ViktorFront from '@/assets/viktor-front-SFW-alpha.png?w=400&format=webp&quality=100&imagetools'
|
||||||
|
|
||||||
const attributions = [
|
const attributions = [
|
||||||
{
|
{
|
||||||
artwork: ViktorRefAlpha,
|
artwork: ViktorRefAlpha,
|
||||||
artist: "sabertoofs",
|
artist: 'sabertoofs',
|
||||||
links: {
|
links: {
|
||||||
furaffinity: "https://www.furaffinity.net/user/sabertoofs",
|
furaffinity: 'https://www.furaffinity.net/user/sabertoofs',
|
||||||
twitter: "https://twitter.com/sabertoofs",
|
twitter: 'https://twitter.com/sabertoofs'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
artwork: ViktorFront,
|
artwork: ViktorFront,
|
||||||
artist: "sabertoofs",
|
artist: 'sabertoofs',
|
||||||
links: {
|
links: {
|
||||||
furaffinity: "https://www.furaffinity.net/user/sabertoofs",
|
furaffinity: 'https://www.furaffinity.net/user/sabertoofs',
|
||||||
twitter: "https://twitter.com/sabertoofs",
|
twitter: 'https://twitter.com/sabertoofs'
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
];
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<article>
|
||||||
<h1>{{ $route.meta.title }}</h1>
|
<section>
|
||||||
<h2>Artwork</h2>
|
<h1>{{ $t(`${$route.meta.title}`) }}</h1>
|
||||||
</section>
|
<h2>{{ $t('attributions.artwork.heading') }}</h2>
|
||||||
<AttributionTable :attributions="attributions" />
|
</section>
|
||||||
<section>
|
<AttributionTable :attributions="attributions" />
|
||||||
<h2>Other</h2>
|
<section>
|
||||||
</section>
|
<h2>{{ $t('attributions.other.heading') }}</h2>
|
||||||
<table>
|
</section>
|
||||||
<thead>
|
<table>
|
||||||
<tr>
|
<thead>
|
||||||
<th>Type</th>
|
<tr>
|
||||||
<th>Source</th>
|
<th>{{ $t('attributions.other.headings[0]') }}</th>
|
||||||
</tr>
|
<th>{{ $t('attributions.other.headings[1]') }}</th>
|
||||||
</thead>
|
</tr>
|
||||||
<tbody>
|
</thead>
|
||||||
<tr>
|
<tbody>
|
||||||
<td>Icons</td>
|
<tr>
|
||||||
<td>
|
<td>{{ $t('attributions.other.icons[0]') }}</td>
|
||||||
<a
|
<td>
|
||||||
href="https://fontawesome.com/license/free"
|
<a
|
||||||
target="_blank"
|
href="https://fontawesome.com/license/free"
|
||||||
rel="noopener noreferrer"
|
target="_blank"
|
||||||
>
|
rel="noopener noreferrer"
|
||||||
Font Awesome
|
>
|
||||||
</a>
|
Font Awesome
|
||||||
</td>
|
</a>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>Heading Font</td>
|
<tr>
|
||||||
<td>
|
<td>{{ $t('attributions.other.headingFont[0]') }}</td>
|
||||||
<a
|
<td>
|
||||||
href="https://github.com/MichalSahar/Secular"
|
<a
|
||||||
target="_blank"
|
href="https://github.com/MichalSahar/Secular"
|
||||||
rel="noopener noreferrer"
|
target="_blank"
|
||||||
>
|
rel="noopener noreferrer"
|
||||||
Secular One
|
>
|
||||||
</a>
|
Secular One
|
||||||
by
|
</a>
|
||||||
<a
|
{{ $t('attributions.other.headingFont[1]') }}
|
||||||
href="https://github.com/MichalSahar"
|
<a href="https://github.com/MichalSahar" target="_blank" rel="noopener noreferrer">
|
||||||
target="_blank"
|
Michal Sahar
|
||||||
rel="noopener noreferrer"
|
</a>
|
||||||
>
|
</td>
|
||||||
Michal Sahar
|
</tr>
|
||||||
</a>
|
<tr>
|
||||||
</td>
|
<td>{{ $t('attributions.other.copyFont[0]') }}</td>
|
||||||
</tr>
|
<td>
|
||||||
<tr>
|
<a
|
||||||
<td>Copy Font</td>
|
href="https://antonkoovit.com/typefaces/arvo"
|
||||||
<td>
|
target="_blank"
|
||||||
<a
|
rel="noopener noreferrer"
|
||||||
href="https://antonkoovit.com/typefaces/arvo"
|
>
|
||||||
target="_blank"
|
Arvo
|
||||||
rel="noopener noreferrer"
|
</a>
|
||||||
>
|
{{ $t('attributions.other.copyFont[1]') }}
|
||||||
Arvo
|
<a href="https://antonkoovit.com/" target="_blank" rel="noopener noreferrer">
|
||||||
</a>
|
Anton Koovit
|
||||||
by
|
</a>
|
||||||
<a
|
</td>
|
||||||
href="https://antonkoovit.com/"
|
</tr>
|
||||||
target="_blank"
|
<tr>
|
||||||
rel="noopener noreferrer"
|
<td>{{ $t('attributions.other.background[0]') }}</td>
|
||||||
>
|
<td>
|
||||||
Anton Koovit
|
{{ $t('attributions.other.background[1][0]') }}
|
||||||
</a>
|
<a href="https://haikei.app/" target="_blank" rel="noopener noreferrer">Haikei</a>
|
||||||
</td>
|
{{ $t('attributions.other.background[1][1]') }}
|
||||||
</tr>
|
<a href="https://zcreativelabs.com/" target="_blank" rel="noopener noreferrer">
|
||||||
<tr>
|
z creative labs
|
||||||
<td>Background</td>
|
</a>
|
||||||
<td>
|
</td>
|
||||||
Layered Waves from
|
</tr>
|
||||||
<a
|
</tbody>
|
||||||
href="https://haikei.app/"
|
</table>
|
||||||
target="_blank"
|
</article>
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Haikei
|
|
||||||
</a>
|
|
||||||
by
|
|
||||||
<a
|
|
||||||
href="https://zcreativelabs.com/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
z creative labs
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss"></style>
|
|
||||||
|
|
|
@ -1,79 +1,47 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import TimelineList from "@/components/TimelineList.vue";
|
import TimelineList from '@/components/TimelineList.vue'
|
||||||
import TimelineItem from "@/components/TimelineItem.vue";
|
import TimelineItem from '@/components/TimelineItem.vue'
|
||||||
import WhiskeyGlassIcon from "@/assets/icons/WhiskeyGlassIcon.vue";
|
import WhiskeyGlassIcon from '@/assets/icons/WhiskeyGlassIcon.vue'
|
||||||
import TreeIcon from "@/assets/icons/TreeIcon.vue";
|
import TreeIcon from '@/assets/icons/TreeIcon.vue'
|
||||||
import CarIcon from "@/assets/icons/CarIcon.vue";
|
import CarIcon from '@/assets/icons/CarIcon.vue'
|
||||||
import HelmetSafetyIcon from "@/assets/icons/HelmetSafetyIcon.vue";
|
import HelmetSafetyIcon from '@/assets/icons/HelmetSafetyIcon.vue'
|
||||||
import BoxesIcon from "@/assets/icons/BoxesIcon.vue";
|
import IndustryIcon from '@/assets/icons/IndustryIcon.vue'
|
||||||
|
import TruckIcon from '@/assets/icons/TruckIcon.vue'
|
||||||
|
import BoxesIcon from '@/assets/icons/BoxesIcon.vue'
|
||||||
|
|
||||||
|
interface Job {
|
||||||
|
title: string
|
||||||
|
desc: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const jobIcons = [
|
||||||
|
WhiskeyGlassIcon,
|
||||||
|
TreeIcon,
|
||||||
|
CarIcon,
|
||||||
|
HelmetSafetyIcon,
|
||||||
|
IndustryIcon,
|
||||||
|
TruckIcon,
|
||||||
|
BoxesIcon
|
||||||
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<article>
|
||||||
<h1>{{ $route.meta.title }}</h1>
|
<section>
|
||||||
<p>
|
<h1>{{ $t(`${$route.meta.title}`) }}</h1>
|
||||||
Viktor's had many different jobs in the past, some of which where very
|
<p v-for="(p, i) in $tm('career.paragraphs')" :key="i">{{ p }}</p>
|
||||||
formative, other's just paid the bills.
|
</section>
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<TimelineList>
|
<TimelineList>
|
||||||
<TimelineItem>
|
<TimelineItem v-for="(job, idx) in ($tm('career.jobs') as Job[])" :key="idx">
|
||||||
<template #icon><WhiskeyGlassIcon /></template>
|
<template #icon>
|
||||||
<template #headline>Bartender</template>
|
<component :is="jobIcons[idx as number]"></component>
|
||||||
<template #content>
|
</template>
|
||||||
<p>
|
<template #headline>{{ job.title }}</template>
|
||||||
Viktor's professional career began as a bartender at a pub in his
|
<template #content>
|
||||||
hometown. There he often doubled as a bouncer when a few guests got
|
<p>{{ job.desc }}</p>
|
||||||
too drunk and started making a fuss. A defining moment of that job was
|
</template>
|
||||||
when someone climbed over the counter and threatened him with a broken
|
</TimelineItem>
|
||||||
bottle. Wrestled to the ground and with a broken bottle in front of
|
</TimelineList>
|
||||||
his face, he had to make a split-second decision. With a powerful
|
</article>
|
||||||
swing of his tail bone, he knocked the attacker down. This experience
|
|
||||||
taught him the importance of effectively defending himself against
|
|
||||||
unpleasant fellows and to eliminate threats before they get close to
|
|
||||||
him ever again.
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
</TimelineItem>
|
|
||||||
<TimelineItem>
|
|
||||||
<template #icon><TreeIcon /></template>
|
|
||||||
<template #headline>Lumberjack</template>
|
|
||||||
<template #content>
|
|
||||||
<p>
|
|
||||||
After that he earned his living as a lumberjack in a remote sawmill.
|
|
||||||
For a while he enjoyed the peace and quiet of country life, but soon
|
|
||||||
he was drawn back into more urban surroundings.
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
</TimelineItem>
|
|
||||||
<TimelineItem>
|
|
||||||
<template #icon><CarIcon /></template>
|
|
||||||
<template #headline>Car Mechanic</template>
|
|
||||||
<template #content>
|
|
||||||
<p>
|
|
||||||
For a while, he made ends meet at an auto repair shop in a suburb, but
|
|
||||||
it was shut down after suffering persistent financial problems.
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
</TimelineItem>
|
|
||||||
<TimelineItem>
|
|
||||||
<template #icon><HelmetSafetyIcon /></template>
|
|
||||||
<template #headline>Construction Worker</template>
|
|
||||||
<template #content>
|
|
||||||
<p>Later, he worked as a construction worker.</p>
|
|
||||||
</template>
|
|
||||||
</TimelineItem>
|
|
||||||
<TimelineItem>
|
|
||||||
<template #icon><BoxesIcon /></template>
|
|
||||||
<template #headline>Werehouse Worker</template>
|
|
||||||
<template #content>
|
|
||||||
<p>
|
|
||||||
he eventually moved to a port town and started working as a warehouse
|
|
||||||
worker at the local post, where he mainly loads goods and ensures that
|
|
||||||
they are properly stored.
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
</TimelineItem>
|
|
||||||
</TimelineList>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,120 +1,73 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import DataTable from "@/components/DataTable.vue";
|
import DataTable from '@/components/DataTable.vue'
|
||||||
|
|
||||||
const dob = new Date("1987-12-08");
|
const dob = new Date('1987-12-08')
|
||||||
const locale = "en-US";
|
const locale = 'en-US'
|
||||||
const dateFormat = new Intl.DateTimeFormat(locale, {
|
const dateFormat = new Intl.DateTimeFormat(locale, {
|
||||||
year: "numeric",
|
year: 'numeric',
|
||||||
month: "long",
|
month: 'long',
|
||||||
day: "2-digit",
|
day: '2-digit'
|
||||||
});
|
})
|
||||||
|
|
||||||
const height = 227;
|
const height = 227
|
||||||
const weight = 175;
|
const weight = 175
|
||||||
|
|
||||||
const toImperial = (cm: number): string => {
|
const toImperial = (cm: number): string => {
|
||||||
const realFeet = (cm * 0.3937) / 12;
|
const realFeet = (cm * 0.3937) / 12
|
||||||
const feet = Math.floor(realFeet);
|
const feet = Math.floor(realFeet)
|
||||||
const inches = Math.round((realFeet - feet) * 12);
|
const inches = Math.round((realFeet - feet) * 12)
|
||||||
|
|
||||||
return `${feet}'${inches}"`;
|
return `${feet}'${inches}"`
|
||||||
};
|
}
|
||||||
|
|
||||||
const toInch = (cm: number): string => {
|
const toInch = (cm: number): string => {
|
||||||
return `${Math.round(cm / 2.45)} in`;
|
return `${Math.round(cm / 2.45)} in`
|
||||||
};
|
}
|
||||||
|
|
||||||
const toLbs = (kg: number): number => {
|
const toLbs = (kg: number): number => {
|
||||||
const nearExact = kg / 0.45359237;
|
const nearExact = kg / 0.45359237
|
||||||
const lbs = Math.floor(nearExact);
|
const lbs = Math.floor(nearExact)
|
||||||
|
|
||||||
return lbs;
|
return lbs
|
||||||
};
|
}
|
||||||
|
|
||||||
const heads = ["Key", "Value"];
|
const heads = ['data.general.heading[0]', 'data.general.heading[1]']
|
||||||
const data = [
|
const data = [
|
||||||
["Full Name", "Viktor Kraastav"],
|
['data.general.fullName[0]', 'data.general.fullName[1]'],
|
||||||
["Date of Birth", dateFormat.format(dob)],
|
['data.general.dob[0]', dateFormat.format(dob)],
|
||||||
["Sex/Gender", "male ♂️"],
|
['data.general.gender[0]', 'data.general.gender[1]'],
|
||||||
["Height", `${height} cm (${toImperial(height)})`],
|
['data.general.height[0]', `${height} cm (${toImperial(height)})`],
|
||||||
["Weight", `${weight} kg (${toLbs(weight)} lbs)`],
|
['data.general.weight[0]', `${weight} kg (${toLbs(weight)} lbs)`]
|
||||||
];
|
]
|
||||||
|
|
||||||
const sexHeads = ["Key", "Value"];
|
const sexHeads = ['data.sexuality.heading[0]', 'data.sexuality.heading[1]']
|
||||||
const sexData = [
|
const sexData = [
|
||||||
["Sexuality", "Gay"],
|
['data.sexuality.identifiesAs[0]', 'data.sexuality.identifiesAs[1]'],
|
||||||
["Preferred position", "Bottom"],
|
['data.sexuality.preferredRole[0]', 'data.sexuality.preferredRole[1]']
|
||||||
];
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<article>
|
||||||
<h1>{{ $route.meta.title }}</h1>
|
<section>
|
||||||
</section>
|
<h1>{{ $t(`${$route.meta.title}`) }}</h1>
|
||||||
|
</section>
|
||||||
|
|
||||||
<DataTable :headings="heads" :data="data"></DataTable>
|
<DataTable :headings="heads" :data="data"></DataTable>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Personality</h2>
|
<h2>{{ $t('general.personality.heading') }}</h2>
|
||||||
<p>
|
<p v-for="(p, i) in $tm('general.personality.paragraphs')" :key="i">{{ p }}</p>
|
||||||
Viktor is not a man of many words, his manner of expression is simple and
|
</section>
|
||||||
direct, and sometimes a little blunt. This brash manner doesn't go down
|
|
||||||
well with everyone and regularly gets him into trouble.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
<section>
|
||||||
At the same time, this is also an expression of his exuberant
|
<h2>{{ $t('general.sexuality.heading') }}</h2>
|
||||||
self-confidence. He won't take any crap from anyone and doesn't hesitate
|
</section>
|
||||||
to give someone a piece of his mind. If that results in fisticuffs, he'll
|
|
||||||
deal with them.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
<DataTable :headings="sexHeads" :data="sexData"></DataTable>
|
||||||
All this may make him look like a really unpleasant fellow, but he greatly
|
|
||||||
appreciates the company of people he trusts. He is very selective about
|
|
||||||
whom he counts among this group of people, though. He appreciates personal
|
|
||||||
contact, preferably over a few beers at his favorite bar.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
<section>
|
||||||
He likes things to be simple, which is why he is not a big fan of
|
<p v-for="(p, i) in $tm('general.sexuality.paragraphs')" :key="i">{{ p }}</p>
|
||||||
high-tech. He does have a smartphone, but he hates using it as much as he
|
</section>
|
||||||
dislikes the fact he's dependent on owning one.
|
</article>
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>Sexuality</h2>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<DataTable :headings="sexHeads" :data="sexData"></DataTable>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<p>
|
|
||||||
As a teenager, Viktor noticed that he felt a bit differently about the
|
|
||||||
boys in his class. Especially in gym class, his eyes tended to linger
|
|
||||||
longer on his classmates as they rough-housed in the locker room, as
|
|
||||||
pubescent boys do. However, he could never quite come to terms with the
|
|
||||||
idea that he might be a little different from his friends, who were all
|
|
||||||
beginning to take an interest in girls, unlike him, who was more
|
|
||||||
interested in his buddies.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
As he got older, he slowly learned to come to terms with the fact that he
|
|
||||||
saw more in his buddies than just friends. However, he still kept a low
|
|
||||||
profile.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Until one evening at a party, when one of his friends complained that he
|
|
||||||
wouldn't get any from his girlfriend. Both of them were already well
|
|
||||||
intoxicated and Viktor made him the slurred proposal that he would help
|
|
||||||
him out as his bro. At first his buddy was irritated by the offer, but
|
|
||||||
eventually agreed. The two snuck into a bedroom at the host's place, where
|
|
||||||
Viktor would let his buddy unload all the pent-up pressure inside him. He
|
|
||||||
wouldn't remember much of the party, but what he didn't forget how good it
|
|
||||||
felt to give himself to another man.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import WelcomeHeader from "@/components/WelcomeHeader.vue";
|
import WelcomeHeader from '@/components/WelcomeHeader.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<WelcomeHeader>
|
<article>
|
||||||
<template #main>Viktor Kraastav</template>
|
<WelcomeHeader>
|
||||||
<template #sub>Character Reference Page</template>
|
<template #main>{{ $t('welcomeHeader.mainTitle') }}</template>
|
||||||
</WelcomeHeader>
|
<template #sub>{{ $t('welcomeHeader.subTitle') }}</template>
|
||||||
|
</WelcomeHeader>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h3>Welcome to Viktor's Ref Page</h3>
|
<h3>{{ $t('home.heading') }}</h3>
|
||||||
|
|
||||||
<p>Here you can learn all about the ankylosaurus named Viktor.</p>
|
<p v-for="(p, i) in $tm('home.paragraphs')" :key="i">{{ p }}</p>
|
||||||
<p>Pick an item from the navigation to dive in!</p>
|
</section>
|
||||||
</section>
|
</article>
|
||||||
</template>
|
</template>
|
||||||
|
|
12
tsconfig.app.json
Normal file
12
tsconfig.app.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||||
|
"include": ["globals.d.ts", "env.d.ts", "src/**/*", "src/**/*.json", "src/**/*.vue", "package.json"],
|
||||||
|
"exclude": ["src/**/__tests__/*"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "@vue/tsconfig/tsconfig.node.json",
|
|
||||||
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*"],
|
|
||||||
"compilerOptions": {
|
|
||||||
"composite": true,
|
|
||||||
"types": ["node"]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +1,11 @@
|
||||||
{
|
{
|
||||||
"extends": "@vue/tsconfig/tsconfig.web.json",
|
"files": [],
|
||||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
|
||||||
"compilerOptions": {
|
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["./src/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
"path": "./tsconfig.config.json"
|
"path": "./tsconfig.node.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
17
tsconfig.node.json
Normal file
17
tsconfig.node.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"extends": "@tsconfig/node18/tsconfig.json",
|
||||||
|
"include": [
|
||||||
|
"globals.d.ts",
|
||||||
|
"vite.config.*",
|
||||||
|
"vitest.config.*",
|
||||||
|
"cypress.config.*",
|
||||||
|
"nightwatch.conf.*",
|
||||||
|
"playwright.config.*"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"types": ["node"]
|
||||||
|
}
|
||||||
|
}
|
23
vite.config.mts
Normal file
23
vite.config.mts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import { imagetools } from 'vite-imagetools'
|
||||||
|
import autoprefixer from 'autoprefixer'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
base: "/viktor/",
|
||||||
|
plugins: [vue(), imagetools()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
css: {
|
||||||
|
devSourcemap: true,
|
||||||
|
postcss: {
|
||||||
|
plugins: [autoprefixer({})]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
|
@ -1,19 +0,0 @@
|
||||||
import { fileURLToPath, URL } from "node:url";
|
|
||||||
|
|
||||||
import { defineConfig } from "vite";
|
|
||||||
import { imagetools } from "vite-imagetools";
|
|
||||||
import vue from "@vitejs/plugin-vue";
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
|
||||||
export default defineConfig({
|
|
||||||
base: "/viktor/",
|
|
||||||
plugins: [vue(), imagetools()],
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
css: {
|
|
||||||
devSourcemap: true,
|
|
||||||
},
|
|
||||||
});
|
|
Loading…
Add table
Add a link
Reference in a new issue