Compare commits
62 commits
Author | SHA1 | Date | |
---|---|---|---|
|
15d487ec6f | ||
|
d716e192bb | ||
|
6aa34c1648 | ||
|
34823665c6 | ||
|
134e2c79cc | ||
|
b0abfd8311 | ||
|
61cd15fd5b | ||
|
f3f94be6ae | ||
|
61147be693 | ||
|
6a1474eb52 | ||
|
f4b1003801 | ||
|
5d94323bdd | ||
|
d2774e8150 | ||
|
89831a64a2 | ||
|
4c8d576f13 | ||
|
0c8a3b0348 | ||
|
0335006ee5 | ||
|
07bd197c29 | ||
|
a0b2062137 | ||
|
d5cf84e592 | ||
|
cd9c1d950d | ||
|
665f2c6b35 | ||
|
30a573e9cf | ||
|
ddde6df30b | ||
|
d5703cc274 | ||
|
778142c4c1 | ||
|
d984651fc8 | ||
|
9a529b62fb | ||
|
c143956e9f | ||
|
120a1a03e3 | ||
|
08e01502c5 | ||
|
314255dc31 | ||
|
1b036d3278 | ||
|
b450849075 | ||
|
f100c524af | ||
|
d196c1e680 | ||
|
f147510c04 | ||
|
6cf6e6d045 | ||
|
ff665a6cea | ||
|
a2e2c1f791 | ||
|
0b4813e285 | ||
|
4853e1ec63 | ||
|
de3862bf77 | ||
|
6094a2a559 | ||
|
939610ed61 | ||
|
5ef3d6c0f9 | ||
|
35562692e4 | ||
|
5f65a341a0 | ||
|
e3f2e0c256 | ||
|
51e72e2021 | ||
|
b8c9d8de17 | ||
|
cc664a764d | ||
|
d0f9434700 | ||
|
b9fe7113ed | ||
|
0e225f95c9 | ||
|
8e0756f166 | ||
|
e67a220223 | ||
|
dd70061798 | ||
|
c7444256f1 | ||
|
3c8b726d83 | ||
|
a670952e15 | ||
|
d6f94c26d4 |
49 changed files with 3100 additions and 6268 deletions
3
.browserslistrc
Normal file
3
.browserslistrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
defaults
|
||||
>1% and not dead
|
||||
last 2 versions
|
|
@ -1,15 +1,15 @@
|
|||
/* eslint-env node */
|
||||
require("@rushstack/eslint-patch/modern-module-resolution");
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended",
|
||||
"@vue/eslint-config-typescript",
|
||||
"@vue/eslint-config-prettier",
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript',
|
||||
'@vue/eslint-config-prettier'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: "latest",
|
||||
},
|
||||
};
|
||||
ecmaVersion: 'latest'
|
||||
}
|
||||
}
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -26,3 +26,5 @@ coverage
|
|||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
|
|
|
@ -1 +1,8 @@
|
|||
{}
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"semi": false,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none"
|
||||
}
|
||||
|
|
6
.vscode/extensions.json
vendored
6
.vscode/extensions.json
vendored
|
@ -1,3 +1,7 @@
|
|||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
|
|
11
index.html
11
index.html
|
@ -17,11 +17,12 @@
|
|||
<meta name="twitter:description" content="The official reference page for Sebin Nyshkim with picture references and in-depth character descriptions" />
|
||||
<meta name="twitter:image" content="https://ref.sebin-nyshkim.net/sebin/preview.png" />
|
||||
|
||||
<meta name="og:type" content="website" />
|
||||
<meta name="og:title" content="Sebin Nyshkim - Reference Page" />
|
||||
<meta name="og:url" content="https://ref.sebin-nyshkim.net/sebin/" />
|
||||
<meta name="og:image" content="https://ref.sebin-nyshkim.net/sebin/preview.png" />
|
||||
<meta name="og:description" content="The official reference page for Sebin Nyshkim with picture references and in-depth character descriptions" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="Sebin Nyshkim - Reference Page" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:url" content="https://ref.sebin-nyshkim.net/sebin/" />
|
||||
<meta property="og:image" content="https://ref.sebin-nyshkim.net/sebin/preview.png" />
|
||||
<meta property="og:description" content="The official reference page for Sebin Nyshkim with picture references and in-depth character descriptions" />
|
||||
</head>
|
||||
<body>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
|
|
6944
package-lock.json
generated
6944
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": "sebin-reference",
|
||||
"version": "0.9.2",
|
||||
"version": "0.10.7",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"build": "run-p type-check build-only",
|
||||
"build": "run-p type-check \"build-only {@}\" --",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --noEmit",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
||||
"type-check": "vue-tsc --build --force",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.2.45",
|
||||
"vue-router": "^4.1.6"
|
||||
"vue": "^3.4.38",
|
||||
"vue-router": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.2.0",
|
||||
"@types/node": "^18.11.18",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vue/eslint-config-prettier": "^7.0.0",
|
||||
"@vue/eslint-config-typescript": "^11.0.2",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"eslint": "^8.32.0",
|
||||
"eslint-plugin-vue": "^9.9.0",
|
||||
"@rushstack/eslint-patch": "^1.10.4",
|
||||
"@tsconfig/node20": "^20.1.4",
|
||||
"@types/node": "^20.16.3",
|
||||
"@vitejs/plugin-vue": "^5.1.3",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"@vue/eslint-config-typescript": "^13.0.0",
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.28.0",
|
||||
"normalize.css": "^8.0.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.8.3",
|
||||
"sass": "^1.57.1",
|
||||
"typescript": "~4.7.4",
|
||||
"vite": "^4.0.4",
|
||||
"vite-imagetools": "^4.0.15",
|
||||
"vue-tsc": "^1.0.24"
|
||||
"npm-run-all2": "^6.2.2",
|
||||
"prettier": "^3.3.3",
|
||||
"sass": "^1.77.8",
|
||||
"typescript": "~5.5.0",
|
||||
"vite": "^5.4.2",
|
||||
"vite-imagetools": "^6.2.9",
|
||||
"vue-tsc": "^2.1.4"
|
||||
}
|
||||
}
|
||||
|
|
77
src/App.vue
77
src/App.vue
|
@ -1,63 +1,82 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, provide } from "vue";
|
||||
import { RouterView } from "vue-router";
|
||||
import { isWarnKey, modalResultKey, nsfwKey, showModalKey } from "@/keys";
|
||||
import SiteHeader from "@/components/SiteHeader.vue";
|
||||
import SiteFooter from "@/components/SiteFooter.vue";
|
||||
import SiteNavigation from "@/components/SiteNavigation.vue";
|
||||
import pkg from "../package.json";
|
||||
import { ref, provide } from 'vue'
|
||||
import { RouterView } from 'vue-router'
|
||||
import { modalResultKey, nsfwKey, showModalKey } from '@/keys'
|
||||
import { version } from '../package.json'
|
||||
import RefModal from '@/components/RefModal.vue'
|
||||
import ButtonGroup from '@/components/ButtonGroup.vue'
|
||||
import Button from '@/components/RefButton.vue'
|
||||
import SiteHeader from '@/components/SiteHeader.vue'
|
||||
import SiteFooter from '@/components/SiteFooter.vue'
|
||||
import SiteNavigation from '@/components/SiteNavigation.vue'
|
||||
|
||||
const version = pkg.version;
|
||||
const isNsfw = ref(false)
|
||||
const isConfirmedHorny = ref(false)
|
||||
|
||||
const isNsfw = ref(false);
|
||||
const isConfirmedHorny = ref(false);
|
||||
const isWarn = ref(false);
|
||||
const nsfwmodal = ref<InstanceType<typeof RefModal>>()
|
||||
|
||||
const showModal = (): void => {
|
||||
if (!isConfirmedHorny.value) {
|
||||
isWarn.value = true;
|
||||
document.body.classList.add("scroll-lock");
|
||||
nsfwmodal.value?.showModal()
|
||||
|
||||
setTimeout(() => {
|
||||
isNsfw.value = false;
|
||||
}, 1);
|
||||
isNsfw.value = false
|
||||
}, 1)
|
||||
} else {
|
||||
isNsfw.value = !isNsfw.value;
|
||||
isNsfw.value = !isNsfw.value
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const modalResult = (value: boolean): void => {
|
||||
isNsfw.value = value;
|
||||
isConfirmedHorny.value = value;
|
||||
isWarn.value = false;
|
||||
document.body.classList.remove("scroll-lock");
|
||||
};
|
||||
isNsfw.value = value
|
||||
isConfirmedHorny.value = value
|
||||
nsfwmodal.value?.close()
|
||||
}
|
||||
|
||||
provide(isWarnKey, isWarn);
|
||||
provide(modalResultKey, modalResult);
|
||||
provide(nsfwKey, isNsfw);
|
||||
provide(showModalKey, showModal);
|
||||
provide(modalResultKey, modalResult)
|
||||
provide(nsfwKey, isNsfw)
|
||||
provide(showModalKey, showModal)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RefModal id="nsfw-warning" ref="nsfwmodal">
|
||||
<template #heading>
|
||||
⚠️⚠️⚠️<br />
|
||||
Whoa, Nelly!
|
||||
</template>
|
||||
|
||||
<template #message>
|
||||
By enabling NSFW mode you confirm that you are of legal age to view adult content.
|
||||
</template>
|
||||
|
||||
<template #buttons>
|
||||
<ButtonGroup col>
|
||||
<Button positive @click.prevent="modalResult(true)"> Yes, show me the goods 👀 </Button>
|
||||
<Button negative @click.prevent="modalResult(false)"> NO, STAHP 😱 </Button>
|
||||
</ButtonGroup>
|
||||
</template>
|
||||
</RefModal>
|
||||
|
||||
<SiteHeader>
|
||||
<!-- max 500px -->
|
||||
<picture>
|
||||
<source
|
||||
srcset="@/assets/sebin-smug-icon.png?w=36;40;48;56;72;80;96;112;108;120;144;168&avif&quality=75&srcset"
|
||||
srcset="
|
||||
@/assets/sebin-smug-icon.png?w=36;40;48;56;72;80;96;112;108;120;144;168&format=avif&quality=75&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 56px, (min-width: 80em) 48px, (min-width: 35em) 40px, 36px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/sebin-smug-icon.png?w=36;40;48;56;72;80;96;112;108;120;144;168&webp&quality=100&srcset
|
||||
@/assets/sebin-smug-icon.png?w=36;40;48;56;72;80;96;112;108;120;144;168&format=webp&quality=100&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 56px, (min-width: 80em) 48px, (min-width: 35em) 40px, 36px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
class="nav-logo"
|
||||
srcset="@/assets/sebin-smug-icon.png?w=36;40;48;56;72;80;96;112;108;120;144;168&png&srcset"
|
||||
srcset="@/assets/sebin-smug-icon.png?w=36;40;48;56;72;80;96;112;108;120;144;168&format=png&as=srcset"
|
||||
sizes="(min-width: 120em) 56px, (min-width: 80em) 48px, (min-width: 35em) 40px, 36px"
|
||||
alt="Sebin Avatar"
|
||||
/>
|
||||
|
|
BIN
src/assets/refs/clothes/lazy/fullbody.png
Normal file
BIN
src/assets/refs/clothes/lazy/fullbody.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
Before Width: | Height: | Size: 232 KiB |
Binary file not shown.
Before Width: | Height: | Size: 42 KiB |
BIN
src/assets/refs/clothes/lazy/undies.png
Normal file
BIN
src/assets/refs/clothes/lazy/undies.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
|
@ -13,7 +13,6 @@
|
|||
width: 100%;
|
||||
max-width: var(--attack-list-max-width);
|
||||
margin: 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);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
<script setup lang="ts">
|
||||
interface Props {
|
||||
col?: boolean
|
||||
grid?: boolean
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="btn-group">
|
||||
<div class="btn-group" :class="{ col: col, grid: grid }">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -10,6 +19,27 @@
|
|||
flex-flow: row wrap;
|
||||
justify-content: space-evenly;
|
||||
|
||||
&.col {
|
||||
flex-flow: column nowrap;
|
||||
gap: 1.5rem;
|
||||
margin: 1rem 0;
|
||||
|
||||
> * {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
margin: 1rem 0;
|
||||
|
||||
> * {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
> * {
|
||||
flex: var(--button-group-flex);
|
||||
margin: 1rem 0;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
import type { ColorDict } from "@/interfaces";
|
||||
import type { ColorDict } from '@/interfaces'
|
||||
|
||||
interface Props {
|
||||
colors: ColorDict[];
|
||||
colors: ColorDict[]
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -21,10 +21,7 @@ defineProps<Props>();
|
|||
<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 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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
<script setup lang="ts">
|
||||
interface Props {
|
||||
headings: string[];
|
||||
data: string[][];
|
||||
headings: string[]
|
||||
data: string[][]
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<table class="data-table">
|
||||
<thead class="data-table__head">
|
||||
<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">
|
||||
{{ heading }}
|
||||
</th>
|
||||
</tr>
|
||||
|
|
161
src/components/FilterButton.vue
Normal file
161
src/components/FilterButton.vue
Normal file
|
@ -0,0 +1,161 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Ratings } from '@/interfaces'
|
||||
|
||||
interface Props {
|
||||
modelValue: number[]
|
||||
name?: string
|
||||
value: Ratings
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: number[]): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const checked = computed({
|
||||
get() {
|
||||
return props.modelValue
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="filter-button">
|
||||
<input
|
||||
class="filter-button__input"
|
||||
type="checkbox"
|
||||
:name="name"
|
||||
:id="Ratings[value].toLowerCase()"
|
||||
:value="value"
|
||||
v-model="checked"
|
||||
/>
|
||||
<label
|
||||
:for="Ratings[value].toLowerCase()"
|
||||
class="filter-button__label"
|
||||
:class="[Ratings[value].toLowerCase()]"
|
||||
>
|
||||
{{ Ratings[value] }}
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.filter-button {
|
||||
flex: 1 1 0;
|
||||
|
||||
&__input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__input:checked + &__label {
|
||||
top: 0.25rem;
|
||||
box-shadow: 0 0.25rem 0 0 var(--color-button-box-shadow);
|
||||
|
||||
&.love {
|
||||
box-shadow: 0 0.25rem 0 0 var(--theme-c-love-dark);
|
||||
}
|
||||
|
||||
&.yes {
|
||||
box-shadow: 0 0.25rem 0 0 var(--theme-c-yes-dark);
|
||||
}
|
||||
|
||||
&.maybe {
|
||||
box-shadow: 0 0.25rem 0 0 var(--theme-c-maybe-dark);
|
||||
}
|
||||
|
||||
&.no {
|
||||
box-shadow: 0 0.25rem 0 0 var(--theme-c-no-dark);
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
display: block;
|
||||
|
||||
position: relative;
|
||||
top: 0;
|
||||
|
||||
background-color: var(--color-button);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
&.love {
|
||||
background-color: var(--theme-c-love);
|
||||
box-shadow: 0 0.5rem 0 0 var(--theme-c-love-dark);
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0.75rem 0 0 var(--theme-c-love-dark);
|
||||
}
|
||||
|
||||
&:active {
|
||||
box-shadow: 0 0.25rem 0 0 var(--theme-c-love-dark);
|
||||
}
|
||||
}
|
||||
|
||||
&.yes {
|
||||
background-color: var(--theme-c-yes);
|
||||
box-shadow: 0 0.5rem 0 0 var(--theme-c-yes-dark);
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0.75rem 0 0 var(--theme-c-yes-dark);
|
||||
}
|
||||
|
||||
&:active {
|
||||
box-shadow: 0 0.25rem 0 0 var(--theme-c-yes-dark);
|
||||
}
|
||||
}
|
||||
|
||||
&.maybe {
|
||||
background-color: var(--theme-c-maybe);
|
||||
box-shadow: 0 0.5rem 0 0 var(--theme-c-maybe-dark);
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0.75rem 0 0 var(--theme-c-maybe-dark);
|
||||
}
|
||||
|
||||
&:active {
|
||||
box-shadow: 0 0.25rem 0 0 var(--theme-c-maybe-dark);
|
||||
}
|
||||
}
|
||||
|
||||
&.no {
|
||||
background-color: var(--theme-c-no);
|
||||
box-shadow: 0 0.5rem 0 0 var(--theme-c-no-dark);
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0.75rem 0 0 var(--theme-c-no-dark);
|
||||
}
|
||||
|
||||
&:active {
|
||||
box-shadow: 0 0.25rem 0 0 var(--theme-c-no-dark);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
77
src/components/FilterListTag.vue
Normal file
77
src/components/FilterListTag.vue
Normal file
|
@ -0,0 +1,77 @@
|
|||
<script setup lang="ts">
|
||||
interface Props {
|
||||
type: string
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span class="filter-list__tag" :class="type.toLowerCase()">
|
||||
<span>{{ type }}</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.filter-list {
|
||||
&__tag {
|
||||
flex: 0 0 0;
|
||||
|
||||
span {
|
||||
display: block;
|
||||
|
||||
font-size: 0.75rem;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
|
||||
background: rgba(#f5f5f5, 0.7);
|
||||
|
||||
border: 1px solid #f5f5f5;
|
||||
border-radius: 1em;
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
||||
&.love span {
|
||||
background: var(--theme-c-love);
|
||||
border: 1px solid var(--theme-c-love-dark);
|
||||
}
|
||||
|
||||
&.yes span {
|
||||
background: var(--theme-c-yes);
|
||||
border: 1px solid var(--theme-c-yes-dark);
|
||||
}
|
||||
|
||||
&.maybe span {
|
||||
background: var(--theme-c-maybe);
|
||||
border: 1px solid var(--theme-c-maybe-dark);
|
||||
}
|
||||
|
||||
&.no span {
|
||||
background: var(--theme-c-no);
|
||||
border: 1px solid var(--theme-c-no-dark);
|
||||
}
|
||||
|
||||
&.category {
|
||||
flex: 0 0 3rem;
|
||||
}
|
||||
|
||||
&.receive {
|
||||
flex: 0 0 3.125rem;
|
||||
|
||||
span {
|
||||
background: var(--theme-c-receive);
|
||||
border: 1px solid var(--theme-c-receive);
|
||||
}
|
||||
}
|
||||
|
||||
&.give {
|
||||
flex: 0 0 2.125rem;
|
||||
|
||||
span {
|
||||
background: var(--theme-c-give);
|
||||
border: 1px solid var(--theme-c-give);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
127
src/components/FilteredList.vue
Normal file
127
src/components/FilteredList.vue
Normal file
|
@ -0,0 +1,127 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { Ratings, Role } from '@/interfaces'
|
||||
import type { Kink } from '@/interfaces'
|
||||
import FilterButton from '@/components/FilterButton.vue'
|
||||
import FilterListTag from '@/components/FilterListTag.vue'
|
||||
|
||||
interface Props {
|
||||
data: Kink[]
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const filterOptions = ref<number[]>([])
|
||||
|
||||
const filteredItems = computed(() =>
|
||||
[...props.data]
|
||||
.sort((a, b) => a.rating - b.rating)
|
||||
.filter((kink) => filterOptions.value.some((filterNum) => kink.rating === filterNum))
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="filter-list">
|
||||
<div class="filter-list__filters">
|
||||
<FilterButton name="rating" :value="Ratings.Love" v-model="filterOptions" />
|
||||
<FilterButton name="rating" :value="Ratings.Yes" v-model="filterOptions" />
|
||||
<FilterButton name="rating" :value="Ratings.Maybe" v-model="filterOptions" />
|
||||
<FilterButton name="rating" :value="Ratings.No" v-model="filterOptions" />
|
||||
</div>
|
||||
|
||||
<div class="filter-list__list-container">
|
||||
<template v-if="filterOptions.length > 0">
|
||||
<ul class="filter-list__list">
|
||||
<li v-for="(item, idx) in filteredItems" :key="idx" class="filter-list__item">
|
||||
<FilterListTag
|
||||
v-if="filterOptions.length > 1"
|
||||
class="category"
|
||||
:type="Ratings[item.rating]"
|
||||
/>
|
||||
<span class="filter-list__item-name">
|
||||
<span>{{ item.name }}</span>
|
||||
</span>
|
||||
<FilterListTag
|
||||
v-if="((item.role ?? 0) & Role.Receive) === Role.Receive"
|
||||
:type="Role[Role.Receive]"
|
||||
/>
|
||||
<FilterListTag
|
||||
v-if="((item.role ?? 0) & Role.Give) === Role.Give"
|
||||
:type="Role[Role.Give]"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<p class="filter-list__intro-msg">Select one of the categories above</p>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.filter-list {
|
||||
background: var(--quickfacts-background);
|
||||
border-radius: 1rem;
|
||||
box-shadow: var(--container-box-shadow);
|
||||
overflow: hidden;
|
||||
|
||||
&__options {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
&__filters {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
|
||||
border-collapse: collapse;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
&__intro-msg {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
&__list-container {
|
||||
border-top: 0.125rem solid var(--color-quickfacts-border);
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
&__list {
|
||||
max-height: 30rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
|
||||
padding: 0.25rem 0.875rem;
|
||||
|
||||
&:hover {
|
||||
background: rgba(#000, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
&__item-name {
|
||||
flex: 1 0 0;
|
||||
line-height: 2;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,20 +1,18 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
interface Props {
|
||||
cols?: number
|
||||
}
|
||||
|
||||
const isExpanded = ref(false);
|
||||
|
||||
const toggle = (): void => {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
};
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="quickfacts" :class="{ open: isExpanded }">
|
||||
<h3 class="quickfacts__head" @click.prevent="toggle">Quickfacts</h3>
|
||||
<div class="quickfacts__list">
|
||||
<details class="quickfacts">
|
||||
<summary class="quickfacts__head">Quickfacts</summary>
|
||||
<div class="quickfacts__list" :class="[`cols-${cols}`]">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -29,11 +27,12 @@ const toggle = (): void => {
|
|||
box-shadow: var(--container-box-shadow);
|
||||
|
||||
overflow: hidden;
|
||||
transition: 0.3s all ease-in-out;
|
||||
|
||||
&:before {
|
||||
display: block;
|
||||
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 1.375rem;
|
||||
right: 1.375rem;
|
||||
|
@ -48,31 +47,39 @@ const toggle = (): void => {
|
|||
}
|
||||
}
|
||||
|
||||
&.open:before {
|
||||
&[open]:before {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
&.open & {
|
||||
&__list {
|
||||
max-height: 25rem;
|
||||
border-top: 0.125rem solid var(--color-quickfacts-border);
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__head {
|
||||
font-family: var(--font-family-headings);
|
||||
font-size: 1.125rem;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
padding: 0.75rem 1.5rem;
|
||||
transition: 0.3s all ease-in-out;
|
||||
cursor: pointer;
|
||||
|
||||
&::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__list {
|
||||
max-height: 0rem;
|
||||
padding: 0 1.5rem;
|
||||
transition: 0.3s all ease-in-out;
|
||||
border-top: 0.125rem solid var(--color-quickfacts-border);
|
||||
padding: 1.5rem;
|
||||
|
||||
&.cols-2 ul {
|
||||
columns: var(--quickfacts-cols-double);
|
||||
}
|
||||
|
||||
&.cols-3 ul {
|
||||
columns: var(--quickfacts-cols-triple);
|
||||
}
|
||||
|
||||
&.cols-4 ul {
|
||||
columns: var(--quickfacts-cols-quadruple);
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
<script setup lang="ts">
|
||||
interface Props {
|
||||
href?: string;
|
||||
download?: boolean | any;
|
||||
positive?: boolean
|
||||
negative?: boolean
|
||||
href?: string
|
||||
download?: boolean | any
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a class="btn" :href="href" :[download]="download">
|
||||
<a
|
||||
class="btn"
|
||||
:class="{ positive: positive, negative: negative }"
|
||||
:href="href"
|
||||
:[download]="download"
|
||||
>
|
||||
<slot></slot>
|
||||
</a>
|
||||
</template>
|
||||
|
|
|
@ -1,32 +1,33 @@
|
|||
<script setup lang="ts">
|
||||
import { inject } from "vue";
|
||||
import { nsfwKey, showModalKey } from "@/keys";
|
||||
import RefToggle from "@/components/RefToggle.vue";
|
||||
import { inject } from 'vue'
|
||||
import { nsfwKey, showModalKey } from '@/keys'
|
||||
import RefToggle from '@/components/RefToggle.vue'
|
||||
|
||||
interface Props {
|
||||
polaroidBorder?: boolean;
|
||||
dropshadow?: boolean;
|
||||
nsfw?: boolean;
|
||||
id: string;
|
||||
cornerText?: boolean
|
||||
polaroidBorder?: boolean
|
||||
dropshadow?: boolean
|
||||
nsfw?: boolean
|
||||
id: string
|
||||
}
|
||||
|
||||
const isNsfw = inject<boolean>(nsfwKey, false);
|
||||
const showModal = inject<Function>(showModalKey, Function);
|
||||
const isNsfw = inject<boolean>(nsfwKey, false)
|
||||
const showModal = inject<Function>(showModalKey, Function)
|
||||
|
||||
defineProps<Props>();
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<figure class="figure">
|
||||
<div
|
||||
class="figure__border"
|
||||
:class="{ 'figure__border--polaroid': polaroidBorder }"
|
||||
:class="{
|
||||
'figure__border--polaroid': polaroidBorder,
|
||||
'figure__border--cornertext': cornerText
|
||||
}"
|
||||
>
|
||||
<template v-if="!nsfw || isNsfw">
|
||||
<div
|
||||
class="figure__image"
|
||||
:class="{ 'figure__image--dropshadow': dropshadow }"
|
||||
>
|
||||
<div class="figure__image" :class="{ 'figure__image--dropshadow': dropshadow }">
|
||||
<slot name="img"></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -54,18 +55,14 @@ defineProps<Props>();
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<figcaption class="figure__meta">
|
||||
<template v-if="!nsfw || isNsfw">
|
||||
<template v-if="!nsfw || isNsfw">
|
||||
<figcaption class="figure__meta">
|
||||
<div class="caption">
|
||||
<slot name="caption"></slot>
|
||||
</div>
|
||||
<div class="copyright">© <slot name="copyright"></slot></div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<p>😳 2 hot 4 u 🍆</p>
|
||||
</template>
|
||||
</figcaption>
|
||||
</figcaption>
|
||||
</template>
|
||||
</div>
|
||||
</figure>
|
||||
</template>
|
||||
|
@ -79,8 +76,7 @@ defineProps<Props>();
|
|||
text-align: center;
|
||||
|
||||
margin: 1em 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);
|
||||
|
||||
&__border {
|
||||
display: flex;
|
||||
|
@ -100,8 +96,42 @@ defineProps<Props>();
|
|||
}
|
||||
}
|
||||
|
||||
&__border--cornertext {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__border--cornertext img,
|
||||
&__border--cornertext &__cencor {
|
||||
margin: 1rem 0;
|
||||
border-radius: 1rem;
|
||||
filter: drop-shadow(0 0 0.625rem black);
|
||||
}
|
||||
|
||||
&__border--cornertext &__meta {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
|
||||
font-size: 0.75rem;
|
||||
|
||||
margin: 0 0 1.35rem 0;
|
||||
border-radius: 1em 0 1rem 0;
|
||||
padding: 0.25rem 0.75rem;
|
||||
|
||||
.caption,
|
||||
.copyright {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.caption {
|
||||
margin: 0 0.125rem 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__border--polaroid &__meta {
|
||||
font-family: "Permanent Marker", fantasy;
|
||||
font-family: 'Permanent Marker', fantasy;
|
||||
color: var(--color-figure-polaroid-text);
|
||||
|
||||
max-width: 35rem;
|
||||
|
|
|
@ -1,78 +1,68 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { debounce } from "@/helpers";
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { debounce } from '@/helpers'
|
||||
|
||||
const activeImage = ref(0);
|
||||
const images = ref<Element[]>([]);
|
||||
const element = document.createElement("div");
|
||||
const galleryViewport = ref<HTMLElement>(element);
|
||||
const galleryItemWidth = ref<number>(1);
|
||||
const activeImage = ref(0)
|
||||
const images = ref<Element[]>([])
|
||||
const element = document.createElement('div')
|
||||
const galleryViewport = ref<HTMLElement>(element)
|
||||
const galleryItemWidth = ref<number>(1)
|
||||
|
||||
const resizeObserverCallback = (entries: ResizeObserverEntry[]): void => {
|
||||
for (const entry of entries) {
|
||||
galleryItemWidth.value = entry.contentRect.width;
|
||||
galleryItemWidth.value = entry.contentRect.width
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const resizeObserver = new ResizeObserver(
|
||||
debounce(resizeObserverCallback, 1000)
|
||||
);
|
||||
const resizeObserver = new ResizeObserver(debounce(resizeObserverCallback, 1000))
|
||||
|
||||
const setActiveImage = (index: number): void => {
|
||||
activeImage.value = index;
|
||||
activeImage.value = index
|
||||
galleryViewport.value.scrollTo({
|
||||
left: galleryItemWidth.value * index,
|
||||
behavior: "smooth",
|
||||
});
|
||||
};
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
|
||||
const getActiveImage = (gallery: HTMLElement, itemWidth: number): number => {
|
||||
return gallery.scrollLeft / itemWidth;
|
||||
};
|
||||
return gallery.scrollLeft / itemWidth
|
||||
}
|
||||
|
||||
const prev = (): void => {
|
||||
if (activeImage.value > 0) {
|
||||
galleryViewport.value.scrollBy({
|
||||
left: galleryItemWidth.value * -1,
|
||||
behavior: "smooth",
|
||||
});
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const next = (): void => {
|
||||
if (activeImage.value < images.value.length - 1) {
|
||||
galleryViewport.value.scrollBy({
|
||||
left: galleryItemWidth.value,
|
||||
behavior: "smooth",
|
||||
});
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const onScroll = (): void => {
|
||||
const newImg = Math.floor(
|
||||
getActiveImage(galleryViewport.value, galleryItemWidth.value)
|
||||
);
|
||||
setActiveImage(newImg);
|
||||
};
|
||||
const newImg = Math.floor(getActiveImage(galleryViewport.value, galleryItemWidth.value))
|
||||
setActiveImage(newImg)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
resizeObserver.observe(galleryViewport.value);
|
||||
images.value = Array.from(galleryViewport.value.children);
|
||||
galleryItemWidth.value =
|
||||
galleryViewport.value.scrollWidth / images.value.length;
|
||||
galleryViewport.value.addEventListener("scroll", debounce(onScroll, 500));
|
||||
});
|
||||
resizeObserver.observe(galleryViewport.value)
|
||||
images.value = Array.from(galleryViewport.value.children)
|
||||
galleryItemWidth.value = galleryViewport.value.scrollWidth / images.value.length
|
||||
galleryViewport.value.addEventListener('scroll', debounce(onScroll, 500))
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gallery">
|
||||
<div class="gallery__images">
|
||||
<a
|
||||
href="#"
|
||||
class="gallery__prev"
|
||||
@click.prevent="prev()"
|
||||
v-show="activeImage > 0"
|
||||
></a>
|
||||
<a href="#" class="gallery__prev" @click.prevent="prev()" v-show="activeImage > 0"></a>
|
||||
<a
|
||||
href="#"
|
||||
class="gallery__next"
|
||||
|
|
|
@ -1,100 +1,98 @@
|
|||
<script setup lang="ts">
|
||||
import { inject } from "vue";
|
||||
import { modalResultKey } from "@/keys";
|
||||
import Button from "@/components/RefButton.vue";
|
||||
import { ref } from 'vue'
|
||||
|
||||
const modalResult = inject(modalResultKey, Function);
|
||||
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>
|
||||
<div class="modal">
|
||||
<div class="modal__background"></div>
|
||||
<div class="modal__message">
|
||||
<div>
|
||||
<h2><slot name="heading"></slot></h2>
|
||||
<p><slot name="message"></slot></p>
|
||||
<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__buttons">
|
||||
<Button
|
||||
class="modal__button positive"
|
||||
@click.prevent="modalResult(true)"
|
||||
>
|
||||
<slot name="yes"></slot>
|
||||
</Button>
|
||||
<Button
|
||||
class="modal__button negative"
|
||||
@click.prevent="modalResult(false)"
|
||||
>
|
||||
<slot name="no"></slot>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="modal__message">
|
||||
<slot name="message"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal__buttons">
|
||||
<slot name="buttons"></slot>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.modal {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: center;
|
||||
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
text-align: center;
|
||||
background: var(--modal-background);
|
||||
color: var(--color-text);
|
||||
|
||||
padding: 2rem;
|
||||
width: var(--modal-width);
|
||||
|
||||
backdrop-filter: blur(1rem);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
margin: auto;
|
||||
border: 0.25rem solid var(--color-modal-border);
|
||||
border-radius: 1rem;
|
||||
padding: 1rem;
|
||||
|
||||
overflow: auto;
|
||||
z-index: 9001;
|
||||
|
||||
&__background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
animation: fade-in 1s;
|
||||
|
||||
&::backdrop {
|
||||
backdrop-filter: blur(1rem);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
animation: fade-in 1s;
|
||||
}
|
||||
|
||||
&__message {
|
||||
flex: 0 1 30rem;
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
|
||||
background: var(--modal-background);
|
||||
|
||||
margin: auto;
|
||||
border: 0.25rem solid var(--color-modal-border);
|
||||
border-radius: 1rem;
|
||||
padding: 1rem;
|
||||
|
||||
z-index: 9002;
|
||||
text-align: center;
|
||||
gap: 1.5rem;
|
||||
|
||||
> * {
|
||||
flex: 0 1 100%;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
&__heading {
|
||||
margin: 1.875rem 0 0 0;
|
||||
}
|
||||
|
||||
&__button {
|
||||
flex: 0 1 100%;
|
||||
margin: 0.75em 0;
|
||||
&__message {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { computed } from 'vue'
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean;
|
||||
id: string;
|
||||
name?: string;
|
||||
modelValue: boolean
|
||||
id: string
|
||||
name?: string
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const checked = computed({
|
||||
get() {
|
||||
return props.modelValue;
|
||||
return props.modelValue
|
||||
},
|
||||
set(value) {
|
||||
emit("update:modelValue", value);
|
||||
},
|
||||
});
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -37,9 +41,8 @@ const checked = computed({
|
|||
|
||||
<style lang="scss">
|
||||
.toggle {
|
||||
font-family: "apple color emoji", "noto color emoji", "segoe ui emoji",
|
||||
"android emoji", "emojisymbols", "emojione mozilla", "twemoji mozilla",
|
||||
"segoe ui symbol";
|
||||
font-family: 'apple color emoji', 'noto color emoji', 'segoe ui emoji', 'android emoji',
|
||||
'emojisymbols', 'emojione mozilla', 'twemoji mozilla', 'segoe ui symbol';
|
||||
|
||||
padding: 0.5em;
|
||||
|
||||
|
@ -84,7 +87,7 @@ const checked = computed({
|
|||
|
||||
&:before,
|
||||
&:after {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
height: 1.1em;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
text-align: center;
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
|
||||
+ p {
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
import { RouterLink } from "vue-router";
|
||||
import router from "@/router";
|
||||
import { RouterLink } from 'vue-router'
|
||||
import router from '@/router'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav class="nav">
|
||||
<ul class="nav__list">
|
||||
<li
|
||||
class="nav__item"
|
||||
v-for="(route, idx) in router.options.routes"
|
||||
:key="idx"
|
||||
>
|
||||
<li class="nav__item" v-for="(route, idx) in router.options.routes" :key="idx">
|
||||
<RouterLink class="nav__link" :to="route.path">
|
||||
{{ route.name }}
|
||||
</RouterLink>
|
||||
|
@ -75,7 +71,7 @@ import router from "@/router";
|
|||
|
||||
&:before,
|
||||
&:after {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
|
||||
|
|
|
@ -5,21 +5,21 @@
|
|||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/sebin-smug-icon.png?w=180;200;240;280;350;400;480;0&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/sebin-smug-icon.png?w=180;200;240;280;350;400;480;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 280px, (min-width: 80em) 240px, (min-width: 35em) 200px, 180px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/sebin-smug-icon.png?w=180;200;240;280;350;400;480;0&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/sebin-smug-icon.png?w=180;200;240;280;350;400;480;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 280px, (min-width: 80em) 240px, (min-width: 35em) 200px, 180px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/sebin-smug-icon.png?w=180;200;240;280;350;400;480;0&png&withoutEnlargement&srcset
|
||||
@/assets/sebin-smug-icon.png?w=180;200;240;280;350;400;480;0&format=png&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 280px, (min-width: 80em) 240px, (min-width: 35em) 200px, 180px"
|
||||
alt="Sebin Avatar"
|
||||
|
@ -68,7 +68,7 @@
|
|||
|
||||
&__mainline,
|
||||
&__subline {
|
||||
font-family: "Exo", sans-serif;
|
||||
font-family: 'Exo', sans-serif;
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
margin: 0;
|
||||
|
|
|
@ -1,76 +1,67 @@
|
|||
const debounce = (fn: Function, delay: number = 300): any => {
|
||||
let timer = 0;
|
||||
let timer = 0
|
||||
const debounced = (...args: any[]): void => {
|
||||
if (!args) args = [];
|
||||
clearTimeout(timer);
|
||||
if (!args) args = []
|
||||
clearTimeout(timer)
|
||||
|
||||
timer = setTimeout(() => {
|
||||
fn.apply(fn, args);
|
||||
}, delay);
|
||||
};
|
||||
fn.apply(fn, args)
|
||||
}, delay)
|
||||
}
|
||||
|
||||
return debounced;
|
||||
};
|
||||
return debounced
|
||||
}
|
||||
|
||||
const getClientLocale = (): string => {
|
||||
return navigator.languages.length > 0 ? navigator.languages[0] : "en-US";
|
||||
};
|
||||
return navigator.languages.length > 0 ? navigator.languages[0] : 'en-US'
|
||||
}
|
||||
|
||||
const getAge = (dateOfBirth: Date): number => {
|
||||
const today = new Date();
|
||||
const today = new Date()
|
||||
|
||||
const thisYear = today.getFullYear();
|
||||
const thisMonth = today.getMonth();
|
||||
const thisDay = today.getDate();
|
||||
const thisYear = today.getFullYear()
|
||||
const thisMonth = today.getMonth()
|
||||
const thisDay = today.getDate()
|
||||
|
||||
const dobYear = dateOfBirth.getFullYear();
|
||||
const dobMonth = dateOfBirth.getMonth();
|
||||
const dobDay = dateOfBirth.getDate();
|
||||
const dobYear = dateOfBirth.getFullYear()
|
||||
const dobMonth = dateOfBirth.getMonth()
|
||||
const dobDay = dateOfBirth.getDate()
|
||||
|
||||
let age = thisYear - dobYear;
|
||||
let age = thisYear - dobYear
|
||||
|
||||
if (thisMonth < dobMonth) age--;
|
||||
if (thisMonth === dobMonth && thisDay < dobDay) age--;
|
||||
if (thisMonth < dobMonth) age--
|
||||
if (thisMonth === dobMonth && thisDay < dobDay) age--
|
||||
|
||||
return age;
|
||||
};
|
||||
return age
|
||||
}
|
||||
|
||||
const toImperial = (cm: number): string => {
|
||||
const realFeet = (cm * 0.3937) / 12;
|
||||
const feet = Math.floor(realFeet);
|
||||
const inches = Math.round((realFeet - feet) * 12);
|
||||
const realFeet = (cm * 0.3937) / 12
|
||||
const feet = Math.floor(realFeet)
|
||||
const inches = Math.round((realFeet - feet) * 12)
|
||||
|
||||
return `${feet}'${inches}"`;
|
||||
};
|
||||
return `${feet}'${inches}"`
|
||||
}
|
||||
|
||||
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 nearExact = kg / 0.45359237;
|
||||
const lbs = Math.floor(nearExact);
|
||||
const nearExact = kg / 0.45359237
|
||||
const lbs = Math.floor(nearExact)
|
||||
|
||||
return lbs;
|
||||
};
|
||||
return lbs
|
||||
}
|
||||
|
||||
const toFahrenheit = (celsius: number): number => {
|
||||
return celsius * 1.8 + 32;
|
||||
};
|
||||
return celsius * 1.8 + 32
|
||||
}
|
||||
|
||||
const dateFormat = new Intl.DateTimeFormat(getClientLocale(), {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "2-digit",
|
||||
});
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: '2-digit'
|
||||
})
|
||||
|
||||
export {
|
||||
debounce,
|
||||
getClientLocale,
|
||||
getAge,
|
||||
toImperial,
|
||||
toInch,
|
||||
toLbs,
|
||||
toFahrenheit,
|
||||
dateFormat,
|
||||
};
|
||||
export { debounce, getClientLocale, getAge, toImperial, toInch, toLbs, toFahrenheit, dateFormat }
|
||||
|
|
|
@ -1,12 +1,25 @@
|
|||
interface ColorDict {
|
||||
name: string;
|
||||
value: string;
|
||||
name: string
|
||||
value: string
|
||||
}
|
||||
|
||||
interface Kink {
|
||||
name: string;
|
||||
receive: boolean;
|
||||
give: boolean;
|
||||
name: string
|
||||
rating: number
|
||||
role?: Role
|
||||
}
|
||||
|
||||
export type { ColorDict, Kink };
|
||||
export enum Ratings {
|
||||
Love,
|
||||
Yes,
|
||||
Maybe,
|
||||
No
|
||||
}
|
||||
|
||||
export enum Role {
|
||||
Give = 1,
|
||||
Receive = 2,
|
||||
Both = Give | Receive
|
||||
}
|
||||
|
||||
export type { ColorDict, Kink }
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import type { InjectionKey, Ref } from "vue";
|
||||
import type { InjectionKey, Ref } from 'vue'
|
||||
|
||||
export const isWarnKey: InjectionKey<Ref<boolean>> = Symbol("isWarnKey");
|
||||
export const nsfwKey: InjectionKey<Ref<boolean>> = Symbol("nsfwKey");
|
||||
export const showModalKey: InjectionKey<Function> = Symbol("showModalKey");
|
||||
export const modalResultKey: InjectionKey<Function> = Symbol("modalResultKey");
|
||||
export const nsfwKey: InjectionKey<Ref<boolean>> = Symbol('nsfwKey')
|
||||
export const showModalKey: InjectionKey<Function> = Symbol('showModalKey')
|
||||
export const modalResultKey: InjectionKey<Function> = Symbol('modalResultKey')
|
||||
|
|
16
src/main.ts
16
src/main.ts
|
@ -1,12 +1,12 @@
|
|||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
import "normalize.css";
|
||||
import "@/scss/main.scss";
|
||||
import 'normalize.css'
|
||||
import '@/scss/main.scss'
|
||||
|
||||
const app = createApp(App);
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(router);
|
||||
app.use(router)
|
||||
|
||||
app.mount("body");
|
||||
app.mount('body')
|
||||
|
|
|
@ -1,43 +1,43 @@
|
|||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import HomeView from "@/views/HomeView.vue";
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '@/views/HomeView.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
scrollBehavior() {
|
||||
return { top: 0, behavior: "smooth" };
|
||||
return { top: 0, behavior: 'smooth' }
|
||||
},
|
||||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
name: "home",
|
||||
component: HomeView,
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeView
|
||||
},
|
||||
{
|
||||
path: "/general",
|
||||
name: "general",
|
||||
component: () => import("@/views/GeneralView.vue"),
|
||||
path: '/general',
|
||||
name: 'general',
|
||||
component: () => import('@/views/GeneralView.vue')
|
||||
},
|
||||
{
|
||||
path: "/anatomy",
|
||||
name: "anatomy",
|
||||
component: () => import("@/views/AnatomyView.vue"),
|
||||
path: '/anatomy',
|
||||
name: 'anatomy',
|
||||
component: () => import('@/views/AnatomyView.vue')
|
||||
},
|
||||
{
|
||||
path: "/clothing",
|
||||
name: "clothing",
|
||||
component: () => import("@/views/ClothingView.vue"),
|
||||
path: '/clothing',
|
||||
name: 'clothing',
|
||||
component: () => import('@/views/ClothingView.vue')
|
||||
},
|
||||
{
|
||||
path: "/abilities",
|
||||
name: "abilities",
|
||||
component: () => import("@/views/AbilitiesView.vue"),
|
||||
path: '/abilities',
|
||||
name: 'abilities',
|
||||
component: () => import('@/views/AbilitiesView.vue')
|
||||
},
|
||||
{
|
||||
path: "/overdrive",
|
||||
name: "overdrive",
|
||||
component: () => import("@/views/OverdriveView.vue"),
|
||||
},
|
||||
],
|
||||
});
|
||||
path: '/overdrive',
|
||||
name: 'overdrive',
|
||||
component: () => import('@/views/OverdriveView.vue')
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
export default router;
|
||||
export default router
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "fontfaces";
|
||||
@import 'fontfaces';
|
||||
|
||||
/* theme colors */
|
||||
:root {
|
||||
|
@ -28,6 +28,18 @@
|
|||
--theme-c-white: #ffffff;
|
||||
--theme-c-black: #000000;
|
||||
|
||||
--theme-c-love: #e00f60;
|
||||
--theme-c-yes: #2faf2a;
|
||||
--theme-c-maybe: #daa520;
|
||||
--theme-c-no: #8b0000;
|
||||
--theme-c-receive: #448dc9;
|
||||
--theme-c-give: #4a7d91;
|
||||
|
||||
--theme-c-love-dark: #9b0a41;
|
||||
--theme-c-yes-dark: #1f771c;
|
||||
--theme-c-maybe-dark: #997416;
|
||||
--theme-c-no-dark: #530000;
|
||||
|
||||
--theme-c-dull-red-translucent: #c64c35bf;
|
||||
--theme-c-indian-yellow-translucent: #eda958bf;
|
||||
--theme-c-dusky-blue-translucent: #4b608fbf;
|
||||
|
@ -100,8 +112,8 @@
|
|||
|
||||
/* general purpose variables */
|
||||
:root {
|
||||
--font-family-copy: "Dosis", sans-serif;
|
||||
--font-family-headings: "Zilla Slab", sans-serif;
|
||||
--font-family-copy: 'Dosis', sans-serif;
|
||||
--font-family-headings: 'Zilla Slab', sans-serif;
|
||||
|
||||
--font-size: 18px;
|
||||
--font-size-h1: 3rem;
|
||||
|
@ -128,11 +140,17 @@
|
|||
|
||||
--page-background: var(--theme-b-page-background-light);
|
||||
--header-background: var(--theme-b-navigation-background-light);
|
||||
--header-margin: 1rem var(--container-spacing-right-safe) 1rem
|
||||
var(--container-spacing-left-safe);
|
||||
--header-margin: 1rem var(--container-spacing-right-safe) 1rem var(--container-spacing-left-safe);
|
||||
--navigation-justify-content: flex-start;
|
||||
|
||||
--quickfacts-background: var(--theme-b-page-background-light);
|
||||
--quickfacts-cols-double: auto;
|
||||
--quickfacts-cols-triple: auto;
|
||||
--quickfacts-cols-quadruple: auto;
|
||||
|
||||
--modal-background: var(--theme-b-modal-background-light);
|
||||
--modal-width: 100%;
|
||||
|
||||
--welcome-header-headings-flex-basis: 100%;
|
||||
--welcome-header-headings-margin: 1.5rem 0 0 0;
|
||||
--welcome-header-mainline-font-size: 2rem;
|
||||
|
@ -165,8 +183,7 @@
|
|||
--social-links-flex-flow: row wrap;
|
||||
--social-links-flex: 1 1 50%;
|
||||
|
||||
--footer-padding: var(--container-spacing-top-safe) 0
|
||||
var(--container-spacing-bottom-safe) 0;
|
||||
--footer-padding: var(--container-spacing-top-safe) 0 var(--container-spacing-bottom-safe) 0;
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
|
@ -207,18 +224,25 @@
|
|||
--font-size-h2: 2.25rem;
|
||||
--font-size-h3: 1.5rem;
|
||||
--navigation-justify-content: center;
|
||||
|
||||
--modal-width: 30rem;
|
||||
|
||||
--welcome-header-headings-flex-basis: 23rem;
|
||||
--welcome-header-headings-margin: 0;
|
||||
--welcome-header-mainline-font-size: var(--font-size-h1);
|
||||
|
||||
--quickfacts-cols-double: 2 auto;
|
||||
--quickfacts-cols-triple: 3 auto;
|
||||
--quickfacts-cols-quadruple: 4 auto;
|
||||
|
||||
--section-max-width: 34rem;
|
||||
|
||||
--button-group-flex: 0 0 auto;
|
||||
|
||||
--table-cell-padding: 0.5rem 1rem;
|
||||
|
||||
--figure-cencor-width: 20rem;
|
||||
--figure-cencor-height: 20rem;
|
||||
--figure-cencor-width: max(50vw, 20rem);
|
||||
--figure-cencor-height: max(50vh, 30rem);
|
||||
|
||||
--gallery-size: 1.5rem;
|
||||
--gallery-arrow-position: 2rem;
|
||||
|
|
|
@ -97,6 +97,8 @@ h3 {
|
|||
|
||||
p {
|
||||
margin: 1rem 0;
|
||||
text-align: justify;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
|
@ -111,54 +113,26 @@ blockquote {
|
|||
padding: 1rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 1rem 0;
|
||||
.social {
|
||||
> * {
|
||||
&:before {
|
||||
margin: 0 0.375rem 0 0;
|
||||
}
|
||||
|
||||
&.col-2 {
|
||||
columns: 2 auto;
|
||||
}
|
||||
&.twitter:before {
|
||||
content: "🐦";
|
||||
}
|
||||
|
||||
&.col-3 {
|
||||
columns: 3 auto;
|
||||
}
|
||||
&.mastodon:before {
|
||||
content: "🐘";
|
||||
}
|
||||
|
||||
&.col-4 {
|
||||
columns: 4 auto;
|
||||
}
|
||||
&.telegram:before {
|
||||
content: "📨";
|
||||
}
|
||||
|
||||
&.social {
|
||||
display: flex;
|
||||
flex-flow: var(--social-links-flex-flow);
|
||||
justify-content: space-evenly;
|
||||
|
||||
margin: 1rem 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
flex: var(--social-links-flex);
|
||||
padding: 0.25rem;
|
||||
text-align: center;
|
||||
|
||||
&:before {
|
||||
margin: 0 0.375rem 0 0;
|
||||
}
|
||||
|
||||
&.twitter:before {
|
||||
content: "🐦";
|
||||
}
|
||||
|
||||
&.mastodon:before {
|
||||
content: "🐘";
|
||||
}
|
||||
|
||||
&.telegram:before {
|
||||
content: "📨";
|
||||
}
|
||||
|
||||
&.furaffinity:before {
|
||||
content: "🐾";
|
||||
}
|
||||
&.furaffinity:before {
|
||||
content: "🐾";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
113
src/sebin.ts
113
src/sebin.ts
|
@ -1,51 +1,82 @@
|
|||
export const firstName = "Sebin",
|
||||
middleName = "Antario",
|
||||
lastName = "Nyshkim",
|
||||
dateOfBirth = new Date("1993-10-17"),
|
||||
gender = "male ♂️",
|
||||
orientation = "gay",
|
||||
position = "vers, prefers top",
|
||||
import { Ratings, Role } from '@/interfaces'
|
||||
|
||||
export const firstName = 'Sebin',
|
||||
middleName = 'Antario',
|
||||
lastName = 'Nyshkim',
|
||||
dateOfBirth = new Date('1993-10-17'),
|
||||
gender = 'male ♂️',
|
||||
orientation = 'gay',
|
||||
position = 'vers, prefers top',
|
||||
height = 210, // cm
|
||||
weight = 124, // kg
|
||||
tailLength = 104, // cm
|
||||
wingspan = 417, // cm
|
||||
colors = {
|
||||
hairPrimary: "#4b608f",
|
||||
hairSecondary: "#6684c0",
|
||||
eyes: "#31c215",
|
||||
scalesPrimary: "#c64c35",
|
||||
scalesSecondary: "#eda958",
|
||||
eyebrows: "#eda958",
|
||||
tailspikes: "#7f4539",
|
||||
horns: "#413a3a",
|
||||
claws: "#413a3a",
|
||||
nipples: "#413a3a",
|
||||
penis: "#413a3a",
|
||||
hairPrimary: '#4b608f',
|
||||
hairSecondary: '#6684c0',
|
||||
eyes: '#31c215',
|
||||
scalesPrimary: '#c64c35',
|
||||
scalesSecondary: '#eda958',
|
||||
eyebrows: '#eda958',
|
||||
tailspikes: '#7f4539',
|
||||
horns: '#413a3a',
|
||||
claws: '#413a3a',
|
||||
nipples: '#413a3a',
|
||||
penis: '#413a3a'
|
||||
},
|
||||
hobbies = ["working out", "travels", "camping", "video games", "tech"],
|
||||
hobbies = ['working out', 'travels', 'camping', 'video games', 'tech'],
|
||||
penis = {
|
||||
shape: "humanoid",
|
||||
type: "grower",
|
||||
special: "ridged, no foreskin",
|
||||
shape: 'humanoid',
|
||||
type: 'grower',
|
||||
special: 'ridged, no foreskin',
|
||||
size: 20, // cm
|
||||
girth: 5, // cm
|
||||
girth: 5 // cm
|
||||
},
|
||||
kinks = [
|
||||
{ name: "Oral", receive: true, give: true },
|
||||
{ name: "Anal", receive: true, give: true },
|
||||
{ name: "Facial", receive: true, give: true },
|
||||
{ name: "Creampie", receive: true, give: true },
|
||||
{ name: "Bukkake", receive: true, give: true },
|
||||
{ name: "Biting", receive: true, give: true },
|
||||
{ name: "Nipple Play", receive: true, give: true },
|
||||
{ name: "Rough", receive: true, give: true },
|
||||
{ name: "Toys", receive: true, give: true },
|
||||
|
||||
{ name: "Frotting", receive: true, give: true },
|
||||
{ name: "Muscle Worship", receive: true, give: true },
|
||||
{ name: "Filled Condoms", receive: true, give: true },
|
||||
{ name: "Growth/Macro", receive: true, give: true },
|
||||
{ name: "Size Difference", receive: true, give: true },
|
||||
{ name: "Underwear", receive: true, give: true },
|
||||
{ name: "Chubbies", receive: true, give: true },
|
||||
];
|
||||
{ name: 'Absorption', rating: Ratings.No },
|
||||
{ name: 'Anal', rating: Ratings.Love, role: Role.Both },
|
||||
{ name: 'Auto-Fellatio', rating: Ratings.Yes },
|
||||
{ name: 'Biting', rating: Ratings.Yes, role: Role.Both },
|
||||
{ name: 'Bukkake', rating: Ratings.Yes, role: Role.Give },
|
||||
{ name: 'Chastity', rating: Ratings.No },
|
||||
{ name: 'Chubby', rating: Ratings.Maybe },
|
||||
{ name: 'Clothed Sex', rating: Ratings.Yes },
|
||||
{ name: 'Cock Slapping', rating: Ratings.Yes, role: Role.Give },
|
||||
{ name: 'Coiling', rating: Ratings.Maybe, role: Role.Give },
|
||||
{ name: 'Competition', rating: Ratings.Maybe, role: Role.Give },
|
||||
{ name: 'Creampie', rating: Ratings.Yes, role: Role.Give },
|
||||
{ name: 'Crushing (Living/Objects)', rating: Ratings.No },
|
||||
{ name: 'Cum From Mouth/Nose', rating: Ratings.Yes, role: Role.Give },
|
||||
{ name: 'Cum Inflation (Light/Medium)', rating: Ratings.Yes },
|
||||
{ name: 'Deep-throat', rating: Ratings.Yes, role: Role.Receive },
|
||||
{ name: 'Dirty Talking', rating: Ratings.Yes },
|
||||
{ name: 'Excessive Cum', rating: Ratings.Love, role: Role.Both },
|
||||
{ name: 'Face-Fucking', rating: Ratings.Yes, role: Role.Give },
|
||||
{ name: 'Facial', rating: Ratings.Yes, role: Role.Give },
|
||||
{ name: 'Feet', rating: Ratings.No },
|
||||
{ name: 'Filled Condoms', rating: Ratings.Yes },
|
||||
{ name: 'Foreplay', rating: Ratings.Yes, role: Role.Both },
|
||||
{ name: 'Frotting', rating: Ratings.Yes },
|
||||
{ name: 'Gangbangs', rating: Ratings.Yes },
|
||||
{ name: 'Growth', rating: Ratings.Love, role: Role.Receive },
|
||||
{ name: 'Handjobs', rating: Ratings.Yes, role: Role.Both },
|
||||
{ name: 'Hotdogging', rating: Ratings.Yes, role: Role.Give },
|
||||
{ name: 'Kissing', rating: Ratings.Yes, role: Role.Both },
|
||||
{ name: 'Macro', rating: Ratings.Love },
|
||||
{ name: 'Milking', rating: Ratings.Yes },
|
||||
{ name: 'Muscle Growth', rating: Ratings.Love, role: Role.Both },
|
||||
{ name: 'Muscle Worship', rating: Ratings.Yes, role: Role.Both },
|
||||
{ name: 'Nipple Play', rating: Ratings.Yes, role: Role.Both },
|
||||
{ name: 'Oral', rating: Ratings.Love, role: Role.Both },
|
||||
{ name: 'Rough', rating: Ratings.Yes, role: Role.Both },
|
||||
{ name: 'Sheath Play', rating: Ratings.Yes, role: Role.Both },
|
||||
{ name: 'Size Difference', rating: Ratings.Love },
|
||||
{ name: 'Slime/Goo Characters', rating: Ratings.Yes },
|
||||
{ name: 'Spanking', rating: Ratings.Maybe, role: Role.Give },
|
||||
{ name: 'Tailsex', rating: Ratings.Yes, role: Role.Both },
|
||||
{ name: 'Toys', rating: Ratings.Yes, role: Role.Both },
|
||||
{ name: 'Underwear', rating: Ratings.Love },
|
||||
{ name: 'Unsanitary', rating: Ratings.No },
|
||||
{ name: 'Verbal Abuse', rating: Ratings.Maybe, role: Role.Give },
|
||||
{ name: 'Vore', rating: Ratings.No }
|
||||
]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import AttackItem from "@/components/AttackItem.vue";
|
||||
import AttackList from "@/components/AttackList.vue";
|
||||
import AttackItem from '@/components/AttackItem.vue'
|
||||
import AttackList from '@/components/AttackList.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -8,8 +8,8 @@ import AttackList from "@/components/AttackList.vue";
|
|||
<h1>{{ $route.name }}</h1>
|
||||
|
||||
<p>
|
||||
Since Sebin is a fire dragon there's a myriad of abilities he has at his
|
||||
disposal to defend himself.
|
||||
Since Sebin is a fire dragon there's a myriad of abilities he has at his disposal to defend
|
||||
himself.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
@ -24,21 +24,21 @@ import AttackList from "@/components/AttackList.vue";
|
|||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/attacks/sebin-fire_breath-hires.png?w=640;400;480;560;1280;800;960;1120;1920;1200;1330;1680&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/attacks/sebin-fire_breath-hires.png?w=640;960;1920;1330;1680&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 560px, (min-width: 80em) 480px, (min-width: 45em) 400px, 640px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/attacks/sebin-fire_breath-hires.png?w=640;400;480;560;1280;800;960;1120;1920;1200;1330;1680&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/attacks/sebin-fire_breath-hires.png?w=640;960;1920;1330;1680&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 560px, (min-width: 80em) 480px, (min-width: 45em) 400px, 640px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/attacks/sebin-fire_breath-hires.png?w=640;400;480;560;1280;800;960;1120;1920;1200;1330;1680&png&withoutEnlargement&srcset
|
||||
@/assets/refs/attacks/sebin-fire_breath-hires.png?w=640;960;1920;1330;1680&format=png&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 560px, (min-width: 80em) 480px, (min-width: 45em) 400px, 640px"
|
||||
alt="Sebin Fire Breath"
|
||||
|
@ -48,12 +48,10 @@ import AttackList from "@/components/AttackList.vue";
|
|||
</template>
|
||||
<template #name>Fire Breath</template>
|
||||
<template #desc>
|
||||
Like most fire dragons, Sebin can breathe fire. In order to do this, he
|
||||
takes a deep breath to enrich the oxygen in his lungs with gases, which,
|
||||
together with special glands in his mouth, produce a combustible
|
||||
mixture. The resulting jet of fire, reaching several hundred degrees
|
||||
Celsius, spreads out on its way to its target, scorching everything in
|
||||
its path.
|
||||
Like most fire dragons, Sebin can breathe fire. In order to do this, he takes a deep breath
|
||||
to enrich the oxygen in his lungs with gases, which, together with special glands in his
|
||||
mouth, produce a combustible mixture. The resulting jet of fire, reaching several hundred
|
||||
degrees Celsius, spreads out on its way to its target, scorching everything in its path.
|
||||
</template>
|
||||
</AttackItem>
|
||||
|
||||
|
@ -63,21 +61,21 @@ import AttackList from "@/components/AttackList.vue";
|
|||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/attacks/sebin-flame_toss-hires.png?w=640;400;480;560;1280;800;960;1120;1920;1200;1330;1680&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/attacks/sebin-flame_toss-hires.png?w=640;960;1920;1330;1680&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 560px, (min-width: 80em) 480px, (min-width: 45em) 400px, 640px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/attacks/sebin-flame_toss-hires.png?w=640;400;480;560;1280;800;960;1120;1920;1200;1330;1680&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/attacks/sebin-flame_toss-hires.png?w=640;960;1920;1330;1680&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 560px, (min-width: 80em) 480px, (min-width: 45em) 400px, 640px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/attacks/sebin-flame_toss-hires.png?w=640;400;480;560;1280;800;960;1120;1920;1200;1330;1680&png&withoutEnlargement&srcset
|
||||
@/assets/refs/attacks/sebin-flame_toss-hires.png?w=640;960;1920;1330;1680&format=png&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 560px, (min-width: 80em) 480px, (min-width: 45em) 400px, 640px"
|
||||
alt="Sebin Fire Breath"
|
||||
|
@ -87,11 +85,10 @@ import AttackList from "@/components/AttackList.vue";
|
|||
</template>
|
||||
<template #name>Flame Toss</template>
|
||||
<template #desc>
|
||||
By spitting fire into his hands, Sebin can form it into a ball and use
|
||||
it as a projectile. His scales are fireproof and can withstand the high
|
||||
temperatures. Due to their high concentration, the projectiles explode
|
||||
upon impact. By combining two fireballs the explosion radius increases
|
||||
dramatically.
|
||||
By spitting fire into his hands, Sebin can form it into a ball and use it as a projectile.
|
||||
His scales are fireproof and can withstand the high temperatures. Due to their high
|
||||
concentration, the projectiles explode upon impact. By combining two fireballs the explosion
|
||||
radius increases dramatically.
|
||||
</template>
|
||||
</AttackItem>
|
||||
|
||||
|
@ -101,21 +98,21 @@ import AttackList from "@/components/AttackList.vue";
|
|||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/attacks/sebin-kindled_fist-hires.png?w=640;400;480;560;1280;800;960;1120;1920;1200;1330;1680&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/attacks/sebin-kindled_fist-hires.png?w=640;960;1920;1330;1680&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 560px, (min-width: 80em) 480px, (min-width: 45em) 400px, 640px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/attacks/sebin-kindled_fist-hires.png?w=640;400;480;560;1280;800;960;1120;1920;1200;1330;1680&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/attacks/sebin-kindled_fist-hires.png?w=640;960;1920;1330;1680&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 560px, (min-width: 80em) 480px, (min-width: 45em) 400px, 640px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/attacks/sebin-kindled_fist-hires.png?w=640;400;480;560;1280;800;960;1120;1920;1200;1330;1680&png&withoutEnlargement&srcset
|
||||
@/assets/refs/attacks/sebin-kindled_fist-hires.png?w=640;960;1920;1330;1680&format=png&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 560px, (min-width: 80em) 480px, (min-width: 45em) 400px, 640px"
|
||||
alt="Sebin Kindled Fist"
|
||||
|
@ -125,10 +122,9 @@ import AttackList from "@/components/AttackList.vue";
|
|||
</template>
|
||||
<template #name>Kindled Fist</template>
|
||||
<template #desc>
|
||||
Apart from throwing projectiles, Sebin can also use the fireballs to
|
||||
wrap his fists in fire. This allows him to inflict severe burns on his
|
||||
opponent with each blow. In addition, he can release the fire from his
|
||||
fists with aimed blows and hurl it at his opponents.
|
||||
Apart from throwing projectiles, Sebin can also use the fireballs to wrap his fists in fire.
|
||||
This allows him to inflict severe burns on his opponent with each blow. In addition, he can
|
||||
release the fire from his fists with aimed blows and hurl it at his opponents.
|
||||
</template>
|
||||
</AttackItem>
|
||||
|
||||
|
@ -138,21 +134,21 @@ import AttackList from "@/components/AttackList.vue";
|
|||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/attacks/sebin-burning_twister-hires.png?w=640;400;480;560;1280;800;960;1120;1920;1200;1330;1680&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/attacks/sebin-burning_twister-hires.png?w=640;960;1920;1330;1680&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 560px, (min-width: 80em) 480px, (min-width: 45em) 400px, 640px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/attacks/sebin-burning_twister-hires.png?w=640;400;480;560;1280;800;960;1120;1920;1200;1330;1680&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/attacks/sebin-burning_twister-hires.png?w=640;960;1920;1330;1680&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 560px, (min-width: 80em) 480px, (min-width: 45em) 400px, 640px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/attacks/sebin-burning_twister-hires.png?w=640;400;480;560;1280;800;960;1120;1920;1200;1330;1680&png&withoutEnlargement&srcset
|
||||
@/assets/refs/attacks/sebin-burning_twister-hires.png?w=640;960;1920;1330;1680&format=png&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 560px, (min-width: 80em) 480px, (min-width: 45em) 400px, 640px"
|
||||
alt="Sebin Burning Twister"
|
||||
|
@ -162,9 +158,8 @@ import AttackList from "@/components/AttackList.vue";
|
|||
</template>
|
||||
<template #name>Burning Twister</template>
|
||||
<template #desc>
|
||||
A technique used in aerial combat, Sebin uses his fire breath to engulf
|
||||
his body in flames while spinning to become a fire tornado that singes
|
||||
opponents.
|
||||
A technique used in aerial combat, Sebin uses his fire breath to engulf his body in flames
|
||||
while spinning to become a fire tornado that singes opponents.
|
||||
</template>
|
||||
</AttackItem>
|
||||
</AttackList>
|
||||
|
|
|
@ -1,59 +1,42 @@
|
|||
<script setup lang="ts">
|
||||
import { inject } from "vue";
|
||||
import { isWarnKey, nsfwKey, showModalKey } from "@/keys";
|
||||
import type { ColorDict } from "@/interfaces";
|
||||
import { tailLength, wingspan, penis, colors } from "@/sebin";
|
||||
import { toImperial, toFahrenheit } from "@/helpers";
|
||||
import RefToggle from "@/components/RefToggle.vue";
|
||||
import RefModal from "@/components/RefModal.vue";
|
||||
import RefGallery from "@/components/RefGallery.vue";
|
||||
import RefFigure from "@/components/RefFigure.vue";
|
||||
import ColorTable from "@/components/ColorTable.vue";
|
||||
import DataTable from "@/components/DataTable.vue";
|
||||
import QuickFacts from "@/components/QuickFacts.vue";
|
||||
import { inject } from 'vue'
|
||||
import { nsfwKey, showModalKey } from '@/keys'
|
||||
import type { ColorDict } from '@/interfaces'
|
||||
import { tailLength, wingspan, penis, colors } from '@/sebin'
|
||||
import { toImperial, toFahrenheit } from '@/helpers'
|
||||
import RefToggle from '@/components/RefToggle.vue'
|
||||
import RefGallery from '@/components/RefGallery.vue'
|
||||
import RefFigure from '@/components/RefFigure.vue'
|
||||
import ColorTable from '@/components/ColorTable.vue'
|
||||
import DataTable from '@/components/DataTable.vue'
|
||||
import QuickFacts from '@/components/QuickFacts.vue'
|
||||
|
||||
const sebinColors: ColorDict[] = [
|
||||
{ name: "Scales", value: colors.scalesPrimary },
|
||||
{ name: "Chest, Wings (front)", value: colors.scalesSecondary },
|
||||
{ name: "Hair", value: colors.hairPrimary },
|
||||
{ name: "Hair Streaks (optional)", value: colors.hairSecondary },
|
||||
{ name: "Eyes", value: colors.eyes },
|
||||
{ name: "Facial Spikes", value: colors.eyebrows },
|
||||
{ name: "Horns/Claws/Nipples", value: colors.horns },
|
||||
{ name: "Tail Spikes", value: colors.tailspikes },
|
||||
];
|
||||
{ name: 'Scales', value: colors.scalesPrimary },
|
||||
{ name: 'Chest, Wings (front)', value: colors.scalesSecondary },
|
||||
{ name: 'Hair', value: colors.hairPrimary },
|
||||
{ name: 'Hair Streaks (optional)', value: colors.hairSecondary },
|
||||
{ name: 'Eyes', value: colors.eyes },
|
||||
{ name: 'Facial Spikes', value: colors.eyebrows },
|
||||
{ name: 'Horns/Claws/Nipples', value: colors.horns },
|
||||
{ name: 'Tail Spikes', value: colors.tailspikes }
|
||||
]
|
||||
|
||||
const sebinPenisHeadings = ["Key", "Value"];
|
||||
const sebinPenisHeadings = ['Key', 'Value']
|
||||
const sebinPenisData = [
|
||||
["Shape", penis.shape],
|
||||
["Type", penis.type],
|
||||
["Special Traits", penis.special],
|
||||
["Color", colors.penis],
|
||||
["Length", `${penis.size} cm (${toImperial(penis.size)})`],
|
||||
["Girth", `${penis.girth} cm (${toImperial(penis.girth)})`],
|
||||
];
|
||||
['Shape', penis.shape],
|
||||
['Type', penis.type],
|
||||
['Special Traits', penis.special],
|
||||
['Color', colors.penis],
|
||||
['Length', `${penis.size} cm (${toImperial(penis.size)})`],
|
||||
['Girth', `${penis.girth} cm (${toImperial(penis.girth)})`]
|
||||
]
|
||||
|
||||
const isNsfw = inject<boolean>(nsfwKey, false);
|
||||
const isWarn = inject<boolean>(isWarnKey, false);
|
||||
const showModal = inject<Function>(showModalKey, Function);
|
||||
const isNsfw = inject<boolean>(nsfwKey, false)
|
||||
const showModal = inject<Function>(showModalKey, Function)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RefModal v-show="isWarn">
|
||||
<template #heading>
|
||||
⚠️⚠️⚠️<br />
|
||||
Whoa, Nelly!
|
||||
</template>
|
||||
|
||||
<template #message>
|
||||
By enabling NSFW mode you confirm that you are of legal age to view adult
|
||||
content.
|
||||
</template>
|
||||
|
||||
<template #yes>Yes, show me the goods 👀</template>
|
||||
<template #no>NO, STAHP 😱</template>
|
||||
</RefModal>
|
||||
|
||||
<section>
|
||||
<h1>{{ $route.name }}</h1>
|
||||
</section>
|
||||
|
@ -64,7 +47,7 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<picture v-if="isNsfw">
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/sebin-ref-body-NSFW.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;2880;3240;3360&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/sebin-ref-body-NSFW.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;2880;3240;3360&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 2240px,
|
||||
(min-width: 100em) 1920px,
|
||||
|
@ -78,7 +61,7 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/sebin-ref-body-NSFW.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;2880;3240;3360&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/sebin-ref-body-NSFW.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;2880;3240;3360&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 2240px,
|
||||
(min-width: 100em) 1920px,
|
||||
|
@ -92,7 +75,7 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/sebin-ref-body-NSFW.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;2880;3240;3360&png&withoutEnlargement&srcset
|
||||
@/assets/refs/sebin-ref-body-NSFW.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;2880;3240;3360&format=png&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 2240px,
|
||||
(min-width: 100em) 1920px,
|
||||
|
@ -109,7 +92,7 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<picture v-else>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/sebin-ref-body-SFW.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;2880;3240;3360&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/sebin-ref-body-SFW.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;2880;3240;3360&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 2240px,
|
||||
(min-width: 100em) 1920px,
|
||||
|
@ -123,7 +106,7 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/sebin-ref-body-SFW.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;2880;3240;3360&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/sebin-ref-body-SFW.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;2880;3240;3360&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 2240px,
|
||||
(min-width: 100em) 1920px,
|
||||
|
@ -137,7 +120,7 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/sebin-ref-body-SFW.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;2880;3240;3360&png&withoutEnlargement&srcset
|
||||
@/assets/refs/sebin-ref-body-SFW.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;2880;3240;3360&format=png&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 2240px,
|
||||
(min-width: 100em) 1920px,
|
||||
|
@ -154,11 +137,7 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
</template>
|
||||
|
||||
<template #caption>
|
||||
<RefToggle
|
||||
id="sebin-fullbody-ref"
|
||||
v-model="isNsfw"
|
||||
@click.prevent="showModal()"
|
||||
>
|
||||
<RefToggle id="sebin-fullbody-ref" v-model="isNsfw" @click.prevent="showModal()">
|
||||
<template #off>😇</template>
|
||||
<template #on>😈</template>
|
||||
</RefToggle>
|
||||
|
@ -174,32 +153,27 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<section>
|
||||
<ColorTable :colors="sebinColors" />
|
||||
|
||||
<QuickFacts>
|
||||
<ul class="col-2">
|
||||
<QuickFacts :cols="2">
|
||||
<ul>
|
||||
<li>Bipedal plantigrade</li>
|
||||
<li>Red and yellow scales</li>
|
||||
<li>Yellow scales under chin, torso, underside of tail</li>
|
||||
<li>Athletic to body builder physique</li>
|
||||
<li>Sharp, black claws on fingers and toes</li>
|
||||
<li>Brown blunt spikes running over back and top-side of tail</li>
|
||||
<li>
|
||||
Tail about {{ tailLength / 100 }} meter ({{ toImperial(tailLength) }})
|
||||
in length
|
||||
</li>
|
||||
<li>Tail about {{ tailLength / 100 }} meter ({{ toImperial(tailLength) }}) in length</li>
|
||||
</ul>
|
||||
</QuickFacts>
|
||||
|
||||
<p>
|
||||
As the offspring of a union between human and dragon, Sebin is a bipedal
|
||||
plantigrade. The majority of his body is covered in red scales. A strip of
|
||||
yellow scales runs from the underside of his jaw, extending down his chest
|
||||
through his legs to the underside of the tip of his tail. The scales on
|
||||
his chest are characterized by a special toughness to better protect vital
|
||||
organs. He also has an athletic to muscular physique that he continuously
|
||||
tends to keep in shape. Finger and toe tips are armed with sharp, black
|
||||
claws, which serve him equally as tools and weapons. Brown spines run from
|
||||
head to spine to tip of tail, though they are too blunt to pose a risk of
|
||||
injury. His tail is about
|
||||
As the offspring of a union between human and dragon, Sebin is a bipedal plantigrade. The
|
||||
majority of his body is covered in red scales. A strip of yellow scales runs from the
|
||||
underside of his jaw, extending down his chest through his legs to the underside of the tip of
|
||||
his tail. The scales on his chest are characterized by a special toughness to better protect
|
||||
vital organs. He also has an athletic to muscular physique that he continuously tends to keep
|
||||
in shape. Finger and toe tips are armed with sharp, black claws, which serve him equally as
|
||||
tools and weapons. Brown spines run from head to spine to tip of tail, though they are too
|
||||
blunt to pose a risk of injury. His tail is about
|
||||
{{ tailLength / 100 }} meters ({{ toImperial(tailLength) }}) in length.
|
||||
</p>
|
||||
</section>
|
||||
|
@ -207,11 +181,9 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<section>
|
||||
<h2>Wings</h2>
|
||||
|
||||
<QuickFacts>
|
||||
<ul class="col-2">
|
||||
<li>
|
||||
Wingspan {{ wingspan / 100 }} meters ({{ toImperial(wingspan) }})
|
||||
</li>
|
||||
<QuickFacts :cols="2">
|
||||
<ul>
|
||||
<li>Wingspan {{ wingspan / 100 }} meters ({{ toImperial(wingspan) }})</li>
|
||||
<li>Closed when on the ground</li>
|
||||
<li>Function like a second pair of arms</li>
|
||||
<li>Pointy talon on end of "hand"</li>
|
||||
|
@ -219,17 +191,14 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
</QuickFacts>
|
||||
|
||||
<p>
|
||||
Sebin's wings have a span of about {{ wingspan / 100 }} meters ({{
|
||||
toImperial(wingspan)
|
||||
}}). They function very much like a second pair of arms: two
|
||||
interconnected limbs that form a bendable arm, at the end of which sits a
|
||||
hand-like structure, adorned with a talon in exchange for a thumb, with a
|
||||
sturdy membrane stretched between its long fingers. On solid ground, he
|
||||
keeps his wings closed so as not to accidentally bump into anything with
|
||||
them. Sebin also pays a lot of attention to his wings when working out, so
|
||||
that they can keep carrying him reliably through the air. Though, he only
|
||||
travels short to medium distances through the air before he has to take a
|
||||
rest - as long as the airspace in the area has been declared open for wing
|
||||
Sebin's wings have a span of about {{ wingspan / 100 }} meters ({{ toImperial(wingspan) }}).
|
||||
They function very much like a second pair of arms: two interconnected limbs that form a
|
||||
bendable arm, at the end of which sits a hand-like structure, adorned with a talon in exchange
|
||||
for a thumb, with a sturdy membrane stretched between its long fingers. On solid ground, he
|
||||
keeps his wings closed so as not to accidentally bump into anything with them. Sebin also pays
|
||||
a lot of attention to his wings when working out, so that they can keep carrying him reliably
|
||||
through the air. Though, he only travels short to medium distances through the air before he
|
||||
has to take a rest - as long as the airspace in the area has been declared open for wing
|
||||
bearers.
|
||||
</p>
|
||||
</section>
|
||||
|
@ -244,7 +213,7 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/sebin-ref-expressions.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;0&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/sebin-ref-expressions.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 2240px,
|
||||
(min-width: 100em) 1920px,
|
||||
|
@ -258,7 +227,7 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/sebin-ref-expressions.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;0&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/sebin-ref-expressions.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 2240px,
|
||||
(min-width: 100em) 1920px,
|
||||
|
@ -272,7 +241,7 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/sebin-ref-expressions.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;0&png&withoutEnlargement&srcset
|
||||
@/assets/refs/sebin-ref-expressions.png?w=480;720;1080;1280;1440;1600;1920;2240;960;2160;2560;0&format=png&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 2240px,
|
||||
(min-width: 100em) 1920px,
|
||||
|
@ -294,8 +263,8 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
</RefFigure>
|
||||
|
||||
<section>
|
||||
<QuickFacts>
|
||||
<ul class="col-2">
|
||||
<QuickFacts :cols="2">
|
||||
<ul>
|
||||
<li>Round pupils, green iris</li>
|
||||
<li>Yellow spikes for eyebrows</li>
|
||||
<li>Yellow spikes on cheeks</li>
|
||||
|
@ -308,69 +277,65 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
</QuickFacts>
|
||||
|
||||
<p>
|
||||
Despite what one might expect with reptilians, Sebin's pupils are rounded
|
||||
instead of slit-shaped. Yellow spikes running above his eyelids serve as
|
||||
eyebrows. These spikes are also found along his jaw bone. Two pointed,
|
||||
slightly curved, black horns protrude from his head. He usually wears his
|
||||
medium-length, blue hair loose. His long, pointed ears are very flexible,
|
||||
allowing him to hear sounds around him without having to turn his head.
|
||||
They are also used for non-verbal communication, to express emotions
|
||||
through body language. His hearing perceives a wider range of frequencies,
|
||||
making it superior to that of a human. His jaws are equipped with
|
||||
razor-sharp teeth that effortlessly sink into anything he manages to bite,
|
||||
be it nourishment or adversaries. Embedded between them lies his tongue,
|
||||
which is typically pointed for reptilians. Glands in his throat produce a
|
||||
mixture which he uses to spit fire, which can reach up to around 100 °C
|
||||
({{ toFahrenheit(100) }} °F).
|
||||
Despite what one might expect with reptilians, Sebin's pupils are rounded instead of
|
||||
slit-shaped. Yellow spikes running above his eyelids serve as eyebrows. These spikes are also
|
||||
found along his jaw bone. Two pointed, slightly curved, black horns protrude from his head. He
|
||||
usually wears his medium-length, blue hair loose. His long, pointed ears are very flexible,
|
||||
allowing him to hear sounds around him without having to turn his head. They are also used for
|
||||
non-verbal communication, to express emotions through body language. His hearing perceives a
|
||||
wider range of frequencies, making it superior to that of a human. His jaws are equipped with
|
||||
razor-sharp teeth that effortlessly sink into anything he manages to bite, be it nourishment
|
||||
or adversaries. Embedded between them lies his tongue, which is typically pointed for
|
||||
reptilians. Glands in his throat produce a mixture which he uses to spit fire, which can reach
|
||||
up to around 100 °C ({{ toFahrenheit(100) }} °F).
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Upper Body</h2>
|
||||
|
||||
<QuickFacts>
|
||||
<ul class="col-2">
|
||||
<QuickFacts :cols="2">
|
||||
<ul>
|
||||
<li>Strong upper body</li>
|
||||
<li>Big pecs</li>
|
||||
<li>Defined abs</li>
|
||||
<li>Black nipples</li>
|
||||
<li>
|
||||
Any muscle mass from athletic to body builder is fine, with a
|
||||
preference towards body builder
|
||||
Any muscle mass from athletic to body builder is fine, with a preference towards body
|
||||
builder
|
||||
</li>
|
||||
</ul>
|
||||
</QuickFacts>
|
||||
|
||||
<p>
|
||||
At the age of 17, Sebin gradually began to keep his body in shape on a
|
||||
regular basis. His favorite exercises include weightlifting (with both
|
||||
dumbbells and barbells), lat pulldown, leg press and cable curls. He works
|
||||
out three days a week with one day off between training days, rotating the
|
||||
body regions he trains each day.
|
||||
At the age of 17, Sebin gradually began to keep his body in shape on a regular basis. His
|
||||
favorite exercises include weightlifting (with both dumbbells and barbells), lat pulldown, leg
|
||||
press and cable curls. He works out three days a week with one day off between training days,
|
||||
rotating the body regions he trains each day.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<RefFigure id="sebin-upper-body-ref" polaroidBorder>
|
||||
<RefFigure id="sebin-upper-body-ref" cornerText>
|
||||
<!-- max 3617px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/upper-body-ref.png?w=400;500;600;700;800;1000;1200;1500;1800&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/upper-body-ref.png?w=400;700;1000;1200;1500;1800&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 400px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/upper-body-ref.png?w=400;500;600;700;800;1000;1200;1500;1800&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/upper-body-ref.png?w=400;700;1000;1200;1500;1800&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 400px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/upper-body-ref.png?w=400;500;600;700;800;1000;1200;1500;1800&jpg&quality=95&withoutEnlargement&srcset
|
||||
@/assets/refs/upper-body-ref.png?w=400;700;1000;1200;1500;1800&format=jpg&quality=95&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 400px"
|
||||
alt="Sebin's upper body closeup"
|
||||
|
@ -386,10 +351,9 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
|
||||
<section>
|
||||
<p>
|
||||
Sebin is very concerned with an even distribution of muscle mass, but pays
|
||||
particular attention to his back, chest and arms. A strong chest with
|
||||
strong arms helps to throw fire projectiles as far as possible. A strong
|
||||
back guarantees a longer stay in the air.
|
||||
Sebin is very concerned with an even distribution of muscle mass, but pays particular
|
||||
attention to his back, chest and arms. A strong chest with strong arms helps to throw fire
|
||||
projectiles as far as possible. A strong back guarantees a longer stay in the air.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
@ -398,27 +362,27 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
</section>
|
||||
|
||||
<RefGallery>
|
||||
<RefFigure id="sebin-muscle-ref12" polaroidBorder>
|
||||
<RefFigure id="sebin-muscle-ref12" cornerText>
|
||||
<!-- max 1964px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref12.png?w=290;640;770;900;580;1280;1540;1800;870;0&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref12.png?w=290;770;900;580;1280;1540;1700;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 900px, (min-width: 80em) 770px, (min-width: 35em) 640px, 290px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref12.png?w=290;640;770;900;580;1280;1540;1800;870;0&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref12.png?w=290;770;900;580;1280;1540;1700;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 900px, (min-width: 80em) 770px, (min-width: 35em) 640px, 290px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref12.png?w=290;640;770;900;580;1280;1540;1800;870;0&jpg&quality=95&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref12.png?w=290;770;900;580;1280;1540;1700;0&format=jpg&quality=95&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 900px, (min-width: 80em) 770px, (min-width: 35em) 640px, 290px"
|
||||
alt="Sebin flexing"
|
||||
|
@ -431,27 +395,27 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<a href="https://twitter.com/KidRhinoBoy">Chirros</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
<RefFigure id="sebin-muscle-ref1" polaroidBorder nsfw>
|
||||
<RefFigure id="sebin-muscle-ref1" cornerText nsfw>
|
||||
<!-- max 2480px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref1.png?w=400;500;600;700;800;1000;1200;1400;1500;1800;2100&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref1.png?w=400;700;1000;1200;1500;1800;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 400px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref1.png?w=400;500;600;700;800;1000;1200;1400;1500;1800;2100&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref1.png?w=400;700;1000;1200;1500;1800;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 400px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref1.png?w=400;500;600;700;800;1000;1200;1400;1500;1800;2100&jpg&quality=95&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref1.png?w=400;700;1000;1200;1500;1800;0&format=jpg&quality=95&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 400px"
|
||||
alt="Sebin beckons"
|
||||
|
@ -464,27 +428,27 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<a href="https://twitter.com/GrisserArt">Grisser</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
<RefFigure id="sebin-muscle-ref2" polaroidBorder>
|
||||
<RefFigure id="sebin-muscle-ref2" cornerText>
|
||||
<!-- max 2953px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref2.png?w=415;625;750;875;1250;1500;1750;1875;2250;2625&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref2.png?w=415;625;875;1250;1500;1750;2250;2625&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 875px, (min-width: 80em) 750px, (min-width: 35em) 625px, 415px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref2.png?w=415;625;750;875;1250;1500;1750;1875;2250;2625&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref2.png?w=415;625;875;1250;1500;1750;2250;2625&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 875px, (min-width: 80em) 750px, (min-width: 35em) 625px, 415px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref2.png?w=415;625;750;875;1250;1500;1750;1875;2250;2625&jpg&quality=95&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref2.png?w=415;625;875;1250;1500;1750;2250;2625&format=jpg&quality=95&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 875px, (min-width: 80em) 750px, (min-width: 35em) 625px, 415px"
|
||||
alt="Sebin jamming out to some tunes"
|
||||
|
@ -497,27 +461,27 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<a href="https://twitter.com/artvalentinapaz">Valentina Paz</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
<RefFigure id="sebin-muscle-ref3" polaroidBorder>
|
||||
<RefFigure id="sebin-muscle-ref3" cornerText>
|
||||
<!-- max 4961px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref3.png?w=400;500;600;700;800;1000;1200;1400;1500;1800;2100&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref3.png?w=400;800;1000;1200;1500;1800;2100&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 400px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref3.png?w=400;500;600;700;800;1000;1200;1400;1500;1800;2100&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref3.png?w=400;800;1000;1200;1500;1800;2100&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 400px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref3.png?w=400;500;600;700;800;1000;1200;1400;1500;1800;2100&jpg&quality=95&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref3.png?w=400;800;1000;1200;1500;1800;2100&format=jpg&quality=95&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 400px"
|
||||
alt="Sebin flexing"
|
||||
|
@ -527,32 +491,30 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
</template>
|
||||
<template #caption>Sebin flexing</template>
|
||||
<template #copyright>
|
||||
<a href="https://www.furaffinity.net/user/Marsel-Defender">
|
||||
Marsel-Defender
|
||||
</a>
|
||||
<a href="https://www.furaffinity.net/user/Marsel-Defender"> Marsel-Defender </a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
<RefFigure id="sebin-muscle-ref4" polaroidBorder nsfw>
|
||||
<RefFigure id="sebin-muscle-ref4" cornerText nsfw>
|
||||
<!-- max 3000px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref4.png?w=400;700;840;980;800;1400;1680;1960;1200;2100;2520;0&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref4.png?w=400;980;1400;1680;1960;2100;2520;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 980px, (min-width: 80em) 840px, (min-width: 35em) 700px, 400px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref4.png?w=400;700;840;980;800;1400;1680;1960;1200;2100;2520;0&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref4.png?w=400;980;1400;1680;1960;2100;2520;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 980px, (min-width: 80em) 840px, (min-width: 35em) 700px, 400px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref4.png?w=400;700;840;980;800;1400;1680;1960;1200;2100;2520;0&jpg&quality=95&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref4.png?w=400;980;1400;1680;1960;2100;2520;0&format=jpg&quality=95&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 980px, (min-width: 80em) 840px, (min-width: 35em) 700px, 400px"
|
||||
alt="Sebin soaping up"
|
||||
|
@ -565,27 +527,27 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<a href="https://twitter.com/O_reowoof">(o)reo</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
<RefFigure id="sebin-muscle-ref5" polaroidBorder nsfw>
|
||||
<RefFigure id="sebin-muscle-ref5" cornerText nsfw>
|
||||
<!-- max 2000px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref5.jpg?w=415;525;630;735;830;1050;0&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref5.jpg?w=415;735;1050;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 735px, (min-width: 80em) 630px, (min-width: 35em) 525px, 415px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref5.jpg?w=415;525;630;735;830;1050;0&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref5.jpg?w=415;735;1050;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 735px, (min-width: 80em) 630px, (min-width: 35em) 525px, 415px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref5.jpg?w=415;525;630;735;830;1050;0&jpg&quality=95&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref5.jpg?w=415;735;1050;0&format=jpg&quality=95&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 735px, (min-width: 80em) 630px, (min-width: 35em) 525px, 415px"
|
||||
alt="Sebin bulging out"
|
||||
|
@ -598,27 +560,27 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<a href="https://www.furaffinity.net/user/sexmuffin">SexMuffin</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
<RefFigure id="sebin-muscle-ref6" polaroidBorder nsfw>
|
||||
<RefFigure id="sebin-muscle-ref6" cornerText nsfw>
|
||||
<!-- max 1500px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref6.png?w=400;545;655;765;800;1090;1310;1200;0&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref6.png?w=400;765;1090;1310;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 765px, (min-width: 80em) 655px, (min-width: 35em) 545px, 400px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref6.png?w=400;545;655;765;800;1090;1310;1200;0&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref6.png?w=400;765;1090;1310;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 765px, (min-width: 80em) 655px, (min-width: 35em) 545px, 400px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref6.png?w=400;545;655;765;800;1090;1310;1200;0&jpg&quality=95&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref6.png?w=400;765;1090;1310;0&format=jpg&quality=95&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 765px, (min-width: 80em) 655px, (min-width: 35em) 545px, 400px"
|
||||
alt="Sebin showing you his rings"
|
||||
|
@ -631,27 +593,27 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<a href="https://twitter.com/tehknuxlight">Knuxlight</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
<RefFigure id="sebin-muscle-ref7" polaroidBorder>
|
||||
<RefFigure id="sebin-muscle-ref7" cornerText>
|
||||
<!-- max 1240px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref7.tiff?w=415;995;0&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref7.tiff?w=415;995;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 1240px, (min-width: 80em) 1190px, (min-width: 35em) 995px, 415px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref7.tiff?w=415;995;0&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref7.tiff?w=415;995;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 1240px, (min-width: 80em) 1190px, (min-width: 35em) 992px, 415px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref7.tiff?w=415;995;0&jpg&quality=95&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref7.tiff?w=415;995;0&format=jpg&quality=95&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 1240px, (min-width: 80em) 1190px, (min-width: 35em) 992px, 415px"
|
||||
alt="Sebin ready to throw down"
|
||||
|
@ -664,27 +626,27 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<a href="https://www.furaffinity.net/user/shonuff44">ShoNuff44</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
<RefFigure id="sebin-muscle-ref8" polaroidBorder>
|
||||
<RefFigure id="sebin-muscle-ref8" cornerText>
|
||||
<!-- max 1245px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref8.png?w=415;1000;0&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref8.png?w=415;1000;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 80em) 1200px, (min-width: 35em) 1000px, 415px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref8.png?w=415;1000;0&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref8.png?w=415;1000;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 80em) 1200px, (min-width: 35em) 1000px, 415px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref8.png?w=415;1000;0&jpg&quality=95&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref8.png?w=415;1000;0&format=jpg&quality=95&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 80em) 1200px, (min-width: 35em) 1000px, 415px"
|
||||
alt="Sebin looking aloof (but chill)"
|
||||
|
@ -697,27 +659,27 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<a href="https://twitter.com/UsurpThem">Usurp</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
<RefFigure id="sebin-muscle-ref9" polaroidBorder>
|
||||
<RefFigure id="sebin-muscle-ref9" cornerText>
|
||||
<!-- max 2550px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref9.jpg?w=400;735;880;1120;800;1470;1760;2240;1200;2205;0&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref9.jpg?w=400;735;1470;1760;2240;1200;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 1120px, (min-width: 80em) 880px, (min-width: 35em) 735px, 400px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref9.jpg?w=400;735;880;1120;800;1470;1760;2240;1200;2205;0&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref9.jpg?w=400;735;1470;1760;2240;1200;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 1120px, (min-width: 80em) 880px, (min-width: 35em) 735px, 400px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref9.jpg?w=400;735;880;1120;800;1470;1760;2240;1200;2205;0&jpg&quality=95&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref9.jpg?w=400;735;1470;1760;2240;1200;0&format=jpg&quality=95&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 1120px, (min-width: 80em) 880px, (min-width: 35em) 735px, 400px"
|
||||
alt="Sebin getting out of the pool"
|
||||
|
@ -730,27 +692,27 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<a href="https://www.furaffinity.net/user/j-cock">j-cock</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
<RefFigure id="sebin-muscle-ref10" polaroidBorder nsfw>
|
||||
<RefFigure id="sebin-muscle-ref10" cornerText nsfw>
|
||||
<!-- max 2421px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref10.jpg?w=380;425;510;600;760;850;1020;1200;1140;1275;1530;1800&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref10.jpg?w=380;600;850;1275;1530;1800&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 600px, (min-width: 80em) 510px, (min-width: 35em) 425px, 380px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref10.jpg?w=380;425;510;600;760;850;1020;1200;1140;1275;1530;1800&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref10.jpg?w=380;600;850;1275;1530;1800&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 600px, (min-width: 80em) 510px, (min-width: 35em) 425px, 380px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref10.jpg?w=380;425;510;600;760;850;1020;1200;1140;1275;1530;1800&jpg&quality=95&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref10.jpg?w=380;600;850;1275;1530;1800&format=jpg&quality=95&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 600px, (min-width: 80em) 510px, (min-width: 35em) 425px, 380px"
|
||||
alt="Sebin getting frisky on the train"
|
||||
|
@ -763,27 +725,27 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<a href="https://twitter.com/Wintech3112">Winty</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
<RefFigure id="sebin-muscle-ref11" polaroidBorder>
|
||||
<RefFigure id="sebin-muscle-ref11" cornerText>
|
||||
<!-- max 3184px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref11.png?w=400;545;655;765;800;1090;1210;1530;1200;1635;1965;2295&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref11.png?w=400;765;1200;1635;1965;2295&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 765px, (min-width: 80em) 655px, (min-width: 35em) 545px, 400px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref11.png?w=400;545;655;765;800;1090;1210;1530;1200;1635;1965;2295&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref11.png?w=400;765;1200;1635;1965;2295&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 765px, (min-width: 80em) 655px, (min-width: 35em) 545px, 400px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/muscle/muscle-ref11.png?w=400;545;655;765;800;1090;1210;1530;1200;1635;1965;2295&png&withoutEnlargement&srcset
|
||||
@/assets/refs/muscle/muscle-ref11.png?w=400;765;1200;1635;1965;2295&format=png&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 765px, (min-width: 80em) 655px, (min-width: 35em) 545px, 400px"
|
||||
alt="Sebin getting ready to lift"
|
||||
|
@ -808,21 +770,17 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/sebin-ref-penis.png?w=480;720;0&avif&quality=75&srcset
|
||||
"
|
||||
srcset="@/assets/refs/sebin-ref-penis.png?w=480;720;0&format=avif&quality=75&as=srcset"
|
||||
sizes="(min-width: 45em) 1155px, (min-width: 30em) 720px, 480px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/sebin-ref-penis.png?w=480;720;0&webp&quality=100&srcset
|
||||
"
|
||||
srcset="@/assets/refs/sebin-ref-penis.png?w=480;720;0&format=webp&quality=100&as=srcset"
|
||||
sizes="(min-width: 45em) 1155px, (min-width: 30em) 720px, 480px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="@/assets/refs/sebin-ref-penis.png?w=480;720;0&png&srcset"
|
||||
srcset="@/assets/refs/sebin-ref-penis.png?w=480;720;0&format=png&as=srcset"
|
||||
sizes="(min-width: 45em) 1155px, (min-width: 30em) 720px, 480px"
|
||||
alt="Sebin's manly parts"
|
||||
loading="lazy"
|
||||
|
@ -840,28 +798,24 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<section>
|
||||
<DataTable :headings="sebinPenisHeadings" :data="sebinPenisData" />
|
||||
|
||||
<QuickFacts>
|
||||
<ul class="col-2">
|
||||
<QuickFacts :cols="2">
|
||||
<ul>
|
||||
<li>Human-shaped with ridges</li>
|
||||
<li>Ring-like sheath surrounding shaft</li>
|
||||
<li>
|
||||
Extends from sheath when erect, lives inside sheath when not erect
|
||||
</li>
|
||||
<li>Extends from sheath when erect, lives inside sheath when not erect</li>
|
||||
<li>External balls</li>
|
||||
</ul>
|
||||
</QuickFacts>
|
||||
|
||||
<p>
|
||||
Despite his reptilian appearance, Sebin has nipples, a feature of the
|
||||
human side of his family. Furthermore, his external testicles represent
|
||||
another humanized feature. Where relatives of his ilk possess a slit in
|
||||
which the penis lies protectively, Sebin possesses a pouch-like sheath
|
||||
from which the tip of the penis protrudes slightly. The shape of his
|
||||
shaft is predominantly humanoid, but it is surrounded by ridges and has
|
||||
no equivalent of a foreskin. When aroused, the coal-black shaft swells
|
||||
and pushes out of the sheath until fully erect, the sheath wrapping
|
||||
around the root of the shaft like a ring. However, he can also push it
|
||||
out in a flaccid state, e.g. when needing to pass water.
|
||||
Despite his reptilian appearance, Sebin has nipples, a feature of the human side of his
|
||||
family. Furthermore, his external testicles represent another humanized feature. Where
|
||||
relatives of his ilk possess a slit in which the penis lies protectively, Sebin possesses a
|
||||
pouch-like sheath from which the tip of the penis protrudes slightly. The shape of his shaft
|
||||
is predominantly humanoid, but it is surrounded by ridges and has no equivalent of a
|
||||
foreskin. When aroused, the coal-black shaft swells and pushes out of the sheath until fully
|
||||
erect, the sheath wrapping around the root of the shaft like a ring. However, he can also
|
||||
push it out in a flaccid state, e.g. when needing to pass water.
|
||||
</p>
|
||||
</section>
|
||||
</template>
|
||||
|
@ -871,16 +825,12 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<h2>But, wait! There's more...</h2>
|
||||
|
||||
<p>
|
||||
Sebin like you haven't seen him yet! Flip the switch to reveal his
|
||||
naughty secrets. If you dare...! Don't say I didn't warn you!!
|
||||
Sebin like you haven't seen him yet! Flip the switch to reveal his naughty secrets. If you
|
||||
dare...! Don't say I didn't warn you!!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<RefToggle
|
||||
id="sebin-manly-bits"
|
||||
v-model="isNsfw"
|
||||
@click.prevent="showModal()"
|
||||
>
|
||||
<RefToggle id="sebin-manly-bits" v-model="isNsfw" @click.prevent="showModal()">
|
||||
<template #off>😇</template>
|
||||
<template #on>😈</template>
|
||||
</RefToggle>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import RefFigure from "@/components/RefFigure.vue";
|
||||
import RefGallery from "@/components/RefGallery.vue";
|
||||
import RefFigure from '@/components/RefFigure.vue'
|
||||
import RefGallery from '@/components/RefGallery.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -9,27 +9,27 @@ import RefGallery from "@/components/RefGallery.vue";
|
|||
</section>
|
||||
|
||||
<RefGallery>
|
||||
<RefFigure id="casual-outfit" polaroidBorder>
|
||||
<RefFigure id="casual-outfit" cornerText>
|
||||
<!-- max 1876px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/clothes/casual/fullbody.png?w=400;650;780;910;800;1300;1560;1820;1200;0&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/casual/fullbody.png?w=400;780;910;1300;1560;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 910px, (min-width: 80em) 780px, (min-width: 35em) 650px, 400px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/clothes/casual/fullbody.png?w=400;650;780;910;800;1300;1560;1820;1200;0&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/casual/fullbody.png?w=400;780;910;1300;1560;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 910px, (min-width: 80em) 780px, (min-width: 35em) 650px, 400px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/clothes/casual/fullbody.png?w=400;650;780;910;800;1300;1560;1820;1200;0&png&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/casual/fullbody.png?w=400;780;910;1300;1560;0&format=png&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 910px, (min-width: 80em) 780px, (min-width: 35em) 650px, 400px"
|
||||
alt="Sebin in his casual outfit"
|
||||
|
@ -38,37 +38,32 @@ import RefGallery from "@/components/RefGallery.vue";
|
|||
</picture>
|
||||
</template>
|
||||
|
||||
<template #caption>
|
||||
<p>Sebin in his casual outfit</p>
|
||||
<p>
|
||||
Black tank top, flannell shirt, shorts (w/ dangling bands), sneakers
|
||||
</p>
|
||||
</template>
|
||||
<template #caption>Sebin in his casual outfit</template>
|
||||
|
||||
<template #copyright>
|
||||
<a href="https://twitter.com/coffeerelated">coffeerelated</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
|
||||
<RefFigure id="sebin-winter-outfit" polaroidBorder>
|
||||
<RefFigure id="sebin-winter-outfit" cornerText>
|
||||
<!-- max 1470px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/clothes/cold/fullbody.png?w=400;655;785;915;800;1310;1200;0&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/cold/fullbody.png?w=400;785;915;1310;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 915px, (min-width: 80em) 785px, (min-width: 35em) 655px, 400px"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/clothes/cold/fullbody.png?w=400;655;785;915;800;1310;1200;0&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/cold/fullbody.png?w=400;785;915;1310;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 915px, (min-width: 80em) 785px, (min-width: 35em) 655px, 400px"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/clothes/cold/fullbody.png?w=400;655;785;915;800;1310;1200;0&jpg&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/cold/fullbody.png?w=400;785;915;1310;0&format=jpg&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 915px, (min-width: 80em) 785px, (min-width: 35em) 655px, 400px"
|
||||
alt="Sebin in his cold weather outfit"
|
||||
|
@ -77,35 +72,32 @@ import RefGallery from "@/components/RefGallery.vue";
|
|||
</picture>
|
||||
</template>
|
||||
|
||||
<template #caption>
|
||||
<p>Sebin in his cold weather outfit</p>
|
||||
<p>Winter coat, scarf, zip hoodie, jeans, winter boots</p>
|
||||
</template>
|
||||
<template #caption>Sebin in his cold weather outfit</template>
|
||||
|
||||
<template #copyright>
|
||||
<a href="https://twitter.com/lara_belem_">Lara Belém</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
|
||||
<RefFigure id="sebin-workout-outfit-fullbody" polaroidBorder>
|
||||
<RefFigure id="sebin-workout-outfit-fullbody" cornerText>
|
||||
<!-- max 1758px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/clothes/workout/fullbody.png?w=400;535;640;745;800;1070;1280;1490;1200;1605;0&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/workout/fullbody.png?w=400;640;800;1070;1280;1490;1605;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 745px, (min-width: 80em) 639px, (min-width: 35em) 532px, 400px"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/clothes/workout/fullbody.png?w=400;535;640;745;800;1070;1280;1490;1200;1605;0&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/workout/fullbody.png?w=400;640;800;1070;1280;1490;1605;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 745px, (min-width: 80em) 639px, (min-width: 35em) 532px, 400px"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/clothes/workout/fullbody.png?w=400;535;640;745;800;1070;1280;1490;1200;1605;0&jpg&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/workout/fullbody.png?w=400;640;800;1070;1280;1490;1605;0&format=jpg&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 745px, (min-width: 80em) 639px, (min-width: 35em) 532px, 400px"
|
||||
alt="Sebin in his workout outfit (fullbody)"
|
||||
|
@ -114,38 +106,32 @@ import RefGallery from "@/components/RefGallery.vue";
|
|||
</picture>
|
||||
</template>
|
||||
|
||||
<template #caption>
|
||||
<p>Sebin in his workout outfit</p>
|
||||
<p>
|
||||
<strong>Full body:</strong> Snapback hat, tank top, fingerless gloves,
|
||||
shorts, sneakers
|
||||
</p>
|
||||
</template>
|
||||
<template #caption>Sebin in his workout outfit (full body)</template>
|
||||
|
||||
<template #copyright>
|
||||
<a href="https://twitter.com/turquoize_art">Atlas</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
|
||||
<RefFigure id="sebin-workout-outfit-close" polaroidBorder>
|
||||
<RefFigure id="sebin-workout-outfit-close" cornerText>
|
||||
<!-- max 2480px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/clothes/workout/closeup.png?w=400;500;600;700;800;1000;1200;1400;1500;1800;2100&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/workout/closeup.png?w=400;600;800;1000;1200;1400;1800;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500x, 400px"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/clothes/workout/closeup.png?w=400;500;600;700;800;1000;1200;1400;1500;1800;2100&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/workout/closeup.png?w=400;600;800;1000;1200;1400;1800;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 400px"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/clothes/workout/closeup.png?w=400;500;600;700;800;1000;1200;1400;1500;1800;2100&jpg&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/workout/closeup.png?w=400;600;800;1000;1200;1400;1800;0&format=jpg&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 400px"
|
||||
alt="Sebin in his workout outfit (close-up)"
|
||||
|
@ -154,98 +140,83 @@ import RefGallery from "@/components/RefGallery.vue";
|
|||
</picture>
|
||||
</template>
|
||||
|
||||
<template #caption>
|
||||
<p>Sebin in his workout outfit</p>
|
||||
<p>
|
||||
<strong>Close-up:</strong> Snapback hat, headphones, tank top,
|
||||
fingerless gloves, shorts, smartwatch
|
||||
</p>
|
||||
</template>
|
||||
<template #caption>Sebin in his workout outfit (close-up)</template>
|
||||
|
||||
<template #copyright>
|
||||
<a href="https://twitter.com/purpledragonrei">Rei</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
|
||||
<RefFigure id="sebin-tracksuit-pants" polaroidBorder>
|
||||
<!-- max 961px -->
|
||||
<RefFigure id="sebin-tracksuit-pants" nsfw cornerText>
|
||||
<!-- max 1656px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/clothes/lazy/pants.jpg?w=400;500;600;700;0&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/lazy/fullbody.png?w=400;850;1210;1460;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 400px"
|
||||
sizes="(min-width: 120em) 850px, (min-width: 80em) 730px, (min-width: 35em) 605px, 400px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/clothes/lazy/pants.jpg?w=400;500;600;700;0&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/lazy/fullbody.png?w=400;850;1210;1460;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 400px"
|
||||
sizes="(min-width: 120em) 850px, (min-width: 80em) 730px, (min-width: 35em) 605px, 400px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/clothes/lazy/pants.jpg?w=400;500;600;700;0&jpg&quality=95&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/lazy/fullbody.png?w=400;850;1210;1460;0&format=jpg&quality=95&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 400px"
|
||||
sizes="(min-width: 120em) 850px, (min-width: 80em) 730px, (min-width: 35em) 605px, 400px"
|
||||
alt="Tracksuit pants"
|
||||
loading="lazy"
|
||||
/>
|
||||
</picture>
|
||||
</template>
|
||||
|
||||
<template #caption>
|
||||
<p>Tracksuit pants</p>
|
||||
<p>
|
||||
Sebin likes to wear comfortable clothes at home when he doesn't need
|
||||
to leave the house or is enjoying some leisure time after work or on
|
||||
weekends.
|
||||
</p>
|
||||
</template>
|
||||
<template #caption>Sebin in leisure tracksuit pants</template>
|
||||
|
||||
<!-- <template #copyright></template> -->
|
||||
<template #copyright>
|
||||
<a href="https://twitter.com/leolynx86">Leolynx</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
|
||||
<RefFigure id="sebin-topless" polaroidBorder>
|
||||
<!-- max 333px -->
|
||||
<RefFigure id="sebin-topless" cornerText>
|
||||
<!-- max 2119px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/clothes/lazy/undies.jpg?w=0&avif&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/lazy/undies.png?w=400;880;1028;1470;1760;0&format=avif&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="333px"
|
||||
sizes="(min-width: 120em) 1028px, (min-width: 80em) 880px, (min-width: 35em) 735px, 400px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/clothes/lazy/undies.jpg?w=0&webp&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/lazy/undies.png?w=400;880;1028;1470;1760;0&format=webp&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="333px"
|
||||
sizes="(min-width: 120em) 1028px, (min-width: 80em) 880px, (min-width: 35em) 735px, 400px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/clothes/lazy/undies.jpg?w=0&withoutEnlargement&srcset
|
||||
@/assets/refs/clothes/lazy/undies.png?w=400;880;1028;1470;1760;0&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="333px"
|
||||
sizes="(min-width: 120em) 1028px, (min-width: 80em) 880px, (min-width: 35em) 735px, 400px"
|
||||
alt="Shorts"
|
||||
loading="lazy"
|
||||
/>
|
||||
</picture>
|
||||
</template>
|
||||
|
||||
<template #caption>
|
||||
<p>Topless w/ jockstrap/boxer briefs</p>
|
||||
<p>
|
||||
For even more comfort, Sebin tends to forgoe pants completely and save
|
||||
on laundry by only wearing the absolute necessary.
|
||||
</p>
|
||||
</template>
|
||||
<template #caption>Sebin in his favorite boxers</template>
|
||||
|
||||
<!-- <template #copyright></template> -->
|
||||
<template #copyright>
|
||||
<a href="https://twitter.com/Retroslime69">Retroslime69</a>
|
||||
</template>
|
||||
</RefFigure>
|
||||
</RefGallery>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { inject } from "vue";
|
||||
import { inject } from 'vue'
|
||||
import {
|
||||
firstName,
|
||||
middleName,
|
||||
|
@ -13,65 +13,37 @@ import {
|
|||
tailLength,
|
||||
wingspan,
|
||||
hobbies,
|
||||
kinks,
|
||||
} from "@/sebin";
|
||||
import { dateFormat, getAge, toImperial, toLbs } from "@/helpers";
|
||||
import { isWarnKey, nsfwKey, showModalKey } from "@/keys";
|
||||
import type { Kink } from "@/interfaces";
|
||||
import DataTable from "@/components/DataTable.vue";
|
||||
import QuickFacts from "@/components/QuickFacts.vue";
|
||||
import RefToggle from "@/components/RefToggle.vue";
|
||||
import RefModal from "@/components/RefModal.vue";
|
||||
kinks
|
||||
} from '@/sebin'
|
||||
import { dateFormat, getAge, toImperial, toLbs } from '@/helpers'
|
||||
import { nsfwKey, showModalKey } from '@/keys'
|
||||
import DataTable from '@/components/DataTable.vue'
|
||||
import QuickFacts from '@/components/QuickFacts.vue'
|
||||
import RefToggle from '@/components/RefToggle.vue'
|
||||
import FilteredList from '@/components/FilteredList.vue'
|
||||
|
||||
const generalHeadings = ["Key", "Value"];
|
||||
const generalHeadings = ['Key', 'Value']
|
||||
const generalData = [
|
||||
["Full Name", `${firstName} ${middleName} ${lastName} `],
|
||||
[
|
||||
"Date of Birth",
|
||||
`${dateFormat.format(dateOfBirth)} (${getAge(dateOfBirth)})`,
|
||||
],
|
||||
["Sex/Gender", gender],
|
||||
["Height", `${height} cm (${toImperial(height)})`],
|
||||
["Weight", `${weight} kg (${toLbs(weight)} lbs)`],
|
||||
["Tail Length", `${tailLength / 100} m (${toImperial(tailLength)})`],
|
||||
["Wingspan", `${wingspan / 100} m (${toImperial(wingspan)})`],
|
||||
];
|
||||
['Full Name', `${firstName} ${middleName} ${lastName} `],
|
||||
['Date of Birth', `${dateFormat.format(dateOfBirth)} (${getAge(dateOfBirth)})`],
|
||||
['Sex/Gender', gender],
|
||||
['Height', `${height} cm (${toImperial(height)})`],
|
||||
['Weight', `${weight} kg (${toLbs(weight)} lbs)`],
|
||||
['Tail Length', `${tailLength / 100} m (${toImperial(tailLength)})`],
|
||||
['Wingspan', `${wingspan / 100} m (${toImperial(wingspan)})`]
|
||||
]
|
||||
|
||||
const nsfwHeadings = ["Key", "Value"];
|
||||
const nsfwHeadings = ['Key', 'Value']
|
||||
const nsfwData = [
|
||||
["Orientation", orientation],
|
||||
["Position", position],
|
||||
];
|
||||
['Orientation', orientation],
|
||||
['Position', position]
|
||||
]
|
||||
|
||||
const kinksHeadings = ["Kink", "Receive", "Give"];
|
||||
const kinksData = kinks.map((kink: Kink): string[] => {
|
||||
const receive = kink.receive ? "✅" : "🚫";
|
||||
const give = kink.give ? "✅" : "🚫";
|
||||
|
||||
return [kink.name, receive, give];
|
||||
});
|
||||
|
||||
const isNsfw = inject<boolean>(nsfwKey, false);
|
||||
const isWarn = inject<boolean>(isWarnKey, false);
|
||||
const showModal = inject<Function>(showModalKey, Function);
|
||||
const isNsfw = inject<boolean>(nsfwKey, false)
|
||||
const showModal = inject<Function>(showModalKey, Function)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RefModal v-show="isWarn">
|
||||
<template #heading>
|
||||
⚠️⚠️⚠️<br />
|
||||
Whoa, Nelly!
|
||||
</template>
|
||||
|
||||
<template #message>
|
||||
By enabling NSFW mode you confirm that you are of legal age to view adult
|
||||
content.
|
||||
</template>
|
||||
|
||||
<template #yes>Yes, show me the goods 👀</template>
|
||||
<template #no>NO, STAHP 😱</template>
|
||||
</RefModal>
|
||||
|
||||
<section>
|
||||
<h1>{{ $route.name }}</h1>
|
||||
|
||||
|
@ -81,8 +53,8 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
<section>
|
||||
<h2>Personality</h2>
|
||||
|
||||
<QuickFacts>
|
||||
<ul class="col-2">
|
||||
<QuickFacts :cols="2">
|
||||
<ul>
|
||||
<li>warm-hearted</li>
|
||||
<li>caring</li>
|
||||
<li>quick to offer help</li>
|
||||
|
@ -95,46 +67,42 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
</QuickFacts>
|
||||
|
||||
<p>
|
||||
Sebin is a warm-hearted guy who cares a lot about the well-being of his
|
||||
loved ones. Bad vibes rarely escape him and he offers his help without
|
||||
hesitation. He also won't avoid difficult conversations in the process.
|
||||
Not being able to help a friend in need is synonymous with failing them, a
|
||||
realization that can leave him feeling uneasy long after the fact.
|
||||
Sebin is a warm-hearted guy who cares a lot about the well-being of his loved ones. Bad vibes
|
||||
rarely escape him and he offers his help without hesitation. He also won't avoid difficult
|
||||
conversations in the process. Not being able to help a friend in need is synonymous with
|
||||
failing them, a realization that can leave him feeling uneasy long after the fact.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
At the same time, he is very open and honest about his feelings. He does
|
||||
not mince words and finds clear words when speaking his mind.
|
||||
Unfortunately, Sebin sometimes forgets his good manners in the heat of the
|
||||
moment, once he gets invested into a quarrel — especially when it comes to
|
||||
topics that are near and dear to his heart. Anyone looking to have a bad
|
||||
time can try their luck at pissing him off even once. This includes an
|
||||
equally vulgar vocabulary. It is not uncommon to hear him swear.
|
||||
At the same time, he is very open and honest about his feelings. He does not mince words and
|
||||
finds clear words when speaking his mind. Unfortunately, Sebin sometimes forgets his good
|
||||
manners in the heat of the moment, once he gets invested into a quarrel — especially when it
|
||||
comes to topics that are near and dear to his heart. Anyone looking to have a bad time can try
|
||||
their luck at pissing him off even once. This includes an equally vulgar vocabulary. It is not
|
||||
uncommon to hear him swear.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Nevertheless, Sebin strives to put his best foot forward at all times. He
|
||||
is of the sociable type and likes to laugh a lot, as he is easily amused.
|
||||
Sometimes to a degree where it can become very childish and immature very
|
||||
quickly.
|
||||
Nevertheless, Sebin strives to put his best foot forward at all times. He is of the sociable
|
||||
type and likes to laugh a lot, as he is easily amused. Sometimes to a degree where it can
|
||||
become very childish and immature very quickly.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Physical strength is not the only thing that plays a big role for Sebin.
|
||||
He is of a firm believe that a healthy body must also have a healthy mind
|
||||
in order to find a balance. But he only came to this realization at the
|
||||
end of a rocky road. While a setback in the past could easily throw him
|
||||
off track, today he stands much more firmly in life. Not only for his own
|
||||
sake, but also to be a kind of anchor for others. He always keeps his
|
||||
cool, so he can be a tower of strenth for others.
|
||||
Physical strength is not the only thing that plays a big role for Sebin. He is of a firm
|
||||
believe that a healthy body must also have a healthy mind in order to find a balance. But he
|
||||
only came to this realization at the end of a rocky road. While a setback in the past could
|
||||
easily throw him off track, today he stands much more firmly in life. Not only for his own
|
||||
sake, but also to be a kind of anchor for others. He always keeps his cool, so he can be a
|
||||
tower of strenth for others.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Hobbies</h2>
|
||||
|
||||
<QuickFacts>
|
||||
<ul class="col-4">
|
||||
<QuickFacts :cols="4">
|
||||
<ul>
|
||||
<li v-for="(hobby, idx) in hobbies" :key="idx">
|
||||
{{ hobby }}
|
||||
</li>
|
||||
|
@ -142,26 +110,24 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
</QuickFacts>
|
||||
|
||||
<p>
|
||||
Sebin is passionate about his hobbies. If he notices even the smallest
|
||||
spark of interest in his hobbies you should bring a lot of time, as he
|
||||
will chew your ear off first. Patience is known to be a virtue — one
|
||||
unknown to this dragon.
|
||||
Sebin is passionate about his hobbies. If he notices even the smallest spark of interest in
|
||||
his hobbies you should bring a lot of time, as he will chew your ear off first. Patience is
|
||||
known to be a virtue — one unknown to this dragon.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
When he indulges in his hobbies, he does so with devotion. Every move has
|
||||
to be right and everything has to be in perfect harmony. Once he is in his
|
||||
flow, he must not be disturbed, otherwise he can sometimes become quite
|
||||
eccentric in expressing his dismay of being disrupted, possibly losing a
|
||||
very important train of thought.
|
||||
When he indulges in his hobbies, he does so with devotion. Every move has to be right and
|
||||
everything has to be in perfect harmony. Once he is in his flow, he must not be disturbed,
|
||||
otherwise he can sometimes become quite eccentric in expressing his dismay of being disrupted,
|
||||
possibly losing a very important train of thought.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Food & Drink</h2>
|
||||
|
||||
<QuickFacts>
|
||||
<ul class="col-4">
|
||||
<QuickFacts :cols="4">
|
||||
<ul>
|
||||
<li>sweets</li>
|
||||
<li>savory food</li>
|
||||
<li>spicy food</li>
|
||||
|
@ -178,21 +144,19 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
</QuickFacts>
|
||||
|
||||
<p>
|
||||
Sebin's day starts with a strong cup of black coffee and sandwiches. He's
|
||||
also a massive sweet tooth which sounds like a big detriment to his
|
||||
fitness routine. That's because it is and it's often very hard for him to
|
||||
resist.
|
||||
Sebin's day starts with a strong cup of black coffee and sandwiches. He's also a massive sweet
|
||||
tooth which sounds like a big detriment to his fitness routine. That's because it is and it's
|
||||
often very hard for him to resist.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Besides snacking, Sebin also likes to eat hearty and savory things. He
|
||||
doesn't disdain a cheese platter with a wide selection, nor a medium-rare
|
||||
steak.
|
||||
Besides snacking, Sebin also likes to eat hearty and savory things. He doesn't disdain a
|
||||
cheese platter with a wide selection, nor a medium-rare steak.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Sebin rarely says no to a good beer with friends, just as he rarely says
|
||||
no to a bar tour to try new and interesting cocktails.
|
||||
Sebin rarely says no to a good beer with friends, just as he rarely says no to a bar tour to
|
||||
try new and interesting cocktails.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
@ -202,8 +166,8 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
|
||||
<DataTable :headings="nsfwHeadings" :data="nsfwData" />
|
||||
|
||||
<QuickFacts>
|
||||
<ul class="col-2">
|
||||
<QuickFacts :cols="2">
|
||||
<ul>
|
||||
<li>Supremely horny</li>
|
||||
<li>Confident, knows what he's got</li>
|
||||
<li>Dominant lover, likes it rough but is no brute</li>
|
||||
|
@ -215,20 +179,19 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
</QuickFacts>
|
||||
|
||||
<p>
|
||||
But above all Sebin is a very naughty hornball. He knows what he's got
|
||||
and he's not afraid to flaunt it. He is a generally dominant lover who
|
||||
likes to have it rough. But he is not lacking in tenderness. He
|
||||
considers himself somewhat of a "service top", who doesn't only have his
|
||||
own fun in mind. His job is only done if he's benn able to satisfy.
|
||||
However, that doesn't mean that he doesn't let others have their fun
|
||||
with him too from time to time. It always depends on his playmates,
|
||||
which makes him effectively a switch. He loves to wear bottomless
|
||||
jockstraps and boxer briefs to direct the attention of onlookers exactly
|
||||
where he wants it. After all he is well endowed enough to peddle it
|
||||
around.
|
||||
But above all Sebin is a very naughty hornball. He knows what he's got and he's not afraid
|
||||
to flaunt it. He is a generally dominant lover who likes to have it rough. But he is not
|
||||
lacking in tenderness. He considers himself somewhat of a "service top", who doesn't only
|
||||
have his own fun in mind. His job is only done if he's benn able to satisfy. However, that
|
||||
doesn't mean that he doesn't let others have their fun with him too from time to time. It
|
||||
always depends on his playmates, which makes him effectively a switch. He loves to wear
|
||||
bottomless jockstraps and boxer briefs to direct the attention of onlookers exactly where he
|
||||
wants it. After all he is well endowed enough to peddle it around.
|
||||
</p>
|
||||
|
||||
<DataTable class="kinks" :headings="kinksHeadings" :data="kinksData" />
|
||||
<h3>Kinks</h3>
|
||||
|
||||
<FilteredList :data="kinks" />
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
|
|
|
@ -1,33 +1,16 @@
|
|||
<script setup lang="ts">
|
||||
import { inject } from "vue";
|
||||
import { isWarnKey, nsfwKey, showModalKey } from "@/keys";
|
||||
import RefToggle from "@/components/RefToggle.vue";
|
||||
import RefModal from "@/components/RefModal.vue";
|
||||
import WelcomeHeader from "@/components/WelcomeHeader.vue";
|
||||
import ButtonGroup from "@/components/ButtonGroup.vue";
|
||||
import Button from "@/components/RefButton.vue";
|
||||
import { inject } from 'vue'
|
||||
import { nsfwKey, showModalKey } from '@/keys'
|
||||
import RefToggle from '@/components/RefToggle.vue'
|
||||
import WelcomeHeader from '@/components/WelcomeHeader.vue'
|
||||
import ButtonGroup from '@/components/ButtonGroup.vue'
|
||||
import Button from '@/components/RefButton.vue'
|
||||
|
||||
const isNsfw = inject<boolean>(nsfwKey, false);
|
||||
const isWarn = inject<boolean>(isWarnKey, false);
|
||||
const showModal = inject<Function>(showModalKey, Function);
|
||||
const isNsfw = inject<boolean>(nsfwKey, false)
|
||||
const showModal = inject<Function>(showModalKey, Function)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RefModal v-show="isWarn">
|
||||
<template #heading>
|
||||
⚠️⚠️⚠️<br />
|
||||
Whoa, Nelly!
|
||||
</template>
|
||||
|
||||
<template #message>
|
||||
By enabling NSFW mode you confirm that you are of legal age to view adult
|
||||
content.
|
||||
</template>
|
||||
|
||||
<template #yes>Yes, show me the goods 👀</template>
|
||||
<template #no>NO, STAHP 😱</template>
|
||||
</RefModal>
|
||||
|
||||
<WelcomeHeader>
|
||||
<template #main>Sebin Nyshkim</template>
|
||||
<template #sub>Character Reference Page</template>
|
||||
|
@ -35,46 +18,33 @@ const showModal = inject<Function>(showModalKey, Function);
|
|||
|
||||
<section>
|
||||
<h3>Welcome to Sebin's Ref Page</h3>
|
||||
<p>
|
||||
On this page your can learn all about Sebin, your friendly neighborhood
|
||||
dragon!
|
||||
</p>
|
||||
<p>On this page your can learn all about Sebin, your friendly neighborhood dragon!</p>
|
||||
|
||||
<p>
|
||||
I started this page to have a single point of truth with all the info
|
||||
artists I commission can possibly need. If you're missing some crucial
|
||||
info, or you would like to give me some general feedback about this page,
|
||||
feel free to reach out!
|
||||
I started this page to have a single point of truth with all the info artists I commission can
|
||||
possibly need. If you're missing some crucial info, or you would like to give me some general
|
||||
feedback about this page, feel free to reach out!
|
||||
</p>
|
||||
|
||||
<ul class="social">
|
||||
<li class="twitter">
|
||||
<a href="https://twitter.com/SebinNyshkim">Twitter</a>
|
||||
</li>
|
||||
<li class="mastodon">
|
||||
<a href="https://meow.social/@SebinNyshkim" rel="me">Mastodon</a>
|
||||
</li>
|
||||
<li class="telegram">
|
||||
<a href="https://t.me/SebinNyshkim">Telegram</a>
|
||||
</li>
|
||||
<li class="furaffinity">
|
||||
<a href="https://www.furaffinity.net/user/sonofdragons">
|
||||
Fur Affinity
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ButtonGroup grid class="social">
|
||||
<Button href="https://twitter.com/SebinNyshkim" class="twitter"> Twitter </Button>
|
||||
<Button href="https://meow.social/@SebinNyshkim" class="mastodon" rel="me"> Mastodon </Button>
|
||||
<Button href="https://t.me/SebinNyshkim" class="telegram"> Telegram </Button>
|
||||
<Button href="https://www.furaffinity.net/user/sonofdragons" class="furaffinity">
|
||||
Fur Affinity
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
||||
<blockquote>
|
||||
<strong>Note:</strong> This page is still under heavy construction, as
|
||||
indicated by the 0.x.x version number in the footer. I'm continously
|
||||
updating the site and adding details and information to it. In case you
|
||||
run into something weird, definitely let me know!
|
||||
<strong>Note:</strong> This page is still under heavy construction, as indicated by the 0.x.x
|
||||
version number in the footer. I'm continously updating the site and adding details and
|
||||
information to it. In case you run into something weird, definitely let me know!
|
||||
</blockquote>
|
||||
|
||||
<h3>Complete Ref Sheet</h3>
|
||||
<p>
|
||||
Just here to fetch the ref sheet? Click the button with the ref sheet you
|
||||
need and get started!
|
||||
Just here to fetch the ref sheet? Click the button with the ref sheet you need and get
|
||||
started!
|
||||
</p>
|
||||
|
||||
<ButtonGroup>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
// import AttackItem from "@/components/AttackItem.vue";
|
||||
// import AttackList from "@/components/AttackList.vue";
|
||||
import RefFigure from "@/components/RefFigure.vue";
|
||||
import RefFigure from '@/components/RefFigure.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -9,27 +9,27 @@ import RefFigure from "@/components/RefFigure.vue";
|
|||
<h1>Overdrive Form</h1>
|
||||
</section>
|
||||
|
||||
<RefFigure id="overdrive-ref" polaroidBorder>
|
||||
<RefFigure id="overdrive-ref" cornerText>
|
||||
<!-- max 1080px -->
|
||||
<template #img>
|
||||
<picture>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/sebin-overdrive_ref-hires.jpg?w=450;500;600;900;0&avif&quality=75&withoutEnlargement&srcset
|
||||
@/assets/refs/sebin-overdrive_ref-hires.jpg?w=450;500;600;900;0&format=avif&quality=75&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 450px"
|
||||
type="image/avif"
|
||||
/>
|
||||
<source
|
||||
srcset="
|
||||
@/assets/refs/sebin-overdrive_ref-hires.jpg?w=450;500;600;900;0&webp&quality=100&withoutEnlargement&srcset
|
||||
@/assets/refs/sebin-overdrive_ref-hires.jpg?w=450;500;600;900;0&format=webp&quality=100&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 450px"
|
||||
type="image/webp"
|
||||
/>
|
||||
<img
|
||||
srcset="
|
||||
@/assets/refs/sebin-overdrive_ref-hires.jpg?w=450;500;600;900;0&jpg&quality=95&withoutEnlargement&srcset
|
||||
@/assets/refs/sebin-overdrive_ref-hires.jpg?w=450;500;600;900;0&format=jpg&quality=95&withoutEnlargement&as=srcset
|
||||
"
|
||||
sizes="(min-width: 120em) 700px, (min-width: 80em) 600px, (min-width: 35em) 500px, 450px"
|
||||
alt="Sebin in Overdrive"
|
||||
|
@ -45,34 +45,30 @@ import RefFigure from "@/components/RefFigure.vue";
|
|||
|
||||
<section>
|
||||
<p>
|
||||
Sebin can enter an Overdrive Form which greatly increases his strength and
|
||||
abilities but it comes at a cost.
|
||||
Sebin can enter an Overdrive Form which greatly increases his strength and abilities but it
|
||||
comes at a cost.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
He enters Overdrive by engulfing himself in a pillar of flames which
|
||||
transforms his exterior physique. During Overdrive transformation his hair
|
||||
ignites and flickers with blue flames. The corners of his eyes flicker
|
||||
with long green flames. His arms and legs become part carbon black and are
|
||||
crossed by several glowing veins which pulsate like flowing lava. Fire in
|
||||
this form burns several degrees hotter than usual because his body becomes
|
||||
a living blast furnace, which is why his limbs have to be of more
|
||||
fire-proof material to withstand the increased heat.
|
||||
He enters Overdrive by engulfing himself in a pillar of flames which transforms his exterior
|
||||
physique. During Overdrive transformation his hair ignites and flickers with blue flames. The
|
||||
corners of his eyes flicker with long green flames. His arms and legs become part carbon black
|
||||
and are crossed by several glowing veins which pulsate like flowing lava. Fire in this form
|
||||
burns several degrees hotter than usual because his body becomes a living blast furnace, which
|
||||
is why his limbs have to be of more fire-proof material to withstand the increased heat.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To complete the transformation he inhales the flames from the pillar
|
||||
surrounding him which heats up his body from within. Even if Sebin is a
|
||||
fire dragon who can sustain high degrees of heat he is essentially
|
||||
overheating himself from the inside. Because of this he can't maintain
|
||||
this form for more than a few hours before he does permanent damage to his
|
||||
own body.
|
||||
To complete the transformation he inhales the flames from the pillar surrounding him which
|
||||
heats up his body from within. Even if Sebin is a fire dragon who can sustain high degrees of
|
||||
heat he is essentially overheating himself from the inside. Because of this he can't maintain
|
||||
this form for more than a few hours before he does permanent damage to his own body.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Direct body contact with Sebin during overdrive causes 3rd degree burns as
|
||||
he emits an extreme temperature, although less than he keeps inside his
|
||||
body. His immediate surroundings are likely to burn or melt.
|
||||
Direct body contact with Sebin during overdrive causes 3rd degree burns as he emits an extreme
|
||||
temperature, although less than he keeps inside his body. His immediate surroundings are
|
||||
likely to burn or melt.
|
||||
</p>
|
||||
|
||||
<h2>Attacks</h2>
|
||||
|
@ -88,75 +84,69 @@ import RefFigure from "@/components/RefFigure.vue";
|
|||
<tr>
|
||||
<td>Fire Breath (improved)</td>
|
||||
<td>
|
||||
The reach of Sebin's Fire Breath increases as well as the frequency
|
||||
at which he can fire shots from his mouth.
|
||||
The reach of Sebin's Fire Breath increases as well as the frequency at which he can fire
|
||||
shots from his mouth.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Flame Toss (improved)</td>
|
||||
<td>
|
||||
Overdrive Form eliminates the need for Sebin to spit fire into his
|
||||
palms. It instead enables him to fire the shots directly from the
|
||||
palm palm of his hands, as the firey veins crossing his arms act as
|
||||
an orifice to do so. The explosion radius of the burning projectiles
|
||||
that explode on impact is greatly increased.
|
||||
Overdrive Form eliminates the need for Sebin to spit fire into his palms. It instead
|
||||
enables him to fire the shots directly from the palm palm of his hands, as the firey
|
||||
veins crossing his arms act as an orifice to do so. The explosion radius of the burning
|
||||
projectiles that explode on impact is greatly increased.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Kindled Fist (improved)</td>
|
||||
<td>
|
||||
As his arms and legs are infused with fire his punches and kicks
|
||||
exert trails of flames while doing so. Landing a punch or kick sears
|
||||
enemies.
|
||||
As his arms and legs are infused with fire his punches and kicks exert trails of flames
|
||||
while doing so. Landing a punch or kick sears enemies.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Searing Discus</td>
|
||||
<td>
|
||||
Overdrive allows Sebin to form rings of fire by igniting flames from
|
||||
his fingertips and swirling them in a circle motion. He can use them
|
||||
for both close quarters or ranged combat.
|
||||
Overdrive allows Sebin to form rings of fire by igniting flames from his fingertips and
|
||||
swirling them in a circle motion. He can use them for both close quarters or ranged
|
||||
combat.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Combustion Flare</td>
|
||||
<td>
|
||||
Clinking both of his wrists against each other like flints unleashes
|
||||
a devestating fire blast from both of his fire-infused hands. A
|
||||
secure foothold is needed to prevent Sebin from being thrown back by
|
||||
the recoil of the attack. Using this technique in the air is
|
||||
Clinking both of his wrists against each other like flints unleashes a devestating fire
|
||||
blast from both of his fire-infused hands. A secure foothold is needed to prevent Sebin
|
||||
from being thrown back by the recoil of the attack. Using this technique in the air is
|
||||
therefore highly risky.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Blazing Pandemonium</td>
|
||||
<td>
|
||||
A heavy impact into the ground from a great height with both fists,
|
||||
tearing deep cracks in the ground around the impact crater. Combined
|
||||
with
|
||||
A heavy impact into the ground from a great height with both fists, tearing deep cracks
|
||||
in the ground around the impact crater. Combined with
|
||||
<strong><em>Kindled Fist</em></strong>
|
||||
the heat in Sebin's arms are forced through the newly created
|
||||
furrows, transforming the scene into an inferno.
|
||||
the heat in Sebin's arms are forced through the newly created furrows, transforming the
|
||||
scene into an inferno.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Scorching Edge</td>
|
||||
<td>
|
||||
A fiery blade towering several meters into the air that Sebin sends
|
||||
careening towards his enemies from his fire-infused legs with a
|
||||
backflip kick, leaving a swath of destruction in its wake. Upon
|
||||
impact the force of the attack is distributed sideways.
|
||||
A fiery blade towering several meters into the air that Sebin sends careening towards
|
||||
his enemies from his fire-infused legs with a backflip kick, leaving a swath of
|
||||
destruction in its wake. Upon impact the force of the attack is distributed sideways.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Circling Fire Shield</td>
|
||||
<td>
|
||||
A rather defensive technique. By spinning around with stretched out
|
||||
arms Sebin creates fire balls, which he usually hurls towards
|
||||
enemies, that circle around his body diagonally. They act as a
|
||||
shield while he can still move his arms relatively freely. Enemies
|
||||
would be well advised to keep their distance to this spinning
|
||||
shield, as the fire balls will still explode on contact.
|
||||
A rather defensive technique. By spinning around with stretched out arms Sebin creates
|
||||
fire balls, which he usually hurls towards enemies, that circle around his body
|
||||
diagonally. They act as a shield while he can still move his arms relatively freely.
|
||||
Enemies would be well advised to keep their distance to this spinning shield, as the
|
||||
fire balls will still explode on contact.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
14
tsconfig.app.json
Normal file
14
tsconfig.app.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["globals.d.ts", "env.d.ts", "src/**/*", "src/**/*.vue", "package.json"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.node.json",
|
||||
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "playwright.config.*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
|
@ -1,16 +1,11 @@
|
|||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.web.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.config.json"
|
||||
"path": "./tsconfig.node.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
20
tsconfig.node.json
Normal file
20
tsconfig.node.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"extends": "@tsconfig/node20/tsconfig.json",
|
||||
"include": [
|
||||
"globals.d.ts",
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*",
|
||||
"nightwatch.conf.*",
|
||||
"playwright.config.*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
|
||||
"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: '/sebin/',
|
||||
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: "/sebin/",
|
||||
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