Project restructuring

Moving all website resources into `/src/` so separate metadata files
and build artifacts from core project files.
This commit is contained in:
April Eaton 2025-12-18 12:24:05 +01:00
parent cdd0359501
commit 3a8c31de81
22 changed files with 33 additions and 0 deletions

1
src/footer/footer.css Normal file
View file

@ -0,0 +1 @@
/* Footer CSS */

1
src/footer/footer.html Normal file
View file

@ -0,0 +1 @@
<!-- Site footer -->

35
src/gear/gear.css Normal file
View file

@ -0,0 +1,35 @@
/* Gear-page specific CSS */
.gl-item {
list-style-type: none;
list-style-position: unset;
margin: 0.25rem;
padding: 0.25rem;
border: solid 2px #7dc4e4;
}
.gl-item-list {
padding: 0rem;
}
.gl-item-name {
padding: 0.25rem;
margin: 0;
font-size: 18pt;
font-style: bold;
}
.gl-item-desc {
padding: 0.25rem;
margin: 0;
font-size: 16pt;
font-style: normal;
}
.gl-divider {
color: #c6a0f6;
width: calc(100% - 0.5rem);
size: 2px;
margin: 0.25rem;
}

44
src/gear/gear.json Normal file
View file

@ -0,0 +1,44 @@
[
{
"name": "Bondage",
"id": "bondage",
"values": [
{
"name": "Rope",
"id": "rope",
"desc": "10m pink, black, or purple"
},
{
"name": "Collars",
"id": "collars",
"desc": "Lots of 'em"
}
]
},
{
"name": "Clothes",
"id": "clothes",
"values": []
},
{
"name": "Gags",
"id": "gags",
"values": []
},
{
"name": "Sensory Deprevation",
"id": "sensory-deprevation",
"values": []
},
{
"name": "Food Play",
"id": "food-play",
"values": [
{
"name": "Pet Bowl",
"id": "pet-bowl",
"desc": "A small dog bowl, ideal for kibble or other mostly solid foods"
}
]
}
]

127
src/gear/gear.mjs Normal file
View file

@ -0,0 +1,127 @@
// Gear list JS
// @ts-check
/**
* @typedef {{name: string, id: string, desc: string}} GearListItem
*
* @typedef {{name: string, id: string, values: GearListItem[]}} GearListSection
*
* @typedef {GearListSection[]} GearList
*/
/**
* Fetches the gear.json to be parsed seperately
* @returns {Promise<GearList>}
*/
async function getGearList() {
try {
const response = await fetch("/gear/gear.json");
const data = await response.json();
return data;
} catch (error) {
console.error("Error fetching JSON:", error);
throw "Could not load gear.json";
}
}
/**
* @param {GearListItem} gear
* @param {string} group
* @returns {HTMLLIElement}
*/
function gearToElement(gear, group) {
let li = document.createElement("li");
li.setAttribute("id", `gl-${group}-${gear.id}`);
li.setAttribute("class", "gl-item");
let heading = document.createElement("h3");
heading.setAttribute("class", "gl-item-name");
heading.textContent = gear.name;
li.appendChild(heading);
let description = document.createElement("p");
description.setAttribute("class", "gl-item-desc");
description.textContent = gear.desc;
li.appendChild(description);
return li;
}
/**
* @param {GearListSection} gear
* @returns {HTMLLIElement[]}
*/
function gearListToList(gear) {
let lis = [];
for (const item of gear.values) {
lis.push(gearToElement(item, gear.id));
}
return lis;
}
/**
* @param {GearListSection} items
* @returns {HTMLUListElement}
*/
function bondageItemsToUl(items) {
let ul = document.createElement("ul");
ul.setAttribute("id", `gl-${items.id}-list`);
ul.setAttribute("class", "gl-item-list");
for (const i of gearListToList(items)) {
ul.appendChild(i);
}
return ul;
}
/**
* @param {string} position
* @returns {Promise<HTMLElement>}
*/
async function generateGearListArticle(position) {
let list = await getGearList();
let bondageItems = document.createElement("article");
bondageItems.setAttribute("id", position);
let heading = document.createElement("h2");
heading.textContent = "Gear List";
bondageItems.appendChild(heading);
let divider = document.createElement("hr");
divider.setAttribute("class", "gl-divider");
bondageItems.appendChild(divider);
for (const section of list) {
let listSection = buildSection(section);
bondageItems.appendChild(listSection);
let divider = document.createElement("hr");
divider.setAttribute("class", "gl-divider");
bondageItems.appendChild(divider);
}
return bondageItems;
}
/**
* @param {GearListSection} items
* @returns {HTMLElement}
*/
function buildSection(items) {
let section = document.createElement("article");
section.setAttribute("id", `gl-${items.id}-body`);
let heading = document.createElement("h3");
heading.textContent = items.name;
section.appendChild(heading);
let ul = bondageItemsToUl(items);
section.appendChild(ul);
return section;
}
/**
* @param {string} position
* @returns void
*/
export async function attachGearList(position) {
document
.getElementById(position)
?.replaceWith(await generateGearListArticle(position));
}

30
src/gear/index.html Normal file
View file

@ -0,0 +1,30 @@
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>Gearlist</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Truculenta:opsz,wght@12..72,100..900&display=swap"
rel="stylesheet"
/>
<link href="/global/global.css" rel="stylesheet" />
<link
rel="stylesheet"
href="/gear/gear.css"
type="text/css"
media="screen"
/>
<script type="module" src="/global/loadin.mjs"></script>
<script type="module" src="/gear/page.mjs"></script>
<script type="module" src="/gear/gear.mjs"></script>
</head>
<body>
<header id="hd-box"></header>
<article id="main"></article>
</body>
</html>

9
src/gear/page.mjs Normal file
View file

@ -0,0 +1,9 @@
import { attachGearList } from "/gear/gear.mjs";
import { loadHTML } from "/global/loadin.mjs";
export function loadDependencies() {
loadHTML("hd-box", "/header/header.html");
attachGearList("main");
}
loadDependencies();

43
src/global/global.css Normal file
View file

@ -0,0 +1,43 @@
/* Global CSS options */
body {
font-family: "Truculenta", sans-serif;
font-optical-sizing: auto;
font-weight: 500;
font-style: normal;
font-variation-settings: "wdth" 100;
font-size: 16pt;
margin: 0;
min-height: 100svh;
height: fit-content;
display: flex;
flex-direction: column;
align-items: center;
background-color: #1e2030;
color: #cad3f5;
}
#main {
max-width: 40rem;
width: 100%;
height: 100%;
margin: 0.25rem;
margin-top: 0;
padding: 0.25rem;
border-left: solid 2px #c6a0f6;
border-right: solid 2px #c6a0f6;
}
a {
color: #cad3f5;
}
code {
font-family: "Roboto Mono", monospace;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
font-size: 12pt;
background-color: #363a4f;
}

8
src/global/loadin.mjs Normal file
View file

@ -0,0 +1,8 @@
export function loadHTML(elementId, url) {
fetch(url)
.then((response) => response.text())
.then((data) => {
document.getElementById(elementId).innerHTML = data;
})
.catch((error) => console.error("Error loading HTML:", error));
}

53
src/header/header.css Normal file
View file

@ -0,0 +1,53 @@
/* Header CSS */
header {
font-family: "Roboto Mono", monospace;
font-optical-sizing: auto;
font-weight: 600;
font-style: normal;
font-size: 14pt;
background-color: #1e2030;
height: 1.5rem;
width: calc(100% - 1.5rem);
margin-bottom: 0.25rem;
margin-top: 0.5rem;
padding: 0.5rem;
border-width: 3px;
border-style: solid;
border-color: #c6a0f6;
position: sticky;
top: 0;
display: flex;
#hd-left {
width: min-width;
padding-left: 0.25rem;
padding-right: 0.25rem;
font-style: italic;
}
#hd-mid {
width: 100%;
}
#hd-right {
display: flex;
width: fit-content;
padding-left: 0.25rem;
padding-right: 0.25rem;
}
.spacer {
width: 2rem;
display: block;
}
a {
text-decoration: none;
color: #cad3f5;
}
}

16
src/header/header.html Normal file
View file

@ -0,0 +1,16 @@
<!-- Site header -->
<link
rel="stylesheet"
href="/header/header.css"
type="text/css"
media="screen"
/>
<div id="hd-left">nekoapril.blog</div>
<div id="hd-mid"></div>
<div id="hd-right">
<a href="/">Home</a>
<span class="spacer"></span>
<span>Kinks</span>
<span class="spacer"></span>
<a href="/gear">Gear</a>
</div>

1
src/index.css Normal file
View file

@ -0,0 +1 @@
/* Landing page CSS */

48
src/index.html Normal file
View file

@ -0,0 +1,48 @@
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>Kinklist</title>
<meta name="description" content="Personal kinklist and gearlist" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Truculenta:opsz,wght@12..72,100..900&display=swap"
rel="stylesheet"
/>
<link href="/global/global.css" rel="stylesheet" />
<script type="module" src="/global/loadin.mjs"></script>
<script type="module" src="/index.mjs"></script>
<!-- Place favicon.ico in the root directory -->
</head>
<body>
<!--[if lt IE 8]>
<p class="browserupgrade">
You are using an <strong>outdated</strong> browser. Please
<a href="http://browsehappy.com/">upgrade your browser</a> to improve
your experience.
</p>
<![endif]-->
<header id="hd-box"></header>
<article id="main">
<p>
Welcome to my personal kink and gear list webpage! Keep in mind that any
gear listed may or may not be available at any given time (in
particular, clothes may be dirty or consumable items like tape or
diapers may be out of stock).
</p>
<p>
Also, if you would like to use this site temeplate for yourself, please
go to
<a href="https://git.aprileaton.net/nekoapril.blog/kinklist"
>the git repository to fork it</a
>, as well as editing the <code>/gear/gear.json</code> and
<code>/kinks/kinks.json</code> to reflect your own kink and gear lists.
</p>
</article>
</body>
</html>

8
src/index.mjs Normal file
View file

@ -0,0 +1,8 @@
// Landing page JS
import { loadHTML } from "/global/loadin.mjs";
function loadDependencies() {
loadHTML("hd-box", "/header/header.html");
}
loadDependencies();

23
src/kinks/index.html Normal file
View file

@ -0,0 +1,23 @@
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>Kinklist</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<!-- Place favicon.ico in the root directory -->
</head>
<body>
<!--[if lt IE 8]>
<p class="browserupgrade">
You are using an <strong>outdated</strong> browser. Please
<a href="http://browsehappy.com/">upgrade your browser</a> to improve
your experience.
</p>
<![endif]-->
This will be my kinklist
</body>
</html>

1
src/kinks/kinks.css Normal file
View file

@ -0,0 +1 @@
/* Kink list specific CSS */

1
src/kinks/kinks.html Normal file
View file

@ -0,0 +1 @@
<!-- Kink list fragment -->

1
src/kinks/kinks.mjs Normal file
View file

@ -0,0 +1 @@
// Kinks page JS