Compare commits

..

No commits in common. "main" and "0.4.0" have entirely different histories.
main ... 0.4.0

37 changed files with 4551 additions and 1963 deletions

View file

@ -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/skip-formatting' "@vue/eslint-config-prettier",
], ],
parserOptions: { parserOptions: {
ecmaVersion: 'latest' ecmaVersion: "latest",
} },
} };

View file

@ -1,8 +1 @@
{ {}
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"tabWidth": 2,
"singleQuote": true,
"printWidth": 100,
"trailingComma": "none"
}

View file

@ -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,13 +17,11 @@
<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 property="og:type" content="website" /> <meta name="og:type" content="website" />
<meta property="og:title" content="Viktor Kraastav - Reference Page" /> <meta name="og:title" content="Viktor Kraastav - Reference Page" />
<meta property="og:locale" content="en_US" /> <meta name="og:url" content="https://ref.sebin-nyshkim.net/viktor/" />
<meta property="og:locale:alternate" content="de_DE" /> <meta name="og:image" content="https://ref.sebin-nyshkim.net/viktor/preview.png" />
<meta property="og:url" content="https://ref.sebin-nyshkim.net/viktor/" /> <meta name="og:description" content="The official reference page for Viktor Kraastav with picture references and in-depth character descriptions" />
<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>

5221
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,38 +1,37 @@
{ {
"name": "viktor-reference", "name": "viktor-reference",
"version": "0.4.3", "version": "0.4.0",
"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", "preview": "vite preview --port 4173",
"build-only": "vite build", "build-only": "vite build",
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false", "type-check": "vue-tsc --noEmit",
"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.3.4", "vue": "^3.2.45",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.2.2",
"vue-router": "^4.2.4" "vue-router": "^4.1.6"
}, },
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.3.3", "@rushstack/eslint-patch": "^1.2.0",
"@tsconfig/node18": "^18.2.1", "@types/node": "^18.15.10",
"@types/node": "^20.5.9", "@types/vue-markdown": "^2.2.1",
"@vitejs/plugin-vue": "^4.3.4", "@vitejs/plugin-vue": "^4.1.0",
"@vue/eslint-config-prettier": "^8.0.0", "@vue/eslint-config-prettier": "^7.1.0",
"@vue/eslint-config-typescript": "^11.0.3", "@vue/eslint-config-typescript": "^11.0.2",
"@vue/tsconfig": "^0.4.0", "@vue/tsconfig": "^0.1.3",
"autoprefixer": "^10.4.15", "autoprefixer": "^10.4.14",
"eslint": "^8.48.0", "eslint": "^8.36.0",
"eslint-plugin-vue": "^9.17.0", "eslint-plugin-vue": "^9.10.0",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^3.0.3", "prettier": "^2.8.7",
"sass": "^1.66.1", "sass": "^1.60.0",
"typescript": "^5.2.2", "typescript": "^4.9.5",
"vite": "^4.4.9", "vite": "^4.2.1",
"vite-imagetools": "^5.0.8", "vite-imagetools": "^4.0.18",
"vue-tsc": "^1.8.8" "vue-tsc": "^1.2.0"
} }
} }

5
postcss.config.js Normal file
View file

@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
}

View file

@ -1,69 +1,70 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from "vue";
import { RouterView } from 'vue-router' import { RouterView } from "vue-router";
import { version } from '../package.json' import { version } from "../package.json";
import router from "@/router";
import ModalDialog from '@/components/ModalDialog.vue' import ModalDialog from "@/components/ModalDialog.vue";
import LocaleSwitcher from '@/components/LocaleSwitcher.vue' import LocaleSwitcher from "@/components/LocaleSwitcher.vue";
import LinkButton from '@/components/LinkButton.vue' import LinkButton from "@/components/LinkButton.vue";
import SiteNavigation from '@/components/SiteNavigation.vue' import SiteNavigation from "@/components/SiteNavigation.vue";
import NavigationItem from '@/components/NavigationItem.vue' import NavigationItem from "@/components/NavigationItem.vue";
import LanguageButton from '@/components/LanguageButton.vue' import LanguageButton from "@/components/LanguageButton.vue";
import SiteFooter from '@/components/SiteFooter.vue' import SiteFooter from "@/components/SiteFooter.vue";
import LanguageIcon from '@/assets/icons/LanguageIcon.vue' import LanguageIcon from "@/assets/icons/LanguageIcon.vue";
const locales = [ const locales = [
{ code: 'en', name: 'English', flag: '🇬🇧' }, { code: "en", name: "English", flag: "🇬🇧" },
{ code: 'de', name: 'Deutsch', flag: '🇩🇪' } { code: "de", name: "Deutsch", flag: "🇩🇪" },
] ];
const langswitcher = ref<InstanceType<typeof ModalDialog>>() const langswitcher = ref<InstanceType<typeof ModalDialog>>();
const showModal = () => { const showModal = () => {
langswitcher.value?.showModal() langswitcher.value?.showModal();
} };
const close = () => { const close = () => {
langswitcher.value?.close() langswitcher.value?.close();
} };
</script> </script>
<template> <template>
<ModalDialog id="lang-select" ref="langswitcher"> <ModalDialog id="lang-select" ref="langswitcher">
<template #heading>{{ $t('langswitcher.title') }}</template> <template #heading>{{ $t("langswitcher.title") }}</template>
<template #message> <template #message>
<p>{{ $t('langswitcher.prompt') }}</p> <p>{{ $t("langswitcher.prompt") }}</p>
<LocaleSwitcher id="locale-switch" v-model="$i18n.locale" :locales="locales" /> <LocaleSwitcher
id="locale-switch"
v-model="$i18n.locale"
:locales="locales"
/>
</template> </template>
<template #buttons> <template #buttons>
<LinkButton @click.prevent="close()"> <LinkButton @click.prevent="close()">
{{ $t('langswitcher.buttonClose') }} {{ $t("langswitcher.buttonClose") }}
</LinkButton> </LinkButton>
</template> </template>
</ModalDialog> </ModalDialog>
<SiteNavigation> <SiteNavigation>
<NavigationItem <NavigationItem
v-for="(route, idx) in $router.getRoutes()" v-for="(route, idx) in router.options.routes"
:key="idx" :key="idx"
:icon="route.meta?.icon" :icon="route.meta?.icon"
:href="route.path" :href="route.path"
> >{{ $t(`${route.meta?.title}`) }}</NavigationItem>
{{ $t(`${route.meta?.title}`) }}
</NavigationItem>
</SiteNavigation> </SiteNavigation>
<main> <main>
<LanguageButton> <LanguageButton>
<LanguageIcon @click.prevent="showModal()" /> <LanguageIcon @click.prevent="showModal()" />
</LanguageButton> </LanguageButton>
<RouterView v-slot="{ Component }"> <RouterView />
<Transition name="fade" mode="out-in">
<component :is="Component" :key="$route.path" />
</Transition>
</RouterView>
</main> </main>
<SiteFooter>v{{ version }} &copy; {{ new Date().getFullYear() }} Sebin Nyshkim</SiteFooter> <SiteFooter>
v{{ version }} &copy; {{ new Date().getFullYear() }} Sebin Nyshkim
</SiteFooter>
</template> </template>

View file

@ -1,23 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import FAIcon from '@/assets/icons/FurAffinityIcon.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>
@ -25,15 +25,19 @@ defineProps<Props>()
<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"> <th class="attribution-table__heading artwork">
{{ $t('attributions.artwork.headings[0]') }} {{ $t("attributions.artwork.headings[0]") }}
</th> </th>
<th class="attribution-table__heading artist"> <th class="attribution-table__heading artist">
{{ $t('attributions.artwork.headings[1]') }} {{ $t("attributions.artwork.headings[1]") }}
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody class="attribution-table__body"> <tbody class="attribution-table__body">
<tr class="attribution-table__row" v-for="(attrib, idx) in attributions" :key="idx"> <tr
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>

View file

@ -1,30 +1,33 @@
<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">{{ $t('data.colors.headings[0]') }}</th> <th class="color-table__heading name">{{ $t("data.colors.headings[0]") }}</th>
<th class="color-table__heading value">{{ $t('data.colors.headings[1]') }}</th> <th class="color-table__heading value">{{ $t("data.colors.headings[1]") }}</th>
<th class="color-table__heading color">{{ $t('data.colors.headings[2]') }}</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">{{ $t(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 class="color-table__cell color" :style="{ 'background-color': color.value }"></td> <td
class="color-table__cell color"
:style="{ 'background-color': color.value }"
></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -44,16 +47,8 @@ defineProps<Props>()
} }
&.value { &.value {
font-family: font-family: Menlo, JetBrains Mono, Source Code Pro, Monaco, Ubuntu Mono,
Menlo, Roboto Mono, Cascadia Code, Consolas, monospace;
JetBrains Mono,
Source Code Pro,
Monaco,
Ubuntu Mono,
Roboto Mono,
Cascadia Code,
Consolas,
monospace;
text-align: center; text-align: center;
} }
} }

View file

@ -1,17 +1,21 @@
<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 class="data-table__heading" v-for="(heading, idx) in headings" :key="idx"> <th
class="data-table__heading"
v-for="(heading, idx) in headings"
:key="idx"
>
{{ $t(heading) }} {{ $t(heading) }}
</th> </th>
</tr> </tr>

View file

@ -7,19 +7,14 @@
<style lang="scss"> <style lang="scss">
.lang-button { .lang-button {
position: fixed; position: fixed;
inset: 0.25rem 0.5rem auto auto; inset: 1rem 1rem auto auto;
min-width: 3rem; display: block;
max-height: 2.5rem;
min-width: 3.5rem;
z-index: 9001; z-index: 9001;
color: var(--color-text);
cursor: pointer; cursor: pointer;
&:before {
position: absolute;
inset: 0.325rem 0 0.7rem 0;
content: '';
background-color: var(--color-background);
border-radius: 0.3rem;
}
svg { svg {
fill: var(--color-text); fill: var(--color-text);
} }

View file

@ -1,10 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
interface Props { interface Props {
href?: string href?: string;
download?: boolean | any download?: boolean | any;
} }
defineProps<Props>() defineProps<Props>();
</script> </script>
<template> <template>

View file

@ -1,46 +1,43 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from "vue";
import CircleCheckIcon from '@/assets/icons/CircleCheckIcon.vue' import CircleCheckIcon from "@/assets/icons/CircleCheckIcon.vue";
import CircleIcon from '@/assets/icons/CircleIcon.vue' import CircleIcon from "@/assets/icons/CircleIcon.vue";
interface LocaleOption { interface LocaleOption {
code: string code: string;
name: string name: string;
flag: string flag: string;
} }
interface Props { interface Props {
modelValue: string modelValue: string;
id: string id: string;
locales: LocaleOption[] locales: LocaleOption[];
} }
interface Emits { const props = defineProps<Props>();
(e: 'update:modelValue', value: string): void const emit = defineEmits(["update:modelValue"]);
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const selectModel = computed({ const selectModel = computed({
get() { get() {
return props.modelValue return props.modelValue;
}, },
set(value) { set(value) {
emit('update:modelValue', value) emit("update:modelValue", value);
} },
}) });
</script> </script>
<template> <template>
<div class="localeselect"> <div class="localeselect">
<div v-for="locale in locales" class="localeselect__locale" :key="`locale-${locale.code}`"> <div v-for="locale in locales" class="localeselect__locale">
<input <input
type="radio" type="radio"
name="lang" name="lang"
class="localeselect__input" class="localeselect__input"
:id="`lang-${locale.code}`" :id="`lang-${locale.code}`"
:value="locale.code" :value="locale.code"
:key="`locale-${locale.code}`"
v-model="selectModel" v-model="selectModel"
/> />
<label class="localeselect__label" :for="`lang-${locale.code}`"> <label class="localeselect__label" :for="`lang-${locale.code}`">

View file

@ -1,27 +1,27 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from "vue";
interface Props { interface Props {
id: string id: string;
} }
defineProps<Props>() defineProps<Props>();
const modal = ref<HTMLDialogElement>() const modal = ref<HTMLDialogElement>();
const showModal = () => { const showModal = () => {
modal.value?.showModal() modal.value?.showModal();
document.body.inert = true document.body.inert = true;
document.body.classList.add('scroll-lock') document.body.classList.add("scroll-lock");
} };
const close = () => { const close = () => {
modal.value?.close() modal.value?.close();
document.body.inert = false document.body.inert = false;
document.body.classList.remove('scroll-lock') document.body.classList.remove("scroll-lock");
} };
defineExpose({ showModal, close }) defineExpose({ showModal, close });
</script> </script>
<template> <template>
@ -44,8 +44,6 @@ defineExpose({ showModal, close })
<style lang="scss"> <style lang="scss">
.modal { .modal {
position: fixed;
background: var(--color-modal-background); background: var(--color-modal-background);
color: var(--color-text); color: var(--color-text);

View file

@ -1,17 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import { RouterLink } from 'vue-router' import { RouterLink } from "vue-router";
interface Props { interface Props {
icon: unknown | object icon: unknown | object;
href: string href: string;
} }
defineProps<Props>() defineProps<Props>();
</script> </script>
<template> <template>
<RouterLink class="navigation__link" :to="href"> <RouterLink class="navigation__link" :to="href">
<component class="navigation__link-icon" :is="icon" /> <component class="navigation__link-icon" :is="icon"></component>
<span class="navigation__link-text"> <span class="navigation__link-text">
<slot></slot> <slot></slot>
</span> </span>

View file

@ -1,14 +1,17 @@
<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 class="figure__image" :class="{ 'figure__image--dropshadow': dropshadow }"> <picture
class="figure__image"
:class="{ 'figure__image--dropshadow': dropshadow }"
>
<slot></slot> <slot></slot>
</picture> </picture>

View file

@ -1,3 +1,8 @@
<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">

View file

@ -26,11 +26,12 @@
position: relative; position: relative;
&:not(:last-child):before { &:not(:last-child):before {
content: ''; content: "";
position: absolute; position: absolute;
inset: var(--timeline-stroke-position-odd); inset: var(--timeline-stroke-position-odd);
height: var(--timeline-stroke-length); height: var(--timeline-stroke-length);
border-left: var(--timeline-stroke-thickness) solid var(--timeline-stroke-color); border-left: var(--timeline-stroke-thickness) solid
var(--timeline-stroke-color);
} }
&:nth-child(odd) { &:nth-child(odd) {
@ -57,7 +58,8 @@
background-color: var(--timeline-circle-background); background-color: var(--timeline-circle-background);
margin: 0; margin: 0;
border: var(--timeline-stroke-thickness) solid var(--timeline-stroke-color); border: var(--timeline-stroke-thickness) solid
var(--timeline-stroke-color);
border-radius: 100%; border-radius: 100%;
padding: var(--timeline-circle-padding); padding: var(--timeline-circle-padding);

View file

@ -10,7 +10,8 @@
position: relative; position: relative;
margin: 0 auto; margin: 0 auto;
padding: 0 var(--container-spacing-right-safe) 0 var(--container-spacing-left-safe); padding: 0 var(--container-spacing-right-safe) 0
var(--container-spacing-left-safe);
list-style: none; list-style: none;

View file

@ -2,9 +2,13 @@
<header class="welcome"> <header class="welcome">
<div class="welcome__image"> <div class="welcome__image">
<picture> <picture>
<source srcset="@/assets/viktor-avatar.png?w=400;800&format=avif&quality=75&as=srcset" /> <source
<source srcset="@/assets/viktor-avatar.png?w=400;800&format=webp&quality=100&as=srcset" /> srcset="@/assets/viktor-avatar.png?w=400;800&avif&quality=75&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>

View file

@ -1,7 +1,7 @@
import en from './translations/en.json' import en from "./translations/en.json";
import de from './translations/de.json' import de from "./translations/de.json";
export default { export default {
en, en,
de de,
} };

View file

@ -1,24 +1,21 @@
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 { createI18n } from "vue-i18n";
import messages from './lang' import messages from "./lang";
import 'normalize.css' import "normalize.css";
import '@/scss/main.scss' import "@/scss/main.scss";
type MessageSchema = typeof messages.en const i18n = createI18n({
locale: "en",
fallbackLocale: "en",
messages,
});
const i18n = createI18n<[MessageSchema], 'en' | 'de'>({ const app = createApp(App);
legacy: false,
locale: 'en',
fallbackLocale: 'en',
messages
})
const app = createApp(App) app.use(router);
app.use(i18n);
app.use(router) app.mount("body");
app.use(i18n)
app.mount('body')

View file

@ -1,68 +1,64 @@
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 new Promise((resolve) => { return { top: 0 };
setTimeout(() => {
resolve({ top: 0 })
}, 500)
})
}, },
routes: [ routes: [
{ {
path: '/', path: "/",
name: 'home', name: "home",
component: HomeView, component: HomeView,
meta: { meta: {
title: 'nav.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: 'nav.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: 'nav.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: 'nav.careerPath', 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: 'nav.attributions', title: "nav.attributions",
icon: CircleInfoIcon icon: CircleInfoIcon,
} },
} },
] ],
}) });
export default router export default router;

View file

@ -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,8 +43,6 @@
--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;
@ -56,7 +54,8 @@
--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 var(--container-spacing-left-safe); --textblock-padding: 0 var(--container-spacing-right-safe) 0
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);
@ -76,14 +75,19 @@
--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 calc(var(--navigation-size) + env(safe-area-inset-bottom)) 0; --navigation-cutout: 0 0
--navigation-cutout-page-background: calc(var(--navigation-size) + env(safe-area-inset-bottom)); 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-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(var(--navigation-size) + env(safe-area-inset-bottom)); --navigation-link-height: calc(
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);
@ -114,10 +118,13 @@
--timeline-item-margin-even: var(--timeline-item-margin); --timeline-item-margin-even: var(--timeline-item-margin);
--timeline-item-text-align-even: left; --timeline-item-text-align-even: left;
--timeline-item-content-padding-odd: 0 0 0 var(--container-spacing-left-safe); --timeline-item-content-padding-odd: 0 0 0 var(--container-spacing-left-safe);
--timeline-item-content-padding-even: var(--timeline-item-content-padding-odd); --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 var(--container-spacing-left-safe); --table-outer-spacing: 0 var(--container-spacing-right-safe) 0
var(--container-spacing-left-safe);
--table-cell-padding: 0.25rem 0.5rem; --table-cell-padding: 0.25rem 0.5rem;
} }
@ -218,9 +225,11 @@
--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 var(--container-spacing-left-safe); --textblock-padding: 0 var(--container-spacing-right-safe) 0
var(--container-spacing-left-safe);
--navigation-cutout: 0 0 0 calc(var(--navigation-size) + env(safe-area-inset-left)); --navigation-cutout: 0 0 0
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);
@ -229,7 +238,9 @@
--navigation-align: stretch; --navigation-align: stretch;
--navigation-position: 0 auto 0 0; --navigation-position: 0 auto 0 0;
--navigation-width: calc(var(--navigation-size) + env(safe-area-inset-left)); --navigation-width: calc(
var(--navigation-size) + env(safe-area-inset-left)
);
--navigation-width-expanded: var(--navigation-width); --navigation-width-expanded: var(--navigation-width);
--navigation-link-display: block; --navigation-link-display: block;
@ -260,8 +271,10 @@
--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) --timeline-stroke-position-even: var(--timeline-stroke-position-top)
var(--timeline-stroke-position-horizontal) auto auto; var(--timeline-stroke-position-horizontal) auto auto;
--timeline-item-margin-odd: 0 0 0 calc(50% - var(--timeline-circle-size) / 2); --timeline-item-margin-odd: 0 0 0
--timeline-item-margin-even: 0 calc(50% - var(--timeline-circle-size) / 2) 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-flex-order: 1;
--timeline-item-content-margin-odd: 0 0 0 1rem; --timeline-item-content-margin-odd: 0 0 0 1rem;
--timeline-item-content-margin-even: 0 1rem 0 0; --timeline-item-content-margin-even: 0 1rem 0 0;
@ -285,13 +298,8 @@
@media (hover: hover) and (min-width: 50em) { @media (hover: hover) and (min-width: 50em) {
:root { :root {
--navigation-width-expanded: calc(var(--navigation-size) * 3.75 + env(safe-area-inset-left)); --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;
} }
} }

View file

@ -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";

View file

@ -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,9 +19,7 @@ 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: transition: color 0.5s, background-color 0.5s;
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;
@ -43,7 +41,7 @@ main {
&:after { &:after {
display: block; display: block;
content: ''; content: "";
position: sticky; position: sticky;
inset: auto 0 var(--navigation-cutout-page-background) 0; inset: auto 0 var(--navigation-cutout-page-background) 0;
@ -97,7 +95,8 @@ 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 var(--color-link-text-underline); box-shadow: inset 0 calc(var(--link-inset-box-shadow) * -1) 0 0
var(--color-link-text-underline);
} }
} }
@ -171,14 +170,3 @@ table {
} }
} }
} }
.fade-enter-active,
.fade-leave-active {
transition: var(--page-transition);
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
transform: var(--page-transform);
}

View file

@ -1,165 +1,162 @@
<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: 'data.colors.front', value: '#e7c7b1' }, { name: "data.colors.front", value: "#e7c7b1" },
{ name: 'data.colors.limbs', value: '#493428' }, { name: "data.colors.limbs", value: "#493428" },
{ name: 'data.colors.back', value: '#422322' }, { name: "data.colors.back", value: "#422322" },
{ name: 'data.colors.spine', value: '#341c1c' }, { name: "data.colors.spine", value: "#341c1c" },
{ name: 'data.colors.tissue', value: '#6bb9db' }, { name: "data.colors.tissue", value: "#6bb9db" },
{ name: 'data.colors.spikes', value: '#f8ebdd' }, { name: "data.colors.spikes", value: "#f8ebdd" },
{ name: 'data.colors.eyesPrimary', value: '#a7eef1' }, { name: "data.colors.eyesPrimary", value: "#a7eef1" },
{ name: 'data.colors.eyesSecondary', value: '#6dabd1' } { name: "data.colors.eyesSecondary", value: "#6dabd1" },
] ];
</script> </script>
<template> <template>
<article> <section>
<section> <h1>{{ $t(`${$route.meta.title}`) }}</h1>
<h1>{{ $t(`${$route.meta.title}`) }}</h1> </section>
</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&format=avif&quality=75&withoutEnlargement&as=srcset @/assets/viktor-ref-NSFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&avif&quality=75&withoutEnlargement&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&format=webp&quality=100&withoutEnlargement&as=srcset @/assets/viktor-ref-NSFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&webp&quality=100&withoutEnlargement&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&format=png&withoutEnlargement&as=srcset @/assets/viktor-ref-NSFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&png&withoutEnlargement&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&format=avif&quality=75&withoutEnlargement&as=srcset @/assets/viktor-ref-SFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&avif&quality=75&withoutEnlargement&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&format=webp&quality=100&withoutEnlargement&as=srcset @/assets/viktor-ref-SFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&webp&quality=100&withoutEnlargement&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&format=png&withoutEnlargement&as=srcset @/assets/viktor-ref-SFW-alpha.png?w=375;420;500;750;840;1000;1125;1260;1500&png&withoutEnlargement&srcset
" "
sizes="(min-width: 64em) 500px, (min-width: 50em) 420px, 375px" sizes="(min-width: 64em) 500px, (min-width: 50em) 420px, 375px"
:alt="`${$t('anatomy.images.back.caption')} by sabertoofs`" :alt="`${$t('anatomy.images.back.caption')} by sabertoofs`"
loading="lazy" loading="lazy"
/> />
</template> </template>
<template #caption> <template #caption>
{{ $t('anatomy.images.back.caption') }} &copy; {{ $t('anatomy.images.back.caption') }} &copy; <a href="http://twitter.com/sabertoofs">sabertoofs</a>
<a href="http://twitter.com/sabertoofs">sabertoofs</a> </template>
</template> </RefImage>
</RefImage>
<ColorTable :colors="colors"></ColorTable> <ColorTable :colors="colors"></ColorTable>
<section> <section>
<p v-for="(p, i) in $tm('anatomy.paragraphs[0]')" :key="i">{{ p }}</p> <p v-for="p in $tm('anatomy.paragraphs[0]')">{{ p }}</p>
</section> </section>
<RefImage dropshadow> <RefImage dropshadow>
<template v-if="$route.query.nsfw"> <template v-if="$route.query.nsfw">
<source <source
srcset=" 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 @/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, 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&format=webp&quality=100&withoutEnlargement&as=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
" "
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&format=png&withoutEnlargement&as=srcset @/assets/viktor-front-NSFW-alpha.png?w=430;530;590;710;860;1060;1180;1420;1290;1590;1770;2130&png&withoutEnlargement&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&format=avif&quality=75&withoutEnlargement&as=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
" "
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&format=webp&quality=100&withoutEnlargement&as=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
" "
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&format=png&withoutEnlargement&as=srcset @/assets/viktor-front-SFW-alpha.png?w=430;530;590;710;860;1060;1180;1420;1290;1590;1770;2130&png&withoutEnlargement&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="`${$t('anatomy.images.front.caption')} by sabertoofs`" :alt="`${$t('anatomy.images.front.caption')} by sabertoofs`"
loading="lazy" loading="lazy"
/> />
</template> </template>
<template #caption> <template #caption>
{{ $t('anatomy.images.front.caption') }} &copy; {{ $t("anatomy.images.front.caption") }} &copy;
<a href="http://twitter.com/sabertoofs">sabertoofs</a> <a href="http://twitter.com/sabertoofs">sabertoofs</a>
</template> </template>
</RefImage> </RefImage>
<section> <section>
<p v-for="(p, i) in $tm('anatomy.paragraphs[1]')" :key="i">{{ p }}</p> <p v-for="p in $tm('anatomy.paragraphs[1]')">{{ p }}</p>
</section> </section>
</article>
</template> </template>

View file

@ -1,103 +1,121 @@
<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&format=webp&quality=100&imagetools' import ViktorRefAlpha from "@/assets/viktor-ref-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' import ViktorFront from "@/assets/viktor-front-SFW-alpha.png?w=400&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>
<article> <section>
<section> <h1>{{ $t(`${$route.meta.title}`) }}</h1>
<h1>{{ $t(`${$route.meta.title}`) }}</h1> <h2>{{ $t("attributions.artwork.heading") }}</h2>
<h2>{{ $t('attributions.artwork.heading') }}</h2> </section>
</section> <AttributionTable :attributions="attributions" />
<AttributionTable :attributions="attributions" /> <section>
<section> <h2>{{ $t("attributions.other.heading") }}</h2>
<h2>{{ $t('attributions.other.heading') }}</h2> </section>
</section> <table>
<table> <thead>
<thead> <tr>
<tr> <th>{{ $t("attributions.other.headings[0]") }}</th>
<th>{{ $t('attributions.other.headings[0]') }}</th> <th>{{ $t("attributions.other.headings[1]") }}</th>
<th>{{ $t('attributions.other.headings[1]') }}</th> </tr>
</tr> </thead>
</thead> <tbody>
<tbody> <tr>
<tr> <td>{{ $t("attributions.other.icons[0]") }}</td>
<td>{{ $t('attributions.other.icons[0]') }}</td> <td>
<td> <a
<a href="https://fontawesome.com/license/free"
href="https://fontawesome.com/license/free" target="_blank"
target="_blank" rel="noopener noreferrer"
rel="noopener noreferrer" >
> Font Awesome
Font Awesome </a>
</a> </td>
</td> </tr>
</tr> <tr>
<tr> <td>{{ $t("attributions.other.headingFont[0]") }}</td>
<td>{{ $t('attributions.other.headingFont[0]') }}</td> <td>
<td> <a
<a href="https://github.com/MichalSahar/Secular"
href="https://github.com/MichalSahar/Secular" target="_blank"
target="_blank" rel="noopener noreferrer"
rel="noopener noreferrer" >
> Secular One
Secular One </a>
</a> {{ $t("attributions.other.headingFont[1]") }}
{{ $t('attributions.other.headingFont[1]') }} <a
<a href="https://github.com/MichalSahar" target="_blank" rel="noopener noreferrer"> href="https://github.com/MichalSahar"
Michal Sahar target="_blank"
</a> rel="noopener noreferrer"
</td> >
</tr> Michal Sahar
<tr> </a>
<td>{{ $t('attributions.other.copyFont[0]') }}</td> </td>
<td> </tr>
<a <tr>
href="https://antonkoovit.com/typefaces/arvo" <td>{{ $t("attributions.other.copyFont[0]") }}</td>
target="_blank" <td>
rel="noopener noreferrer" <a
> href="https://antonkoovit.com/typefaces/arvo"
Arvo target="_blank"
</a> rel="noopener noreferrer"
{{ $t('attributions.other.copyFont[1]') }} >
<a href="https://antonkoovit.com/" target="_blank" rel="noopener noreferrer"> Arvo
Anton Koovit </a>
</a> {{ $t("attributions.other.copyFont[1]") }}
</td> <a
</tr> href="https://antonkoovit.com/"
<tr> target="_blank"
<td>{{ $t('attributions.other.background[0]') }}</td> rel="noopener noreferrer"
<td> >
{{ $t('attributions.other.background[1][0]') }} Anton Koovit
<a href="https://haikei.app/" target="_blank" rel="noopener noreferrer">Haikei</a> </a>
{{ $t('attributions.other.background[1][1]') }} </td>
<a href="https://zcreativelabs.com/" target="_blank" rel="noopener noreferrer"> </tr>
z creative labs <tr>
</a> <td>{{ $t("attributions.other.background[0]") }}</td>
</td> <td>
</tr> {{ $t("attributions.other.background[1][0]") }}
</tbody> <a
</table> href="https://haikei.app/"
</article> target="_blank"
rel="noopener noreferrer"
>
Haikei
</a>
{{ $t("attributions.other.background[1][1]") }}
<a
href="https://zcreativelabs.com/"
target="_blank"
rel="noopener noreferrer"
>
z creative labs
</a>
</td>
</tr>
</tbody>
</table>
</template> </template>
<style lang="scss"></style>

View file

@ -1,18 +1,13 @@
<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 IndustryIcon from '@/assets/icons/IndustryIcon.vue' import IndustryIcon from "@/assets/icons/IndustryIcon.vue";
import TruckIcon from '@/assets/icons/TruckIcon.vue' import TruckIcon from "@/assets/icons/TruckIcon.vue";
import BoxesIcon from '@/assets/icons/BoxesIcon.vue' import BoxesIcon from "@/assets/icons/BoxesIcon.vue";
interface Job {
title: string
desc: string
}
const jobIcons = [ const jobIcons = [
WhiskeyGlassIcon, WhiskeyGlassIcon,
@ -21,27 +16,25 @@ const jobIcons = [
HelmetSafetyIcon, HelmetSafetyIcon,
IndustryIcon, IndustryIcon,
TruckIcon, TruckIcon,
BoxesIcon BoxesIcon,
] ];
</script> </script>
<template> <template>
<article> <section>
<section> <h1>{{ $t(`${$route.meta.title}`) }}</h1>
<h1>{{ $t(`${$route.meta.title}`) }}</h1> <p v-for="p in $tm('career.paragraphs')">{{ p }}</p>
<p v-for="(p, i) in $tm('career.paragraphs')" :key="i">{{ p }}</p> </section>
</section>
<TimelineList> <TimelineList>
<TimelineItem v-for="(job, idx) in ($tm('career.jobs') as Job[])" :key="idx"> <TimelineItem v-for="(job, idx) in $tm('career.jobs')">
<template #icon> <template #icon>
<component :is="jobIcons[idx as number]"></component> <component :is="jobIcons[idx as number]"></component>
</template> </template>
<template #headline>{{ job.title }}</template> <template #headline>{{ job.title }}</template>
<template #content> <template #content>
<p>{{ job.desc }}</p> <p>{{ job.desc }}</p>
</template> </template>
</TimelineItem> </TimelineItem>
</TimelineList> </TimelineList>
</article>
</template> </template>

View file

@ -1,73 +1,71 @@
<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 = ['data.general.heading[0]', 'data.general.heading[1]'] const heads = ["data.general.heading[0]", "data.general.heading[1]"];
const data = [ const data = [
['data.general.fullName[0]', 'data.general.fullName[1]'], ["data.general.fullName[0]", "data.general.fullName[1]"],
['data.general.dob[0]', dateFormat.format(dob)], ["data.general.dob[0]", dateFormat.format(dob)],
['data.general.gender[0]', 'data.general.gender[1]'], ["data.general.gender[0]", "data.general.gender[1]"],
['data.general.height[0]', `${height} cm (${toImperial(height)})`], ["data.general.height[0]", `${height} cm (${toImperial(height)})`],
['data.general.weight[0]', `${weight} kg (${toLbs(weight)} lbs)`] ["data.general.weight[0]", `${weight} kg (${toLbs(weight)} lbs)`],
] ];
const sexHeads = ['data.sexuality.heading[0]', 'data.sexuality.heading[1]'] const sexHeads = ["data.sexuality.heading[0]", "data.sexuality.heading[1]"];
const sexData = [ const sexData = [
['data.sexuality.identifiesAs[0]', 'data.sexuality.identifiesAs[1]'], ["data.sexuality.identifiesAs[0]", "data.sexuality.identifiesAs[1]"],
['data.sexuality.preferredRole[0]', 'data.sexuality.preferredRole[1]'] ["data.sexuality.preferredRole[0]", "data.sexuality.preferredRole[1]"],
] ];
</script> </script>
<template> <template>
<article> <section>
<section> <h1>{{ $t(`${$route.meta.title}`) }}</h1>
<h1>{{ $t(`${$route.meta.title}`) }}</h1> </section>
</section>
<DataTable :headings="heads" :data="data"></DataTable> <DataTable :headings="heads" :data="data"></DataTable>
<section> <section>
<h2>{{ $t('general.personality.heading') }}</h2> <h2>{{ $t("general.personality.heading") }}</h2>
<p v-for="(p, i) in $tm('general.personality.paragraphs')" :key="i">{{ p }}</p> <p v-for="p in $tm('general.personality.paragraphs')">{{ p }}</p>
</section> </section>
<section> <section>
<h2>{{ $t('general.sexuality.heading') }}</h2> <h2>{{ $t("general.sexuality.heading") }}</h2>
</section> </section>
<DataTable :headings="sexHeads" :data="sexData"></DataTable> <DataTable :headings="sexHeads" :data="sexData"></DataTable>
<section> <section>
<p v-for="(p, i) in $tm('general.sexuality.paragraphs')" :key="i">{{ p }}</p> <p v-for="p in $tm('general.sexuality.paragraphs')">{{ p }}</p>
</section> </section>
</article>
</template> </template>

View file

@ -1,18 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import WelcomeHeader from '@/components/WelcomeHeader.vue' import WelcomeHeader from "@/components/WelcomeHeader.vue";
</script> </script>
<template> <template>
<article> <WelcomeHeader>
<WelcomeHeader> <template #main>{{ $t("welcomeHeader.mainTitle") }}</template>
<template #main>{{ $t('welcomeHeader.mainTitle') }}</template> <template #sub>{{ $t("welcomeHeader.subTitle") }}</template>
<template #sub>{{ $t('welcomeHeader.subTitle') }}</template> </WelcomeHeader>
</WelcomeHeader>
<section> <section>
<h3>{{ $t('home.heading') }}</h3> <h3>{{ $t("home.heading") }}</h3>
<p v-for="(p, i) in $tm('home.paragraphs')" :key="i">{{ p }}</p> <p v-for="p in $tm('home.paragraphs')">{{ p }}</p>
</section> </section>
</article>
</template> </template>

View file

@ -1,12 +0,0 @@
{
"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/*"]
}
}
}

8
tsconfig.config.json Normal file
View file

@ -0,0 +1,8 @@
{
"extends": "@vue/tsconfig/tsconfig.node.json",
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*"],
"compilerOptions": {
"composite": true,
"types": ["node"]
}
}

View file

@ -1,11 +1,16 @@
{ {
"files": [], "extends": "@vue/tsconfig/tsconfig.web.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"references": [ "references": [
{ {
"path": "./tsconfig.node.json" "path": "./tsconfig.config.json"
},
{
"path": "./tsconfig.app.json"
} }
] ]
} }

View file

@ -1,17 +0,0 @@
{
"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"]
}
}

View file

@ -1,23 +0,0 @@
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({})]
}
}
})

19
vite.config.ts Normal file
View file

@ -0,0 +1,19 @@
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,
},
});