Compare commits
6 commits
5755823792
...
f4bc779c43
Author | SHA1 | Date | |
---|---|---|---|
f4bc779c43 | |||
1143b57085 | |||
3efb80f45b | |||
3d32dd4ef7 | |||
301f5ae4da | |||
260b1dc978 |
10 changed files with 301 additions and 23 deletions
|
@ -6,8 +6,13 @@
|
|||
:alt="`${$data.firstName.toLowerCase()}'s ${name}`"
|
||||
:width="[250, 500]"
|
||||
sizes="(min-width: 64em) 500px, 250px"
|
||||
></eleventy-image
|
||||
><span class="no-image" webc:else>Here you would see an illustration… If I had one…</span>
|
||||
></eleventy-image>
|
||||
<p class="copyright" webc:if="alt">
|
||||
<template webc:nokeep @text="alt"></template>
|
||||
©
|
||||
<a :href="href" target="_blank" @text="artist"></a>
|
||||
</p>
|
||||
<span class="no-image" webc:else>Here you would see an illustration… If I had one…</span>
|
||||
<figcaption>
|
||||
<p @text="name"></p>
|
||||
<slot></slot>
|
||||
|
@ -16,18 +21,23 @@
|
|||
|
||||
<style webc:scoped="ability">
|
||||
:host {
|
||||
--grid-spacing: 1.5em;
|
||||
|
||||
display: grid;
|
||||
grid-template:
|
||||
'image' 15em
|
||||
'copyright' auto
|
||||
'caption' 1fr / 1fr;
|
||||
place-items: center;
|
||||
gap: 1.5em;
|
||||
|
||||
margin-block: 1.5em;
|
||||
margin-inline: 0;
|
||||
|
||||
@media (min-width: 45em) {
|
||||
grid-template: 'image caption' 1fr / 15em 1fr;
|
||||
grid-template:
|
||||
'image caption' 1fr
|
||||
'copyright caption' / 15em 1fr;
|
||||
column-gap: var(--grid-spacing);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,11 +64,22 @@
|
|||
text-wrap: balance;
|
||||
}
|
||||
|
||||
:host .copyright {
|
||||
grid-area: copyright;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:host figcaption {
|
||||
grid-area: caption;
|
||||
|
||||
display: grid;
|
||||
gap: 0.5em;
|
||||
|
||||
margin-block: var(--grid-spacing) 0;
|
||||
|
||||
@media (min-width: 45em) {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
:host figcaption p {
|
||||
|
|
110
src/components/analytics-banner.webc
Normal file
110
src/components/analytics-banner.webc
Normal file
|
@ -0,0 +1,110 @@
|
|||
<script src="https://ackee.sebin-nyshkim.net/tracker.js" webc:keep></script>
|
||||
|
||||
<script>
|
||||
const ackeeBanner = document.querySelector('#ackee-banner');
|
||||
const yesBtn = ackeeBanner.querySelector('.positive');
|
||||
const noBtn = ackeeBanner.querySelector('.negative');
|
||||
const confirmKey = 'ackeeDetailed';
|
||||
|
||||
yesBtn.addEventListener('click', () => localStorage.setItem(confirmKey, true));
|
||||
noBtn.addEventListener('click', () => localStorage.setItem(confirmKey, false));
|
||||
|
||||
if (localStorage.getItem(confirmKey) === null) {
|
||||
ackeeBanner.show();
|
||||
}
|
||||
|
||||
const ackeeServer = 'https://ackee.sebin-nyshkim.net';
|
||||
const ackeeDomainId = '76704028-959b-4bce-997b-5e1ca0c19aa7';
|
||||
const ackeeOpts = {
|
||||
detailed: localStorage.getItem(confirmKey) === 'true',
|
||||
ignoreLocalhost: false
|
||||
};
|
||||
|
||||
if (localStorage.getItem(confirmKey) === 'true') {
|
||||
const instance = ackeeTracker.create(ackeeServer, ackeeOpts);
|
||||
instance.record(ackeeDomainId);
|
||||
}
|
||||
</script>
|
||||
|
||||
<dialog id="ackee-banner" webc:root="override">
|
||||
<form method="dialog">
|
||||
<p class="message">
|
||||
<strong>📊 Analytics 💡</strong> May I collect some anonymized data about the device you use
|
||||
to view this site? I won't know who you are. See: <a href="/privacy">Privacy Policy</a>
|
||||
</p>
|
||||
<ref-button class="positive">Yeah sure</ref-button>
|
||||
<ref-button class="negative">Nope</ref-button>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<style webc:scoped="analytics-banner">
|
||||
:host {
|
||||
--clr-yes: oklch(65% 0.2 140deg);
|
||||
--clr-no: oklch(40% 0.2 40deg);
|
||||
|
||||
position: sticky;
|
||||
inset: auto 1em 1em 1em;
|
||||
|
||||
font-size: 0.875em;
|
||||
color: inherit;
|
||||
|
||||
background: linear-gradient(
|
||||
to bottom right,
|
||||
var(--clr-box-gradient-start) 0%,
|
||||
var(--clr-box-gradient-end) 50%
|
||||
);
|
||||
|
||||
margin: 1em;
|
||||
border: none;
|
||||
border-radius: 1em;
|
||||
padding: 0;
|
||||
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
:host form {
|
||||
--areas: 'message message' 'yes no';
|
||||
--gradient-dir: ellipse at bottom right;
|
||||
--gradient-start: var(--clr-quick-info-gradient-end);
|
||||
--gradient-end: var(--clr-quick-info-gradient-start);
|
||||
|
||||
display: grid;
|
||||
grid-template-areas: var(--areas);
|
||||
align-items: center;
|
||||
gap: 1em;
|
||||
|
||||
background: var(--clr-box-background);
|
||||
background: radial-gradient(
|
||||
var(--gradient-dir),
|
||||
var(--gradient-start) 70%,
|
||||
var(--gradient-end) 100%
|
||||
);
|
||||
|
||||
margin: var(--border-thin);
|
||||
border-radius: inherit;
|
||||
padding: 1em;
|
||||
|
||||
@media (min-width: 48em) {
|
||||
--areas: 'message yes no';
|
||||
}
|
||||
}
|
||||
|
||||
:host .message {
|
||||
grid-area: message;
|
||||
margin-block: 0;
|
||||
}
|
||||
|
||||
:host .positive {
|
||||
--gradient-base: var(--clr-yes);
|
||||
--clr-text: var(--theme-c-primary-100);
|
||||
|
||||
grid-area: yes;
|
||||
}
|
||||
|
||||
:host .negative {
|
||||
--gradient-base: var(--clr-no);
|
||||
--clr-text: var(--theme-c-primary-100);
|
||||
|
||||
grid-area: no;
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,5 @@
|
|||
<section webc:root="override">
|
||||
<navigation label="Character" :collection="char"></navigation>
|
||||
<navigation webc:if="char" label="Character" :collection="char"></navigation>
|
||||
|
||||
<slot></slot>
|
||||
</section>
|
||||
|
|
61
src/components/page-footer.webc
Normal file
61
src/components/page-footer.webc
Normal file
|
@ -0,0 +1,61 @@
|
|||
<script webc:setup>
|
||||
const feedbackLink = 'https://cloud.sebin-nyshkim.net/apps/forms/s/eHjsosE9FB4fCpjBt4mbPfje';
|
||||
</script>
|
||||
|
||||
<footer webc:root="override">
|
||||
<p>
|
||||
Made with ❤️ and <a href="https://11ty.dev" target="_blank">11ty</a><br />
|
||||
Icons by <a href="https://fontawesome.com/">Font Awesome</a>
|
||||
</p>
|
||||
<p>
|
||||
© <time @text="new Date().getFullYear()"></time> Sebin Nyshkim<br />
|
||||
Got
|
||||
<a :href="feedbackLink" target="_blank"> feedback </a>?<br />
|
||||
<a href="/privacy">Privacy Policy</a>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<style webc:scoped="footer">
|
||||
:host {
|
||||
--columns: 1fr;
|
||||
|
||||
grid-area: footer;
|
||||
display: grid;
|
||||
grid-template-columns: var(--columns);
|
||||
gap: 1em;
|
||||
width: 100%;
|
||||
max-width: 65ch;
|
||||
padding-inline: var(--inbox-spacing);
|
||||
|
||||
@media (min-width: 35em) {
|
||||
--columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
:host p {
|
||||
--text-align: center;
|
||||
|
||||
margin: 0;
|
||||
text-align: var(--text-align);
|
||||
|
||||
@media (min-width: 35em) {
|
||||
margin: revert;
|
||||
}
|
||||
}
|
||||
|
||||
:host p:first-child {
|
||||
text-align: var(--text-align);
|
||||
|
||||
@media (min-width: 35em) {
|
||||
--text-align: start;
|
||||
}
|
||||
}
|
||||
|
||||
:host p:last-child {
|
||||
text-align: var(--text-align);
|
||||
|
||||
@media (min-width: 35em) {
|
||||
--text-align: end;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -56,11 +56,15 @@
|
|||
|
||||
:host :where(picture, img) {
|
||||
grid-area: image;
|
||||
display: grid;
|
||||
grid-template-columns: subgrid;
|
||||
justify-items: center;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
:host .image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
max-height: 35rem;
|
||||
object-fit: contain;
|
||||
|
@ -72,7 +76,7 @@
|
|||
}
|
||||
|
||||
:host .dropshadow {
|
||||
filter: drop-shadow(0.5em 0.25em 0.375em oklch(0 0 0 / 0.5));
|
||||
filter: drop-shadow(0.5em 0.25em 0.375em var(--clr-box-shadow));
|
||||
}
|
||||
|
||||
:host .caption {
|
||||
|
|
|
@ -14,5 +14,8 @@
|
|||
<link rel="stylesheet" :href="getBundleFileUrl('css')" webc:keep />
|
||||
<script type="module" :src="getBundleFileUrl('js')" webc:keep></script>
|
||||
</head>
|
||||
<body @raw="content"></body>
|
||||
<body>
|
||||
<template @raw="content" webc:nokeep></template>
|
||||
<analytics-banner></analytics-banner>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
layout: base.webc
|
||||
feedbackLink: https://cloud.sebin-nyshkim.net/apps/forms/s/eHjsosE9FB4fCpjBt4mbPfje
|
||||
---
|
||||
|
||||
<popup-modal id="nsfw-warning" :open="isOpen">
|
||||
|
@ -32,16 +31,7 @@ feedbackLink: https://cloud.sebin-nyshkim.net/apps/forms/s/eHjsosE9FB4fCpjBt4mbP
|
|||
<template @raw="content" webc:nokeep></template>
|
||||
</page-content>
|
||||
|
||||
<footer>
|
||||
<p>
|
||||
Made with ❤️ and <a href="https://11ty.dev" target="_blank">11ty</a><br />
|
||||
Icons by <a href="https://fontawesome.com/">Font Awesome</a>
|
||||
</p>
|
||||
<p>
|
||||
© <time @text="new Date().getFullYear()"></time> Sebin Nyshkim<br />
|
||||
Got <a :href="feedbackLink" target="_blank">feedback</a>?
|
||||
</p>
|
||||
</footer>
|
||||
<page-footer></page-footer>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
|
|
44
src/layouts/page.webc
Normal file
44
src/layouts/page.webc
Normal file
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
layout: base.webc
|
||||
---
|
||||
|
||||
<main>
|
||||
<page-content>
|
||||
<h1 @text="$data.title || $data.eleventyNavigation.key"></h1>
|
||||
<template @raw="content" webc:nokeep></template>
|
||||
</page-content>
|
||||
|
||||
<page-footer></page-footer>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
main {
|
||||
--inbox-spacing: 1rem;
|
||||
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
justify-items: center;
|
||||
align-items: start;
|
||||
gap: 1em;
|
||||
grid-auto-columns: 100%;
|
||||
grid-template-areas:
|
||||
'content'
|
||||
'footer';
|
||||
|
||||
margin-block: var(--container-spacing-top-safe) var(--container-spacing-bottom-safe);
|
||||
margin-inline: var(--container-spacing-left-safe) var(--container-spacing-right-safe);
|
||||
|
||||
margin-block: var(--page-spacing);
|
||||
|
||||
@media (min-width: 64em) {
|
||||
--inbox-spacing: 1.5rem;
|
||||
|
||||
grid-auto-columns: revert;
|
||||
grid-template-columns: minmax(0, 65ch);
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-inline: var(--inbox-spacing);
|
||||
}
|
||||
</style>
|
45
src/privacy.md
Normal file
45
src/privacy.md
Normal file
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
layout: 'page.webc'
|
||||
title: Privacy Policy
|
||||
---
|
||||
|
||||
I use a self-hosted [Ackee](https://ackee.electerious.com) instance to gain insights about the types of devices that visit this site.
|
||||
|
||||
Ackee is open-source analytics software. The data it collects is anonymized in a way that does not allow me to identify individual visitors. It also does not save any cookies and does not follow you around the web. I merely store whether you agree to analytics collection or not in your browser's local storage.
|
||||
|
||||
## Data that Ackee collects
|
||||
|
||||
Ackee gives me insight into the following data points:
|
||||
|
||||
* Number of visits per day
|
||||
* Number of visitors that currently view the site
|
||||
* Approximate duration of stay
|
||||
* Number of times a given page was visited
|
||||
* Sites a visit originated from (referrer)
|
||||
* Name and manufacturer of the device
|
||||
* Name and version of the operating system
|
||||
* Name and version of the browser
|
||||
* Screen size of the device
|
||||
* Primary language of the browser or operating system
|
||||
|
||||
Ackee uses the IP, user-agent and domainId to identify a user. All information will be hashed together with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography)) that changes daily. The final hash is called `clientId`.
|
||||
|
||||
The daily salt is never stored anywhere. It avoids that database backups can be used to stick data together to reconstruct the browsing history of a user.
|
||||
|
||||
## Purpose of collecting data
|
||||
|
||||
I collect this data to better understand my audience, improve the site's user experience, and to inform future development and editorial decisions. The data will not be shared with anyone, ever.
|
||||
|
||||
## Data retention
|
||||
|
||||
Ackee removes data from previous records when a new record with an existing identification gets added. This way the user identifier and other identifiable data is only stored once in the database.
|
||||
|
||||
In other words: Ackee forgets who you are as soon as it sees you again. It's not possible to reconstruct a browsing history, even on a daily basis.
|
||||
|
||||
## User consent
|
||||
|
||||
Data collection is opt-in. I will not collect any analytics data until you explicitly allow me to do so. If you previously opted-in but changed your mind, click the button below:
|
||||
|
||||
<button onclick="localStorage.setItem('ackeeDetailed', false)" class="items-center bg-sky-600 *:stroke-[2.5] 2xl:px-6 2xl:py-3 2xl:rounded-2xl button duration-300 font-bold gap-2 hover:bg-sky-700 inline-flex no-underline px-5 py-2 rounded-xl text-white transition-colors">Opt-out</button>
|
||||
|
||||
By using this site, you acknowledge that you have read and understood this privacy policy. If you have any questions or concerns about how your data is collected or used, please feel free to [contact me](https://sebin-nyshkim.net/contact).
|
|
@ -8,18 +8,18 @@ As a fire dragon, Sebin possesses a myriad of abilities that he can unleash upon
|
|||
|
||||
## Attacks
|
||||
|
||||
<ability :@src="`attack-fire-breath.png`" @name="Fire Breath">
|
||||
<ability :@src="`attack-fire-breath.png`" @alt="Illustration" @href="`https://bsky.app/profile/riversausage.bsky.social`" @artist="River Sausage" @name="Fire Breath">
|
||||
The signature move of any fire dragon. Special glands in his mouth together with a deep, deliberate breath to enrich his lungs create a combustible cocktail. The resulting jet of fire upon exhaling is a torrent of several hundred degrees Celsius, scorching everything in its wake.
|
||||
</ability>
|
||||
|
||||
<ability :@src="`attack-flame-toss.png`" @name="Flame Toss">
|
||||
<ability :@src="`attack-flame-toss.png`" @alt="Illustration" @href="`https://bsky.app/profile/riversausage.bsky.social`" @artist="River Sausage" @name="Flame Toss">
|
||||
When there isn't a direct line of sight between Sebin and his opponent, he can spit fire into his hands and shape it into a throwable projectile. His scales are naturally fireproof, allowing him to withstand the intense heat without flinching. Even if he misses his target, the resulting blast upon impact is enough to cause considerable damage alone—especially when he combines two projectiles to increase the explosion radius.
|
||||
</ability>
|
||||
|
||||
<ability :@src="`attack-kindled-fist.png`" @name="Kindled Fist">
|
||||
<ability :@src="`attack-kindled-fist.png`" @alt="Illustration" @href="`https://bsky.app/profile/riversausage.bsky.social`" @artist="River Sausage" @name="Kindled Fist">
|
||||
Sebin's fists become instruments of destruction when he engulfs them in his flames. Targeted blows are capable of inflicting agonizing burns on his opponents. He can also release controlled blasts of fire from his fists, unleashing smoldering waves of heat to scorch and disorient his foes.
|
||||
</ability>
|
||||
|
||||
<ability :@src="`attack-burning-twister.png`" @name="Burning Twister">
|
||||
<ability :@src="`attack-burning-twister.png`" @alt="Illustration" @href="`https://bsky.app/profile/riversausage.bsky.social`" @artist="River Sausage" @name="Burning Twister">
|
||||
This aerial attack is a masterpiece of Sebin's pyromancy skills. By engulfing himself in flames while spinning at breakneck speeds in mid-air, he creates a whirling vortex of fire that can singe even the most elusive opponents. Those who dare to face him in the skies will soon find themselves caught in the maelstrom of his most fearsome attack.
|
||||
</ability>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue